必须同时配置prefix: 'tw-'、coreplugins.preflight: false和twmerge——仅加前缀不改模板类名会导致样式丢失,仅关preflight不重启服务仍用旧规则,twmerge混入无前缀类则无法合并冲突,三者缺一不可。

Tailwind 工具类和第三方组件库(如 Element Plus、Naive UI)或手写 CSS 冲突时,样式“不生效”几乎从不因为 Tailwind 本身弱,而是双方选择器权重相同(都是 0,0,1,0),浏览器按最终 CSS 字符串里谁排在后面决定输赢——这不是 bug,是 CSS 层叠规则的必然结果。
为什么 text-blue-500 总被 .el-button 覆盖?
两者特异性完全一致:text-blue-500 编译为 .text-blue-500 { color: #3b82f6; },.el-button 是 .el-button { color: #333; },权重同为 0,0,1,0。谁赢,只看构建后 CSS 文件中哪条规则位置靠后。
- Vue SFC 的
<style scoped></style>会加属性选择器(如.btn[data-v-abc123]),权重升到0,0,1,1,反而容易压过 Tailwind —— 但若你用:global(.el-input)却没写父级上下文,就可能漏掉容器限定,导致实际权重不升反降 - 手动堆 class(如
class="btn btn btn-primary")无效:仍是0,0,1,0,权重没变 - 用
button[type="submit"]或.my-page .btn才真正提权:0,0,1,1和0,0,2,0都稳压单类
怎么用 @layer 让自定义样式和 Tailwind 平起平坐?
Tailwind 默认把所有工具类注入 @layer utilities,而你手写的 .card { @apply p-4; } 若没声明层级,就落在未归层或 @layer base 里——天然被压。必须显式对齐层级:
-
@layer components放组件级覆盖(如.n-button-wrapper) -
@layer utilities放扩展工具类(如.text-brand),且必须写在@tailwind utilities之前 -
@apply不能跨层调用:在@layer base里用@apply bg-blue-500会静默失效 - 调试时直接看 Styles 面板里带
data-layer="components"的<style></style>块是否生成
构建时自动加容器前缀比手写更可靠
手动写 body .my-app .btn 效果有限:body 只贡献 d 位 +1,而很多 UI 库已用 div#app .el-button(0,1,1,1)压你。真正有效的做法是:
- HTML 根节点加唯一类名:
<div id="app" class="my-app-sandbox"> <li>Vite 用户装 <code>vite-plugin-css-prefix,配prefix: '.my-app-sandbox '(注意末尾空格) - PostCSS 用户用
postcss-prefix-selector,设exclude: [/^\.my-/],否则会把你的.my-btn错转成.my-app-sandbox .my-btn - 确认类名不撞车:别叫
.ant-sandbox或.tailwind-scope,防止和框架内部类冲突 - 如果某条规则根本没出现在 Styles 面板,说明选择器没匹配上:检查是否用了
scoped、CSS Modules,或 class 拼错了 - 如果看到
button, [type='button'] { background-color: transparent; }带data-tailwind="true",那问题不是类名覆盖,而是@tailwind base(preflight)在重置标签,默认关掉它:corePlugins: { preflight: false } -
!important是最后手段:它会让所有工具类带!important,后续再想用!text-sm微调就语法非法;且一旦第三方也用!important(比如弹窗组件),你就得再套一层,陷入死循环
别跳过 DevTools 的 Computed 面板
所有“为什么没生效”的猜测都该让位于实证。打开浏览器开发者工具 → 选中元素 → Computed 面板 → 找被划掉的规则 → 点开看右侧 Specificity 值(如 0,0,1,0)和 Source 行号。











