Skip to content

CSS 命名规范:打造语义化的样式系统

代码阅读的次数远多于编写的次数。当你六个月后回来维护自己的代码,或者新同事需要理解你的样式时,类名的好坏会直接影响工作效率。

想象你正在浏览一个代码库,看到这样的类名:.s1.box2.red。你能猜出它们的用途吗?很难,对吧?现在看看这些:.sidebar-navigation.product-card.button--primary。是不是立刻就明白了它们的作用?

这就是好的命名的力量——它让代码自解释,减少了理解和维护的成本。本文将深入探讨如何创建清晰、语义化、可维护的 CSS 类名。

为什么命名很重要?

糟糕命名的代价

让我们看一个真实项目中常见的问题:

html
<div class="box mt20 fl w50">
  <h2 class="t1 blue">Title</h2>
  <p class="t2 gray">Description text here...</p>
  <a href="#" class="btn1 red">Click</a>
</div>
css
.box {
  background: #fff;
}
.mt20 {
  margin-top: 20px;
}
.fl {
  float: left;
}
.w50 {
  width: 50%;
}
.t1 {
  font-size: 24px;
}
.t2 {
  font-size: 16px;
}
.blue {
  color: blue;
}
.gray {
  color: gray;
}
.btn1 {
  padding: 10px;
}
.red {
  color: red;
}

这种命名方式存在严重问题:

  1. 不知道目的.box 是什么 box?商品卡片?侧边栏?
  2. 难以搜索:搜索 .box 会找到太多结果
  3. 紧耦合.blue 如果要改成绿色怎么办?
  4. 难以维护.btn1.btn2... 到 .btn15 谁还记得区别?
  5. 缺乏语义:新成员完全不知道这段代码是做什么的

良好命名的价值

现在看改进后的版本:

html
<div class="product-card product-card--featured">
  <h2 class="product-card__title">Product Name</h2>
  <p class="product-card__description">Product description here...</p>
  <a href="#" class="product-card__button">Add to Cart</a>
