Home / Blog / Convert a website to Tailwind

How to Convert Any Website to Tailwind CSS

Published July 20, 2026 · Updated October 2, 2026 · 12 min read · by the CSS DNA team

Quick answer: To convert a website to Tailwind, extract its design tokens first (colors, font stacks, type sizes, radii, shadows) and write them into a theme, then rebuild components with utilities that reference that theme. In Tailwind v4 the theme is an @theme block in your CSS, where --color-brand becomes bg-brand. In v3 it is theme.extend in tailwind.config.js. Converting element by element from DevTools gives you arbitrary values and no system.

“Convert this site to Tailwind” usually means one of two jobs. Either you own the site and want to move its hand-written CSS onto Tailwind, or you like how someone else’s site looks and want its design language in your own Tailwind project. Both go the same way: get the tokens out, put them in a theme, then write markup. The order matters more than the tooling.

Two things people get wrong about Tailwind v4 themes

The first is that v4 “dropped” tailwind.config.js. It didn’t, but it stopped looking for it. The upgrade guide says JavaScript config files are still supported and no longer detected automatically; you load one with @config "../tailwind.config.js";. So a converter that hands a v4 project a config file has given you something that silently does nothing until you add that line. The corePlugins, safelist and separator options aren’t supported at all in v4.

The second is that you need to convert your palette to OKLCH. Tailwind’s own default palette is defined in OKLCH (--color-indigo-500: oklch(58.5% 0.233 277.117) in the theme docs), which is where the idea comes from. But @theme takes any CSS color. Opacity modifiers like bg-brand/50 compile to color-mix(in oklab, …) according to the functions and directives reference, and color-mix() works on hex input just as well. Keep the source site’s hex values if you want an exact match. Converting is worth it when you plan to generate tints and shades, because even steps in OKLCH lightness look even, which is not true of hex or HSL. The hex to OKLCH post covers that part.

Browser floor: Tailwind v4.0 targets Safari 16.4+, Chrome 111+ and Firefox 128+, because it depends on @property and color-mix(). If the site you’re converting must work in older browsers, the upgrade guide recommends staying on v3.4, and the config-file route below is the one to use.

Why converting element by element goes wrong

The obvious approach is to inspect an element, read its computed styles and guess the closest utility. It produces text-[17px] on one page and text-[1.06rem] on the next, three nearly identical purples, and padding values that are each a pixel off the scale. You get a Tailwind codebase that has no theme, which is harder to maintain than the CSS you started with. Tailwind assumes you define the system first and compose against it. A conversion should do the same.

Step 1: extract the tokens

If the site already uses CSS custom properties, most of the system is sitting in :root. This console snippet lists every custom property declared on :root or html in the stylesheets the page can read:

(() => {
  const out = {};
  for (const sheet of document.styleSheets) {
    let rules;
    try { rules = sheet.cssRules; } catch { continue; }   // cross-origin sheet
    for (const r of rules) {
      if (!r.style || !/^(:root|html)$/.test(r.selectorText)) continue;
      for (const p of r.style) if (p.startsWith("--")) out[p] = r.style.getPropertyValue(p).trim();
    }
  }
  console.log(Object.entries(out).map(([k, v]) => `${k}: ${v};`).join("\n"));
})();

It has three blind spots. Stylesheets served from another origin without CORS headers can’t be read. Variables declared under a theme class or inside a media query are skipped. And plenty of sites hard-code values instead of using variables at all, especially sites built on an older framework. For those, the tokens have to come from computed styles: which colors are actually painted, how often, and on what. The CSS variables post goes through four ways to do this, and extracting design tokens covers the computed-style route. For fonts, Font Finder lists every font a URL uses.

Step 2: map tokens to theme namespaces

Every v4 theme variable belongs to a namespace, and the namespace decides which utilities it creates. The v3 config keys map onto them almost one to one:

