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.
- 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.
-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:
- Direct hit — the value matches Tailwind's default scale exactly, and you get a clean utility class like
p-4ortext-xl. - Arbitrary value fallback — the value doesn't match the scale, so the converter emits a bracket-notation class like
p-[13px]ortext-[#a1b2c3]. This is valid Tailwind syntax (v3+) and works identically to the CSS. - Unsupported — the property has no Tailwind equivalent, or the value is too complex to map reliably. These show up in the warnings panel so you can handle them manually or keep them as custom CSS.
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.
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:
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.
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:
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.
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:
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.
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 shorthand — flex: 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:
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:
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:
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:
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:
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:
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:
- Convert one rule at a time. Paste a single selector's declaration block into the converter. Copy the output. Apply it to the corresponding element in your markup. Verify it looks identical. Repeat.
- Don't trust arbitrary values blindly. Every
[...]class in the output is a signal. It means the value doesn't match the scale. Decide, per value: is this intentional (a brand color, a specific custom spacing), or is it ad-hoc and should be snapped to the scale? Arbitrary values are fine in moderation, but a stylesheet full ofp-[13px]andmt-[11px]is just CSS in disguise. - Extract repeated class strings. If you find yourself pasting the same 8-class string (
flex items-center justify-between px-4 py-2 rounded-lg bg-white border) into multiple places, that's a component. Either extract it into a React/Vue/Svelte component, or use Tailwind's@applyto name it. - Audit the warnings panel. The converter's 'unsupported' warnings are often the most interesting output. They're the properties that Tailwind doesn't have opinions about — things like
clip-path,mask-image,backdrop-filterwith complex values,scroll-snap-*properties. Decide per property whether to keep them as custom CSS or restructure. - Update your Tailwind config as you go. Every recurring arbitrary value (color, spacing, shadow, animation) is a candidate for your config. Adding custom tokens as you encounter them keeps the utility classes clean and makes the design system evolve with the conversion.
Frequently Asked Questions
Does the converter work for Tailwind v4?
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?
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?
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?
How does it handle media queries?
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]`?
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.Use the CSS to Tailwind Converter — free, no signup required.
⚡ Open CSS to Tailwind Converter