smacss五类规则按职责与影响范围硬性分层:base仅含全局重置(如body行高、h1–h6字号),layout管大容器(.l-header),module为可复用独立组件(.c-card),state用is-/has-描述布尔态(is-expanded),theme仅用于多皮肤场景。

SMACSS里五类规则到底怎么分层
不是靠直觉分,是按职责和影响范围硬性切分。Base、Layout、Module、State、Theme这五类,核心区别在「是否可复用」和「是否依赖上下文」。
常见错误是把所有通用样式塞进base.css,结果按钮、卡片、表单全混在一起,改一个font-size波及全站。Base只放真正全局的重置和默认值:比如body行高、a颜色、h1–h6字号阶梯——这些改了,整个页面视觉节奏才统一。
-
Layout只管大块区域容器:头部、主内容区、侧栏、页脚,用.l-header这类前缀,不写具体样式细节 -
Module才是组件主体,比如.card、.nav-primary,必须能独立存在、不依赖父级class -
State只用is-或has-前缀,比如is-collapsed、has-error,且只描述状态,不定义布局 -
Theme极少用,除非真有多套皮肤切换,否则别提前抽象
为什么模块class名要带命名空间前缀
避免样式泄漏和意外覆盖。没前缀的.button在复杂项目里大概率被其他团队或第三方库污染。
SMACSS要求模块级class必须有明确作用域标识,比如.btn不够,得是.c-button(c-表示component)或.mod-button。这不是炫技,是防止.button:hover在某个弹窗里意外干掉另一个.button的悬停动效。
立即学习“前端免费学习笔记(深入)”;
- 前缀建议统一用
c-(component)、l-(layout)、u-(utility),别混用 - 禁止嵌套过深:
.c-card .c-card__header .c-text--title这种写法等于放弃维护性 - 如果用BEM,SMACSS不反对,但
__和--必须严格对应层级,不能.c-button__icon--large这种跨层修饰
State类该加在哪个DOM节点上
加在「承载该状态的最外层模块容器」上,不是加在子元素,也不是加在body上。
比如一个折叠面板,展开/收起的状态属于整个.c-accordion,那就把is-expanded加在<div class="c-accordion">上,而不是它的<code><button></button>或<div class="c-accordion__content">。否则JS控制时要反复找父级,CSS里也得写<code>.c-accordion__content.is-expanded这种反模式选择器。
- 状态类必须可预测:只有
is-active、is-disabled、is-loading这类布尔态,禁用is-red这种描述外观的 - 多个状态可共存:
class="c-button is-disabled is-loading"合法,但CSS里不能写.c-button.is-disabled.is-loading这种双状态耦合规则 - 避免用
[data-state="loading"]代替class——CSS无法高效匹配属性选择器,尤其在动画中会掉帧
SMACSS和CSS-in-JS冲突吗
不冲突,但分工要划清。SMACSS管的是「哪些样式该归到哪一层」,不是「样式必须写在.css文件里」。
比如React里一个Card组件,它的结构样式(padding、border-radius、阴影)仍应走c-card这一套命名和分层逻辑,哪怕实际是用styled-components写的。关键在思维:这个样式是通用模块?还是仅在此处生效的临时调整?后者就该用内联style或scoped CSS,别硬塞进module.css。
- JS里动态加的class,必须和SMACSS约定一致:
el.classList.add('is-hovered'),不能写el.classList.add('hover') - 工具类(如
u-text-center)可以抽成CSS-in-JS的hook,但命名和语义不能变 - 真正容易翻车的是「伪模块」:一个组件里同时混着
import './Card.css'和styled.div,结果Card的边框来自CSS文件,圆角却来自JS——这种撕裂感比没规范更糟
命名空间和分层意识比文件位置重要得多。很多人卡在「该不该用SMACSS」,其实问题从来不在架构,而在改一个margin时,敢不敢确定它只影响当前模块。










