|
| 1 | +--- |
| 2 | +name: tailwind-impl-apply-directive |
| 3 | +description: > |
| 4 | + Use when writing custom CSS that needs to share design tokens with |
| 5 | + Tailwind utilities via @apply, organizing component / base / utility |
| 6 | + CSS via @layer, or fixing the "Cannot apply unknown utility class" |
| 7 | + error in Vue, Svelte, or CSS-module scoped styles by adding |
| 8 | + @reference. Prevents the utility-extraction anti-pattern (wrapping |
| 9 | + every utility in @apply destroys utility-first benefits like JIT |
| 10 | + pruning, in-context readability, and consistent design tokens), the |
| 11 | + scoped-style trap in v4 (Vue SFC, Svelte SFC, and CSS modules |
| 12 | + evaluate @apply against an empty token registry without @reference, |
| 13 | + failing with "Cannot apply unknown utility class"), the layer-order |
| 14 | + trap (custom CSS outside @layer leaks above utilities and gets |
| 15 | + overridden), the important-modifier syntax flip between v3 |
| 16 | + (leading bang, !font-bold) and v4 (trailing bang, font-bold!), and |
| 17 | + the @apply !important keyword vs modifier confusion. Covers @apply |
| 18 | + semantics (copies utility declarations into the current rule), |
| 19 | + @layer base / components / utilities (specificity ascends in that |
| 20 | + order, matches @tailwind ordering in v3 and @import "tailwindcss" |
| 21 | + cascade in v4), @reference "path" (v4 only, makes tokens visible |
| 22 | + in scoped contexts without duplicating CSS), the v3 vs v4 important |
| 23 | + syntax flip, plugin-vs-layer ordering, and when @apply is the right |
| 24 | + tool versus when component extraction (React / Vue / Svelte |
| 25 | + component) is the right tool instead. |
| 26 | + Keywords: tailwind @apply, @apply utility, apply directive, |
| 27 | + custom CSS tailwind, share tokens custom CSS, @layer base |
| 28 | + components utilities, layer order tailwind, tailwind 3 |
| 29 | + @apply !important, tailwind 4 @apply font-bold!, important |
| 30 | + modifier tailwind, !font-bold v3, font-bold! v4, leading |
| 31 | + bang vs trailing bang, @reference tailwindcss, @reference |
| 32 | + app.css, Cannot apply unknown utility class, Cannot apply |
| 33 | + unknown utility, vue scoped style apply, svelte scoped |
| 34 | + style apply, css modules apply, scoped @apply broken v4, |
| 35 | + tailwind component extraction, when not to use @apply, |
| 36 | + @apply anti pattern, button class @apply, when to extract |
| 37 | + utilities, plugin order @layer, third-party library |
| 38 | + override tailwind, override third party styles, @apply |
| 39 | + inside scoped style, tailwind 3-4 directive differences. |
| 40 | +license: MIT |
| 41 | +compatibility: "Designed for Claude Code. Requires Tailwind CSS v3.4 or v4.0+." |
| 42 | +metadata: |
| 43 | + author: OpenAEC-Foundation |
| 44 | + version: "1.0" |
| 45 | +--- |
| 46 | + |
| 47 | +# @apply, @layer, @reference |
| 48 | + |
| 49 | +`@apply` lets custom CSS reuse Tailwind utility declarations. `@layer` |
| 50 | +sorts custom CSS into Tailwind's cascade. `@reference` (v4) makes |
| 51 | +tokens visible inside scoped style blocks. |
| 52 | + |
| 53 | +ALWAYS prefer component extraction (React, Vue, Svelte component) |
| 54 | +over @apply when the consumer is a component framework. NEVER reach |
| 55 | +for @apply just because a class name appears twice. See |
| 56 | +`references/anti-patterns.md` AP-1 for the full philosophy. |
| 57 | + |
| 58 | +Companion skills : |
| 59 | + |
| 60 | +- `tailwind-impl-config-v3` : v3 JS-config and PostCSS pipeline |
| 61 | +- `tailwind-impl-config-v4` : v4 CSS-first config and directives |
| 62 | +- `tailwind-impl-build-vite` : Vite plugin wiring |
| 63 | + |
| 64 | +## Quick Reference |
| 65 | + |
| 66 | +### @apply (both versions) |
| 67 | + |
| 68 | +```css |
| 69 | +.btn-primary { |
| 70 | + @apply rounded-lg bg-blue-500 px-4 py-2 text-white; |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +ALWAYS use inside a regular CSS selector, NEVER at the top level. The |
| 75 | +declarations are copied verbatim from the utility class into the rule. |
| 76 | + |
| 77 | +### @layer (both versions) |
| 78 | + |
| 79 | +```css |
| 80 | +@layer base { |
| 81 | + h1 { @apply text-4xl font-bold; } |
| 82 | +} |
| 83 | + |
| 84 | +@layer components { |
| 85 | + .btn { @apply rounded bg-blue-500 px-4 py-2 text-white; } |
| 86 | +} |
| 87 | + |
| 88 | +@layer utilities { |
| 89 | + .text-shadow-sm { text-shadow: 0 1px 2px rgb(0 0 0 / 0.1); } |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +Order in the cascade : `base < components < utilities`. Anything in |
| 94 | +`utilities` wins over `components` ; anything in `components` wins |
| 95 | +over `base`. |
| 96 | + |
| 97 | +### @reference (v4 only) |
| 98 | + |
| 99 | +```vue |
| 100 | +<style scoped> |
| 101 | +@reference "../app.css"; |
| 102 | +
|
| 103 | +.card { @apply rounded bg-white shadow; } |
| 104 | +</style> |
| 105 | +``` |
| 106 | + |
| 107 | +The first line MUST be `@reference` when the scoped block uses |
| 108 | +`@apply` or `@variant`. Without it, v4 raises `Cannot apply unknown |
| 109 | +utility class`. |
| 110 | + |
| 111 | +### Important modifier syntax flip |
| 112 | + |
| 113 | +| Version | In HTML class | Inside @apply | |
| 114 | +| ------- | ---------------- | ----------------------------------- | |
| 115 | +| v3 | `!font-bold` | `@apply font-bold !important;` | |
| 116 | +| v4 | `font-bold!` | `@apply font-bold!;` | |
| 117 | + |
| 118 | +NEVER write `!font-bold` in v4. NEVER write `font-bold!` in v3. |
| 119 | + |
| 120 | +## Decision Trees |
| 121 | + |
| 122 | +### Should I use @apply or extract a component ? |
| 123 | + |
| 124 | +``` |
| 125 | +Are you in a component framework (React, Vue, Svelte, Solid) ? |
| 126 | +├── Yes : ALWAYS extract a component. Pass props for variants. |
| 127 | +│ @apply is the wrong tool inside a framework that already |
| 128 | +│ gives you component reuse with type-checked props. |
| 129 | +└── No : continue |
| 130 | +Is the consumer plain HTML, MDX, or a CMS template ? |
| 131 | +├── Yes : @apply is acceptable for shared class names (.btn, .card). |
| 132 | +│ Document the class semantics next to the @apply rule. |
| 133 | +└── No : continue |
| 134 | +Is this a third-party-library override |
| 135 | +(react-select dropdown, Algolia DocSearch, embedded widget) ? |
| 136 | +├── Yes : @apply is the right tool. The library owns the markup ; |
| 137 | +│ you can only style it from outside. |
| 138 | +└── No : revisit. Most uses of @apply outside the above two cases |
| 139 | + are utility hiding, not utility reuse. |
| 140 | +``` |
| 141 | + |
| 142 | +### Which @layer should this rule go in ? |
| 143 | + |
| 144 | +``` |
| 145 | +What kind of CSS am I adding ? |
| 146 | +├── Global element default (h1, p, body) : @layer base |
| 147 | +├── Named, multi-utility component class (.btn) : @layer components |
| 148 | +├── Single-purpose helper class (.text-shadow) : @layer utilities |
| 149 | +└── None : Tailwind will sort it AFTER utilities. Almost always wrong. |
| 150 | +``` |
| 151 | + |
| 152 | +### My @apply errors with "Cannot apply unknown utility class". Why ? |
| 153 | + |
| 154 | +``` |
| 155 | +v3 or v4 ? |
| 156 | +├── v3 : the utility is not generated. |
| 157 | +│ - JIT did not see the class in any scanned file |
| 158 | +│ - The class is typo'd or uses a custom token that is not |
| 159 | +│ registered in tailwind.config.js theme |
| 160 | +│ Fix : add the file to content, fix the typo, or extend the theme. |
| 161 | +└── v4 : you are inside a scoped style block (Vue, Svelte, CSS module). |
| 162 | + Tokens are not visible there by default. |
| 163 | + Fix : add `@reference "../app.css";` as the FIRST line of the |
| 164 | + scoped block. The path points to your global entry CSS. |
| 165 | +``` |
| 166 | + |
| 167 | +## Patterns |
| 168 | + |
| 169 | +### Pattern : extracting a component class |
| 170 | + |
| 171 | +```css |
| 172 | +@layer components { |
| 173 | + .btn { |
| 174 | + @apply inline-flex items-center rounded px-4 py-2 font-medium; |
| 175 | + } |
| 176 | + .btn-primary { |
| 177 | + @apply btn bg-blue-500 text-white hover:bg-blue-600; |
| 178 | + } |
| 179 | +} |
| 180 | +``` |
| 181 | + |
| 182 | +ALWAYS put @apply rules inside `@layer components`. Without the layer, |
| 183 | +the rule lands after Tailwind utilities, so `class="btn px-2"` cannot |
| 184 | +override the layer's `px-4` without `!`. |
| 185 | + |
| 186 | +### Pattern : overriding third-party widget CSS |
| 187 | + |
| 188 | +```css |
| 189 | +@layer components { |
| 190 | + .select2-dropdown { |
| 191 | + @apply rounded-b-lg shadow-md; |
| 192 | + } |
| 193 | + .ais-SearchBox-input { |
| 194 | + @apply rounded border border-gray-300 px-3 py-2; |
| 195 | + } |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +When the library renders markup you do not control, @apply is the |
| 200 | +clean bridge between your design tokens and their selectors. |
| 201 | + |
| 202 | +### Pattern : base layer for element defaults |
| 203 | + |
| 204 | +```css |
| 205 | +@layer base { |
| 206 | + body { |
| 207 | + @apply bg-gray-50 text-gray-900 antialiased; |
| 208 | + } |
| 209 | + h1 { @apply text-4xl font-bold tracking-tight; } |
| 210 | + h2 { @apply text-3xl font-semibold; } |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +Base layer rules apply to every matching element without needing a |
| 215 | +class. ALWAYS use base for "every h1 should look like this" not |
| 216 | +"this specific h1 should look like this". |
| 217 | + |
| 218 | +### Pattern : v4 scoped @apply in Vue / Svelte |
| 219 | + |
| 220 | +Vue SFC : |
| 221 | + |
| 222 | +```vue |
| 223 | +<script setup lang="ts"> |
| 224 | +import "./style.css" |
| 225 | +</script> |
| 226 | +
|
| 227 | +<template> |
| 228 | + <button class="btn">Save</button> |
| 229 | +</template> |
| 230 | +
|
| 231 | +<style scoped> |
| 232 | +@reference "./style.css"; |
| 233 | +
|
| 234 | +.btn { |
| 235 | + @apply rounded bg-blue-500 px-4 py-2 text-white; |
| 236 | +} |
| 237 | +</style> |
| 238 | +``` |
| 239 | + |
| 240 | +Svelte component : |
| 241 | + |
| 242 | +```svelte |
| 243 | +<style> |
| 244 | + @reference "../app.css"; |
| 245 | +
|
| 246 | + h1 { @apply text-3xl font-bold; } |
| 247 | +</style> |
| 248 | +``` |
| 249 | + |
| 250 | +The `@reference` line is REQUIRED in v4 for scoped @apply. It loads |
| 251 | +the token registry into the file's parse context WITHOUT emitting |
| 252 | +the global CSS again. |
| 253 | + |
| 254 | +### Pattern : !important modifier |
| 255 | + |
| 256 | +v3 in HTML : |
| 257 | + |
| 258 | +```html |
| 259 | +<button class="!font-bold">Force bold</button> |
| 260 | +``` |
| 261 | + |
| 262 | +v3 inside @apply : |
| 263 | + |
| 264 | +```css |
| 265 | +.btn { @apply font-bold !important; } |
| 266 | +``` |
| 267 | + |
| 268 | +v4 in HTML : |
| 269 | + |
| 270 | +```html |
| 271 | +<button class="font-bold!">Force bold</button> |
| 272 | +``` |
| 273 | + |
| 274 | +v4 inside @apply : |
| 275 | + |
| 276 | +```css |
| 277 | +.btn { @apply font-bold!; } |
| 278 | +``` |
| 279 | + |
| 280 | +NEVER mix the syntaxes. The v3 leading-bang form does NOT parse in v4. |
| 281 | + |
| 282 | +## Anti-Patterns (summary) |
| 283 | + |
| 284 | +NEVER wrap every reused utility set in @apply. Component extraction |
| 285 | +(React/Vue/Svelte component with props) is the right tool inside a |
| 286 | +component framework. |
| 287 | + |
| 288 | +NEVER put @apply rules outside `@layer`. They land after Tailwind's |
| 289 | +utility layer and lose all override behavior. |
| 290 | + |
| 291 | +NEVER omit `@reference` in v4 Vue / Svelte / CSS-modules scoped styles |
| 292 | +that use @apply. The error is always "Cannot apply unknown utility class". |
| 293 | + |
| 294 | +NEVER mix `!font-bold` (v3) and `font-bold!` (v4) syntax across versions. |
| 295 | + |
| 296 | +NEVER use @apply for a single utility. `class="font-bold"` in the |
| 297 | +template is already that. |
| 298 | + |
| 299 | +See `references/anti-patterns.md` for the full catalog. |
| 300 | + |
| 301 | +## Plugin Order and @layer Interaction |
| 302 | + |
| 303 | +Tailwind sorts the final CSS as : |
| 304 | + |
| 305 | +1. `@layer base` (your custom base + Tailwind preflight) |
| 306 | +2. `@layer components` (your component classes + plugin component classes) |
| 307 | +3. `@layer utilities` (your utility classes + Tailwind utilities + plugin utilities) |
| 308 | + |
| 309 | +A custom utility class declared via `@layer utilities` competes with |
| 310 | +Tailwind utilities at the same specificity. ALWAYS rely on order |
| 311 | +within the layer (later wins) rather than `!important`. |
| 312 | + |
| 313 | +Plugins that register utilities or components inject INTO these layers. |
| 314 | +A `addUtilities({ ... })` call lands in `utilities`, NOT before yours. |
| 315 | +ALWAYS verify final order in DevTools when a plugin override misbehaves. |
| 316 | + |
| 317 | +## v3 vs v4 Differences |
| 318 | + |
| 319 | +| Feature | v3 | v4 | |
| 320 | +| ------------------------ | ---------------------------------------- | ---------------------------------------- | |
| 321 | +| @apply syntax | `@apply font-bold;` | `@apply font-bold;` | |
| 322 | +| Important inside @apply | `@apply font-bold !important;` | `@apply font-bold!;` | |
| 323 | +| Important in HTML | `class="!font-bold"` | `class="font-bold!"` | |
| 324 | +| @layer support | `@layer base/components/utilities` | `@layer base/components/utilities` | |
| 325 | +| @reference | NOT supported (not needed) | REQUIRED in scoped style blocks | |
| 326 | +| Scoped @apply works ? | Yes, via PostCSS context | Only with `@reference` | |
| 327 | + |
| 328 | +## Reference Links |
| 329 | + |
| 330 | +- `references/methods.md` : every directive's exact syntax, accepted |
| 331 | + forms, order rules, layer semantics |
| 332 | +- `references/examples.md` : full patterns for components, layered |
| 333 | + base styles, scoped @apply in Vue/Svelte/CSS modules |
| 334 | +- `references/anti-patterns.md` : utility-hiding mistakes, scoped-style |
| 335 | + errors, layer mistakes, important syntax errors |
| 336 | + |
| 337 | +## Sources |
| 338 | + |
| 339 | +- v4 directives reference : https://tailwindcss.com/docs/functions-and-directives |
| 340 | +- v3 directives reference : https://v3.tailwindcss.com/docs/functions-and-directives |
| 341 | +- v4 scoped @apply trap : https://github.com/tailwindlabs/tailwindcss/issues/16346 |
| 342 | +- L-002 (project lesson) : v4 scoped style + @apply ALWAYS needs @reference |
0 commit comments