Order Tailwind Styles in Layers
Here's how to order Tailwind CSS styles in CSS @layers
.
This articles has been updated for Tailwind v4. The original article for v3 is preserved below.
Insertion Order
This is for the veterans out there.
You find yourself using Tailwind to compile your CSS. It's your pal because you haven't had to create a .css file in a while. Well, there's that one file, maybe global.css
, where you're importing tailwind. Let's consider that file.
These days you can import Tailwind with one index.css import:
@import "tailwindcss"
But sometimes it's useful to import the bits of Tailwind separately. Ordering in CSS matters a lot. Let's take the scenario of where we have our own design system that implements a Tailwind theme and has component css selectors besides.
The conceptual order will be base styles like reset and tag selectors, then our design system, then all our utility classes, generated by Tailwind. Let's define that in CSS @layer
s.
@layer tailwind-base, design-system-base, design-system-components, tailwind-utilities;
@import 'tailwindcss/preflight.css' layer(tailwind-base);
@import 'tailwindcss/theme.css' layer(tailwind-base);
@import 'design-system-theme.css';
@import 'tailwindcss/utilities.css' layer(tailwind-utilities);
@import 'design-system-base.css' layer(design-system-base);
@import 'design-system-components.css' layer(design-system-components);
.my-project-widget {
color: red;
}
The individual Tailwind stylesheets and our other stylesheets are assigned a layer()
as they are imported.
The nice thing here is that we're using more native CSS at-rules, like @layer
. Gone are the @tailwind
directives. Well, there are still some around, like @utility
, which is from Tailwind, but they don't matter to this ordering example.
If you're on v4, you're done.
... Now, if you're still on v3, here's how the original article used to read:
For tailwindcss@3 - Insertion Order
This is for the veterans out there.
You find yourself using Tailwind to compile your CSS. It's your pal because you haven't had to create a .css file in a while. Well, there's that one file, maybe global.css
, where you're importing tailwind. Let's consider that file.
The imports come in 3 bits:
@tailwind base;
@tailwind components;
@tailwind utilities;
This specifies where the tailwind styles will be inserted into the file.
If you want your code to show up in one of those ordered spots, write your styles inside of @layer
directives. Tailwind specfies 3 special layers: base
, components
, and utilities
. So if I write custom code in:
@layer components {
.my-component {
color: red;
}
}
Once built, that .my-component
selector will show up in the @tailwind components
position.
There's a downside, in my opinion, though. It will have stripped the @layer
at-rule.
Layer Order
You see, CSS has a @layer
at rule. Tailwind seems nice because it's using the native keyword, but its directive acts differently by stripping the at-rule.
If you leave the @layer
at-rules in the built CSS, you get to control specificity/order with layers as well as the other tools of CSS. When Tailwind strips the @layer
at-rule, all those Tailwind-defined styles become immediately more specific than any of our app's layered styles.
Here's a grand diagram on where layers fit in the cascade.
How will we get layers back if tailwind is stripping them away? We can specify them again in our project globals.css
file.
@layer tailwind-base {
@tailwind base;
}
Now Tailwind's base styles will be in a layer we called "tailwind-base".
And if we extend that pattern, we can control all of Tailwind's styles and they interleave with our design system and project styles.
A full example of a global.css
:
@layer important-overrides, tailwind-base, design-system-base, tailwind-components, design-system-components, tailwind-utilities, project;
@import "design-system-base.css" layer(design-system-base);
@import "design-system-components.css" layer(design-system-components);
@layer tailwind-base {
@tailwind base;
}
@layer tailwind-components {
@tailwind components;
}
@layer tailwind-utilities {
@tailwind utilities;
}
@layer important-overrides {
.design-system-widget {
color: blue !important;
}
}
.my-project-widget {
color: red;
}
To note:
- We've defined our layer specificity order. Last one in wins (ie,
tailwind-utilities
). - It's the first instance of a
@layer
declaration that defines the order. This is the first line ofglobal.css
, where the order is given in one csv of layer names, without block bodies. - When
!important
is used, layer specificity is reverse. First one in wins (ie,important-overrides
). - Stylesheet
@import
s can be put into a layer if they don't have one themselves. This is done with thelayer()
function on import. - Selectors without a layer are more specific than any selectors in a layer. In this case
.my-project-widget
will have the most specific attributes.
Now you are rightly ordered, and you have beaten Tailwind.