Skip to content
Pickode
IT – Italiano
Pickode

Blog

How to Create a shadcn Theme From Any Website

A shadcn theme is a set of CSS variables in globals.css. Learn how to take a website's colors, radius and type and map them to shadcn variables, light and dark.

By Pickode8 min read

A shadcn theme is a set of CSS variables, usually in globals.css, that every shadcn/ui component reads: --background, --foreground, --primary, --primary-foreground, --border, --ring, --radius and a few more, defined once for light mode and once for dark mode. To create a shadcn theme from any website, you collect that site’s design tokens (colors by role, radius, type), map each one to the matching variable, and add a dark variant. You can do it by hand with DevTools, or extract the tokens with a tool and adjust the result.

This guide explains how shadcn theming works, how to read a site’s tokens manually, how to speed it up with Pickode, and what to check before you ship.

What a shadcn theme is

shadcn/ui isn’t a component library you install as a dependency: the CLI copies component source files into your project. Those components don’t hard-code colors. They use Tailwind utility classes such as bg-primary, text-muted-foreground or border-input, and those utilities point to CSS variables. Change the variables and every button, card, dialog and input follows.

In current projects that use Tailwind CSS v4, the variables live in globals.css: plain values in :root and .dark, then an @theme inline block that exposes them to Tailwind as colors and radii. Here’s a shortened example with illustrative values for a blue brand:

@import "tailwindcss";

@custom-variant dark (&:is(.dark *));

:root {
  --radius: 0.5rem;
  --background: oklch(0.99 0 0);
  --foreground: oklch(0.21 0.02 260);
  --primary: oklch(0.55 0.2 260);
  --primary-foreground: oklch(0.99 0 0);
  --muted: oklch(0.96 0.01 260);
  --muted-foreground: oklch(0.5 0.02 260);
  --border: oklch(0.91 0.01 260);
  --input: oklch(0.91 0.01 260);
  --ring: oklch(0.55 0.2 260);
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
}

A real file has more pairs (card, popover, secondary, accent, destructive, chart and sidebar colors). Older projects on Tailwind v3 store colors as HSL channels and reference them from tailwind.config.js; the idea is the same.

When people search for “shadcn colors” they often mean the base colors the CLI offers at setup (neutral, gray, zinc, stone, slate). Those are only starting palettes: a theme from a real website replaces them with the brand’s own values.

Design tokens 101: color, radius, type

Design tokens are the named decisions behind a visual style: “primary is this blue”, “cards have an 8px radius”, “body text is 16px Inter”. A website rarely publishes them, but it always applies them, and a shadcn theme is basically design tokens written in shadcn’s vocabulary.

For a theme you need three groups:

  • Colors by role: background, text, primary, secondary, accent, error, border, and the text color that sits on top of each filled color.
  • Shape: the corner radius of buttons, inputs and cards. shadcn derives smaller and larger radii from a single --radius.
  • Type: font families and the type scale. shadcn doesn’t define fonts in its color variables, so you set them separately, for example as --font-sans in your Tailwind theme.

Spacing and shadows matter for a faithful look, but shadcn components use Tailwind’s own spacing scale, so they’re usually optional in the theme.

The manual method: read the tokens with DevTools

You can collect everything with the browser’s developer tools:

  1. Look for existing variables. Inspect the html or body element and check the Styles pane for custom properties on :root. Many modern sites already define --color-primary or similar.

  2. Sample the roles. Inspect the page background, body text, a primary button, a secondary button, a link, an input border and an error message. Note each color, background-color and border-color.

  3. Find the radius. Inspect a button and a card and note border-radius. If they differ, pick the card radius as --radius and check how the button compares.

  4. Note the fonts. Check font-family on headings and body text, and the sizes of the main headings.

  5. Write globals.css. Convert the values to the format you prefer (any valid CSS color works, oklch is what recent shadcn templates use) and fill in the variables.

It works, but it’s slow and easy to get wrong. The Computed tab shows resolved values, so you see the color but not its intent; hover and focus colors only appear when you force those states; and a site may use five slightly different grays where you need one --border. On a large site, finding every role can take an hour.

Extract a site’s tokens with Pickode

Pickode’s design tokens window does the collecting for you. Pickode is a browser extension: open any page, open Design tokens from its panel, and it lists colors by role (primary, secondary, accent, success, error, background, text, border), brand colors, gradients, a spacing scale with 4/8 px grid detection, the type scale, radii, shadows and fonts.

From there you can export the tokens in the format your project uses: CSS variables, a Tailwind v4 @theme, a tailwind.config.js, a globals.css for shadcn, a WordPress theme.json or JSON. For a shadcn project, export the globals.css and drop it into your project, then review it with the mapping below.

