Skip to content

CSS 方法论:构建可维护的样式系统

当你的项目从几个页面扩展到几十个页面,CSS 文件从几百行增长到几千行时,你会发现一个问题:样式越来越难管理。修改一个组件的样式可能会意外影响到其他地方,团队成员之间的命名风格不统一,新加入的开发者不知道样式应该写在哪里。

这就像一个快速扩张的城市,如果没有城市规划,道路就会混乱不堪,建筑布局杂乱无章。CSS 方法论就是这样的"城市规划",它帮助我们建立一套系统化的规则和约定,让代码更容易理解、维护和扩展。

为什么需要 CSS 方法论?

在深入具体方法论之前,让我们先理解为什么需要它们。

没有方法论的问题

css
/* 糟糕的 CSS 组织 */
.title {
  font-size: 24px;
  color: #333;
}

.big-title {
  font-size: 32px;
}

.header .title {
  color: #fff;
}

.sidebar .title {
  font-size: 18px;
}

/* 到底哪个样式会被应用?优先级怎么计算? */

这种随意的 CSS 写法会导致几个严重问题:

  1. 命名冲突.title 在不同地方有不同的样式
  2. 优先级难以控制.header .title.sidebar .title 哪个优先级更高?
  3. 难以重用:每次都要写新的样式,无法复用已有代码
  4. 难以维护:修改一处可能影响多处

方法论的价值

CSS 方法论提供了:

  • 统一的命名约定:团队成员都按照同样的规则命名
  • 清晰的组织结构:每个人都知道样式应该写在哪里
  • 更好的可维护性:样式更容易理解和修改
  • 减少冲突:通过规范避免样式冲突
  • 更好的复用性:组件化的思维让代码更容易复用

BEM 方法论

BEM (Block Element Modifier) 是目前最流行的 CSS 方法论之一,由俄罗斯搜索引擎公司 Yandex 提出。它的核心思想是将界面划分为独立的块(Block),每个块包含元素(Element),并通过修饰符(Modifier)来表示不同的状态或变体。

BEM 的命名规则

BEM 使用非常具体的命名约定:

.block {}                    /* 块 */
.block__element {}           /* 元素 */
.block--modifier {}          /* 块的修饰符 */
.block__element--modifier {} /* 元素的修饰符 */

让我们通过一个实际例子来理解:

html
<!-- 一个卡片组件 -->
<div class="card card--featured">
  <div class="card__header">
    <h2 class="card__title">Product Title</h2>
    <span class="card__badge card__badge--new">New</span>
  </div>
  <div class="card__body">
    <p class="card__description">Product description goes here...</p>
  </div>
  <div class="card__footer">
    <button class="card__button card__button--primary">Buy Now</button>
    <button class="card__button card__button--secondary">Details</button>
  </div>
</div>

对应的 CSS:

css
/* 块:card */
.card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  background-color: white;
  overflow: hidden;
}

