类名与 id 是 HTML 里改动最频繁、约定却最少的部分。名字一旦与样式或脚本耦合,改一处要动三处。下面六条约定按改动成本从低到高排列。

六条约定
- 类名全小写,单词之间用短横线。写
user-avatar,不写userAvatar或User_Avatar。 - 名字描述用途,不描述外观。
price-red换成price-discount,改配色时不必动名字。 - id 在页面内保持唯一,留给确实需要唯一性的场合:锚点跳转、label 的 for 关联、表单控件联动、脚本挂载点。
- 不用 class 充当 JS 钩子。交互标记写进 data-* 属性,样式与行为各占一层。
- 缩写只用公认写法。nav、btn、cta 可以留,自造缩写跳过。
- 同一模块内的元素统一前缀。
card-title、card-body,避免通用名字在组合时撞车。
正反对照
<!-- 改动成本高的写法 -->
<div class="box RedBox" id="top" onclick="toggleMenu()"></div>
<!-- 按约定写 -->
<div class="site-nav" id="site-nav" data-toggle="menu"></div>
外观词不进类名;行为不写在 onclick 属性里,改用 data-* 标记后统一监听。
钩子分离后的监听写法
document.addEventListener('click', (event) => {
const trigger = event.target.closest('[data-toggle]');
if (!trigger) return;
const targetId = trigger.dataset.target;
document.getElementById(targetId)?.classList.toggle('is-open');
});
行为绑定到 data 属性,类名调整不再影响脚本。
检查清单
- 类名中不含大写字母与下划线;
- 名字描述用途而非视觉表现;
- id 唯一,只在需要唯一性的场景出现;
- JS 钩子写在 data-* 属性上;
- 同一模块的元素前缀一致。
例外情况
组件作用域方案(CSS Modules、Vue scoped、CSS-in-JS)会生成局部类名,全局冲突风险下降,但 data-* 钩子与命名可读性这两条约定仍然有效。
