Using @apply for Duplication

Learn the usage of @apply and @layer directives.

@apply directive

The @apply directive lets us use Tailwind classes in the definition of other CSS selectors. So, we can redefine our header classes in CSS like this:

@layer components {
  .title { @apply text-6xl font-bold }
  .subtitle { @apply text-4xl font-semibold }
  .subsubtitle { @apply text-lg font-medium italic }
}

We can use these like any other CSS classes:

<div class="title">Title</div>

@layer directive

The @layer directive can either be base, components, or utilities. As far as the browser is concerned, if we use @layer, the selectors are defined as part of whatever layer we declare, no matter where the selector definitions are actually located in our CSS files.

Using @layer components defines the selector as part of the components and before the utilities. This means if we combine one of our definitions with a Tailwind utility, the utility wins, which we want. We can define, say, a big title using the below command:

<div class="title text-5xl">Title</div>

To understand why @layer is important, we need to know a general principle of CSS, all else being equal if two CSS classes are trying to adjust the same underlying properly, the one defined last wins. We know that there is also a principle of specificity, where the most specific definition wins, but because all the Tailwind utilities have the same specificity, that is not an issue here.

In a CSS file, if we have two definitions for the same CSS selector that define the same property, the selector defined later in the file wins. In Tailwind, if we have two utility classes that define the same property, the latter class wins, so class="text-xl text-2xl" will give our text that is sized 2xl.

Selector inside a layer

When we define a custom selector inside a layer, the selector is loaded at the end of that layer and before the next layer. This has some consequences for how custom CSS might interact with other Tailwind utilities or CSS. For example, we can make our definitions part of the HTML by using @apply on tags, not class selectors. In this case, we put the definition in the base layer:

@layer base {
  h1 { @apply text-4xl font-bold }
  h2 { @apply text-2xl font-semibold }
  h3 { @apply text-lg font-medium italic }
}

Here, we redefine the h1, h2, and h3 elements directly, so we can use the following commands:

<h1>Title</h1>

By being in the base layer, these definitions execute before all utilities, so that <h1 class="text-6xl"> behaves as we would want, with the 6xl taking precedence. If h1 was defined in the utilities layer, it would have precedence, because it’s defined later than text-6xl. Tailwind will consider this part of the Preflight styles and define it before any components because we have moved the layer to base. This placement allows us to mix tags, components, and utilities as expected.

This is all quite useful, and it allows us to effectively build up our own framework using Tailwind utilities as building blocks. It helps us realize that we are building up a framework and taking upon ourselves all the attendant naming and maintenance responsibilities.

In the following playground, we try the text sizes and styles as follows:

  • text-5xl
  • text-4xl and font-bold
  • text-2xl and font-semibold
  • text-lg, font-medium, and italic

Get hands-on with 1200+ tech skills courses.