/* 块的修饰符:特色卡片 */
.card--featured {
  border: 2px solid #3498db;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* 元素:卡片头部 */
.card__header {
  padding: 20px;
  background-color: #f8f9fa;
  border-bottom: 1px solid #e0e0e0;
}

/* 元素:卡片标题 */
.card__title {
  margin: 0;
  font-size: 20px;
  color: #333;
}

/* 元素:徽章 */
.card__badge {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: bold;
}

/* 元素的修饰符:新品徽章 */
.card__badge--new {
  background-color: #2ecc71;
  color: white;
}

/* 元素:卡片主体 */
.card__body {
  padding: 20px;
}

/* 元素:描述文本 */
.card__description {
  margin: 0;
  color: #666;
  line-height: 1.6;
}

/* 元素:卡片底部 */
.card__footer {
  padding: 20px;
  background-color: #f8f9fa;
  border-top: 1px solid #e0e0e0;
  display: flex;
  gap: 10px;
}

/* 元素:按钮 */
.card__button {
  flex: 1;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.3s;
}

/* 按钮修饰符:主要按钮 */
.card__button--primary {
  background-color: #3498db;
  color: white;
}

.card__button--primary:hover {
  background-color: #2980b9;
}

/* 按钮修饰符:次要按钮 */
.card__button--secondary {
  background-color: #ecf0f1;
  color: #333;
}

.card__button--secondary:hover {
  background-color: #bdc3c7;
}

BEM 的核心原则

1. Block(块)的独立性

块应该是独立的、可重用的组件,不依赖页面上的其他元素:

css
/* ✅ 好的做法:块是独立的 */
.button {
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
}

/* ❌ 避免:块依赖父元素 */
.sidebar .button {
  padding: 10px 20px; /* 不要这样做 */
}

2. Element(元素)属于块

元素是块的组成部分,不能独立存在:

css
/* ✅ 好的做法:元素总是属于某个块 */
.menu__item {
  padding: 10px;
}

.menu__link {
  color: #333;
  text-decoration: none;
}

/* ❌ 避免:元素独立存在 */
.item {
  /* 这个元素属于哪个块?不清楚 */
}

3. Modifier(修饰符)表示状态或变体

修饰符用来表示块或元素的不同状态、主题或大小:

css
/* ✅ 好的做法:使用修饰符表示状态 */
.button--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.button--large {
  padding: 15px 30px;
  font-size: 18px;
}

.button--primary {
  background-color: #3498db;
  color: white;
}

/* ❌ 避免:使用额外的类名 */
.button.disabled {
  /* 不符合 BEM 规范 */
}

BEM 实际应用示例

让我们看一个导航菜单的完整示例:

html
<nav class="nav nav--horizontal">
  <div class="nav__logo">
    <img src="logo.png" alt="Logo" class="nav__logo-image" />
  </div>
  <ul class="nav__list">
    <li class="nav__item">
      <a href="#" class="nav__link nav__link--active">Home</a>
    </li>
    <li class="nav__item">
      <a href="#" class="nav__link">About</a>
    </li>
    <li class="nav__item nav__item--has-dropdown">
      <a href="#" class="nav__link">Services</a>
      <ul class="nav__dropdown">
        <li class="nav__dropdown-item">
          <a href="#" class="nav__dropdown-link">Service 1</a>
        </li>
        <li class="nav__dropdown-item">
          <a href="#" class="nav__dropdown-link">Service 2</a>
        </li>
      </ul>
    </li>
  </ul>
</nav>
css
/* 块:导航 */
.nav {
  background-color: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  padding: 0 20px;
}

/* 修饰符:水平导航 */
.nav--horizontal {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* 元素:Logo 容器 */
.nav__logo {
  padding: 10px 0;
}

/* 元素:Logo 图片 */
.nav__logo-image {
  height: 40px;
  width: auto;
}

/* 元素:导航列表 */
.nav__list {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 5px;
}

/* 元素:导航项 */
.nav__item {
  position: relative;
}

/* 元素修饰符:有下拉菜单的项 */
.nav__item--has-dropdown:hover .nav__dropdown {
  display: block;
}

/* 元素:导航链接 */
.nav__link {
  display: block;
  padding: 15px 20px;
  color: #333;
  text-decoration: none;
  transition: background-color 0.3s;
}

.nav__link:hover {
  background-color: #f8f9fa;
}

/* 链接修饰符:激活状态 */
.nav__link--active {
  color: #3498db;
  border-bottom: 2px solid #3498db;
}

/* 元素:下拉菜单 */
.nav__dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 200px;
  background-color: white;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border-radius: 4px;
  padding: 10px 0;
  list-style: none;
}

/* 元素:下拉菜单项 */
.nav__dropdown-item {
  margin: 0;
}

/* 元素:下拉菜单链接 */
.nav__dropdown-link {
  display: block;
  padding: 10px 20px;
  color: #333;
  text-decoration: none;
  transition: background-color 0.3s;
}

.nav__dropdown-link:hover {
  background-color: #f8f9fa;
}

BEM 的优缺点

优点

  • 清晰直观:从类名就能看出元素的层级关系
  • 避免冲突:每个组件的类名都是唯一的
  • 易于维护:修改某个组件不会影响其他部分
  • 工具支持:有很多工具和插件支持 BEM

缺点

  • 类名较长.card__button--primary 比较冗长
  • HTML 冗余:需要写很多类名
  • 学习曲线:团队需要时间适应这种命名方式

SMACSS 方法论

SMACSS (Scalable and Modular Architecture for CSS) 由 Jonathan Snook 提出,它将 CSS 规则分为五个类别,每个类别有特定的用途和命名规则。

SMACSS 的五大类别

1. Base(基础)

基础规则定义元素的默认样式,通常使用元素选择器:

css
/* Base 规则 */
html {
  font-size: 16px;
  line-height: 1.6;
  color: #333;
}

body {
  margin: 0;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  background-color: #f8f9fa;
}

a {
  color: #3498db;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin-top: 0;
  line-height: 1.2;
}

p {
  margin: 0 0 1em 0;
}

img {
  max-width: 100%;
  height: auto;
}

2. Layout(布局)

布局规则定义页面的主要结构,使用 l-layout- 前缀:

css
/* Layout 规则 */
.l-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
}

