DeveloperApril 2026 · 15 min read

How to Convert CSS to Tailwind: The Complete Developer's Guide

Convert CSS to Tailwind utility classes the right way. Learn the spacing scale, color system, flexbox mapping, arbitrary values, and when not to convert.

Try the CSS to Tailwind Converter
Free, no signup
DG
Derek Giordano
Designer & Developer
In this guide
01Why Convert CSS to Tailwind?02How the Conversion Actually Works03The Tailwind Spacing Scale04Colors: Keywords, Hex, and the Arbitrary Value Escape Hatch05Typography: Size, Weight, Line Height06Flexbox and Grid: The Conversion Patterns Everyone Asks For07Borders, Shadows, and Transitions08Responsive Variants and Dark Mode (What Converters Can't Do)09When NOT to Convert (And What to Do Instead)10Common Pitfalls and Edge Cases11Workflow: From Paste to Production12Frequently Asked Questions
⚡ Key Takeaways
  • Convert CSS to Tailwind utility classes the right way.
  • Why Convert CSS to Tailwind?.
  • How the Conversion Actually Works.
  • The Tailwind Spacing Scale.
  • Covers colors: keywords, hex, and the arbitrary value escape hatch.

Why Convert CSS to Tailwind?

If you're reading this, you probably have a stack of existing CSS — maybe a legacy stylesheet, maybe a design you prototyped with inline styles, maybe output from a design tool — and you want to move to Tailwind. Or you're a newer Tailwind developer who still thinks in CSS and wants a quick translation layer while you learn the utility vocabulary.

Both are valid reasons. Converting CSS to Tailwind isn't about abandoning what you know — it's about mapping the properties you already understand (padding, color, display: flex) onto Tailwind's design system. The mechanics are simple. The interesting work happens in the edges: when values don't fit the scale, when properties have no direct equivalent, and when the conversion forces you to make a design decision you'd been avoiding.

The CSS to Tailwind converter handles the mechanical part — paste CSS, get utility classes. This guide covers what the converter does, what it can't do, and the judgment calls that remain yours.

How the Conversion Actually Works

Every Tailwind utility class is a one-to-one mapping of a CSS declaration. p-4 is padding: 1rem. text-center is text-align: center. flex is display: flex. There is no magic. The converter's job is to reverse that mapping: given a CSS declaration, find the utility class that produces the same result.

💡 Tip
Always include -webkit-backdrop-filter alongside backdrop-filter for Safari support. Without the prefix, the effect is invisible to roughly 25% of mobile users.

Three things can happen when you feed a declaration to the converter:

That three-state output — hit, fallback, skipped — is the whole model. Once you understand it, everything the converter does makes sense, and you'll know exactly where to look when something seems off.

The Tailwind Spacing Scale

Tailwind's spacing scale is the single most important thing to understand when converting CSS. It's a fixed set of values that covers padding, margin, gap, width, height, top/right/bottom/left, and most sizing properties.

⚠ Warning
On iOS Safari, backdrop-filter inside a position: fixed element can cause severe scroll performance issues. Test thoroughly on real iOS devices.

The scale is based on a 0.25rem (4px) unit, multiplied. A quick reference of the most-used values:

0 → 0px 0.5 → 2px (0.125rem) 1 → 4px (0.25rem) 2 → 8px (0.5rem) 3 → 12px (0.75rem) 4 → 16px (1rem) 5 → 20px (1.25rem) 6 → 24px (1.5rem) 8 → 32px (2rem) 10 → 40px (2.5rem) 12 → 48px (3rem) 16 → 64px (4rem) 20 → 80px (5rem) 24 → 96px (6rem)

So padding: 1rem becomes p-4. margin-top: 0.5rem becomes mt-2. gap: 24px becomes gap-6. If you've ever wondered why a Tailwind component looks a certain way at p-4, it's because p-4 is 16px — the same 1rem base you've been using in CSS all along.

The converter handles both rem and px input. Pasting padding: 16px and padding: 1rem both produce p-4. It normalizes the value before looking it up in the scale.

Values that land between scale stops — like padding: 13px or margin: 18px — get converted to arbitrary values (p-[13px], m-[18px]). That's fine as a translation, but it's often a signal that the original value was ad-hoc and snapping it to the nearest scale value would produce a more consistent design.

Colors: Keywords, Hex, and the Arbitrary Value Escape Hatch

Tailwind's color system is one of its biggest strengths and one of the hardest things to automate. The default palette has about 220 color tokens (slate-50 through slate-950, red-50 through red-950, and so on across 22 hue families). Mapping an arbitrary hex code to the nearest palette color requires perceptual color distance math, and even then the result is a guess.

The converter takes the pragmatic approach: it maps the handful of CSS color keywords that have unambiguous Tailwind equivalents, and falls back to arbitrary values for everything else.

Direct keyword maps — these produce clean utility classes:

transparent → bg-transparent currentcolor → bg-current black → bg-black white → bg-white #000 / #fff → bg-black / bg-white red → bg-red-500 blue → bg-blue-500 green → bg-green-500 gray / grey → bg-gray-500

Everything else gets an arbitrary value. A declaration like color: #FF6B6B becomes text-[#FF6B6B]. This is intentional: the converter doesn't guess which Tailwind red you want. red-400, red-500, and red-600 all look "red" at a glance, but they're different colors. Better to preserve your exact value and let you decide whether to snap it to a palette color later.

If you're converting a full design system, the right workflow is: run the conversion, note which arbitrary color values keep appearing, then add them to your tailwind.config.js as custom color tokens. That way text-[#FF6B6B] becomes text-coral across your codebase.

Typography: Size, Weight, Line Height

Font size is the one typography property where Tailwind's scale maps cleanly onto common CSS values.

12px / 0.75rem → text-xs 14px / 0.875rem → text-sm 16px / 1rem → text-base 18px / 1.125rem → text-lg 20px / 1.25rem → text-xl 24px / 1.5rem → text-2xl 30px / 1.875rem → text-3xl 36px / 2.25rem → text-4xl 48px / 3rem → text-5xl 60px / 3.75rem → text-6xl 72px / 4.5rem → text-7xl 96px / 6rem → text-8xl 128px / 8rem → text-9xl

Font weight maps by numeric value — 100 through 900, with semantic names like thin, light, normal, medium, semibold, bold, extrabold, black. A declaration like font-weight: 600 becomes font-semibold.

Text alignment is straightforward: text-align: center becomes text-center, and the same pattern works for left, right, and justify.

Text transform is similar — text-transform: uppercase becomes uppercase (no prefix; Tailwind treats it as a direct utility). The same for lowercase and capitalize. text-transform: none becomes normal-case.

Line height is where things get interesting. Tailwind's leading-* utilities map to a small set of common ratios:

line-height: 1 → leading-none line-height: 1.25 → leading-tight line-height: 1.5 → leading-normal line-height: 1.75 → leading-relaxed line-height: 2 → leading-loose

Anything else becomes an arbitrary value — line-height: 1.6 becomes leading-[1.6]. Letter-spacing (tracking-*) similarly falls through to arbitrary values for most CSS inputs, because the Tailwind tracking scale uses em-based values (-0.05em, 0.025em) that rarely show up verbatim in hand-written CSS.

Flexbox and Grid: The Conversion Patterns Everyone Asks For

Flexbox is probably the single most common thing developers paste into a CSS-to-Tailwind converter. The good news: almost every flexbox property has a clean Tailwind equivalent.

display: flex → flex display: inline-flex → inline-flex flex-direction: row → flex-row flex-direction: column → flex-col flex-wrap: wrap → flex-wrap justify-content: center → justify-center justify-content: space-between → justify-between justify-content: space-around → justify-around justify-content: flex-start → justify-start justify-content: flex-end → justify-end align-items: center → items-center align-items: flex-start → items-start align-items: stretch → items-stretch align-items: baseline → items-baseline

The naming pattern is worth internalizing: justify-* controls the main axis (horizontal in flex-row), items-* controls the cross axis, and flex-start / flex-end become start / end without the flex- prefix. Once you see the pattern, you can write these classes from memory without touching the converter.

Flex shorthandflex: 1 is common and maps to flex-1. flex: auto becomes flex-auto, flex: none becomes flex-none. Unusual shorthand values like flex: 2 1 300px fall back to arbitrary values — flex-[2_1_300px] — because there's no scale that covers every combination.

Grid is where the converter gets clever. Tailwind's grid utilities map repeat-based templates:

grid-template-columns: repeat(3, 1fr) → grid-cols-3 grid-template-columns: repeat(4, minmax(0, 1fr)) → grid-cols-4 grid-template-columns: 200px 1fr 200px → grid-cols-[200px_1fr_200px]

The first two patterns both produce simple grid-cols-N classes — this is the standard way Tailwind emits repeat(N, minmax(0, 1fr)) under the hood, and minmax(0, 1fr) is the form Tailwind itself generates internally. Anything else falls through to an arbitrary value with spaces converted to underscores (arbitrary values can't contain spaces).

Borders, Shadows, and Transitions

Borders are where the converter demonstrates the value of parsing shorthand. A CSS declaration like border: 1px solid #ccc is three properties smashed into one. The converter parses the shorthand and emits:

border: 1px solid #ccc → border border-[#ccc] border: 2px dashed red → border-2 border-dashed border-red-500

The logic: 1px width is the Tailwind default (just border), wider widths get an explicit suffix (border-2, border-4, border-8). solid is the default style and gets dropped from the output. dashed, dotted, and double become border-dashed, etc. The color runs through the same keyword-or-arbitrary lookup as standalone border-color.

Border radius maps a set of common values to semantic suffixes:

0 → rounded-none 2px / 0.125rem → rounded-sm 4px / 0.25rem → rounded 6px / 0.375rem → rounded-md 8px / 0.5rem → rounded-lg 12px / 0.75rem → rounded-xl 16px / 1rem → rounded-2xl 24px / 1.5rem → rounded-3xl 9999px → rounded-full

Box shadow is one of the properties where arbitrary values do the heavy lifting. Tailwind has five semantic shadow tokens (shadow-sm, shadow, shadow-md, shadow-lg, shadow-xl, shadow-2xl), and the converter doesn't try to map custom shadow strings to them — there's no reliable way to say "this 0 2px 8px rgba(0,0,0,0.1) shadow is actually shadow-md." Instead, it emits an arbitrary value with spaces escaped as underscores:

box-shadow: 0 2px 8px rgba(0,0,0,0.1) → shadow-[0_2px_8px_rgba(0,0,0,0.1)]

The underscore escaping is a Tailwind convention, not the converter's invention. Tailwind parses shadow-[...] and treats underscores as spaces specifically to allow multi-word values inside bracket notation.

Transitions work the same way: the whole transition string becomes an arbitrary value. If you need semantic transitions (transition-colors, transition-transform, duration-200), you'll want to restructure manually — there's no one-to-one way to convert transition: all 0.2s ease into Tailwind's specific-property transitions without knowing which properties actually change.

Responsive Variants and Dark Mode (What Converters Can't Do)

Here's the limit of any CSS-to-Tailwind converter: it converts a single declaration block. It has no way to know about your media queries, your dark mode strategy, your hover states, or your design system breakpoints. Those are layered on top of utility classes after the conversion.

If your CSS looks like this:

.card { padding: 1rem; } @media (min-width: 768px) { .card { padding: 2rem; } }

The converter will happily convert the first rule (p-4) and the second rule (p-8) separately, but it won't combine them into the responsive class p-4 md:p-8. That's a human judgment — you have to decide that the 768px breakpoint corresponds to Tailwind's md breakpoint, which is exactly 768px by default.

The standard Tailwind breakpoint scale is worth memorizing:

sm → 640px md → 768px lg → 1024px xl → 1280px 2xl → 1536px

If your CSS uses these exact values, the conversion is mechanical: wrap the converted classes in a md: prefix. If your CSS uses different breakpoints (say, 720px or 900px), you either adjust your design to Tailwind's breakpoints or add custom breakpoints to tailwind.config.js.

Dark mode is the same story. @media (prefers-color-scheme: dark) styles in your CSS need to be rewritten with a dark: prefix on the utility class, and you need to have darkMode: 'media' or darkMode: 'class' configured in your Tailwind config for the prefix to work.

Hover and focus states follow the same pattern. :hover and :focus pseudo-class styles in your CSS become hover: and focus: prefixes in Tailwind. The conversion is always per-declaration — the converter gives you the utility classes, and you add the state prefix.

When NOT to Convert (And What to Do Instead)

Not every CSS file benefits from being converted to Tailwind. Three cases where the conversion adds friction instead of removing it:

1. Complex, reusable component styles. If you've written a 200-line stylesheet for a custom UI component — a data grid, a date picker, a rich text editor — converting every property to utility classes produces a massive className string and loses the readability that made the component maintainable in the first place. Tailwind's @apply directive exists for this: define the complex component style once in your CSS, use the utility classes inside @apply, and reference the component class from your markup. You get Tailwind's design tokens without the utility-soup markup.

2. Animations and keyframes. Tailwind has limited animation utilities (animate-spin, animate-ping, animate-pulse, animate-bounce). Custom @keyframes rules have no utility equivalent — you'll define them in your Tailwind config's extend.keyframes and reference them with animate-[name]. The converter ignores @keyframes blocks entirely; you'll handle those by hand.

3. Generated CSS from design tools. If you're pasting output from Figma's CSS export, Webflow, or a similar tool, a lot of the declarations are noise — things like position: relative on every element, font-family declarations copied from inherited styles, transform origin values that default to center anyway. Converting this verbatim produces markup crowded with utility classes that do nothing. A better workflow: convert the CSS, delete the classes that match Tailwind defaults, and keep only the ones that actually override the default value.

Common Pitfalls and Edge Cases

A few specific things that trip developers up during CSS-to-Tailwind conversion:

`margin: auto` — the CSS pattern margin: 0 auto for horizontal centering doesn't convert cleanly as a single rule. You'll see margin-left: auto; margin-right: auto converted to ml-auto mr-auto (or combined as mx-auto if you write it yourself). The converter accepts margin: auto directly and emits m-auto, but the common shorthand 0 auto needs to be split into separate declarations first.

Negative values — Tailwind supports negative spacing with a hyphen prefix: margin: -1rem becomes -m-4. The converter handles this correctly. Just be aware that in your CSS source, margin: -16px and margin: -1rem both convert identically.

`display: none` — becomes hidden. This is a Tailwind-specific naming choice that trips up people who expect display-none or similar. Same idea for display: flex → just flex (no display- prefix), display: grid → just grid.

Shorthand padding/margin — CSS padding: 8px 16px is two values: vertical and horizontal. Tailwind has no single utility for that combination — you'll get py-2 px-4 as a pair. The converter handles this correctly by splitting the shorthand; just don't expect a single class to replace two-value padding.

Vendor prefixes-webkit-transform, -moz-user-select, and similar are ignored by the converter. Modern Tailwind doesn't need vendor prefixes for any of its utilities; the Tailwind compiler (via autoprefixer) handles that at build time.

CSS custom properties (`var(--foo)`) — fall through to arbitrary values. color: var(--brand) becomes text-[var(--brand)]. This is valid Tailwind syntax and works correctly, but you'll probably want to move these into your Tailwind config as theme extensions for cleaner classes.

Workflow: From Paste to Production

A practical workflow for converting a real stylesheet:

Frequently Asked Questions

Does the converter work for Tailwind v4?+
It targets Tailwind v3 conventions, which cover the vast majority of utility class names you'll use. Most output works identically in v4, but a small number of classes have renamed in v4 (like gap-x/gap-y syntax changes). If you're on v4, spot-check the output against the Tailwind v4 docs. For v2, the arbitrary-value syntax ([brackets]) isn't supported — you'll need to add custom values to your config instead.
What happens to CSS variables and `var()` references?+
They pass through as arbitrary values. A declaration like color: var(--brand) becomes text-[var(--brand)], which is valid Tailwind and works at runtime. For cleaner output, add the variable to your Tailwind config's theme section so you can use a named utility class like text-brand instead.
Why didn't my color convert to a Tailwind palette name?+
The converter only maps unambiguous color keywords — red, blue, black, white, transparent, and a handful of others — to palette classes. Hex codes, RGB values, and HSL values always fall through to arbitrary values, because mapping #3B82F6 to blue-500 requires knowing that #3B82F6 is the Tailwind default blue-500. That would bake Tailwind's default palette into the converter and break for anyone using a custom palette. Arbitrary values are the correct answer.
Can I paste a full CSS file?+
The converter processes one declaration block at a time. If you paste a full CSS file with multiple selectors, it'll extract the declarations from the first block and ignore the rest. For bulk conversions, paste selectors one at a time, or use a build-time tool like twin.macro that compiles CSS-in-JS to Tailwind utility classes automatically.
How does it handle media queries?+
It doesn't — media queries are stripped before parsing. You'll need to manually wrap the converted classes with responsive prefixes (md:, lg:, etc.). This is intentional: the converter can't know whether 768px in your CSS corresponds to Tailwind's md breakpoint or something custom in your config. That judgment is yours.
What's the difference between `p-4` and `p-[1rem]`?+
Functionally nothing — they both produce padding: 1rem. Semantically, p-4 uses the Tailwind spacing scale and inherits any changes you make to the scale in your config. p-[1rem] is a one-off literal value that won't change if you update the scale. Prefer p-4 when the value fits the scale; use p-[...] only when it doesn't.
Try it yourself

Use the CSS to Tailwind Converter — free, no signup required.

⚡ Open CSS to Tailwind Converter
DG
Derek Giordano
Written by the creator of Ultimate Design Tools. BA in Business Marketing.
⚡ Try the free Tailwind Component Builder →
⚡ Try the free Tailwind Class Sorter →