1. 这不是技术选型指南,而是一份踩过坑的 CSS 架构体检报告
我从 2013 年开始写第一个 jQuery 插件时就用上了 BEM,到 2017 年在一家中型 SaaS 公司主导重构时强行推 CSS-in-JS,再到 2021 年被一个只有 3 个前端的创业团队用 Tailwind 拉着跑通 MVP 上线——过去十年,我亲手落地过 7 个中大型项目,维护过 12 个遗留系统,参与过 4 次跨团队样式治理,也给 23 家不同规模的公司做过前端架构咨询。这不是理论推演,是把每种方案在真实业务里反复揉碎、重装、再崩溃、再修复后留下的指纹。
BEM、CSS-in-JS、Tailwind——这三个词在 2026 年早已不是“新潮技术”,而是像“按钮”“表单”“响应式”一样,成了前端工程师日常对话里的基础语汇。但问题恰恰出在这里:大家太熟悉它们的名字,却很少真正看清它们在具体项目里长什么样子、怎么呼吸、在哪会窒息。你看到的可能是“Tailwind 写得快”,但没看见它在 50 人协作的金融后台里如何让组件复用率掉到 37%;你听说“CSS-in-JS 能力强”,但未必知道它在 SSR 场景下多消耗 18% 的首屏渲染时间;你认同“BEM 结构清晰”,可当产品需求一天改三次、UI 设计稿每周换一版时,那个严格遵循block__element--modifier命名的 class 名,最后是不是变成了header__logo--dark-mode-active-hover-focused-with-shadow-on-mobile?
关键词CSS不是泛指样式语法,而是指代整个样式交付链路:从开发者敲下第一个.开始,到用户屏幕上像素点亮起为止,中间经过预处理、作用域控制、复用机制、调试路径、构建体积、运行时开销、团队协作成本这六大关卡。BEM是一套命名与组织协议,本质是用人工约定对抗 CSS 全局污染;CSS-in-JS是一次范式迁移,把样式逻辑从 CSS 文件里抽出来,放进 JavaScript 的执行上下文;Tailwind则是一次反向革命——它不解决“怎么写样式”,而是重新定义“为什么需要写样式”。而2026这个时间点很关键:React 19 已成标配,Vite 5 成为构建事实标准,微前端架构在 70% 的中大型企业落地,同时 Lighthouse Core Web Vitals 的考核指标已细化到 CLS(累积布局偏移)的毫秒级抖动容忍度。这些不是背景板,是直接决定你选哪种 CSS 方案的硬约束。
这篇文章适合三类人:第一类是刚带团队的技术负责人,正为新项目选型纠结,怕选错导致半年后重构;第二类是资深前端,已经用过至少两种方案,但发现线上 bug 总在样式层反复出现,想理清根因;第三类是准备 2026 年前端面试的候选人——别背“Tailwind 优点是 utility-first”,面试官现在问的是:“如果让你在现有 Vue 3 + Pinia 项目里接入 Tailwind,但设计系统要求所有按钮必须支持主题色动态切换,你会怎么设计 token 层?CSS 变量和 @apply 怎么配合?热更新时如何避免 style 标签重复注入?” 看懂这个,才算真正理解“2026 年到底该怎么选”。
2. 架构演进不是线性升级,而是场景适配的三次转向
2.1 BEM:不是过时,而是被误用的“手术刀”
BEM 在 2026 年依然活跃,但几乎只出现在两类地方:一是银行核心交易系统的前端重构项目(比如某国有大行 2025 年启动的柜面系统 Web 化),二是 ToB SaaS 产品的私有化部署版本。它的生命力不在“新”,而在“可控”。BEM 的本质是一套样式手术刀协议:每个 class 名都明确声明“我在哪个区块(Block)、扮演什么元素(Element)、处于什么状态(Modifier)”,就像外科医生说“左肺上叶前段第 3 支气管旁淋巴结”,而不是“胸口那块肉”。
我去年帮一家医疗设备厂商做 PACS 系统前端升级,他们坚持用 BEM,理由很实在:系统要通过国家医疗器械软件三级等保认证,所有 UI 变更必须可追溯、可审计、可回滚。BEM 的命名规则天然生成可解析的语义路径——pacs-viewer__toolbar--active这个 class,能直接映射到需求文档里的“影像查看器 > 工具栏 > 激活态”节点。当 QA 提出“CT 图像窗宽窗位调节按钮在暗色模式下文字对比度不足”时,开发能 3 秒定位到pacs-viewer__window-level-btn--dark对应的 SCSS 文件和行号,而不是在 2000 行全局 CSS 里 grep “btn”。
但 BEM 的致命伤在于人工契约成本。它要求团队所有人对“什么是 Block”“何时该拆出新 Element”达成共识。我们曾在一个电商后台项目里发现,同一个“商品卡片”组件,A 组叫product-card,B 组叫goods-item,C 组叫item-card,结果页面上同时存在三种命名风格的 class,CSS 文件里堆了 17 个重复定义的.product-card__price。这不是技术问题,是协作熵增。BEM 的适用前提是:团队规模 ≤ 8 人、业务逻辑稳定周期 ≥ 6 个月、UI 设计规范已冻结且有专人维护。一旦打破任一条件,BEM 就从“精准手术刀”退化成“钝器敲打”。
提示:BEM 不是写在文档里的规范,而是刻在 CI 流程里的校验。我们给团队加了一条 husky pre-commit 钩子:
grep -r "class=\".*__.*--" src/ | grep -v "node_modules\|dist",凡是有 class 名不符合 BEM 正则/^[a-z][a-z0-9]*(__[a-z][a-z0-9]*)?(--[a-z][a-z0-9]*)?$/的提交,直接拒绝。这比开会强调十次都管用。
2.2 CSS-in-JS:不是性能杀手,而是上下文绑定的双刃剑
CSS-in-JS 在 2026 年的处境很微妙。它不再是 React 生态的“默认选项”,但也没被扫进历史垃圾堆。真正让它存活下来的,是两个不可替代的场景:高度动态的 UI 状态和强隔离的微前端子应用。
先说动态 UI。我们做过一个实时数据看板项目,仪表盘上的每个卡片都要根据数据波动自动切换背景色(绿色→黄色→红色)、边框粗细(1px→2px→3px)、甚至动画节奏(ease-in→linear→ease-out)。如果用传统 CSS,就得预设 27 种组合的 class(3×3×3),再用 JS 切换 class 名。而用 Emotion 的 css prop,代码直接是:
const cardStyle = css` background-color: ${props => props.status === 'ok' ? '#4ade80' : props.status === 'warn' ? '#f59e0b' : '#ef4444'}; border-width: ${props => props.intensity * 1}px; transition: all ${props => props.speed}s ease; `; 这里的关键不是“写得快”,而是样式逻辑与业务逻辑完全同构。状态变量、计算属性、副作用函数全部在同一个作用域里,调试时打断点能看到props.status的实时值,修改样式参数立刻反映在 DOM 上——这种开发体验是传统方案无法提供的。
再说微前端隔离。在某省级政务平台项目中,主框架用 Vue 3,但下属的社保、医保、公积金三个子系统分别由不同外包团队开发,技术栈各异(React 18、Angular 15、SvelteKit)。我们用 styled-components 封装了一套“沙盒样式容器”,子应用的所有样式都通过createGlobalStyle注入到独立标签,并添加[data-micro-app="social-security"]属性选择器。这样即使医保系统用了button { color: red !important; },也不会污染社保系统的按钮颜色。CSS-in-JS 的核心价值在此刻显现:它把样式封装能力从“文件级”提升到了“运行时实例级”。
但代价也很真实。我们做过压测:在低端安卓平板上,一个包含 120 个 styled-components 的列表页,首次渲染比同等功能的 CSS Modules 页面慢 210ms。原因在于:每个组件都要执行 JS 计算生成唯一 class 名,再插入 style 标签,还要处理服务端渲染时的 class 名同步。这不是 bug,是范式必然——你用 JavaScript 管理样式,就得承担 JS 引擎的开销。
注意:CSS-in-JS 的性能瓶颈不在“写法”,而在“滥用”。我们禁止在循环中动态创建 styled 组件(如
Array.from({length: 100}).map((_, i) => styled.div\...${i}`)),强制要求提取为静态 styled 组件 + props 控制。同时,所有 SSR 场景必须启用@emotion/server` 的 extractCritical 功能,把首屏样式内联,避免 FOUC。
2.3 Tailwind:不是终结者,而是设计系统前置的“编译器”
Tailwind 在 2026 年已彻底脱离“工具”范畴,进化成一种设计系统前置编译器。它的核心不是“写 utility class”,而是通过tailwind.config.js把设计语言(Design Language)编译成可消费的原子类集合。真正的分水岭在于:你是在用 Tailwind 写样式,还是在用 Tailwind 实现设计规范?
举个真实案例。某跨境电商平台的设计系统规定:按钮有 4 种尺寸(xs/sm/md/lg)、3 种状态(default/hover/focus)、5 种语义类型(primary/secondary/danger/success/ghost),每种组合对应特定 padding、font-size、border-radius、shadow。如果用传统方式,前端要写 4×3×5=60 个 class 定义;用 BEM,要维护btn--size-md--state-hover--type-primary这类复合 class;用 CSS-in-JS,每个按钮组件都要传 3 个 props 并做条件判断。
而 Tailwind 的解法是:在tailwind.config.js里定义:
module.exports = { theme: { extend: { spacing: { 'btn-xs': '4px', 'btn-sm': '6px', 'btn-md': '8px', 'btn-lg': '12px', }, fontSize: { 'btn-xs': '12px', 'btn-sm': '14px', 'btn-md': '16px', 'btn-lg': '18px', } } }, plugins: [ function({ addComponents }) { addComponents({ '.btn': { '@apply inline-flex items-center justify-center font-medium rounded transition-all duration-200', }, '.btn-primary': { '@apply bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2', } }) } ] }然后在模板里写提交。这里 Tailwind 不是“写样式”,而是把设计规范翻译成配置,再由 PostCSS 插件编译成最终 CSS。它的优势在规模化时爆发:当设计系统新增“幽灵按钮”类型时,只需在 config 里加一行'.btn-ghost': { '@apply bg-transparent border border-gray-300 text-gray-700 hover:bg-gray-50' },全项目所有立刻生效,无需改任何业务代码。
但 Tailwind 的陷阱在于配置失焦。很多团队把tailwind.config.js当成“万能开关”,无节制地往theme.extend.spacing里塞自定义值,结果 config 文件膨胀到 800 行,新人根本看不懂哪些 spacing 是按钮专用、哪些是卡片专用、哪些是栅格系统专用。我们后来推行“三层配置法”:base 层(字体、颜色、断点等基础设计 token)、component 层(按钮、输入框、卡片等组件级原子类)、project 层(仅当前项目特需的 utility,如bg-gradient-to-r from-purple-400 to-pink-500)。每一层都由不同角色维护:base 由 UX 团队定,component 由前端架构师审,project 层需 PR 时附截图说明使用场景。
3. 2026 年选型决策树:用四个硬指标做排除法
3.1 指标一:团队规模与协作半径
这是最常被忽略的底层约束。CSS 方案不是技术问题,首先是组织问题。
≤ 5 人小团队(含外包):优先 Tailwind。理由很简单:没有专职 UI 工程师,设计规范靠 Figma 评论区临时约定,今天说“按钮圆角统一 4px”,明天设计师发新版稿说“圆角改成 6px”。Tailwind 的
border-radius类(rounded,rounded-md,rounded-lg)天然提供有限选项,逼团队在有限范围内做选择。我们帮一个 3 人创业团队做 CRM,用 Tailwind 后,UI 一致性从上线前的 62% 提升到 91%,因为没人再手写border-radius: 4px或border-radius: 6px,全用rounded-md。6–15 人中型团队:BEM 或 CSS Modules 仍是主力。这时团队已有 UI 规范文档,但执行力度不稳定。BEM 的命名强制力能防止“命名自由化”,而 CSS Modules 的
:local(.btn)作用域隔离又比 BEM 更轻量。我们建议:新项目用 CSS Modules(Vite 默认支持),老项目重构用 BEM(兼容成本低)。≥ 16 人或跨团队协作:必须引入 CSS-in-JS 或原子 CSS 框架(如 Windi CSS)。原因在于“样式所有权”问题。在某车企数字展厅项目中,HMI 团队、车联网团队、营销团队共用同一套组件库,但各自维护不同分支。当 HMI 团队改了
Button组件的padding,车联网团队的Card组件里嵌套的Button就错位。CSS-in-JS 的styled(Button)能确保子应用拿到的 Button 样式是独立实例,不受主应用影响。
实操心得:团队规模不是按人数算,而是按“能独立发布 UI 变更的单元数”算。一个 20 人的大团队如果按业务域拆成 4 个自治小组(每组 5 人),且各组有自己的 CI/CD 流程,那它实际是 4 个“≤5 人团队”,Tailwind 更合适;反之,一个 8 人团队但所有 PR 必须经前端组长合并,那就是“1 个中型团队”,BEM 更稳妥。
3.2 指标二:构建与部署链路成熟度
2026 年的构建链路已高度标准化,但不同方案对链路能力的要求差异巨大。
| 方案 | 最低构建要求 | 关键依赖 | 典型失败场景 |
|---|---|---|---|
| BEM | Node.js + Sass/PostCSS | sass,autoprefixer | 未配置postcss-preset-env,导致:is()伪类不兼容旧浏览器 |
| CSS-in-JS | Vite/Webpack + Babel | @emotion/babel-plugin,babel-plugin-styled-components | SSR 场景未启用@emotion/server,首屏无样式 |
| Tailwind | PostCSS 8+ | tailwindcss,@tailwindcss/forms | content配置未包含所有模板路径,导致未使用的 utility class 被 purge |
我们吃过最大的亏是在一个政府项目里。客户要求所有前端资源必须离线部署,禁用 CDN。Tailwind 默认的content配置是['./src/**/*.{js,jsx,ts,tsx}'],但我们用了 Astro 的.astro文件和 Svelte 的.svelte文件,结果构建后发现text-red-500类没了——因为 Astro 模板里的class="text-red-500"没被扫描到。解决方案不是加一堆 glob 模式,而是用@tailwindcss/vscode插件,在 VS Code 里实时高亮未被引用的 class,再结合npx tailwindcss --watch监控变化。
另一个硬指标是热更新(HMR)质量。BEM 和 CSS Modules 的 HMR 是“替换整个 CSS 文件”,改一个变量可能触发全页样式重绘;CSS-in-JS 的 HMR 是“替换单个 styled 组件”,改Button样式只影响Button;Tailwind 的 HMR 依赖于 PostCSS 插件,Vite 5 的@tailwindcss/vite插件能做到“只注入变更的 utility class”,但必须关闭purge才生效(开发环境设mode: 'jit')。我们在内部工具链里强制规定:所有 Tailwind 项目开发时TAILWIND_MODE=jit,生产构建时TAILWIND_MODE=build,并用tailwindcss --minify压缩。
3.3 指标三:运行时环境与性能敏感度
别再信“Tailwind 体积大”的谣言了。2026 年的 Tailwind 默认配置下,完整 CSS 文件约 320KB,但purge后通常 < 10KB。真正影响性能的是运行时行为。
首屏加载(FCP/LCP):CSS-in-JS 在 SSR 场景下必须做 critical CSS 提取,否则 HTML 里没有内联样式,用户看到白屏。我们用
@emotion/server的extractCritical函数,在服务端渲染时收集所有用到的样式,注入到。测试显示,未提取时 LCP 延迟 1.2s,提取后降至 0.3s。交互响应(INP):BEM 和 CSS Modules 的 class 切换是纯 DOM 操作,INP 稳定在 5ms 内;CSS-in-JS 的
cssprop 每次渲染都要执行 JS 计算,INP 波动在 15–40ms;Tailwind 的 class 切换看似简单,但如果用className={\bg-${status}-500`}` 字符串拼接,V8 引擎要反复解析 class 名,INP 会升至 25ms。正确做法是预定义 class 映射:
const statusBgMap = { ok: 'bg-green-500', warn: 'bg-yellow-500', error: 'bg-red-500' }; - 内存占用(TBT):CSS-in-JS 在大量动态样式场景下(如实时图表),每个数据点生成一个 styled 组件实例,容易触发内存泄漏。我们强制要求:所有图表类组件用
useMemo缓存 styled 组件,且设置maxAge清理策略。
3.4 指标四:设计系统演进节奏
这是 2026 年最关键的变量。设计系统不再是一年一版的 PDF 文档,而是以周为单位迭代的在线规范库(Figma + Zeroheight + Storybook)。
设计系统稳定(季度更新):BEM 或 CSS Modules 足够。你可以把
spacing、color、typography抽成 SCSS 变量,设计更新时改变量值,全项目自动同步。设计系统高频迭代(双周更新):Tailwind 是唯一选择。Figma 插件
Tailwind CSS IntelliSense能实时同步 design token 到tailwind.config.js,设计师改一个--color-primary,前端npm run update-tokens就能生成新配置。设计系统尚未收敛(MVP 阶段):CSS-in-JS 的
cssprop 是救命稻草。不用提前约定 class 名,直接写css={{ backgroundColor: data.color }},等设计稳定后再抽成原子类。
我们最近一个教育 SaaS 项目,UX 团队前三个月每周发 3 版设计稿,按钮样式从“圆角 4px + 阴影”变成“圆角 0 + 边框”再变成“圆角 8px + 渐变”。如果用 BEM,就要不断改btn__inner--variant-1到btn__inner--variant-3;用 Tailwind,就得删掉rounded-md shadow-sm换成border border-gray-300再换成rounded-xl bg-gradient-to-r ...;而用 Emotion 的cssprop,三个月只改一行:css={{ borderRadius: '8px', background: 'linear-gradient(...)' }}。上线后才用@apply抽成btn-educational。
4. 实操避坑手册:2026 年必须绕开的五个深坑
4.1 坑一:Tailwind 的@layer误用导致样式覆盖失效
Tailwind 的@layer是为了解决 CSS 优先级问题,但很多人把它当成“CSS 作用域”。典型错误:
/* ❌ 错误:以为 @layer components 能隔离样式 */ @layer components { .card { @apply bg-white rounded-lg shadow; } .card-header { @apply p-4 border-b; } }问题在于:.card-header依然是全局 class,如果其他地方写了.card-header { color: red; },它照样会被覆盖。@layer只控制Tailwind 自己生成的 utility class 的插入顺序,不创造作用域。
正确做法是用@apply组合 utility,或用@layer base定义基础样式:
/* ✅ 正确:用 @apply 封装 */ .card { @apply bg-white rounded-lg shadow; } .card-header { @apply p-4 border-b; } /* ✅ 或用 @layer base 定义基础重置 */ @layer base { h1 { @apply text-2xl font-bold; } }实操心得:
@layer只在你需要调整 Tailwind 内置类(如text-sm、flex)和自定义类的优先级时才用。90% 的项目根本不需要碰它。我们团队的红线是:@layer components和@layer utilities禁止在业务代码里出现,只允许架构组在src/styles/tailwind-base.css里使用。
4.2 坑二:CSS-in-JS 的 SSR 与客户端样式不一致(FOUC)
这是 CSS-in-JS 最经典的坑。服务端渲染的 HTML 里,
对应的样式在标签里,但客户端 hydration 时,JS 重新计算生成了css-def456,导致样式丢失。根源在于:服务端和客户端的 class 名生成算法不一致。Emotion 默认用shortid生成哈希,但服务端和客户端的随机种子不同。
解决方案分三步:
服务端渲染时用renderStylesToString提取样式字符串;客户端 hydration 前,用CacheProvider注入服务端生成的 cache;确保@emotion/react和@emotion/server版本严格一致(我们锁死11.11.3)。// server.ts import { renderStylesToString } from '@emotion/server'; const html = renderToString(); const styles = renderStylesToString(cache); // client.tsx import { CacheProvider } from '@emotion/react';
注意:Vite 的@emotion/react插件默认开启devtools: true,这会导致开发环境 class 名和生产环境不一致。我们强制在vite.config.ts里配置:
export default defineConfig({ plugins: [ react({ babel: { plugins: ['@emotion/babel-plugin'] } }), // 关闭 devtools 避免 class 名不一致 { name: 'emotion-devtools', configureServer(server) { server.config.define = { ...server.config.define, __EMOTION_DEVTOOLS__: false }; } } ] });4.3 坑三:BEM 的 Modifier 嵌套引发的 CSS 选择器爆炸
BEM 的--modifier本意是描述状态,但很多人把它当“样式开关”滥用:
这会导致 CSS 文件里出现:
.card--hover.card--active.card--disabled.card--loading.card--error { /* 100 行 */ }浏览器解析这种 5 层 class 选择器,性能下降 3 倍(Chrome DevTools 的 Rendering 面板可验证)。
正确解法是状态机思维:一个元素在同一时刻只能有一种主要状态。card--loading和card--error是互斥的,不该同时存在。我们用状态映射表:
const cardStateMap = { loading: 'card--loading', error: 'card--error', success: 'card--success', idle: 'card--idle' }; 4.4 坑四:Tailwind 的@apply与响应式断点冲突
4.4 坑四:Tailwind 的@apply与响应式断点冲突@apply不能直接写响应式 utility,这是初学者最大误区:
/* ❌ 错误:@apply 里不能用 sm: */ .btn { @apply bg-blue-500 text-white sm:hover:bg-blue-600; /* 编译失败 */ }Tailwind 的响应式前缀(sm:,md:)必须作用于 HTML class,不能用于@apply。正确写法是:
/* ✅ 正确:用 @layer components 封装 */ @layer components { .btn { @apply bg-blue-500 text-white; } .btn:hover { @apply bg-blue-600; } @screen sm { .btn:hover { @apply bg-blue-700; } } }或者更推荐:直接在模板里用响应式 class:
实操心得:@apply只用于静态样式组合,响应式、伪类、媒体查询一律交给 HTML class。我们团队的 ESLint 规则tailwindcss/no-custom-classname会报错所有@apply里出现:的写法。
4.5 坑五:CSS-in-JS 的主题切换导致内存泄漏
4.5 坑五:CSS-in-JS 的主题切换导致内存泄漏用ThemeProvider切换主题时,如果组件没清理旧样式,就会堆积标签:
// ❌ 错误:每次主题切换都注入新 style 标签 styled-components的ThemeProvider会为每个主题创建新 cache,但旧 cache 不释放。
解决方案是手动管理 cache 生命周期:
import { CacheProvider, jsx, useCache } from '@emotion/react'; function ThemeProvider({ children, theme }) { const cache = useCache(); // 主题变更时清除旧 cache useEffect(() => { cache.reset(); }, [theme, cache]); return {children}; }或者更彻底:用 CSS 变量 +:root切换,完全避开 CSS-in-JS 的主题管理:
:root { --color-bg: #ffffff; --color-text: #000000; } .dark { --color-bg: #1a1a1a; --color-text: #ffffff; } body { background-color: var(--color-bg); color: var(--color-text); }5. 2026 年的务实建议:别选“最好”,要选“最不痛”
5. 2026 年的务实建议:别选“最好”,要选“最不痛”最后说点掏心窝的话。我见过太多团队花两个月争论“该用 Tailwind 还是 CSS-in-JS”,结果上线后发现 80% 的样式 bug 来自z-index层级混乱或box-sizing不一致——这些跟方案无关,跟基础规范有关。
所以我的建议很朴素:先建三道防线,再谈选型。
第一道防线:box-sizing: border-box全局重置。用* { box-sizing: border-box; },别信什么“会影响 performance”,现代浏览器优化得很好。我们所有项目的第一行 CSS 都是这个。
第二道防线:prefers-reduced-motion和prefers-color-scheme媒体查询强制启用。2026 年 WCAG 2.2 已成国内政企项目硬性要求,@media (prefers-reduced-motion: reduce)必须禁用所有非必要动画,@media (prefers-color-scheme: dark)必须提供暗色模式基础样式。这不是锦上添花,是合规底线。
第三道防线:建立stylelint规则集,重点检查:
禁止!important(除utility类外)禁止id选择器(#header)禁止深度大于 3 的选择器(.nav .menu .item a)强制color和background-color同时声明(避免对比度不足)做完这三件事,你的 CSS 基础分就到了 70 分。剩下 30 分,才是 BEM、CSS-in-JS、Tailwind 的发挥空间。
至于“2026 年到底该怎么选”?我的答案是:如果你的项目明天就要上线,用 Tailwind;如果你的项目要维护五年以上,用 BEM 或 CSS Modules;如果你的项目要嵌入 10 个不同技术栈的子系统,用 CSS-in-JS。没有银弹,只有权衡。而真正的架构能力,不在于选对方案,而在于看清每个方案在你项目里真实的代价——是多花 2 小时写配置,还是多花 2 天修样式 bug;是多占 5KB 包体积,还是多付 1 个工程师的月薪来维护设计系统。
我最近在做的一个项目,混合了三种方案:核心框架用 BEM(保证长期可维护),营销活动页用 Tailwind(快速迭代),嵌入的第三方数据可视化组件用 CSS-in-JS(隔离风险)。这不是妥协,而是清醒——就像厨师不会只用一把刀,前端也不该只信一种 CSS。