返回项目目录
开发工具精选项目

CSS Modules

让 CSS 类名和动画名称默认拥有局部作用域的模块化方案。

主要语言CSS
开源许可MIT
项目类型开发工具
维护状态稳定维护
OVERVIEW

项目概述

CSS Modules 保留标准 CSS 的编写方式,在构建时把局部名称转换为唯一标识,并向 JavaScript 导出名称映射,从而减少全局样式冲突。

CSS Modules 不是单一运行时库,而是一套由构建工具实现的 CSS 模块化约定:模块文件中的类名和动画名称默认处于局部作用域,编译后变成全局唯一标识;JavaScript 或 TypeScript 导入该文件时会获得“源码名称到生成名称”的映射对象。开发者仍然编写普通 CSS,并可通过 composes 复用其他局部类,通过 :global 显式进入全局作用域。CSS Modules 的底层交换格式称为 ICSS,不同 Bundler 的文件命名、导出方式和生成类名可能略有差异。Vite、Webpack、Next.js 等主流工具通常已经集成该能力,因此它常被用作组件样式隔离的低抽象默认方案。

版本模式无统一版本号
常见文件名*.module.css
核心输出Local → Global 映射
FEATURES

主要特点

CSS Modules 以文件和局部名称为边界解决传统 CSS 的全局冲突,同时保留选择器、媒体查询、变量和动画等原生能力。

01

默认局部作用域

模块中的类名和动画名称在构建时转换为唯一标识,不同文件可以安全复用 card、title 或 button 等常见名称。

02

显式模块依赖

组件通过 import 获得类名映射,样式依赖和组件依赖出现在同一模块图中,便于构建工具追踪与拆包。

03

继续使用标准 CSS

可直接使用选择器、伪类、媒体查询、Container Query、CSS Variables、Animation 和 Cascade Layer,无需学习 JavaScript 样式对象。

04

类名组合

composes 可以让一个局部类复用同文件或其他模块中的类名,减少重复声明,并在导出映射中返回组合后的 Class List。

05

可控的全局逃生口

:global 可用于 Reset、第三方组件状态类或应用级约定,让局部隔离与必要的全局选择器共存。

06

构建时名称转换

开发环境可保留易读文件名和局部名称,生产环境可生成更短标识;浏览器不需要额外 CSS Modules 运行时。

07

预处理器兼容

许多工具支持 *.module.scss、*.module.less 等组合形式,可在局部作用域基础上继续使用 Sass、Less 或 PostCSS。

08

主流工具内建支持

Vite、Webpack、Next.js 以及多种框架工具链都能处理 CSS Modules,常见项目无需自行实现名称转换。

USE CASES

适用场景

适合希望继续编写普通 CSS,又需要组件级隔离、清晰依赖关系和低迁移成本的应用与组件库。

React、Vue 与组件化应用

每个组件维护自己的 *.module.css,通过导入映射应用局部类,降低页面和组件之间的意外覆盖。

传统 CSS 渐进迁移

团队可以逐个文件改为 .module.css,无需一次性重写为 Utility CSS 或 CSS-in-JS。

中小型产品界面

适合营销站、后台、内容应用和 SaaS 产品,以很少的工具概念获得可靠的组件样式隔离。

保留原生 CSS 能力的团队

当团队熟悉 CSS Cascade、Selector、Media Query 和 Variable 时,CSS Modules 能在不改变作者模型的情况下增加局部作用域。

服务端渲染与静态生成

样式由框架构建流程提取或拆分,适合 SSR 和 SSG;具体加载顺序与 CSS Chunk 行为由所用框架决定。

轻量组件库

可为组件提供稳定的局部样式边界,但发布前要明确生成 CSS、导出映射和消费者 Bundler 的处理方式。

EVALUATION

优点与注意事项

技术选型不仅要看能力,也要理解它带来的团队成本。

主要优点

CSS Modules 擅长的地方

  • 保留普通 CSS 语法,现有 CSS 知识、浏览器工具和大部分 PostCSS 生态可以继续使用
  • 类名默认局部化,显著降低大型应用中的命名冲突和意外级联
  • 没有浏览器端样式生成引擎,最终运行方式就是普通类名与 CSS
  • 组件显式导入样式,依赖关系比全局样式表更容易追踪
  • 可按文件渐进采用,适合从传统全局 CSS 迁移
  • 主流 Bundler 与框架普遍支持,基础使用通常不需要额外配置

需要注意

采用前应考虑的问题

它是一套约定而非单一实现

不同 Bundler 对导出方式、类名格式、ICSS、命名规则和 Source Map 的支持可能不同,跨工具迁移前需要核对文档。

局部作用域不等于完全封装

继承、CSS Variables、元素选择器、全局 Reset 和 Cascade Layer 仍可跨越组件边界,团队仍需理解标准 CSS 级联。

默认类型通常不够精确

TypeScript 工具链常把模块导出视为字符串字典,不一定能发现写错的具体类名;严格类型需要额外声明生成器或编辑器插件。

组合不能替代组件变体系统

composes 适合复用类名,但复杂 Size、Tone、State 和 Compound Variant 仍需组件代码、类名工具或更高层方案组织。

全局逃生口容易被滥用

大量 :global 会重新引入命名冲突和远距离覆盖,应限制用途并为真正的全局类建立明确命名规则。

动态样式仍需其他机制

运行时颜色、坐标和尺寸通常要通过 CSS Variables、Inline Style 或预定义 Modifier Class 传入,CSS Modules 不会动态生成规则。

样式顺序由构建图决定

