Tailwind CSS Advanced: Systems, Plugins and Production
Migrating and Maintaining
In this lesson you will
- Plan and run a v3 to v4 upgrade
- Recognise the renamed utilities and changed defaults
- Set up conventions and tooling that keep a codebase maintainable
Introduction
Many existing projects use Tailwind v3. Tailwind v4 changed the set-up, some class names and some defaults. The changes below come from the official upgrade guide, and I compiled the important ones with Tailwind 4.3.3 to see what actually happens. Always read the current guide before you upgrade, because details can change between releases.
Before you upgrade
- Check browser support. The upgrade guide lists the minimum browsers as Safari 16.4, Chrome 111 and Firefox 128. If you must support older browsers, stay on v3.4.
- Use Node.js 20 or newer for the upgrade tool.
- Work on a new branch, with a clean working tree and passing tests.
- Take screenshots of key pages (or run visual tests) so you can compare after the upgrade.
The upgrade tool
Terminal
npx @tailwindcss/upgradeIt updates dependencies, migrates your configuration to CSS and rewrites many class names. Review the diff carefully, and test every important page. The tool handles most simple cases, but complex configs or unusual patterns need manual work.
What changes in set-up
| v3 | v4 |
|---|---|
@tailwind base; @tailwind components; @tailwind utilities; | @import "tailwindcss"; |
PostCSS plugin inside the tailwindcss package | @tailwindcss/postcss |
CLI inside tailwindcss | @tailwindcss/cli |
| Vite via PostCSS | @tailwindcss/vite is available |
postcss-import and autoprefixer needed | not needed |
tailwind.config.js detected automatically | not detected, load it with @config or move settings into CSS |
safelist option | @source inline() |
@layer utilities for custom classes | @utility |
theme(screens.xl) | theme(--breakpoint-xl) or var(--breakpoint-xl) |
corePlugins, safelist and separator are not supported. resolveConfig is removed, so read CSS variables, for example with getComputedStyle, if you need theme values in JavaScript.
Quick quiz
What replaces the three @tailwind directives of v3 in v4?
Removed utilities
Opacity utilities are gone. Use the slash modifier:
| Removed | Use instead |
|---|---|
bg-opacity-50, text-opacity-50 and similar | bg-black/50, text-black/50 |
I compiled bg-opacity-50 and text-opacity-50, and neither generated any CSS. bg-red-500/50 did.
Renamed utilities
The guide lists these renames, which shift the scale by one step:
| v3 | v4 |
|---|---|
shadow-sm → | shadow-xs |
shadow → | shadow-sm |
rounded-sm → | rounded-xs |
rounded → | rounded-sm |
blur-sm / blur → | blur-xs / blur-sm |
drop-shadow-sm / drop-shadow → | drop-shadow-xs / drop-shadow-sm |
ring (3px) → | ring-3 |
outline-none → | outline-hidden |
The danger is silent change: shadow-sm still exists in v4, but it now produces the larger shadow that shadow used to. I compared the output: shadow-xs is 0 1px 2px 0 with 5 percent black, and shadow-sm is a two-layer 0 1px 3px 0 and 0 1px 2px -1px shadow with 10 percent black. If you upgrade by hand, your pages may look different without any error. The upgrade tool renames these for you, which is a strong reason to use it.
Other names in the guide: flex-shrink-* became shrink-*, flex-grow-* became grow-*, overflow-ellipsis became text-ellipsis, and decoration-slice became box-decoration-slice. In my test with 4.3.3, the old names still generated CSS, so they may keep working for a while, but use the new names in all new code.
Changed defaults
These do not produce errors. They change how things look:
- Border and divide colour: the default is now
currentcolor, not light gray. Specify the colour, for exampleborder border-gray-200. I confirmed the base reset usesborder: 0 solidwith a current colour. - Ring: the default width is
1px(was3px) and the colour iscurrentcolor(was blue). Usering-3 ring-blue-500for the old look. My compile showedringas1px + offsetandring-3as3px. - Outline:
outlineis 1px, andoutline-<number>defaults to a solid style. - Placeholder colour: now the current text colour at 50 percent opacity.
- Button cursor: buttons use
cursor: default, notpointer. Addcursor-pointerif you want it, or a global rule. - Hover:
hover:applies only on devices that support hover. To restore the old behaviour, use@custom-variant hover (&:hover);. - Hidden attribute:
flexand similar no longer overridehidden.
Syntax changes
- Important modifier: put
!at the end:font-bold!. I compiled it and confirmed it adds!important. The old leading form!font-boldis deprecated, although it still compiled in my test. - CSS variables in arbitrary values:
bg-[--brand]becomesbg-(--brand). I confirmedp-(--my-pad)generatespadding: var(--my-pad). - Grid values: underscores, not commas, for spaces:
grid-cols-[max-content_auto]. - Variant order: variants stack left to right.
first:*:pt-0becomes*:first:pt-0. - Gradients: variants no longer reset the whole gradient. Use
via-noneto unset a middle stop. - Prefix: the prefix comes first,
tw:flex, and is configured with@import "tailwindcss" prefix(tw);. - Transforms:
transform-noneno longer resets individual transforms. Usescale-none,rotate-noneortranslate-none. - Container: the
centerandpaddingoptions are gone. Customise it with@utility container { margin-inline: auto; padding-inline: 2rem; }.
Testing the upgrade
- Run the build and fix errors.
- Search the code for removed and renamed utilities (
bg-opacity,shadow-sm,rounded,ring,outline-none,flex-shrink). - Compare screenshots of key pages in light and dark themes and at several widths.
- Test keyboard focus, hover and form states.
- Check the production CSS size.
Keeping a Tailwind codebase healthy
- Editor support: the official IntelliSense extension offers autocomplete and shows the CSS each class generates.
- Class sorting: the official Prettier plugin sorts classes into a consistent order, which reduces noisy diffs and merge conflicts.
- Linting: use lint rules to flag unknown classes, conflicting classes and arbitrary values you want to restrict.
- Conventions: write down when to use components,
@apply, arbitrary values and tokens (see the design systems lesson). - Upgrades: update Tailwind regularly in small steps. Read release notes and run the visual tests.
- Dependencies: keep
tailwind-mergeand plugins compatible with your Tailwind version. A mismatch can silently produce wrong merges. - Documentation: keep a living style guide of components and tokens.
- Review: treat long class lists as a signal to extract a component, not as a failure.
Looking ahead
Tailwind keeps adding modern CSS features, such as container queries, @starting-style, field-sizing and text shadows, as they become available in browsers. The core idea stays the same: small utilities, a shared theme and your own components built on top. Practise by building a complete project, upgrading it once and writing down what you learned.
Try it yourself
Take a small v3 project (or a copy of a v4 one with renamed classes). Run the upgrade tool on a branch, read every change in the diff, and list the three differences you would have missed doing it by hand. Add the Prettier plugin and a visual test for one page.
Summary
- Upgrade on a branch with
npx @tailwindcss/upgrade, check browser support first and review the whole diff. - Watch for removed opacity utilities, renamed scales (
shadow-smnow means the oldshadow), and changed defaults for borders, rings, hover and buttons. - Keep the codebase healthy with the official editor support and Prettier plugin, written conventions, visual tests and regular small upgrades.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading