Understanding CSS → Tailwind
Properties become classes, values become tokens.
What a CSS-to-Tailwind converter is really doing, the perfect-translation cases, and the ones where you have to make a design-system decision.
The bijection.
Most CSS properties have a Tailwind utility class with the same effect. padding: 16px ↔ p-4 (assuming 4px base spacing). color: #111827 ↔ text-gray-900. border-radius: 8px ↔ rounded-lg. display: flex ↔ flex. For most CSS in most codebases, the translation is mechanical.
The off-token values.
The interesting case is values that don't map to a Tailwind token. padding: 14px doesn't have a default Tailwind class (the spacing scale jumps 12px → 16px). The converter has three choices: snap to nearest token (p-3 or p-4, losing precision), emit an arbitrary value (p-[14px], exact match but escape-hatch), or suggest extending the config (add 3.5: '14px', makes it a real token). The right answer depends on whether 14px is a deliberate design choice or just where the source happened to land.
A worked translation.
Source CSS: .card {
display: flex;
padding: 24px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
} Translated: flex p-6 bg-white rounded-xl shadow-md. Five properties become five utility classes. The shadow translation is approximate (Tailwind's shadow-md isn't pixel-identical but visually close); if exact, use arbitrary value shadow-[0_4px_6px_rgba(0,0,0,0.1)].
One-to-one translation
property → class with token suffix
Token-snapping where possible; arbitrary value otherwise.
padding: 24px → p-6 ; border-radius: 12px → rounded-xl
= Five-class utility string
Selectors are harder.
CSS uses selectors (.card:hover, .card .title, @media (min-width: 768px)); Tailwind encodes them as class prefixes (hover:, group-hover:, md:). The translation map: pseudo-classes go to prefix form; descendant selectors go to group-* patterns when the parent has className="group"; media queries go to responsive prefixes. Some complex selectors (sibling-of-uncle-aunt-cousin combinators) have no clean Tailwind equivalent — you'd write a CSS partial for those rare cases.
When CSS is the right answer.
Tailwind is great for utility composition; CSS is great for genuine custom rules. Complex selectors, CSS-only animations with multiple keyframes, @container queries with bespoke logic, generated content via ::before/::after with elaborate styles — all of these are easier in plain CSS. The right Tailwind project still has a small CSS file for the bespoke 5 %. Forcing every rule into utility classes is a purity bet that hurts maintainability.
The reverse direction.
Going from Tailwind back to CSS is rare but possible — useful when porting a Tailwind-prototyped component to a vanilla-CSS codebase, or generating a stylesheet from a design tokens file. The conversion is mechanical: each utility class maps to its underlying property declaration. The output CSS is verbose (no class composition) but valid.