Tailwind CSS v4 changed how the framework is configured, built and themed, and most of the work we get is about that change: moving a v3 project over, replacing Sass or CSS-in-JS, or giving a growing app a token layer that holds up. Sometimes we tell you to leave your styling alone, and we say when below.
What changed in Tailwind CSS v4?
Four changes matter to a delivery plan:
- Configuration moved into CSS. You import Tailwind with
@import "tailwindcss"and declare tokens in an@themeblock, so a color, a font or a breakpoint lives in one stylesheet. - The build engine was rewritten, and Tailwind now detects your template files without a
contentarray. - Design tokens are exposed as native CSS custom properties, which any component, including one outside Tailwind, can read.
- The target browsers are modern ones. Check your analytics before you migrate, because older browsers are not supported by v4.
The old tailwind.config.js can still be loaded for a while, which lets a large project move in steps.
How do we migrate from Tailwind v3 to v4?
We run the official upgrade tool on a branch, then fix what it cannot. The tool handles renamed utilities and the new import. The manual part is where the risk sits: custom plugins, @apply in large stylesheets, third-party components that assumed v3 defaults, and visual changes from default values that moved.
Our sequence:
- Inventory the config, plugins and every place that uses
@applyor theme functions. - Run the upgrade tool and fix the build.
- Compare screens before and after with visual regression, so a changed default shows up as a diff and not as a bug report.
- Move the theme from the JavaScript config into
@theme, then delete the config. - Ship behind your normal release process, one app at a time in a monorepo.
Can you move us off Sass or CSS-in-JS?
Yes, and it is a different job from a version bump. The question is whether the move pays for itself.
| Starting point | What moves | Watch for |
|---|---|---|
| Sass with BEM | Components get utility classes, variables become tokens | Deep selector overrides that rely on cascade order |
| styled-components or Emotion | Styles leave the JavaScript bundle, runtime cost goes away | Dynamic props that computed styles at runtime |
| Bootstrap or a grid framework | Layout utilities replace the grid classes | Plugins that inject their own markup and styles |
Runtime CSS-in-JS is the strongest case, because it does not fit well with server components. For a stable Sass codebase that nobody complains about, we often recommend leaving it and using Tailwind only for new work.
How do design tokens work with @theme?
We treat @theme as the public interface of your design language. Raw values (palette, type scale, spacing) sit at the bottom. Above them go semantic tokens named by role, such as surface, text and accent, and components use only those. A dark theme, a second brand or a white-label tenant is then a second set of values, not a rewrite.
The same tokens can sync with Figma variables, so design and code read one source. When the tokens need to be shared across several products, the next step is a component library, which is the point where our shadcn development work and our design system agency team come in.
How do we keep large Tailwind codebases readable?
Utility classes get long. The fix is better boundaries: wrap repeated patterns in components instead of @apply chains, use a variant helper for components with many states, and sort classes automatically with the Prettier plugin.
What about performance?
Tailwind generates only the CSS your templates use, so the shipped stylesheet stays small as the app grows. The larger wins are usually elsewhere: removing a runtime CSS-in-JS library, avoiding a render-blocking font, and splitting critical CSS on content-heavy pages. We measure with Lighthouse and real-user data before and after, so you see what changed. If your site is built on Next.js or Astro, the styling work sits alongside the rest of our frontend development services.
What do we typically deliver?
- A v3 to v4 migration with a visual regression report.
- A Sass or CSS-in-JS replacement, screen by screen.
- A token layer in
@theme, wired to Figma variables if you use them. - A component set with documented variants.
- Handover notes on class conventions, so new engineers write code that matches.
Cost depends on stylesheet size, how much custom plugin code exists and whether the work includes a component library. It is scoped after a short audit of the repository.