Tokenv3 theme.extend keyv4 @theme namespaceUtility you get
Colorscolors--color-*bg-brand, text-brand
Font stacksfontFamily--font-*font-display
Font sizesfontSize--text-* (and --text-*--line-height)text-lead
Font weightsfontWeight--font-weight-*font-heavy
Letter spacingletterSpacing--tracking-*tracking-tight
Line heightlineHeight--leading-*leading-relaxed
Spacingspacing--spacing base, or --spacing-*p-4, gap-gutter
RadiiborderRadius--radius-*rounded-card
ShadowsboxShadow--shadow-*shadow-raised
Breakpointsscreens--breakpoint-*tablet: variant
EasingtransitionTimingFunction--ease-*ease-snappy

Spacing is the one that changed shape. v4’s default theme defines a single --spacing: 0.25rem and computes p-4 as four of those, so any multiple works without being listed. If the source site uses a 4px grid, you don’t need to declare a spacing scale at all. Declare named spacing tokens only for values that sit off the grid.

CSS variables to Tailwind theme

Paste custom properties from any stylesheet. Each one is sorted into a Tailwind namespace by its name and value. Nothing leaves your browser.

Tailwind v4 · app.css

Tailwind v3 · tailwind.config.js

A worked example: this site’s own tokens

Rather than invent a brand, here is this blog itself. The stylesheet on this page declares these custom properties on :root, which you can check in the page source:

:root {
  --accent: #7c5cff;
  --bg: #0a0b10;
  --tx: #f2f2f7;
  --mut: #9193a6;
  --mut2: #7a7c92;
  --ok: #5bd6a3;
  --no: #ff8792;
}

That’s the colors, and the short names are a problem straight away. bg-bg and text-tx are legal and unreadable, so rename while you map. The rest of the system isn’t in variables at all. It’s hard-coded across rules: two font families, Space Grotesk and JetBrains Mono, radii of 9, 12, 14, 16 and 18px, and headings at 24 and 18px. Reading those took a pass through the stylesheet, which is the normal experience on a real site. Normalized, the v4 theme looks like this:

@import "tailwindcss";

@theme {
  --color-accent:  #7c5cff;
  --color-canvas:  #0a0b10;
  --color-ink:     #f2f2f7;
  --color-muted:   #9193a6;
  --color-subtle:  #7a7c92;
  --color-success: #5bd6a3;
  --color-danger:  #ff8792;

  --font-sans: "Space Grotesk", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", monospace;

  --text-h2: 24px;
  --text-h3: 18px;
  --text-lead: 18px;

  --radius-sm:   9px;
  --radius-md:   12px;
  --radius-lg:   16px;
  --radius-xl:   18px;
}

Two decisions are visible there. The 14px radius was dropped and its uses moved to 12 or 16, since five radii for one small site is drift, not design. And --font-sans overrides Tailwind’s default stack, so every element gets Space Grotesk without a class, which matches the original. The same theme for v3:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        accent: "#7c5cff",
        canvas: "#0a0b10",
        ink: "#f2f2f7",
        muted: "#9193a6",
        subtle: "#7a7c92",
        success: "#5bd6a3",
        danger: "#ff8792",
      },
      fontFamily: {
        sans: ["Space Grotesk", "system-ui", "sans-serif"],
        mono: ["JetBrains Mono", "monospace"],
      },
      fontSize: { h2: "24px", h3: "18px", lead: "18px" },
      borderRadius: { sm: "9px", md: "12px", lg: "16px", xl: "18px" },
    },
  },
};

If you’d rather work in OKLCH, the accent becomes oklch(59.9% 0.230 286.2) and the canvas oklch(15.1% 0.011 276.2). Those are the values the tool above produces with the OKLCH box ticked.

That pass through the stylesheet is the slow part, and it gets slower on a site that isn’t yours. CSS DNA, a browser extension for inspecting CSS, reads computed styles from the live page and ranks the palette by how often each color is used, which works on minified CSS and generated class names (free). Exporting the result as a Tailwind v4 @theme block or a v3 config is a Pro feature, $5/mo. Add CSS DNA to Chrome (free).

Step 3: normalize before you commit

