# @layer Is Native CSS, Not a Tailwind Directive

**MUST** · **ID:** `layout-layer-directives` · **Category:** layout
**Source:** [Tailwind](https://tailwindcss.com/docs)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/layout-layer-directives

> MUST: Use @layer (base/components/utilities) for custom CSS to integrate with Tailwind specificity

Use @layer base for element defaults, but never define a utility inside @layer utilities — that no longer produces a real utility

The three-layer habit from v3 — `@layer base`, `@layer components`, `@layer utilities` — reads identically in v4 but means something else. Tailwind no longer intercepts these blocks; they are the browser's own cascade layers (`@layer` is real CSS now, and Tailwind registers `theme`, `base`, `components`, and `utilities` as native layers). One of the three still works exactly as you expect: `@layer base { h1 { ... } }` for element defaults and resets is correct v4. The other two changed. A class you write inside `@layer utilities` is no longer registered as a Tailwind utility — it is just a rule that happens to sit in a low-priority layer. So `hover:` and `md:` cannot be applied to it (the compiler has never heard of the name and will not generate the variant), and it has no place in Tailwind's property-count sort order, so its precedence against real utilities is whatever the layer position gives it. It looks like a utility, it is styled like a utility, and it fails the moment anyone tries to use it like a utility. The v4 answer is `@utility`, which registers the name with the compiler. See layout-custom-utilities.

## Bad — do not do this

`layout-layer-directives-bad`

```tsx
export function LayerDirectivesBad() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-1">The v3 three-layer habit</h4>
        <p className="text-xs text-muted-foreground mb-3">
          Reads the same in v4. Two of the three no longer mean what you think.
        </p>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-error">{`@layer components {
  .card { padding: 1rem; border-radius: 0.5rem; }
}

@layer utilities {
  .scrollbar-hide { scrollbar-width: none; }
}`}</pre>
        </div>
      </div>

      <div className="bg-card border border-border rounded-lg p-4">
        <h5 className="text-sm font-medium mb-1">At the call site</h5>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto mt-3">
          <pre className="text-error">{`<div class="hover:scrollbar-hide md:scrollbar-hide">
<!-- neither variant exists. both are dropped. -->`}</pre>
        </div>
        <ul className="mt-3 text-xs space-y-2">
          <li className="flex items-start gap-2">
            <span className="mt-1.5 w-1.5 h-1.5 shrink-0 rounded-full bg-error" />
            <span>
              In v4 <code className="font-mono">@layer</code> is a native CSS cascade layer. Tailwind does not read
              these blocks.
            </span>
          </li>
          <li className="flex items-start gap-2">
            <span className="mt-1.5 w-1.5 h-1.5 shrink-0 rounded-full bg-error" />
            <span>
              <code className="font-mono">.scrollbar-hide</code> is a plain class, not a registered utility — so the
              compiler cannot generate a variant of a name it has never heard of.
            </span>
          </li>
          <li className="flex items-start gap-2">
            <span className="mt-1.5 w-1.5 h-1.5 shrink-0 rounded-full bg-error" />
            <span>It has no place in the property-count sort order, so overriding it is a coin toss.</span>
          </li>
        </ul>
      </div>

      <p className="text-xs text-error">
        It looks like a utility and fails the moment anyone uses it like one.
      </p>
    </div>
  );
}
```

## Good — do this

`layout-layer-directives-good`

```tsx
export function LayerDirectivesGood() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-1">
          <code className="font-mono">@layer base</code> — still correct
        </h4>
        <p className="text-xs text-muted-foreground mb-3">
          Element defaults and resets. This is the one v4 use of <code className="font-mono">@layer</code>.
        </p>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-foreground">{`@layer base {
  h1 { font-size: var(--text-2xl); }
  a  { text-underline-offset: 2px; }
}`}</pre>
        </div>
      </div>

      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-1">
          <code className="font-mono">@utility</code> — for anything variant-addressable
        </h4>
        <p className="text-xs text-muted-foreground mb-3">
          Registers the name with the compiler, which is what buys you variants.
        </p>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-foreground">{`@utility scrollbar-hide {
  scrollbar-width: none;
}`}</pre>
        </div>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto mt-2">
          <pre className="text-foreground">{`<div class="hover:scrollbar-hide md:scrollbar-hide">
<!-- both variants generated -->`}</pre>
        </div>
      </div>

      <div className="bg-card border border-border rounded-lg p-4">
        <h5 className="text-sm font-medium mb-2">And the third block?</h5>
        <p className="text-xs text-muted-foreground">
          <code className="font-mono">@layer components</code> for a <code className="font-mono">.card</code> class has
          no v4 replacement because it needs none — extract a React component instead. See{' '}
          <span className="font-medium">&ldquo;@apply Is Not How You Share Styles&rdquo;</span>.
        </p>
      </div>

      <p className="text-xs text-success">
        @layer base for elements. @utility for utilities. Components for components.
      </p>
    </div>
  );
}
```

## References

- [Tailwind v4: Adding custom styles](https://tailwindcss.com/docs/adding-custom-styles)
- [Tailwind v4: Functions and directives](https://tailwindcss.com/docs/functions-and-directives)
- [MDN: @layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)