.l-header {
  background-color: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  position: sticky;
  top: 0;
  z-index: 100;
}

.l-main {
  min-height: calc(100vh - 200px);
  padding: 40px 0;
}

.l-sidebar {
  width: 300px;
  padding: 20px;
}

.l-content {
  flex: 1;
  padding: 20px;
}

.l-footer {
  background-color: #2c3e50;
  color: white;
  padding: 40px 0;
  margin-top: 60px;
}

/* 两栏布局 */
.l-two-column {
  display: flex;
  gap: 40px;
}

3. Module(模块)

模块是可重用的组件,这是 SMACSS 的核心:

css
/* Module: Card */
.card {
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.card-header {
  padding: 20px;
  background-color: #f8f9fa;
  border-bottom: 1px solid #e0e0e0;
}

.card-title {
  margin: 0;
  font-size: 20px;
  color: #333;
}

.card-body {
  padding: 20px;
}

.card-footer {
  padding: 20px;
  background-color: #f8f9fa;
  border-top: 1px solid #e0e0e0;
}

/* Module: Button */
.button {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.3s;
}

/* Module: Alert */
.alert {
  padding: 15px 20px;
  border-radius: 4px;
  margin-bottom: 20px;
}

.alert-title {
  font-weight: bold;
  margin-bottom: 5px;
}

.alert-message {
  margin: 0;
}

4. State(状态)

状态规则描述模块或布局在特定状态下的样式,使用 is- 前缀:

css
/* State 规则 */
.is-hidden {
  display: none !important;
}

.is-visible {
  display: block;
}

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

.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.is-loading {
  position: relative;
  pointer-events: none;
}

.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* 模块特定的状态 */
.button.is-disabled {
  background-color: #95a5a6;
}

.alert.is-dismissible {
  padding-right: 40px;
  position: relative;
}

5. Theme(主题)

主题规则定义颜色、字体等可视化外观,使用 theme- 前缀:

css
/* Theme 规则 */
.theme-dark {
  background-color: #2c3e50;
  color: #ecf0f1;
}

.theme-dark .card {
  background-color: #34495e;
  color: #ecf0f1;
}

.theme-dark .button {
  background-color: #3498db;
  color: white;
}

/* 品牌主题 */
.theme-brand-primary {
  background-color: #3498db;
  color: white;
}

.theme-brand-success {
  background-color: #2ecc71;
  color: white;
}

.theme-brand-warning {
  background-color: #f39c12;
  color: white;
}

.theme-brand-danger {
  background-color: #e74c3c;
  color: white;
}

SMACSS 文件组织

SMACSS 推荐按类别组织文件:

styles/
├── base/
│   ├── _reset.css      # 重置样式
│   ├── _typography.css # 排版基础
│   └── _elements.css   # 元素基础样式
├── layout/
│   ├── _grid.css       # 网格系统
│   ├── _header.css     # 头部布局
│   ├── _footer.css     # 底部布局
│   └── _sidebar.css    # 侧边栏布局
├── modules/
│   ├── _button.css     # 按钮模块
│   ├── _card.css       # 卡片模块
│   ├── _form.css       # 表单模块
│   └── _navigation.css # 导航模块
├── state/
│   └── _states.css     # 状态样式
├── theme/
│   ├── _default.css    # 默认主题
│   └── _dark.css       # 暗色主题
└── main.css            # 主文件,导入所有样式

SMACSS 实际应用示例

html
<!-- 使用 SMACSS 的页面结构 -->
<div class="l-container">
  <!-- 布局:头部 -->
  <header class="l-header">
    <nav class="navigation">
      <a href="#" class="navigation-link is-active">Home</a>
      <a href="#" class="navigation-link">About</a>
      <a href="#" class="navigation-link">Contact</a>
    </nav>
  </header>

  <!-- 布局:主内容区 -->
  <main class="l-main">
    <div class="l-two-column">
      <!-- 布局:内容 -->
      <div class="l-content">
        <!-- 模块:卡片 -->
        <article class="card">
          <header class="card-header">
            <h2 class="card-title">Article Title</h2>
          </header>
          <div class="card-body">
            <p>Article content goes here...</p>
            <button class="button theme-brand-primary">Read More</button>
          </div>
        </article>

        <!-- 模块:警告框(带状态) -->
        <div class="alert theme-brand-warning is-dismissible">
          <h3 class="alert-title">Warning</h3>
          <p class="alert-message">This is a warning message.</p>
        </div>
      </div>

      <!-- 布局:侧边栏 -->
      <aside class="l-sidebar">
        <!-- 模块:侧边栏卡片 -->
        <div class="card">
          <div class="card-header">
            <h3 class="card-title">Sidebar</h3>
          </div>
          <div class="card-body">
            <p>Sidebar content...</p>
          </div>
        </div>
      </aside>
    </div>
  </main>

  <!-- 布局:底部 -->
  <footer class="l-footer">
    <p>&copy; 2025 Your Company</p>
  </footer>
</div>

SMACSS 的优缺点

优点

  • 清晰的分类:CSS 有明确的组织结构
  • 易于扩展:添加新样式时知道应该放在哪里
  • 语义化前缀:通过前缀就能知道规则的用途
  • 灵活:不强制特定的命名格式

缺点

  • 需要纪律:团队需要严格遵守分类规则
  • 可能过度分类:有时很难判断应该属于哪个类别
  • 学习成本:需要理解五大类别的区别

OOCSS 方法论

OOCSS (Object Oriented CSS) 由 Nicole Sullivan 提出,它借鉴了面向对象编程的思想,强调将样式看作可重用的"对象"。

OOCSS 的两大原则

原则 1:分离结构和皮肤

结构(structure)指的是布局相关的属性,如宽度、高度、定位等。皮肤(skin)指的是视觉相关的属性,如颜色、字体、阴影等。

css
/* ❌ 不好的做法:结构和皮肤混在一起 */
.button-primary {
  /* 结构 */
  display: inline-block;
  padding: 10px 20px;
  /* 皮肤 */
  background-color: #3498db;
  color: white;
  border-radius: 4px;
}

.button-secondary {
  /* 结构(重复了) */
  display: inline-block;
  padding: 10px 20px;
  /* 皮肤 */
  background-color: #95a5a6;
  color: white;
  border-radius: 4px;
}

/* ✅ 好的做法:分离结构和皮肤 */
/* 结构 */
.button {
  display: inline-block;
  padding: 10px 20px;
  border: none;
  cursor: pointer;
  transition: all 0.3s;
}

/* 皮肤 */
.skin-primary {
  background-color: #3498db;
  color: white;
}

.skin-secondary {
  background-color: #95a5a6;
  color: white;
}

.skin-rounded {
  border-radius: 4px;
}

.skin-pill {
  border-radius: 50px;
}

使用时组合这些类:

html
<button class="button skin-primary skin-rounded">Primary Button</button>
<button class="button skin-secondary skin-pill">Secondary Button</button>

原则 2:分离容器和内容

内容的样式不应该依赖于它所在的容器。

css
/* ❌ 不好的做法:内容依赖容器 */
.sidebar h2 {
  font-size: 20px;
  color: #333;
  margin-bottom: 10px;
}

.main-content h2 {
  font-size: 24px;
  color: #333;
  margin-bottom: 15px;
}

/* ✅ 好的做法:分离容器和内容 */
/* 标题对象 */
.heading {
  color: #333;
  line-height: 1.2;
}

.heading-large {
  font-size: 24px;
  margin-bottom: 15px;
}

.heading-medium {
  font-size: 20px;
  margin-bottom: 10px;
}

.heading-small {
  font-size: 16px;
  margin-bottom: 8px;
}

/* 使用 */
/* <div class="sidebar">
     <h2 class="heading heading-medium">Sidebar Title</h2>
   </div>
   
   <div class="main-content">
     <h2 class="heading heading-large">Main Title</h2>
   </div> */

OOCSS 实际应用

让我们创建一个灵活的媒体对象(Media Object):

css
/* 媒体对象:结构 */
.media {
  display: flex;
  align-items: flex-start;
  gap: 15px;
}

.media-figure {
  flex-shrink: 0;
}

.media-body {
  flex: 1;
}

/* 媒体对象:尺寸变体 */
.media-small .media-figure {
  width: 50px;
}

.media-medium .media-figure {
  width: 100px;
}

.media-large .media-figure {
  width: 150px;
}

/* 皮肤:间距 */
.spacing-tight {
  gap: 10px;
}

.spacing-comfortable {
  gap: 20px;
}

.spacing-loose {
  gap: 30px;
}

/* 皮肤:背景 */
.bg-white {
  background-color: white;
}

.bg-gray {
  background-color: #f8f9fa;
}

/* 皮肤:边框 */
.bordered {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 15px;
}

使用这些对象:

html
<!-- 评论组件 -->
<div class="media media-medium spacing-comfortable bordered bg-white">
  <div class="media-figure">
    <img src="avatar.jpg" alt="User Avatar" class="avatar" />
  </div>
  <div class="media-body">
    <h3 class="heading heading-small">John Smith</h3>
    <p>This is a comment using the media object pattern...</p>
  </div>
</div>

<!-- 产品列表项 -->
<div class="media media-large spacing-loose bg-gray">
  <div class="media-figure">
    <img src="product.jpg" alt="Product" />
  </div>
  <div class="media-body">
    <h3 class="heading heading-medium">Product Name</h3>
    <p>Product description...</p>
    <button class="button skin-primary skin-rounded">Add to Cart</button>
  </div>
</div>

OOCSS 的工具类

OOCSS 鼓励创建大量可重用的工具类:

css
/* 显示工具 */
.d-block {
  display: block;
}
.d-inline {
  display: inline;
}
.d-inline-block {
  display: inline-block;
}
.d-flex {
  display: flex;
}
.d-grid {
  display: grid;
}
.d-none {
  display: none;
}

/* 弹性布局工具 */
.flex-row {
  flex-direction: row;
}
.flex-column {
  flex-direction: column;
}
.justify-start {
  justify-content: flex-start;
}
.justify-center {
  justify-content: center;
}
.justify-between {
  justify-content: space-between;
}
.align-start {
  align-items: flex-start;
}
.align-center {
  align-items: center;
}
.align-end {
  align-items: flex-end;
}

/* 间距工具 */
.m-0 {
  margin: 0;
}
.m-1 {
  margin: 8px;
}
.m-2 {
  margin: 16px;
}
.m-3 {
  margin: 24px;
}
.p-0 {
  padding: 0;
}
.p-1 {
  padding: 8px;
}
.p-2 {
  padding: 16px;
}
.p-3 {
  padding: 24px;
}

/* 文本工具 */
.text-left {
  text-align: left;
}
.text-center {
  text-align: center;
}
.text-right {
  text-align: right;
}
.text-bold {
  font-weight: bold;
}
.text-normal {
  font-weight: normal;
}

/* 颜色工具 */
.text-primary {
  color: #3498db;
}
.text-success {
  color: #2ecc71;
}
.text-danger {
  color: #e74c3c;
}
.bg-primary {
  background-color: #3498db;
}
.bg-success {
  background-color: #2ecc71;
}
.bg-danger {
  background-color: #e74c3c;
}

/* 边框工具 */
.border {
  border: 1px solid #e0e0e0;
}
.border-top {
  border-top: 1px solid #e0e0e0;
}
.border-bottom {
  border-bottom: 1px solid #e0e0e0;
}
.rounded {
  border-radius: 4px;
}
.rounded-lg {
  border-radius: 8px;
}
.circle {
  border-radius: 50%;
}

使用工具类构建界面:

html
<div class="d-flex justify-between align-center p-2 border-bottom">
  <h2 class="text-bold m-0">Dashboard</h2>
  <button class="button skin-primary rounded">New Item</button>
</div>

<div class="d-grid p-3">
  <div class="bordered rounded-lg p-2 bg-white">
    <h3 class="text-center text-primary">Statistics</h3>
    <p class="text-center m-0">1,234 users</p>
  </div>
</div>

OOCSS 的优缺点

优点

  • 极高的复用性:样式可以在任何地方使用
  • 更小的 CSS 文件:避免重复代码
  • 快速开发:组合现有类而不是写新样式
  • 一致性:使用相同的基础类保证一致性

缺点

  • HTML 可能很冗长:需要多个类名
  • 可读性下降:HTML 中充满了工具类
  • 语义化问题:类名不能表达内容含义
  • 难以维护:修改样式需要改 HTML

如何选择合适的方法论?

项目规模考虑

小型项目(1-5 个页面):

  • 可以不使用严格的方法论
  • 保持命名一致即可
  • OOCSS 的工具类思想可以借鉴

中型项目(5-20 个页面):

  • 推荐使用 BEM
  • 简单直观,容易上手
  • 适合小团队协作

大型项目(20+ 页面或多人协作):

  • BEM + SMACSS 组合使用
  • BEM 用于组件命名
  • SMACSS 用于文件组织
  • 严格的规范有助于团队协作

团队技能考虑

初级团队

  • 从 BEM 开始
  • 规则简单明了
  • 有大量学习资源

中级团队

  • SMACSS 适合有经验的团队
  • 需要良好的规划能力
  • 能够建立完善的组织结构

高级团队

  • 可以混合多种方法论
  • 根据项目需求定制
  • 建立自己的规范

项目类型考虑

组件库 / 设计系统

  • OOCSS 非常适合
  • 提供大量可组合的基础类
  • 灵活性极高

内容网站 / 博客

  • SMACSS 比较适合
  • 清晰的内容层级
  • 语义化更重要

应用程序 / 后台系统

  • BEM 最合适
  • 组件化思维
  • 可维护性强

混合使用方法论

在实际项目中,我们通常会混合使用多种方法论的优点:

css
/* 使用 SMACSS 的分类 + BEM 的命名 */

/* Layout */
.l-container {
  max-width: 1200px;
  margin: 0 auto;
}

/* Module: Card (BEM 命名) */
.card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
}

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