An extraction mirrors every inconsistency in the source. Fixing them in the theme is cheap; fixing them after two hundred components reference them is not.

  • Collapse near-duplicate colors. If #6d4aff and #6c4bfe both appear, they were meant to be one color. The color palette post explains how to spot these by distance rather than by eye.
  • Build ramps from the core colors. One brand purple isn’t enough for hover states, borders and tinted backgrounds. Generate a 50 to 950 ramp in OKLCH so those shades come from the system.
  • Turn the measured type sizes into a scale. Sizes of 13, 14.5, 16, 22 and 34px usually want to become a deliberate ratio.
  • Reset namespaces you replace. --color-*: initial; at the top of @theme removes Tailwind’s default palette, so nobody can reach for bg-blue-500 by accident.

Step 4: rebuild components against the theme

With the theme in place, markup is mechanical. A button from the example site:

<a class="inline-flex items-center gap-2 rounded-md bg-accent px-5 py-3
          font-semibold text-white shadow-lg shadow-accent/40
          hover:bg-accent/90">
  Add to Chrome
</a>

Every value refers to a token, so changing --color-accent changes every button. The /40 and /90 modifiers work on the hex value through color-mix(), which is the point from the top of this post. When the theme doesn’t have a value you need, that’s a signal to add a token, not to reach for bg-[#7d5dff].

If you’re migrating an existing v3 project rather than starting from a site, Tailwind ships an upgrade tool, npx @tailwindcss/upgrade, that converts the config and renamed utilities for you. It needs the v3 project to start from, so it doesn’t help with a site you don’t own.

Step 5: check what you shipped

Conversions break contrast more often than layout. Colors that worked on one background get reused on another, and muted greys that were fine at 18px get used at 13px. Run text and background pairs through a contrast check before you call it done; the WCAG vs APCA post covers which standard to trust. If the converted site has a dark theme, the Tailwind v4 dark mode post covers wiring the toggle and redefining theme variables for it.

The options, compared

We make CSS DNA, so weigh that.

MethodGood forLimits
Reading DevTools by handOne component, or a site with a handful of tokensSlow, and you invent the system as you go
Console snippet plus the tool aboveSites that already use custom propertiesMisses hard-coded values and cross-origin sheets
npx @tailwindcss/upgradeMoving your own v3 project to v4Needs an existing Tailwind project
CSS DNAAny live site, including minified and framework CSSTailwind and other exports are Pro; it reads one page at a time

Frequently asked questions

How do I convert a website to Tailwind CSS?

Extract the site’s design tokens first: colors, font stacks, type sizes, radii and shadows. Put them into a Tailwind v4 @theme block or a v3 theme.extend config, normalize near-duplicates, then rebuild each component with utilities that reference those tokens. Starting from components instead of tokens produces arbitrary values everywhere.

Does Tailwind v4 still use tailwind.config.js?

Only if you load it. v4 configures the theme in CSS with @theme and doesn’t detect a JavaScript config automatically, but @config "./tailwind.config.js"; loads one for backward compatibility. The corePlugins, safelist and separator options aren’t supported in v4; use @source inline() to safelist utilities.

Do I have to use OKLCH colors in Tailwind v4?

No. Tailwind’s default palette is written in OKLCH, but @theme accepts any CSS color, including hex and rgb(). Opacity modifiers such as bg-brand/50 compile to color-mix() in the oklab space, which works with any input format. OKLCH is worth using when you generate tints and shades, because its lightness steps look even.

What is the difference between @theme and :root in Tailwind v4?

Variables in @theme create utilities: --color-brand gives you bg-brand, text-brand and the rest. Variables in :root are ordinary custom properties with no utilities. Use @theme for design tokens you want as classes, :root for everything else. Theme variables must be declared at the top level, not nested.

Can CSS DNA export a site’s theme to Tailwind?

Yes, as a Pro feature. The free version reads a page’s computed styles, ranks its palette and shows fonts and the type scale. Pro, $5/month with no account, adds exports to a Tailwind v4 @theme block, a v3 config, CSS variables, SCSS, JSON and Figma.

Start with the tokens, not the markup

Read any live site’s palette, fonts and type scale in your browser, then export it as a Tailwind theme with Pro.

Add CSS DNA to Chrome (free)

Free: element CSS, eyedropper, ranked palette. Pro, $5/mo, cancel anytime: exports, DESIGN.md, CSS audit. No account.