样式为什么越写越乱

项目早期样式很好写,几个文件、几个类名,改起来很顺手。可随着页面增多、组件变多、人员更替,全局样式互相覆盖、!important 到处救火、同一个颜色出现在十几个文件里,就进入了「改一处坏三处」的恶性循环。

样式乱的本质是缺约束:没有统一的颜色和间距来源,没有明确的命名规则,没有模块边界。CSS 全局作用域的特性,让任何一处没有约束的写法都可能影响别处。

工程化不是引入多少工具,而是建立三层秩序:数值统一(变量与令牌)、命名统一(BEM)、边界统一(模块化与隔离)。这三点立住了,样式规模再大也能保持可维护。

CSS 本身不难,难的是让成千上万行样式长期不乱。
本文核心知识点
  • CSS 变量数值统一与主题切换。
  • BEM可读的类名命名。
  • 预处理器嵌套、混入与模块化。
  • 设计令牌设计与代码之间的桥梁。
CSS 工程化配图:蓝调抽象插画中左侧多组移动端色块卡片与右侧层叠浏览器窗口以箭头串联的科技场景
样式像拼图,规则统一才能严丝合缝。

CSS 变量与主题化

CSS 自定义属性(变量)把散落各处的魔法值收敛到一处。颜色、间距、圆角、字体都定义为变量,组件里只引用变量名,改一处全局生效。

:root {
  --color-primary: #1E5FA8;
  --space-md: 16px;
  --radius-md: 8px;
}

.button {
  background: var(--color-primary);
  padding: var(--space-md);
  border-radius: var(--radius-md);
}

变量的另一个大价值是主题化:主题只是变量集合的替换。在 html 上加一个 data-theme 属性,覆盖一组变量,整个站点的配色就切换了,组件代码一行不用改。

变量还天然支持作用域覆盖,组件内部可以按需重定义局部变量,实现灵活又受控的定制。

BEM 命名规范

BEM 把类名分成三部分:Block(块)、Element(元素)、Modifier(修饰符),分别用双下划线和双连字符连接。类名自带结构信息,不用看 HTML 也能猜到层级。

.card
.card__title
.card__image
.card--featured

块是独立组件,元素属于块的一部分,修饰符表达状态变化。这套命名让选择器天然扁平,几乎没有嵌套,特异性低,覆盖和复用都容易。

BEM 不强制嵌套缩进,配合 CSS 变量使用,能写出可读性和性能都不错的样式。团队统一这套规则,代码评审时类名就能反映意图。

预处理器与模块化

SCSS 这类预处理器带来嵌套、变量、混入和文件拆分,让 CSS 有了编程语言的表达能力。文件按模块拆分,再统一入口引入,样式结构和组件结构一一对应。

// variables.scss
$color-primary: #1E5FA8;

// button.scss
.button {
  &--primary { background: $color-primary; }
  &:hover { opacity: 0.9; }
}

嵌套要克制,层级超过两层就要警惕。预处理器负责的是「写的时候省事」,最终产出的仍是标准 CSS,所以性能优化依然要以产物为准。

现代 CSS 已经支持很多预处理器的能力(嵌套、变量),新项目可以优先用原生 CSS,把预处理器留给确实需要混入等高级特性的团队。

设计令牌体系

设计令牌是设计与代码之间的约定:设计稿里的色板、字号、间距,以统一的 token 命名进入代码。设计师改 token 定义,前端不用逐个组件改样式。

{
  "color": {
    "primary": { "default": "#1E5FA8" },
    "surface": { "default": "#FFFFFF" }
  },
  "space": {
    "sm": "8px", "md": "16px", "lg": "24px"
  }
}

令牌通常分层:基础令牌(原始值)和语义令牌(按用途引用基础令牌)。组件只消费语义令牌,换主题时只需替换基础层,语义保持不变。

令牌可以用工具生成 CSS 变量、SCSS 变量甚至 Tailwind 配置,一份定义多处消费,避免各写各的导致漂移。