</div>
css
.product-card {
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.product-card--featured {
  border: 2px solid #3498db;
}

.product-card__title {
  font-size: 24px;
  color: #333;
  margin-bottom: 10px;
}

.product-card__description {
  font-size: 16px;
  color: #666;
  line-height: 1.6;
}

.product-card__button {
  display: inline-block;
  padding: 10px 20px;
  background-color: #3498db;
  color: white;
  border-radius: 4px;
}

改进后的好处:

  • 清晰的意图:一看就知道是产品卡片
  • 易于搜索:搜索 product-card 能找到所有相关样式
  • 语义化:描述了"是什么",而不是"看起来怎样"
  • 可维护:即使要改颜色,类名依然 make sense
  • 自文档化:不需要额外注释就能理解代码

命名的核心原则

原则 1:描述用途,而非外观

css
/* ❌ 基于外观命名 */
.red-text {
  color: red;
}

.big-box {
  font-size: 24px;
}

.left-col {
  float: left;
  width: 30%;
}

/* ✅ 基于用途命名 */
.error-message {
  color: red; /* 即使以后改成橙色,类名依然合理 */
}

.page-title {
  font-size: 24px;
}

.sidebar {
  float: left;
  width: 30%;
}

为什么这样更好?当设计变更时,基于用途的命名依然有意义:

css
/* 设计变更:错误消息现在是橙色的 */
.error-message {
  color: #f39c12; /* 类名依然准确 */
}

/* 如果用 .red-text... */
.red-text {
  color: #f39c12; /* 类名说谎了! */
}

原则 2:保持一致性

选择一种命名风格并坚持使用:

css
/* ❌ 不一致的命名 */
.user-profile {
} /* kebab-case */
.userSettings {
} /* camelCase */
.User_Dashboard {
} /* Pascal + underscore */
.ACCOUNT {
} /* UPPERCASE */

/* ✅ 一致的命名 */
.user-profile {
}
.user-settings {
}
.user-dashboard {
}
.user-account {
}

原则 3:有意义且具体

避免过于泛泛或过于 abbreviated 的名称:

css
/* ❌ 太泛泛 */
.container {
} /* 什么容器? */
.box {
} /* 什么盒子? */
.item {
} /* 什么项目? */
.data {
} /* 什么数据? */

/* ❌ 过度缩写 */
.usr-prof {
} /* user-profile */
.nav-lnk {
} /* navigation-link */
.btn-prim {
} /* button-primary */

/* ✅ 清晰具体 */
.page-container {
}
.product-card {
}
.cart-item {
}
.user-statistics {
}
.navigation-link {
}
.button-primary {
}

原则 4:可读性优先

css
/* ❌ 难以阅读 */
.usrprofimgupldbtn {
}

/* ❌ 过长 */
.user-profile-image-upload-button-container-wrapper {
}

/* ✅ 平衡的长度 */
.user-profile__upload-button {
}
.profile-image-uploader {
}

常用命名模式

模式 1:基于功能的命名

描述元素的功能或作用:

css
/* 导航相关 */
.main-navigation {
}
.breadcrumb-navigation {
}
.pagination {
}

/* 表单相关 */
.search-form {
}
.login-form {
}
.contact-form {
}
.form-validation-error {
}

/* 内容相关 */
.article-content {
}
.sidebar-widget {
}
.comment-list {
}
.tag-cloud {
}

/* 操作相关 */
.dropdown-toggle {
}
.modal-trigger {
}
.tooltip-container {
}

模式 2:基于内容的命名

描述元素包含的内容类型:

css
/* 用户相关 */
.user-avatar {
}
.user-profile {
}
.user-settings {
}
.author-bio {
}

/* 产品相关 */
.product-grid {
}
.product-card {
}
.product-image {
}
.product-price {
}
.product-rating {
}

/* 文章相关 */
.article-header {
}
.article-body {
}
.article-footer {
}
.related-articles {
}

模式 3:基于位置的命名

描述元素在页面中的位置(谨慎使用):

css
/* 主要区域 */
.site-header {
}
.site-footer {
}
.main-content {
}
.sidebar {
}

/* 次要区域 */
.header-nav {
}
.footer-links {
}
.sidebar-primary {
}
.sidebar-secondary {
}

/* ⚠️ 注意:避免过度依赖位置命名 */
/* ❌ 不好 */
.left-column {
} /* 如果改成右边呢? */
.top-menu {
} /* 如果改到侧边呢? */

/* ✅ 更好 */
.primary-menu {
}
.secondary-navigation {
}

模式 4:基于状态的命名

描述元素的状态:

css
/* 状态类通常使用 is- 或 has- 前缀 */
.is-active {
}
.is-disabled {
}
.is-loading {
}
.is-visible {
}
.is-hidden {
}
.is-expanded {
}
.is-collapsed {
}

.has-error {
}
.has-warning {
}
.has-success {
}
.has-dropdown {
}
.has-icon {
}

/* 使用示例 */
.button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.form-field.has-error {
  border-color: #e74c3c;
}

.menu-item.is-active {
  font-weight: bold;
  color: #3498db;
}

命名长度的平衡

避免过短

css
/* ❌ 太短,不清楚 */
.btn {
}
.nav {
}
.hdr {
}
.ftr {
}
.usr {
}

/* ✅ 适当的长度 */
.button {
}
.navigation {
}
.header {
}
.footer {
}
.user {
}

避免过长

css
/* ❌ 太长,难以使用 */
.main-navigation-menu-item-link-active-state {
}
.product-card-image-container-wrapper-element {
}

/* ✅ 合理的长度 */
.nav-link.is-active {
}
.product-card__image {
}

找到平衡点

css
/* 好的命名长度示例 */
.search-bar {
} /* 2 个单词 */
.user-profile {
} /* 2 个单词 */
.product-card {
} /* 2 个单词 */
.comment-form {
} /* 2 个单词 */
.navigation-menu {
} /* 2 个单词 */
.article-metadata {
} /* 2 个单词 */

/* 稍长但仍可接受 */
.shopping-cart-item {
} /* 3 个单词 */
.user-settings-panel {
} /* 3 个单词 */
.main-content-area {
} /* 3 个单词 */

一般建议:

  • 1-3 个单词最理想
  • 超过 4 个单词要考虑是否可以简化
  • 使用缩写要确保团队都理解

具体场景的命名实践

布局命名

css
/* 主要布局容器 */
.site {
} /* 整个网站容器 */
.site-header {
} /* 网站头部 */
.site-footer {
} /* 网站底部 */
.site-main {
} /* 主要内容区 */

/* 页面级容器 */
.page {
} /* 页面容器 */
.page-header {
} /* 页面头部 */
.page-content {
} /* 页面内容 */
.page-sidebar {
} /* 页面侧边栏 */

/* 内容区域 */
.container {
} /* 居中容器 */
.wrapper {
} /* 包装器 */
.section {
} /* 区块 */
.row {
} /* 行 */
.column {
} /* 列 */

组件命名

css
/* 卡片组件 */
.card {
}
.card__header {
}
.card__body {
}
.card__footer {
}
.card__title {
}
.card__image {
}
.card--featured {
}
.card--large {
}

/* 按钮组件 */
.button {
}
.button--primary {
}
.button--secondary {
}
.button--large {
}
.button--small {
}
.button--block {
}
.button--disabled {
}

/* 表单组件 */
.form {
}
.form__group {
}
.form__label {
}
.form__input {
}
.form__textarea {
}
.form__select {
}
.form__checkbox {
}
.form__radio {
}
.form__error {
}
.form__help-text {
}

/* 模态框组件 */
.modal {
}
.modal__overlay {
}
.modal__container {
}
.modal__header {
}
.modal__title {
}
.modal__close {
}
.modal__body {
}
.modal__footer {
}
.modal--large {
}
.modal--small {
}

工具类命名

css
/* 显示相关 */
.d-block {
} /* display: block */
.d-inline {
} /* display: inline */
.d-inline-block {
} /* display: inline-block */
.d-flex {
} /* display: flex */
.d-none {
} /* display: none */

/* 间距相关 */
.m-0 {
} /* margin: 0 */
.m-1 {
} /* margin: 8px */
.m-2 {
} /* margin: 16px */
.mt-1 {
} /* margin-top: 8px */
.mb-2 {
} /* margin-bottom: 16px */
.p-1 {
} /* padding: 8px */
.px-2 {
} /* padding-left + padding-right: 16px */

/* 文本相关 */
.text-left {
} /* text-align: left */
.text-center {
} /* text-align: center */
.text-right {
} /* text-align: right */
.text-bold {
} /* font-weight: bold */
.text-uppercase {
} /* text-transform: uppercase */

/* 颜色相关 */
.text-primary {
} /* color: primary */
.text-secondary {
} /* color: secondary */
.bg-primary {
} /* background-color: primary */
.border-primary {
} /* border-color: primary */

JavaScript 钩子命名

JavaScript 专用的类名应该有明确的前缀,不应用于样式:

html
<!-- ✅ 好的做法:分离样式和行为 -->
<button class="button button--primary js-submit-form">Submit</button>

<div class="modal js-modal" data-modal-id="123">
  <!-- ... -->
</div>

<form class="contact-form js-ajax-form">
  <!-- ... -->
</form>
css
/* CSS:只为样式类写规则 */
.button {
}
.button--primary {
}
.modal {
}

/* JavaScript:使用 js- 前缀的类作为钩子 */
/* 不要为 js- 前缀的类写样式! */
javascript
// JavaScript:只选择 js- 前缀的类
document
  .querySelector(".js-submit-form")
  .addEventListener("click", handleSubmit);
document.querySelector(".js-modal").addEventListener("click", openModal);
document.querySelector(".js-ajax-form").addEventListener("submit", submitForm);

这种分离的好处:

  1. 修改样式不会破坏 JavaScript
  2. 修改 JavaScript 不会破坏样式
  3. 清楚知道哪些类被 JavaScript 使用
  4. 删除元素时知道可能影响的 JavaScript

特殊前缀的使用

布局前缀 (l-)

css
.l-container {
}
.l-header {
}
.l-footer {
}
.l-sidebar {
}
.l-main {
}
.l-grid {
}
.l-two-column {
}

组件前缀 (c-)

css
.c-button {
}
.c-card {
}
.c-modal {
}
.c-navigation {
}
.c-dropdown {
}

工具类前缀 (u-)

css
.u-hide {
}
.u-sr-only {
} /* screen reader only */
.u-text-center {
}
.u-margin-bottom {
}

状态前缀 (is-, has-)

css
.is-active {
}
.is-disabled {
}
.is-loading {
}
.has-error {
}
.has-dropdown {
}

主题前缀 (t-)

css
.t-dark {
}
.t-light {
}
.t-high-contrast {
}

响应式命名

断点后缀

css
/* Mobile first approach */
.grid {
} /* 基础样式 */
.grid--2-cols-md {
} /* Medium 及以上 2 列 */
.grid--3-cols-lg {
} /* Large 及以上 3 列 */
.grid--4-cols-xl {
} /* Extra Large 及以上 4 列 */

.hide-mobile {
} /* 在移动端隐藏 */
.hide-desktop {
} /* 在桌面端隐藏 */
.show-mobile-only {
} /* 只在移动端显示 */

响应式工具类

css
/* 显示/隐藏 */
.d-none {
} /* 总是隐藏 */
.d-none-sm {
} /* 小屏隐藏 */
.d-none-md {
} /* 中屏隐藏 */
.d-none-lg {
} /* 大屏隐藏 */

.d-block-sm {
} /* 小屏显示为 block */
.d-flex-md {
} /* 中屏显示为 flex */

/* 文本对齐 */
.text-center {
} /* 总是居中 */
.text-center-md {
} /* 中屏及以上居中 */
.text-left-lg {
} /* 大屏及以上左对齐 */

命名中的数字使用

避免无意义的数字

css
/* ❌ 不好:数字没有含义 */
.box1 {
}
.box2 {
}
.box3 {
}

.button1 {
}
.button2 {
}

/* ✅ 好:使用描述性名称 */
.product-card {
}
.testimonial-card {
}
.feature-card {
}

.button--primary {
}
.button--secondary {
}

合理使用数字

数字在某些情况下是合理的:

css
/* ✅ 表示尺寸级别 */
.heading-1 {
} /* h1 */
.heading-2 {
} /* h2 */
.heading-3 {
} /* h3 */

/* ✅ 表示间距级别 */
.spacing-1 {
} /* 8px */
.spacing-2 {
} /* 16px */
.spacing-3 {
} /* 24px */

/* ✅ 表示权重 */
.font-weight-400 {
} /* normal */
.font-weight-700 {
} /* bold */

/* ✅ 表示网格列数 */
.col-1 {
} /* 1/12 */
.col-6 {
} /* 6/12 = 50% */
.col-12 {
} /* 12/12 = 100% */

多单词命名的连接

Kebab-case(推荐)

css
/* ✅ Kebab-case:单词之间用连字符 */
.user-profile {
}
.shopping-cart {
}
.contact-form {
}
.navigation-menu {
}
.product-card {
}

优点:

  • CSS 标准做法
  • 易于阅读
  • 大多数团队的选择
  • 兼容性最好

camelCase(不推荐)

css
/* ❌ CamelCase:不推荐在 CSS 中使用 */
.userProfile {
}
.shoppingCart {
}

缺点:

  • 不符合 CSS 惯例
  • 在 class 属性中不易阅读
  • 容易和 JavaScript 混淆

snake_case(不推荐)

css
/* ❌ Snake_case:不推荐在 CSS 中使用 */
.user_profile {
}
.shopping_cart {
}

缺点:

  • 不符合 CSS 惯例
  • 下划线在 BEM 中有特殊含义
  • 难以区分单词边界

统一规则

选择一种并坚持使用:

css
/* ✅ 一致的命名 */
.user-profile {
}
.user-settings {
}
.user-dashboard {
}
.user-notifications {
}

/* ❌ 混乱的命名 */
.userProfile {
}
.user_settings {
}
.user-dashboard {
}
.UserNotifications {
}

避免的命名陷阱

陷阱 1:基于样式命名

css
/* ❌ 避免 */
.float-left {
}
.margin-top-20 {
}
.color-red {
}
.bg-blue {
}

/* ✅ 改进 */
.sidebar {
} /* 而不是 .float-left */
.section-spacing {
} /* 而不是 .margin-top-20 */
.error-text {
} /* 而不是 .color-red */
.primary-background {
} /* 而不是 .bg-blue */

陷阱 2:过度 abbreviation

css
/* ❌ 避免过度缩写 */
.nav {
} /* navigation 也不长 */
.btn {
} /* button 也不长 */
.usr {
} /* user 很短 */
.img {
} /* image 很短 */

/* ✅ 完整单词更清晰 */
.navigation {
}
.button {
}
.user {
}
.image {
}

/* ✅ 但这些缩写是可以接受的(众所周知) */
.nav {
} /* navigation - 如果团队认可 */
.btn {
} /* button - 如果团队认可 */

陷阱 3:过于泛泛

css
/* ❌ 太泛泛 */
.wrapper {
} /* 什么的包装? */
.container {
} /* 什么容器? */
.item {
} /* 什么项目? */
.content {
} /* 什么内容? */

/* ✅ 更具体 */
.modal-wrapper {
}
.page-container {
}
.cart-item {
}
.article-content {
}

陷阱 4:使用保留字或常见名称

css
/* ⚠️ 小心使用这些名称 */
.header {
} /* 太常见,容易冲突 */
.footer {
} /* 同上 */
.container {
} /* 同上 */
.nav {
} /* 同上 */

/* ✅ 更具体的名称 */
.site-header {
}
.page-footer {
}
.content-container {
}
.main-navigation {
}

团队协作的命名规范

创建命名词典

建立团队共享的命名词典:

markdown
# CSS 命名词典

## 布局

- `site-` - 网站级别 (site-header, site-footer)
- `page-` - 页面级别 (page-content, page-sidebar)
- `section-` - 区块级别 (section-hero, section-features)

## 组件

- `card-` - 卡片相关
- `button-` - 按钮相关
- `form-` - 表单相关
- `modal-` - 模态框相关

## 工具类

- `u-` - 工具类前缀
- `is-` - 状态前缀
- `has-` - 属性前缀
- `js-` - JavaScript 钩子

## 尺寸

- `--small` - 小尺寸
- `--medium` - 中尺寸(通常省略)
- `--large` - 大尺寸

## 变体

- `--primary` - 主要样式
- `--secondary` - 次要样式
- `--tertiary` - 第三样式

代码审查清单

在代码审查时检查:

markdown
## 命名审查清单

- [ ] 类名是否描述用途而非外观?
- [ ] 命名是否一致(都用 kebab-case)?
- [ ] 是否避免了过度缩写?
- [ ] 类名长度是否适中(1-3 个单词)?
- [ ] 是否使用了约定的前缀(l-, c-, u-)?
- [ ] JavaScript 钩子是否使用 js- 前缀?
- [ ] 状态类是否使用 is- 或 has- 前缀?
- [ ] 是否避免了基于样式的命名?

实际项目示例

电商网站命名示例

html
<!-- 产品列表页 -->
<div class="page page--product-list">
  <header class="site-header">
    <nav class="main-navigation">
      <a href="/" class="nav__logo">Store Name</a>
      <ul class="nav__menu">
        <li class="nav__item">
          <a href="/products" class="nav__link is-active">Products</a>
        </li>
        <li class="nav__item">
          <a href="/cart" class="nav__link">
            Cart <span class="cart-badge">3</span>
          </a>
        </li>
      </ul>
    </nav>
  </header>

  <main class="page-content">
    <aside class="filter-sidebar">
      <div class="filter-group">
        <h3 class="filter-group__title">Category</h3>
        <div class="filter-group__options">
          <label class="checkbox-label">
            <input type="checkbox" class="checkbox-input js-filter-checkbox" />
            <span class="checkbox-text">Electronics</span>
          </label>
        </div>
      </div>
    </aside>

    <div class="product-grid">
      <article class="product-card">
        <div class="product-card__image-container">
          <img src="product.jpg" alt="Product" class="product-card__image" />
          <span class="product-badge product-badge--sale">Sale</span>
        </div>
        <div class="product-card__content">
          <h2 class="product-card__title">Product Name</h2>
          <div class="product-rating">
            <span class="product-rating__stars">★★★★☆</span>
            <span class="product-rating__count">(24)</span>
          </div>
          <div class="product-price">
            <span class="product-price__current">$99.99</span>
            <span class="product-price__original">$129.99</span>
          </div>
          <button class="button button--primary button--block js-add-to-cart">
            Add to Cart
          </button>
        </div>
      </article>
    </div>
  </main>
</div>

从这个示例可以看到:

  1. 清晰的层级:page → site-header → main-navigation
  2. 语义化命名:product-card、filter-sidebar
  3. BEM 结构:product-card__title、button--primary
  4. 状态类:is-active
  5. JS 钩子:js-filter-checkbox、js-add-to-cart
  6. 一致性:都使用 kebab-case

总结

良好的命名规范是可维护 CSS 的基 foundation。

核心原则

  • 语义化:描述用途而非外观
  • 一致性:统一的命名风格
  • 清晰性:易于理解和搜索
  • 具体性:避免过于泛泛

最佳实践

  • 使用 kebab-case
  • 采用 BEM 或类似方法论
  • 为不同类型使用前缀(l-, c-, u-, is-, has-, js-)
  • 避免过度缩写
  • 保持类名长度适中(1-3 个单词)
  • 创建团队命名词典

记住

好的类名应该:

  • ✅ 自解释,看名字就知道用途
  • ✅ 易于搜索和替换
  • ✅ 在六个月后依然 make sense
  • ✅ 新团队成员能快速理解

代码是写给人看的,偶然才给机器执行。投入时间在命名上,会在后续的维护中得到十倍的回报。