{"id":372576,"date":"2022-08-24T06:11:47","date_gmt":"2022-08-24T13:11:47","guid":{"rendered":"https:\/\/css-tricks.com\/?p=372576"},"modified":"2022-08-24T06:11:48","modified_gmt":"2022-08-24T13:11:48","slug":"using-css-cascade-layers-to-manage-custom-styles-in-a-tailwind-project","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/using-css-cascade-layers-to-manage-custom-styles-in-a-tailwind-project\/","title":{"rendered":"Using CSS Cascade Layers to Manage Custom Styles in a Tailwind Project"},"content":{"rendered":"\n

If a utility class only does one thing, chances are you don\u2019t want it to be overridden by any styles coming from elsewhere. One approach is to use !important<\/code> to be 100% certain the style will be applied, regardless of specificity conflicts.<\/p>\n\n\n\n

The Tailwind config file has an !important<\/code> option that will automatically add !important<\/code> to every utility class. There\u2019s nothing wrong with using !important<\/code> this way, but nowadays there are better ways to handle specificity. Using CSS Cascade Layers<\/a> we can avoid the heavy-handed approach of using !important<\/code>.<\/p>\n\n\n\n\n\n\n\n

Cascade layers allow us to group styles into \u201clayers\u201d. The precedence of a layer always beats the specificity of a selector. Specificity only matters inside each layer. Establishing a sensible layer order helps avoid styling conflicts and specificity wars. That\u2019s what makes CSS Cascade Layers a great tool for managing custom styles alongside styles from third-party frameworks<\/a>, like Tailwind.<\/p>\n\n\n\n

A Tailwind source .css<\/code> file usually starts something like this:<\/p>\n\n\n\n

@tailwind base;\n@tailwind components;\n@tailwind utilities;\n@tailwind variants;<\/code><\/pre>\n\n\n\n

Let\u2019s take a look at the official Tailwind docs<\/a> about directives:<\/p>\n\n\n\n

Directives are custom Tailwind-specific at-rules you can use in your CSS that offer special functionality for Tailwind CSS projects. Use the @tailwind<\/code> directive to insert Tailwind\u2019s base<\/code>, components<\/code>, utilities<\/code> and variants<\/code> styles into your CSS.<\/p><\/blockquote>\n\n\n\n

In the output CSS file that gets built, Tailwind\u2019s CSS reset \u2014 known as Preflight<\/a> \u2014 is included first as part of the base styles. The rest of base<\/code> consists of CSS variables needed for Tailwind to work. components<\/code> is a place for you to add your own custom classes. Any utility classes you\u2019ve used in your markup will appear next. Variants are styles for things like hover and focus states and responsive styles, which will appear last in the generated CSS file.<\/p>\n\n\n

The Tailwind @layer<\/code> directive<\/h3>\n\n\n

Confusingly, Tailwind has its own @layer<\/code> syntax. This article is about the CSS standard, but let\u2019s take a quick look at the Tailwind version (which gets compiled away and doesn\u2019t end up in the output CSS). The Tailwind @layer<\/code> directive is a way to inject your own extra styles into a specified part of the output CSS file.<\/p>\n\n\n\n

For example, to append your own styles to the base<\/code> styles, you would do the following:<\/p>\n\n\n\n

@layer base {\n  h1 {\n    font-size: 30px;\n  }\n}<\/code><\/pre>\n\n\n\n

The components<\/code> layer is empty by default \u2014 it\u2019s just a place to put your own classes. If you were doing things the Tailwind way, you\u2019d probably use @apply<\/code><\/a> (although the creator of Tailwind recently advised against it<\/a>), but you can also write classes the regular way:<\/p>\n\n\n\n

@layer components {\n  .btn-blue {\n    background-color: blue;\n    color: white;\n  }\n}<\/code><\/pre>\n\n\n\n

The CSS standard is much more powerful. Let\u2019s get back to that\u2026<\/p>\n\n\n

Using the CSS standard @layer<\/code><\/h3>\n\n\n

Here\u2019s how we can rewrite this to use the CSS standard @layer<\/code>:<\/p>\n\n\n\n

@layer tailwind-base, my-custom-styles, tailwind-utilities;\n\n@layer tailwind-base {\n  @tailwind base;\n}\n\n@layer tailwind-utilities {\n  @tailwind utilities;\n  @tailwind variants;\n} <\/code><\/pre>\n\n\n\n

Unlike the Tailwind directive, these don\u2019t get compiled away. They\u2019re understood by the browser. In fact, DevTools in Edge, Chrome, Safari, and Firefox will even show you any layers you\u2019ve defined.<\/p>\n\n\n\n

\"CSS<\/figure>\n\n\n\n

You can have as many layers as you want \u2014 and name them whatever you want \u2014 but in this example, all my custom styles are in a single layer (my-custom-styles<\/code>). The first line establishes the layer order:<\/p>\n\n\n\n

@layer tailwind-base, my-custom-styles, tailwind-utilities;<\/code><\/pre>\n\n\n\n

This needs to be provided upfront. Be sure to include this line before any other code that uses @layer<\/code>. The first layer in the list will be the least<\/em> powerful, and the last layer in the list will be the most<\/em> powerful. That means tailwind-base<\/code> is the least powerful<\/em> layer and any code in it will be overridden by all the subsequent layers. That also means tailwind-utilities<\/code> will always trump any other styles \u2014 regardless of source order or specificity<\/em>. (Utilities and variants could<\/em> go in separate layers, but the maintainers of Tailwind will ensure variants always trump utilities, so long as you include the variants below the utilities directive.)<\/p>\n\n\n\n

Anything that isn\u2019t in a layer will override anything that is in a layer (with the one exception being styles that use !important<\/code>). So, you could also opt to leave utilities<\/code> and variants<\/code> outside of any layer:<\/p>\n\n\n\n

@layer tailwind-base, tailwind-components, my-custom-styles;\n\n@layer tailwind-base {\n  @tailwind base;\n}\n\n@layer tailwind-components {\n  @tailwind components;\n}\n\n@tailwind utilities;\n@tailwind variants;<\/code><\/pre>\n\n\n\n

What did this actually buy us? There are plenty of times when advanced CSS selectors come in pretty handy. Let\u2019s create a version of :focus-within<\/code> that only responds to keyboard focus rather than mouse clicks using the :has<\/code> selector (which lands in Chrome 105<\/a>). This will style a parent element when any of its children receive focus. Tailwind 3.1 introduced custom variants<\/a> \u2014 e.g. <div class=\"[&:has(:focus-visible)]:outline-red-600\"><\/code> \u2014 but sometimes it\u2019s easier to just write CSS:<\/p>\n\n\n\n

@layer tailwind-base, my-custom-styles;\n@layer tailwind-base {\n  @tailwind base;\n}\n\n@tailwind utilities;\n\n@layer my-custom-styles {\n  .radio-container {\n    padding: 4px 24px;\n    border: solid 2px rgb(230, 230, 230);\n  }\n  .radio-container:has(:focus-visible) {\n    outline: solid 2px blue;\n  }\n}<\/code><\/pre>\n\n\n\n

Let\u2019s say in just one instance we want to override the outline-color<\/code> from blue<\/code> to something else. Let\u2019s say the element we\u2019re working with has both the Tailwind class .outline-red-600<\/code> and our own .radio-container:has(:focus-visible)<\/code> class:<\/p>\n\n\n\n

<div class=\"outline-red-600 radio-container\"> ... <\/div><\/code><\/pre>\n\n\n\n

Which outline-color<\/code> will win?<\/p>\n\n\n\n

Ordinarily, the higher specificity of .radio-container:has(:focus-visible)<\/code> would mean the Tailwind class has no effect \u2014 even if it\u2019s lower in the source order. But, unlike the Tailwind @layer<\/code> directive that relies on source order, the CSS standard @layer<\/code> overrules specificity.<\/p>\n\n\n\n