@apply 在响应式或暗色模式组合下会静默失效,因其为 PostCSS 静态处理,不支持嵌套变体、媒体查询或交叉逻辑;推荐用 @layer components 实现静态复用,动态场景则使用 tailwind-variants。

@apply 在响应式或暗色模式组合下会静默失效
写 @apply md:p-6 hover:bg-gray-100 dark:hover:bg-gray-800 看似合理,但编译后大概率只生成普通声明,md: 和 dark:hover: 会被丢弃或忽略——因为 @apply 是 PostCSS 静态处理,不递归解析嵌套变体、不包裹媒体查询、也不理解交叉逻辑。
常见错误现象:
- 页面在中屏尺寸下无 padding,hover 时背景不变,暗色模式下 hover 失效
- Vue / Svelte 单文件组件的
<style scoped>里用@apply,样式根本不出现在最终 CSS 中 - 跨文件复用带
focus:或disabled:的类,构建后 focus 样式缺失
这不是配置问题,是机制限制。别指望它“多试几次就对了”。
@layer components 是最轻量且可靠的静态复用方式
把语义化类名写进 @layer components,JIT 引擎能完整识别所有变体并生成对应规则,且不增加额外 CSS 体积。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 在全局 CSS(如
src/styles/components.css)中定义:@layer components { .avatar { @apply w-16 h-16 rounded-full border-2 border-white; } .card { @apply p-4 bg-white rounded-md shadow-sm hover:shadow-md transition-shadow; } } - 确保该文件被
@tailwind components引入,且路径在tailwind.config.js的content数组中被扫描到 - 不推荐在
@layer utilities里放复合样式——它本意是扩展原子能力,不是封装组件
动态变体场景必须用 tailwind-variants
当组件需要根据 variant、size、disabled 等 prop 切换样式时,硬编码类名或 @apply 都会失控。
示例(TypeScript):
import { tv } from "tailwind-variants";
const button = tv({
base: "inline-flex items-center justify-center rounded font-medium transition-colors",
variants: {
variant: {
primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500",
outline: "border border-gray-300 text-gray-700 hover:bg-gray-50",
},
size: {
sm: "text-sm px-3 py-1.5",
md: "text-base px-4 py-2",
},
},
});
// 使用:button({ variant: "primary", size: "md" }) → 返回合法原子类字符串
关键点:
-
tv()是运行时函数,不依赖 PostCSS,天然支持md:、dark:、hover:任意组合 - 类型推导完整,IDE 能自动提示可用变体,重构安全
- 不生成冗余 CSS,PurgeCSS 仍可正常工作
真正容易被忽略的是抽象层级错位
很多人想“抽一个类解决所有问题”,结果把 md:p-6 和 p-4 强行合并成 .card-padding,反而锁死响应式调整能力。Tailwind 的原子性优势正在于可拆解。
判断要不要封装,只看两点:
- 这个组合是否在 3 个以上不同上下文中**完全一致地重复出现**?如果是,用
@layer components - 这个组合是否随 props 动态变化?如果是,用
tv(),别碰@apply - 其余情况——直接写原子类。少写两个类名,远比后期调试失效的
@apply省时间


















