
Tailwind CSS to Vanilla CSS Converter
Convert Tailwind utility classes to plain CSS. Supports 200+ classes, responsive breakpoints, and state prefixes.
Last reviewed: April 2026New to this tool? Click here for instructions
Paste any Tailwind utility class string and instantly receive the equivalent vanilla CSS rules — entirely in your browser, with no compilation step, no Node.js dependency, and no upload of your source.
What This Tool Does
This converter takes a space-separated string of Tailwind utility classes and emits the standard CSS property/value pairs each class compiles to. The simplest case is the canonical Tailwind button row — paste flex items-center px-4 py-2 bg-blue-500 and you receive four CSS declarations expanded out: display: flex; align-items: center; padding: 0.5rem 1rem; background-color: #3b82f6;. The output is plain CSS that runs anywhere, with no Tailwind runtime required.
The converter recognizes more than 200 utility classes covering display, position, overflow, flexbox, grid, typography, sizing, spacing, borders, shadows, transitions, transforms, opacity, cursor, user-select, ring shadows, the full default color palette (Slate through Rose, every shade), and most spacing-token derived properties. It also handles Tailwind's prefix system: responsive prefixes (sm:, md:, lg:, xl:, 2xl:) compile into @media (min-width: ...) blocks; state prefixes (hover:, focus:, active:, disabled:, group-hover:) compile into the appropriate pseudo-class selectors; dark: compiles into a @media (prefers-color-scheme: dark) block. Every computation runs client-side — the text you paste is never uploaded, stored, or logged.
How to Use It
The interface is a two-pane split: input on the left, generated CSS on the right. Three input modes handle the most common workflows.
Mode 1: Classes → CSS
Default mode. Paste a space-separated class string — exactly what you would write inside a class="..." attribute — into the left textarea. The converter parses each class, looks up its CSS equivalent in the internal class map, and emits one CSS rule per class on the right. Output updates live as you type, debounced to 150ms to avoid thrashing the parser on every keystroke. Use the Copy button to grab the result as plain text or Download to save it as styles.css.
Mode 2: HTML → CSS
Use this when you have a chunk of HTML markup with Tailwind classes scattered across multiple elements and want the entire stylesheet at once. The converter extracts every class="..." attribute from the input, deduplicates the resulting class list, and emits CSS rules for the union. This is the fastest path when you paste a Tailwind UI Kit snippet or a component example from a documentation site and want to extract the styling separately from the markup.
Mode 3: Batch (one class per line)
Useful when you have an audit script that dumped a list of classes one per line, or when you want to comment out specific classes mid-stream. The converter processes each non-empty line as a single class. Blank lines and lines that don't match a known Tailwind pattern are silently skipped — unknown classes are tracked and reported in the status bar at the bottom so you can spot typos or unsupported utilities.
Worked Example: A Hero Card
Suppose you copy this string from a Tailwind UI marketing example:
grid grid-cols-3 gap-4 p-6 bg-gradient-to-r from-purple-500 to-pink-500 rounded-2xl shadow-lg
Pasted into Classes mode, the converter emits eight CSS rules. Let's walk through each translation so the mapping is obvious.
grid→display: grid. Tailwind'sgridutility is a direct alias for the CSS grid display value. No spacing or size implications — it just turns the element into a grid container.grid-cols-3→grid-template-columns: repeat(3, minmax(0, 1fr)). Thegrid-cols-Nfamily compiles to arepeat()declaration with N equal-width columns. Theminmax(0, 1fr)idiom is Tailwind's standard pattern for letting columns shrink below their intrinsic content width when necessary — without it, long content forces columns wider than 1fr.gap-4→gap: 1rem. Tailwind's spacing scale runs on a 0.25rem base unit:gap-1is 0.25rem,gap-4is 1rem (16px at default root font size),gap-8is 2rem, and so on. The same scale applies top-*,m-*,w-*, and every other spacing-derived utility.p-6→padding: 1.5rem. Same scale — 6 × 0.25rem = 1.5rem (24px). For asymmetric padding, Tailwind offerspx-*/py-*for horizontal/vertical pairs, andpt-*/pr-*/pb-*/pl-*for individual sides.bg-gradient-to-r→background-image: linear-gradient(to right, var(--tw-gradient-stops)). This is one of the few Tailwind utilities that compiles to a CSS custom property reference rather than a literal value — the--tw-gradient-stopsvariable gets filled by thefrom-*andto-*utilities that follow. In plain CSS, you'd inline the gradient stops directly.from-purple-500&to-pink-500→ gradient stops. In Tailwind's compiled output these set--tw-gradient-from: #a855f7and--tw-gradient-to: #ec4899, which thebg-gradient-to-rrule references. In plain CSS the simplest equivalent is:background-image: linear-gradient(to right, #a855f7, #ec4899);rounded-2xl→border-radius: 1rem. Tailwind's rounding scale is non-linear:rounded-smis 0.125rem,roundedis 0.25rem,rounded-mdis 0.375rem,rounded-lgis 0.5rem,rounded-xlis 0.75rem,rounded-2xlis 1rem,rounded-3xlis 1.5rem, androunded-fullis 9999px.shadow-lg→box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1). Tailwind's shadow utilities stack two box-shadows to produce a more natural depth effect than a single drop shadow — the offset, blur, and spread values are tuned to match modern UI conventions.
The combined output gives you a complete CSS rule block you can paste into any stylesheet, with no Tailwind dependency at runtime. The whole conversion takes about half a millisecond on a modern device.
Bundle Size Comparison: Tailwind Classes vs Inline CSS vs Component CSS
One question that comes up repeatedly during migration: how much does my page actually shrink (or grow) when I move away from Tailwind? The honest answer is that it depends on how much the same utility cluster repeats across your markup. The chart below shows a representative comparison for a typical landing page with 50 unique components, each using a moderate number of utility classes.
.px-4 rule exists exactly once in the output, regardless of how many elements use it.The takeaway is not that Tailwind always wins on bundle size — for a five-component prototype, hand-written CSS would beat Tailwind's setup overhead. The takeaway is that the gap narrows and reverses as component count grows, because atomic utility CSS deduplicates by construction while hand-written component CSS repeats the same declaration across class bodies. The numbers above assume a typical real-world distribution: most utilities are used 5 to 50 times across the page, with a long tail of single-use classes.
Common Use Cases
Migrating Off Tailwind
Teams sometimes inherit a Tailwind codebase and decide to migrate to plain CSS — perhaps because they're consolidating onto a design system that uses semantic class names, or because a downstream consumer (a CMS, an email template, a static site generator) can't carry utility classes through its template pipeline. The migration path is incremental: take one component at a time, paste its Tailwind class string into this tool, capture the generated CSS into a component stylesheet with a meaningful class name, and replace the inline utility cluster with that single class. Doing this incrementally lets you ship the migration without a big-bang rewrite.
Copying a Tailwind UI Library Example into a CSS-only Project
Tailwind UI, Headless UI, Flowbite, daisyUI, and dozens of community libraries publish components as HTML snippets pre-styled with Tailwind classes. If your project doesn't use Tailwind — maybe it's a legacy WordPress theme, a Rails app on Bootstrap, or a Django site with hand-written CSS — you can still use those components by running their HTML through HTML mode here. You get a complete stylesheet for the snippet that you can drop into your existing CSS without adopting Tailwind's build pipeline.
Sharing Snippets with Non-Tailwind Colleagues
Code review and pair programming get awkward when half the team uses Tailwind and the other half doesn't. A back-end engineer reviewing a front-end PR shouldn't need to memorize the Tailwind spacing scale to know that px-4 py-2 means horizontal padding 16px and vertical padding 8px. Run the snippet through this tool and paste the plain CSS alongside the original markup in your PR description — reviewers can verify the styling without learning a separate vocabulary.
Debugging "Why is Tailwind Doing X Here?"
When a Tailwind class produces unexpected results — the element doesn't align the way you expected, the spacing looks wrong, the responsive breakpoint doesn't kick in — converting the class string to plain CSS makes the actual generated declarations visible. Half the time the bug is obvious once you see the literal CSS: maybe flex is being overridden by an earlier block, or a responsive prefix is wrapped in the wrong media query order. Browser devtools show the computed style, but they don't show you what Tailwind intended — this tool does.
Documentation, Blog Posts, and Code Samples
When writing technical content about CSS — tutorials, blog posts, internal docs — you often want to show plain CSS rather than Tailwind classes because readers may not use Tailwind. Author your example in Tailwind (because it's faster), convert it here, and paste the plain CSS into the article. Readers across the entire CSS ecosystem can apply your example, not just Tailwind users.
Edge Cases and Limitations
A few patterns are worth knowing about before you hit them mid-conversion.
Arbitrary Value Syntax
Tailwind v3 introduced arbitrary value syntax for cases where the design token scale doesn't have the value you need: w-[37px], bg-[#abc123], text-[14px]/[1.7]. This tool currently recognizes named utilities from Tailwind's default scale — arbitrary-value classes are reported in the status bar as unknown and skipped. The workaround is straightforward: read the bracket contents directly and write the CSS by hand, since arbitrary values are just literal CSS values escaped through the bracket syntax. For example, w-[37px] trivially becomes width: 37px.
Responsive Prefixes (sm:, md:, lg:, xl:, 2xl:)
Responsive prefixes compile into @media (min-width: ...) blocks using Tailwind's default breakpoint values: sm: is 640px, md: is 768px, lg: is 1024px, xl: is 1280px, 2xl: is 1536px. The output groups all classes sharing a prefix into a single media query block to keep the result tidy. Note that Tailwind's breakpoints are mobile-first: md:flex means "apply display: flex at viewport widths ≥ 768px," not "apply only at the md range."
State Prefixes (hover:, focus:, active:, disabled:)
State prefixes compile into the corresponding CSS pseudo-class selectors: hover:bg-blue-600 becomes .hover\:bg-blue-600:hover { background-color: #2563eb; }. The backslash escape in the selector is needed because the colon is part of the class name — without escaping, browsers would interpret the colon as a pseudo-class separator. Most editors and CSS minifiers handle the escape correctly, but some older PostCSS plugins choke on it.
Dark Mode Prefix (dark:)
The dark: prefix compiles to a @media (prefers-color-scheme: dark) block by default. If your Tailwind config uses darkMode: 'class', the output would instead be scoped under .dark as an ancestor selector. This tool emits the media-query form; rewriting to the class form is a simple find-and-replace.
Group Hover and Peer Modifiers
group-hover: compiles to .group:hover .group-hover\:foo — it requires a parent element marked with the group class. The peer family (peer-checked:, peer-focus:, etc.) works similarly but with a sibling-combinator selector. This tool emits the group-hover pattern; peer modifiers are not yet recognized and appear in the unknown-classes list.
Custom Theme Variables
If your tailwind.config.js defines custom colors (bg-primary, text-accent, etc.) or custom spacing tokens, this tool will not recognize them because it has no access to your config. Only Tailwind's default scale is supported. To convert custom-theme classes, either inline the resolved values from your config, or extend the tool's class map locally if you need to do this often.
The @apply Directive
This tool is the inverse of @apply: it expands utility classes into CSS declarations, where @apply takes utility classes inside a CSS selector and inlines them. If you have an @apply rule like .btn { @apply px-4 py-2 bg-blue-500; }, you can convert it by treating the utility list as input here, then wrapping the output in your selector. The result is what Tailwind's PostCSS plugin would emit at build time.
@layer Semantics
Tailwind v3 organizes its output into three layers (base, components, utilities) using the @layer directive. Vanilla CSS doesn't have layers in the same sense — the cascade is determined by source order and specificity. When migrating, you usually flatten layered Tailwind CSS into a single source file and let source order handle the cascade. CSS Cascade Layers (the standard @layer at-rule, supported in all modern browsers since 2022) are a separate language feature that Tailwind v4 uses internally; they're worth learning if you're migrating to layered architecture.
Behind the Scenes: How Tailwind Compiles to CSS
The JIT Compiler (Tailwind v3+)
Tailwind's Just-In-Time (JIT) compiler, shipped as the default in v3, scans your source files (HTML, JSX, Vue templates, anything you list in the content array of your config) for class strings, then emits only the CSS that those scanned classes need. Before JIT (Tailwind v1 and v2), the development build shipped megabytes of CSS containing every possible utility for every possible color, breakpoint, and state — a production build then ran purgecss to strip unused classes. JIT eliminated that two-step workflow: development and production now both run the same compiler, output is small from the start, and arbitrary-value syntax (w-[37px]) becomes possible because the compiler generates CSS on demand rather than from a fixed catalog.
The Design Token Config
Your tailwind.config.js (or the v4 @theme block) is a flat JSON-like object defining design tokens: the color palette, spacing scale, font-size scale, breakpoints, shadow definitions, and so on. The compiler reads this config and uses it as the source of truth for what every utility class compiles to. Override the spacing scale and p-4 now means whatever you set the index 4 to, not the default 1rem. This is what makes Tailwind feel like a design system rather than a CSS framework — everything is one config edit away from being themed.
Tailwind CSS v4 Changes
Tailwind v4 (January 2025) introduces several shifts. The compiler is rewritten in Rust (the Oxide engine), which the team measures as roughly 5–10x faster on cold builds and over 100x faster on incremental rebuilds — relevant for developers running Tailwind in HMR-driven dev servers. Configuration moves from JavaScript to CSS-first: instead of editing tailwind.config.js, you declare theme tokens with the @theme at-rule inside your CSS files. PostCSS is no longer required for simple setups — Tailwind v4 ships its own CSS processor with built-in support for nesting, custom properties, and color mixing. Most v3 patterns still work through a compatibility bridge, but new projects should adopt the CSS-first config directly.
The theme() Function
Inside @apply rules and arbitrary-value brackets, the theme() function lets you reference design tokens by their config path: theme('spacing.4') resolves to whatever your config has set at spacing.4 (default: 1rem). This is what allows custom CSS in your stylesheets to stay consistent with the rest of your Tailwind output — you never hardcode 1rem when you can write theme('spacing.4') and let the config drive the value. At build time, the compiler resolves every theme() call to a literal value before emitting CSS.
Comparison: Tailwind vs UnoCSS vs Tachyons vs vanilla-extract vs CSS-in-JS
Tailwind is the most popular utility-first CSS approach, but it's not the only one. The alternatives differ on philosophy, build model, and runtime cost. The table below summarizes the major tradeoffs.
| Tool | Approach | Build Model | Strengths | Tradeoffs |
|---|---|---|---|---|
| Tailwind CSS | Utility-first with predefined design tokens | JIT scanner; PostCSS or Rust (v4) | Largest ecosystem, deepest IDE tooling, well-documented design tokens | Configuration is JavaScript (v3) or CSS (v4); markup verbosity; learning curve for the class vocabulary |
| UnoCSS | Utility-first, on-demand atomic engine | Vite plugin or standalone CLI | Faster than Tailwind on small projects; presets emulate Tailwind, Windi, Bootstrap; first-class custom rules | Smaller ecosystem; presets diverge subtly from Tailwind's behavior in edge cases |
| Tachyons | Utility-first (the original) | None — single CSS file you include | Zero build step; trivial to drop into legacy projects; tiny mental model | No design-token customization; fixed class catalog; no JIT; bundle ships unused classes |
| vanilla-extract | Type-safe CSS-in-TypeScript | Compile-time; static CSS extraction | Full TypeScript typing for design tokens; zero-runtime CSS extraction; theming via TS contracts | Requires TypeScript and a bundler plugin (Vite/Webpack); steeper learning curve than utility classes |
| CSS-in-JS (styled-components, Emotion) | Component-scoped styles via JS template literals | Runtime (mostly) or compile-time (linaria) | Dynamic styling driven by props; auto-scoped class names; theming via React Context | Runtime cost in hot render paths; per-component bundle size; debugging requires source maps |
For most production projects in 2026, the realistic choice is between Tailwind (largest ecosystem, broadest tooling support) and UnoCSS (faster builds, more configurable, near-identical mental model). Tachyons remains a great fit for prototypes and content sites that don't need a build step. vanilla-extract and CSS-in-JS suit teams that already work in TypeScript-heavy React codebases and want styling to participate in the type system.