.card__header {
  padding: 20px;
}

.card__title {
  margin: 0;
}

/* OOCSS 工具类 */
.d-flex {
  display: flex;
}

.gap-2 {
  gap: 16px;
}

/* State */
.is-hidden {
  display: none;
}

使用示例:

html
<div class="l-container">
  <div class="card card--featured">
    <div class="card__header d-flex gap-2">
      <h2 class="card__title">Featured Article</h2>
    </div>
  </div>
</div>

常见问题与解决

问题 1:类名太长怎么办?

BEM 的类名可能会很长,但这是可以接受的:

css
/* 不要为了简短而牺牲清晰度 */
/* ❌ */
.c-h-t {
  /* 什么意思? */
}

/* ✅ */
.card__header-title {
  /* 虽然长,但一看就懂 */
}

可以使用构建工具压缩类名:

css
/* 开发时 */
.navigation__dropdown-menu-item--active {
}

/* 生产环境(压缩后) */
.a1b2c3 {
}

问题 2:HTML 中类名太多?

这是 OOCSS 的常见问题:

html
<!-- 可能看起来很乱 -->
<div
  class="d-flex justify-between align-center p-2 m-3 bg-white rounded border"
>
  <!-- ... -->
</div>

解决方案:平衡使用

html
<!-- 创建语义化的组合类 -->
<div class="panel-header">
  <!-- ... -->