If you’re building with an AI assistant, Copy for AI gives you the tokens (or a whole component with the site’s tokens, breakpoints and states) as a Markdown brief for Claude, ChatGPT, Cursor or v0. It only contains facts found on the page, so you can ask your assistant to apply them to your shadcn variables without it guessing the colors.

Viewing, copying and downloading the tokens is free. See pricing and the design tokens docs for details.

Map tokens to shadcn variables

Whether you collected the tokens by hand or with a tool, this table is the core of the job:

Site role shadcn variable Notes
Page background --background Also a good default for --card and --popover
Body text --foreground Also --card-foreground and --popover-foreground
Primary (main buttons, links) --primary The brand color in most themes
Text on primary buttons --primary-foreground Usually white or a very dark tone
Secondary buttons --secondary and --secondary-foreground Often a light tint of the primary or a neutral
Subtle backgrounds, captions --muted and --muted-foreground Muted text is where contrast often fails
Accent --accent and --accent-foreground In the default components it’s mostly used for hover backgrounds in menus and lists, not for big brand areas
Error --destructive Used by destructive buttons and form errors
Borders and inputs --border and --input Pick one neutral gray, even if the site uses several
Focus outline --ring Often the primary color, or a lighter version of it
Corner radius --radius One value; smaller and larger radii are derived from it

Two roles need a decision. Success has no variable in the default shadcn set: if your app needs it, add --success and --success-foreground in :root and .dark, then map --color-success in @theme inline. Gradients don’t map to a single variable either: keep them as separate custom properties and use them where the original site does.

Light and dark mode with oklch and Tailwind v4 @theme

Most websites have only a light theme, so the dark values are usually yours to design. A practical approach with oklch, which describes a color as lightness, chroma and hue:

  • Keep the hue of each brand color, and adjust lightness and chroma for the dark background.
  • Swap the neutrals: --background becomes very dark, --foreground very light, and --border a low-lightness gray.
  • Lighten the primary a little if it looks heavy on dark backgrounds, and check that --primary-foreground still reads well.
.dark {
  --background: oklch(0.18 0.02 260);
  --foreground: oklch(0.97 0 0);
  --primary: oklch(0.68 0.17 260);
  --primary-foreground: oklch(0.18 0.02 260);
  --muted: oklch(0.26 0.02 260);
  --muted-foreground: oklch(0.72 0.02 260);
  --border: oklch(0.3 0.02 260);
  --input: oklch(0.3 0.02 260);
  --ring: oklch(0.68 0.17 260);
}

Because the @theme inline block only references variables, you don’t repeat it for dark mode: the .dark class swaps the values and every utility follows.

Check contrast before you ship

A theme copied from a website can still fail accessibility in your app: you may use the brand color for small text the original site only used on large buttons, or put muted text on a muted background. WCAG 2.2 level AA asks for a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text, and 3:1 for the visual boundaries of interface components, which includes focus indicators.

Check at least these pairs, in light and dark mode: --foreground on --background, --primary-foreground on --primary, --muted-foreground on --background and on --muted, and --ring against the background.

Once your themed app is running in the browser, Pickode’s contrast map marks every text that fails against its actual background, with AA and AAA thresholds, and suggests the nearest color that passes. It’s free, and the full accessibility audit runs axe-core against WCAG 2.2 AA.

Limits and good practice

  • Token extraction reads what the page actually uses. Treat the role assignment as a starting point and check it by eye, especially for sites with many similar grays.
  • Pickode doesn’t work on chrome:// pages, the Chrome Web Store or PDFs.
  • Use this to rebuild the look of sites you work on, to migrate a client’s brand or to study a style. Don’t clone another company’s brand identity for your own product.

FAQ

Where are shadcn theme colors defined?

In your global stylesheet, usually globals.css (the path is set in components.json). Colors are CSS variables in :root for light mode and .dark for dark mode; with Tailwind v4 an @theme inline block exposes them as utilities like bg-primary.

Can I use hex colors instead of oklch?

Yes. The variables accept any valid CSS color. Recent shadcn templates use oklch because it makes lighter and darker variants of the same hue more predictable, but hex or rgb work too.

Can I get a shadcn theme from a website automatically?

With Pickode you can export a site’s tokens as a globals.css for shadcn and drop it into your project. Review the mapping (especially accent, muted and border) and design the dark mode values yourself if the site has no dark theme.

What’s the difference between design tokens and a shadcn theme?

Design tokens are the brand’s decisions in a neutral form: colors by role, radii, type, spacing. A shadcn theme is one way to write some of those tokens, using the variable names shadcn components expect.

If you want to try it on a site you work on, install Pickode, open Design tokens on the page and compare what it finds with the table above.

Stop rebuilding. Start picking.

Install Pickode, open any site and press Alt+I.

Free · 7-day Pro trial · No account needed