26

I want to create some custom styles for my UI and in the official Tailwind documentation described 2 options to accomplish it.

  1. @apply - Use @apply to inline any existing utility classes into your own custom CSS.
  2. @layer - Use the @layer directive to tell Tailwind which “bucket” a set of custom styles belong to.

So far I understand that @apply just creates a custom style class and adds it to the Tailwind system. It's exactly what I need and it's fine, but I'm also wondering about the @layer directive.

Based on the description, it adds a bucket of custom styles to the tailwind layout system, but I can't understand what benefits this move gives to us.

What if I just use @apply and that's all?
Will it harm performance?
So I'm just trying to understand in what cases should I use @layer.

0

2 Answers 2

40

Actually you can freely use both (or none of them) at the same time - there are no restrictions. You can even not to use @tailwind directives but you will loose whole point of using Tailwind then. And no - @apply directive does not adds custom styles into Tailwind's system (it just converts utility class into CSS property and works kinda as CSS alias) while @layer does add styles into Tailwind.

There are few benefits described by Tailwind itself

  1. Tailwind will automatically move any CSS within a @layer directive to the same place as the corresponding @tailwind rule, so you don’t have to worry about authoring your CSS in a specific order to avoid specificity issues.

Tailwind "separates" all classes into three categories - base (any base styles, e.g. heading, paragraphs, images - it is modified normalize.css), utilities (one-property classes - e.g. text-sm responds only for text size) and components (multiple CSS properties, like buttons, containers, etc). However the border between these definitions is quite blurry

Let say we have this CSS file

@tailwind base; @tailwind utilities; 

Here you may inject these styles by using @tailwind directives - @tailwind base will inject all Tailwind's base styles, any styles registered via @layer base and registered via config file or plugins.

And if you define base style for h1 tag one after another

@layer utilities { h1 { @apply text-7xl; } } @layer base { h1 { @apply text-2xl; } } 

Despite text-2xl was called later and you would expect it will overwrite text-7xl in fact you will see the opposite - that is because utilities were injected after base in compiled file. Change the order

@tailwind utilities; @tailwind base; 

and now h1 will be styled by text-2xl utility. It may help to keep track of high-priority CSS selectors placed in different source files

  1. Any custom CSS added to a layer will only be included in the final build if that CSS is actually used in your HTML, just like all of the classes built in to Tailwind by default.

Let's register two buttons

@tailwind base; @tailwind components; @tailwind utilities; .btn-blue { @apply px-4 py-2 inline-block bg-blue-500; } @layer components { .btn-red { @apply px-4 py-2 inline-block bg-red-500; } } 

And HTML file would be (we're using only one of them)

<button type="button" class="btn-blue"> Button </button> 

If you compile CSS you will see only .btn-blue class being compiled in final CSS. That means that any non-used components registered via @tailwind components or config will be purged from final build. However if you change HTML into

<button type="button" class="btn-red"> Button </button> 

You will see both .btn-blue and .btn-red in compiled file. So in this case .btn-blue acts like regular CSS - it has not being purged despite it is not used. So answering on "Will it harm performance" - in some manner yes, as using @layer directive prevent unused styles to be compiled into bundle. It is good for reusable components "from project to project", where let say you have input, button, select components, etc, but not every project will use them

  1. Wrapping any custom CSS in a @layer directive also makes it possible to use modifiers with those rules, like hover: and focus: or responsive modifiers like md: and lg:

This simply means that you can use components classes with variants like hover:btn-red - all px-4 py-2 inline-block bg-red-500 now will be applied only on hover.

In order to make sense let's change HTML a little

<button type="button" class="hidden sm:btn-red"> Button </button> 

Now you can see on mobile devices button will be hidden and only after sm-breakpoint button styles will be applied. You CANNOT do the same with sm:btn-blue - as it is not registered as component and Tailwind does know nothing about it

Same is valid for native Tailwind components or any

<div class="sm:container"> I will behave as container only after 640px screen width </div> 

Another way to use it - override defined properties. Back to red button you will see it has px-4 property. But if you wish, you may change it

<button type="button" class="btn-red px-8"> Button (with px-8 being applied) </button> <button type="button" class="btn-blue px-8"> Button (px-8 being ignored) </button> 

There are almost no restrictions in using variant with @apply so instead of hidden sm:btn-red you can use simply btn-red and change CSS into

@layer components { .btn-red { @apply px-4 py-2 hidden sm:inline-block bg-red-500; } } 

P.S. In all examples I've used @apply directive - however you may write any CSS instead or even mix them

Next part is opinion based and can be skipped

Despite it's a good feature to have I personally almost never use it. I never had issue with selector orders or stacking custom components with variants. Maybe hidden sm:btn-blue may have more potential to readability - like you know for sure it is hidden on mobile and only after some breakpoint there will be button in this place

It is good unused styles may be purged. However there may be some situations where you do NOT want such behaviour - when your classes comes from external API or backend and you still want them to be compiled in final build. This way either safelist it or just don't use @layer at all

Also if I starts a new project with predefined UI-kit, I'm always sure, I will use .btn-blue class - and if not - why should I even create such stylings in a first place?

So it's up to you and your project would you like to use it or not

Sign up to request clarification or add additional context in comments.

1 Comment

It's a very detailed informative answer. Basically, I agree, layout is a very specific thing and benefits are vague. Thank you Ihar for your effort and time, I didn't expect such great explanation. I've finally understood some tech nuances I've never had an idea about.
5

Basically, @IharAljaksszejenka has shared everything worth knowing. However, with the release of TailwindCSS v4, some structural changes have occurred.

@layer

The @layer CSS at-rule is used to declare a cascade layer and can also be used to define the order of precedence in case of multiple cascade layers.

The @tailwind directives have been removed. Instead, can add all TailwindCSS layers and styling to our project with a single import:

@import "tailwindcss"; 

This import is, by the way, a shorthand for this:

@layer theme, base, components, utilities; @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/utilities.css" layer(utilities); 

First, define the strength of the layers relative to each other (there are no two layers with the same strength). After that, we import the individual parts of TailwindCSS into the appropriate layer (but this is all handled by @import "tailwindcss";).

Starting from v4, your unlayered styles will be stronger than any TailwindCSS styles, as every TailwindCSS class has been placed into a separate layer. Therefore, it's also a good idea to place our default settings in one of the layers where we find it logically appropriate.

Unlayered (non-layered) styles are by default stronger than any styles placed in a layer. This is determined by CSS syntax, not by TailwindCSS. The following are useful for understanding how CSS specificity works:

Starting from v4, a new @utility CSS directive has been introduced in TailwindCSS. This replaces the classes we previously placed in @layer components and @layer utilities. But why is it beneficial to use? Well, with @utility, TailwindCSS can better determine where in the compiled CSS this should be placed. If we don't use @utility and instead add it manually, placing the class in the layer ourselves, it could lead to unexpected behavior.

@apply

A long-existing TailwindCSS-specific directive that allows us to nest classes under other classes. This way, don't even need to write actual CSS code or variables in the CSS; it's enough to know the appropriate classes we want to apply:

.custom { @apply bg-red-800 text-white border-2; } 

It's important to note that starting from v4, when using the @apply (or @variant) directive within <style> blocks in Vue, Svelte, or Astro, the use of @reference is required.

<style> @reference "../../app.css"; .custom { @apply bg-red-800 text-white border-2; } </style> 

Although the official recommendations prefer the use of CSS variables and advise against excessive use of @apply:

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.