Tailwind CSS to Vanilla CSS Converter

Convert Tailwind utility classes to plain CSS. Supports 200+ classes, responsive breakpoints, and state prefixes.

Last reviewed: April 2026

New to this tool? Click here for instructions

Input
Generated CSS
Paste Tailwind classes above to convert them to CSS.

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.

  1. griddisplay: grid. Tailwind's grid utility is a direct alias for the CSS grid display value. No spacing or size implications — it just turns the element into a grid container.
  2. grid-cols-3grid-template-columns: repeat(3, minmax(0, 1fr)). The grid-cols-N family compiles to a repeat() declaration with N equal-width columns. The minmax(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.
  3. gap-4gap: 1rem. Tailwind's spacing scale runs on a 0.25rem base unit: gap-1 is 0.25rem, gap-4 is 1rem (16px at default root font size), gap-8 is 2rem, and so on. The same scale applies to p-*, m-*, w-*, and every other spacing-derived utility.
  4. p-6padding: 1.5rem. Same scale — 6 × 0.25rem = 1.5rem (24px). For asymmetric padding, Tailwind offers px-*/py-* for horizontal/vertical pairs, and pt-*/pr-*/pb-*/pl-* for individual sides.
  5. bg-gradient-to-rbackground-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-stops variable gets filled by the from-* and to-* utilities that follow. In plain CSS, you'd inline the gradient stops directly.
  6. from-purple-500 & to-pink-500 → gradient stops. In Tailwind's compiled output these set --tw-gradient-from: #a855f7 and --tw-gradient-to: #ec4899, which the bg-gradient-to-r rule references. In plain CSS the simplest equivalent is: background-image: linear-gradient(to right, #a855f7, #ec4899);
  7. rounded-2xlborder-radius: 1rem. Tailwind's rounding scale is non-linear: rounded-sm is 0.125rem, rounded is 0.25rem, rounded-md is 0.375rem, rounded-lg is 0.5rem, rounded-xl is 0.75rem, rounded-2xl is 1rem, rounded-3xl is 1.5rem, and rounded-full is 9999px.
  8. shadow-lgbox-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.

CSS Bundle Size Comparison: Tailwind utilities vs inline CSS vs unscoped component CSS A grouped bar chart showing approximate CSS bundle size in kilobytes (gzipped) for a typical 50-component landing page rendered three ways. Tailwind JIT output: 9.8 KB. Hand-written component CSS (BEM-style): 23.4 KB. Inline style attributes only: 41.7 KB. Tailwind produces the smallest bundle because atomic utility classes are deduplicated automatically at build time. 50 40 30 20 10 CSS size (KB, gzipped) 9.8 KB Tailwind JIT (atomic, deduped) 23.4 KB Component CSS (BEM-style, hand-written) 41.7 KB Inline style attrs (no class reuse possible) CSS Bundle Size: Typical 50-component Landing Page
Approximate gzipped CSS payload for the same 50-component page rendered three different ways. Numbers are representative; actual results vary by component count, repetition, and minification settings. The key insight: Tailwind's atomic dedup wins at scale because every .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.

Utility-first and CSS-tooling alternatives: design philosophy, build model, and 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
Choose by the constraint that matters most to your team: Tailwind for design-system breadth, UnoCSS for speed and configurability, Tachyons for zero build, vanilla-extract for type safety, CSS-in-JS for prop-driven dynamic styles.

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.

Frequently Asked Questions

Utility classes solve three concrete problems that custom CSS does not: naming exhaustion, specificity wars, and dead-code accumulation. Naming exhaustion is the moment you can no longer think of a sensible class name for the seventh card variant in your project. Specificity wars happen when one rule overrides another in unpredictable order because authors keep adding more selectors to win. Dead-code accumulation is the well-documented tendency for CSS bundles to grow forever as features ship and components retire. The tradeoff is markup noise: a single button might carry ten classes inline. Whether that tradeoff is worth taking depends on your team's tolerance for verbose HTML versus its tolerance for stylesheet entropy.
Yes — and that's the most common complaint, especially from authors who learned CSS in the BEM era. A typical Tailwind button might carry ten classes where a custom CSS approach would use a single .btn-primary. The pragmatic responses are: extract repeated patterns into framework components so the noise lives in one definition; use Tailwind's @apply directive to package a utility cluster into a named class for frequently-repeated combinations; or accept the noise as a deliberate tradeoff in exchange for never having to invent class names or fight specificity. The choice often comes down to whether you read CSS or HTML more often when debugging.
For a small page using fewer than fifty utility classes, hand-written CSS will win on raw bytes. For a moderate site using a few hundred utilities, the two approaches converge. For a large site with thousands of design tokens and component variants, Tailwind's JIT output tends to be smaller than hand-written CSS because Tailwind's atomic classes deduplicate automatically — there is exactly one .px-4 rule no matter how many elements use it. Real-world JIT bundles often sit around 10 KB gzipped for medium projects. Bundle size differences are usually small enough that this should rarely be the deciding factor.
@apply is a Tailwind PostCSS directive that inlines utility classes into a custom CSS selector at build time. Writing .btn-primary { @apply inline-flex items-center px-4 py-2 bg-blue-500 text-white rounded-lg; } produces a single .btn-primary rule with all the corresponding declarations expanded inline. Use it when a utility combination is repeated dozens of times and inline noise becomes unreadable, or when third-party HTML cannot carry utility classes and you need to style it through a single class hook. Tailwind's authors discourage @apply for most other cases because it reintroduces the very abstractions Tailwind was designed to avoid.
Switch if: your team produces content-heavy output where editors paste HTML from external sources; you ship a component library that consumers must theme without depending on your build configuration; your project is small enough that a single hand-written stylesheet is faster to author; or your team has deep CSS expertise and finds utility class noise actively painful. Don't switch if you're already productive in Tailwind and your bundle size is acceptable, or if your codebase has hundreds of components and rewriting them would consume weeks.
Tailwind v3 (December 2021) shipped the JIT compiler as default. Configuration lived in tailwind.config.js as a JavaScript object, and PostCSS plus the @tailwindcss/postcss plugin were required to compile. Tailwind v4 (January 2025) shifts to a CSS-first configuration model: theme tokens are declared with @theme inside CSS files rather than in a JavaScript config. The compiler is rewritten in Rust (the Oxide engine), reported as roughly 5–10x faster on cold builds and over 100x faster on incremental rebuilds. PostCSS is no longer required for simple setups. Many v3 JavaScript config patterns still work through a legacy bridge.
Yes, two paths exist. The Tailwind CDN script loads the entire compiler in the browser and generates utility CSS at runtime by scanning the loaded DOM — suitable for prototypes and demos, not production. The second path is the standalone Tailwind CLI binary, which bundles the compiler into a single executable you can run without Node.js. Download the binary for your platform from the tailwindcss-cli GitHub repo, run tailwindcss -i input.css -o output.css --watch, and you get a build pipeline with zero npm dependencies.
Tailwind's dark: prefix compiles to one of two CSS patterns. With darkMode: 'media' (default), .dark\:bg-gray-800 becomes @media (prefers-color-scheme: dark) { .dark\:bg-gray-800 { background-color: #1f2937; } }. With darkMode: 'class', it becomes .dark .dark\:bg-gray-800 { background-color: #1f2937; }, requiring a .dark class on a parent that JavaScript toggles. For plain CSS, the cleanest pattern is to declare CSS custom properties for every color in :root, then override them inside @media (prefers-color-scheme: dark) or under a .dark class selector. This gives you the same theming flexibility Tailwind offers without any utility-class infrastructure.