多个模块拥有相同特异性时,最终导入与 CSS Chunk 顺序仍会影响结果,不能只依赖源码文件名推断优先级。

组件库发布需要额外测试

如果直接发布未编译的 .module.css,消费者必须具备兼容处理能力;如果预编译,则要避免类名映射、CSS 文件和 JavaScript 导出失配。

QUICK START

快速开始

使用 Vite 自带的 CSS Modules 支持创建 React 示例,无需安装额外的样式运行时或 Vite 插件。

1创建 Vite React 项目
bash
npm create vite@latest css-modules-app -- --template react-ts
cd css-modules-app
npm install
npm run dev
2创建局部作用域样式
css
.card {
  width: min(32rem, 100%);
  padding: 1.5rem;
  border: 1px solid rgb(148 163 184 / 0.3);
  border-radius: 1rem;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 20px 60px rgb(15 23 42 / 0.12);
}

.title {
  margin: 0 0 0.5rem;
  color: var(--brand);
}

.action {
  margin-top: 1rem;
  padding: 0.75rem 1rem;
  border: 0;
  border-radius: 0.75rem;
  background: var(--brand);
  color: white;
  cursor: pointer;
}

.action:hover {
  opacity: 0.9;
}

@media (min-width: 48rem) {
  .card {
    padding: 2rem;
  }
}
3在组件中导入名称映射
tsx
import styles from "./Card.module.css";

export function Card() {
  return (
    <article className={styles.card}>
      <h1 className={styles.title}>CSS Modules</h1>
      <p>熟悉的 CSS,默认局部的类名。</p>
      <button className={styles.action}>开始使用</button>
    </article>
  );
}
4使用 CSS Variables 创建主题
css
.light {
  --brand: #1572b6;
  --surface: #ffffff;
  --text: #0f172a;
}

.dark {
  --brand: #60a5fa;
  --surface: #0f172a;
  --text: #f8fafc;
}

.page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: var(--surface);
  color: var(--text);
}
5根据组件状态组合局部类
tsx
import styles from "./Page.module.css";
import { Card } from "./Card";

export function Page({ dark = false }: { dark?: boolean }) {
  const themeClass = dark ? styles.dark : styles.light;

  return (
    <main className={`${themeClass} ${styles.page}`}>
      <Card />
    </main>
  );
}
6复用其他模块中的类
css
.buttonReset {
  appearance: none;
  border: 0;
  font: inherit;
}

.primaryButton {
  composes: buttonReset;
  padding: 0.75rem 1rem;
  border-radius: 0.75rem;
  background: #1572b6;
  color: white;
}
7验证生产输出
bash
npm run build
npm run preview

下一步:默认优先使用局部类,把 :global 限制在 Reset、第三方挂载点或明确的公共约定中。生成类名属于构建细节,不应在测试、脚本或外部系统中硬编码。

ALTERNATIVES

类似项目

这些方案都能让组件拥有局部样式,但在作者语言、类型安全、主题系统、原子化和构建配置上采用了不同抽象。

COMPARISON

CSS Modules vs vanilla-extract

CSS Modules 和 vanilla-extract 都在构建时生成局部类名,并让组件通过模块导入使用样式。CSS Modules 直接编写 CSS,抽象更低;vanilla-extract 在 .css.ts 中使用类型化对象,并内建主题契约、变量和可选 Recipes、Sprinkles 等设计系统能力。

比较维度CSS Modulesvanilla-extract
作者语言普通 CSS,可结合 Sass、Less 或 PostCSS.css.ts 文件中的 TypeScript 样式对象
局部作用域类名和动画名称默认局部化style、keyframes 和变量生成局部标识
类型安全基础导入有类型,精确类名通常需要额外工具CSS 属性、值、主题契约和 Recipe Variant 可静态检查
主题能力通常自行组织 CSS Variables 和主题容器类内建 createThemeContract、createTheme 与 assignVars
组件变体通过多个类、组件条件和类名组合工具实现可选 Recipes 提供类型安全 Variant 与 Compound Variant
动态值使用 CSS Variables、Inline Style 或 Modifier Class使用预生成样式或 Dynamic 包更新类型化变量
构建支持常由框架或 Bundler 原生提供需要安装对应 vanilla-extract 官方集成
更适合偏爱原生 CSS、渐进迁移和低抽象的团队需要类型安全 Token、主题和组件样式 API 的设计系统
如何选择

如果团队希望继续直接编写 CSS,只解决全局命名冲突并保持最低工具抽象,CSS Modules 通常是稳妥默认选择;如果项目需要 TypeScript 检查 CSS 值、强制多个主题实现同一 Token 契约,或建立 Recipes 与 Sprinkles 这样的设计系统 API,vanilla-extract 更完整。两者都依赖构建处理,组件库场景应重点验证消费者工具链和 CSS 输出策略。

VERIFICATION

资料核验

版本、维护信息与本页采用的官方资料来源。

最后核验2026 年 7 月 27 日
核验版本CSS Modules(无统一版本号)
内容维护Docs100 编辑整理
项目维护状态稳定维护

CSS Modules 是由不同构建工具实现的模块化约定,没有统一的软件包版本。本页依据 CSS Modules 官方文档仓库、Vite CSS Modules 功能说明和 Webpack css-loader 文档整理;快速开始采用 Vite 当前内建的 *.module.css 工作流。

维护状态核验2026 年 7 月 26 日 · 最近可见代码活动:2024 年 3 月 23 日

官方仓库未归档,核验时最近可见的代码活动日期为 2024 年 3 月 23 日。项目更新频率较低但仍有维护迹象,因此标记为稳定维护,而不是活跃更新。

查看官方仓库