How to make a shadcn/ui theme from any website
Updated 3 November 2026. Every command and output on this page is real.
To make a shadcn/ui theme from a website, run npx designlang <url> --framework shadcn; it maps the site’s extracted background, foreground, primary, secondary, accent, muted and border colours and its radius onto shadcn’s CSS variables, ready to paste into globals.css.
Steps
- Extract the site with the shadcn emitter. Run npx designlang stripe.com --framework shadcn.
- Open the theme file. Open design-extract-output/stripe-com-shadcn-theme.css. It holds a :root block of shadcn variables.
- Paste it into globals.css. Replace the :root variables in your shadcn globals.css with the generated ones.
- Check contrast and dark mode. Check the foreground pairs for WCAG contrast and rerun with --dark if the site has a dark theme.
shadcn/ui is the default component kit for a lot of AI-generated UI. v0, Cursor and Claude all reach for it, which is why so many new apps share the same neutral look. The components are fine. The theme is the part that makes them yours, and a shadcn theme is just a handful of CSS variables.
designlang fills those variables from a real site. It reads the brand colours, the page surface, body text, muted surfaces, borders and the common corner radius, and writes them in shadcn’s format.
Generate it
npx designlang stripe.com --framework shadcnThis is the complete theme file it wrote for stripe.com. Captured with designlang 13.3.2 on 3 November 2026.
/* shadcn/ui Theme — extracted from https://stripe.com */
/* Paste into your globals.css */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 211 78% 11%;
--primary: 248 98% 61%;
--primary-foreground: 0 0% 100%;
--secondary: 211 78% 11%;
--secondary-foreground: 0 0% 100%;
--accent: 151 100% 42%;
--accent-foreground: 0 0% 100%;
--muted: 215 86% 97%;
--muted-foreground: 0 0% 0%;
--border: 218 18% 57%;
--radius: 8px;
}
}Each value is an HSL channel triplet: 248 98% 61% is #533afd, Stripe’s purple, and 211 78% 11% is the deep navy #061b31 used for body text.
Apply it
Open the globals.css that shadcn created and replace the variables inside :root with the generated ones. Every component that reads --primary, --border or --radius now uses the new values. Buttons, inputs, cards, dialogs and badges all change at once, because shadcn components never hardcode colours.
If your project uses Tailwind v4
Newer shadcn projects on Tailwind v4 store full colour values (often oklch(...)) instead of bare channels, and read them with var(--primary). Wrap each generated triplet in hsl() when you paste it:
:root {
--primary: hsl(248 98% 61%);
--primary-foreground: hsl(0 0% 100%);
--radius: 8px;
}How the colours are chosen
| shadcn variable | Extracted from |
|---|---|
--background | The largest page surface. |
--foreground | The body text colour. |
--primary | The brand primary: the colour of main buttons and links. |
--secondary | The second brand colour. |
--accent | The highlight colour, if the site has one (#00d66f on stripe.com). |
--muted | A raised or tinted surface. |
--border | The most common border colour. |
--radius | The common control radius. |
Things to check
- Foreground pairs. The emitter picks a readable foreground for each colour, but always check. The
--accent-foregroundabove is white on a bright green, which is low contrast; the tokens file pairs the same accent with navy (onAccent: #061b31), which reads better. Swap it if you use the accent for text. - Variables the emitter does not write, such as
--card,--popover,--ring,--input,--destructiveand the chart colours, keep their shadcn defaults. Set--ringto the primary and--inputto the border for a closer match. - Dark mode. Rerun with
--darkto read the site’s dark theme, then put those values in your.darkblock. - Fonts. shadcn does not set a font. Use the family from
stripe-com-DESIGN.mdor the Tailwind file.
Going further than colours
A shadcn theme covers colour and radius. A brand also has a type scale, spacing rhythm, shadows and motion. For those, use the Tailwind output from the same run (see converting a website to a Tailwind theme) and the motion presets in stripe-com-motion.css.
Other frameworks get the same treatment: --framework react, --framework vue and --framework svelte write theme objects for those stacks.
A button before and after
Nothing in the component changes. This is the stock shadcn button markup, which reads the variables through Tailwind classes:
<Button>Start trial</Button>
// renders roughly as:
<button class="bg-primary text-primary-foreground rounded-md h-10 px-4 ...">Start trial</button>With the default theme, bg-primary is near-black. With the stripe.com theme it is #533afd, text-primary-foreground is white (6.19:1 contrast, which passes AA), and rounded-md follows the 8px --radius. Every variant (secondary, outline, ghost) shifts the same way, because they all read the same variables.
Choosing the source site
- For your own product, point it at your production marketing site or app. That is where the brand is most complete.
- For a new product with no site yet, read a few sites whose feel you want and compare them with
npx designlang brands a.com b.com c.combefore picking one to start from. - If the result picks the wrong primary, pass
--palette 8to merge near-duplicates, or edit the one variable by hand. It is a CSS file.
Keep AI-written components on the theme
AI editors often bypass the variables and write bg-[#3b82f6] straight into a component. Run npx designlang check src on the result to catch it, and give the editor your tokens through MCP so it uses bg-primary in the first place. The MCP guide shows the setup.