</div>
css
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  margin: 24px;
  background-color: white;
  border-radius: 4px;
  border: 1px solid #e0e0e0;
}

问题 3:何时使用嵌套选择器?

方法论鼓励扁平化选择器,但有时嵌套是合理的:

css
/* ❌ 避免深层嵌套 */
.header .nav .menu .item .link {
}

/* ✅ BEM 方式 */
.nav__link {
}

/* ✅ 但这种嵌套是合理的 */
.card:hover {
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.card__button:disabled {
  opacity: 0.5;
}

总结

CSS 方法论不是限制,而是帮助我们更好地组织代码的工具。

核心要点

  • BEM:适合中小型项目,命名直观,易于上手
  • SMACSS:适合大型项目,分类清晰,组织有序
  • OOCSS:适合组件库,高度复用,灵活组合

选择建议

  • 不要盲目追求某一种方法论
  • 根据项目需求和团队情况选择
  • 可以混合使用多种方法论的优点
  • 最重要的是保持一致性

实践原则

  • 保持选择器扁平化
  • 避免过度嵌套
  • 提高代码复用性
  • 建立清晰的命名约定
  • 良好的文件组织结构

方法论的目标是让代码更易维护、更易协作、更易扩展。选择适合你项目的方法,并坚持使用它,这比选择哪种方法更重要。