SCSS 样式系统设计
本文从一个前端架构师的视角,聊聊企业级项目中 SCSS 样式系统该怎么设计。不局限于语法,更关注分层、组织和可维护性。
一、为什么需要样式系统
很多项目初期样式代码写得很爽,到了中期就开始失控:
- 同一个颜色值散落在十多个文件中
- 改一个圆角要搜遍全项目
- 想调间距不敢动,怕影响其他地方
- 新页面写的样式和已有组件格格不入
样式系统要做的事就三件:统一变量、约束模式、提升复用。
不是说用了 SCSS 就有了样式系统。SCSS 只是工具,样式系统是架构层面的设计。
二、目录结构设计
企业级项目推荐按「分层 + 模块」组织 SCSS 文件:
命名约定:
为什么用 _ 前缀?
SCSS 的 @use / @forward 机制会跳过带 _ 的文件(partial),避免生成多余的 CSS 文件。这是 SCSS 本身的设计模式。
三、设计令牌层(Design Tokens)
设计令牌是样式系统的「原子单位」,所有样式值都从这里取。
3.1 _variables.scss
变量命名原则:语义优先,不要写死视觉值。
将来换主题时,改变量名比改视觉值痛苦得多。
3.2 _functions.scss —— 让变量更灵活
四、混合宏层(Mixins)
4.1 _mixins.scss
4.2 使用示例
为什么用 @use 替代 @import?
@import在 Dart Sass 中已标记为弃用,将在未来版本移除@use有命名空间隔离,不会造成变量冲突@use只导入一次,避免重复
五、Reset 层
每个项目都需要一套基础样式重置,消除浏览器差异。
5.1 _reset.scss
六、主题切换方案
主题切换的核心思路:SCSS 变量定义色值 → CSS 自定义属性承载 → 组件中统一用 var() 消费。这样切主题只需改 CSS 自定义属性的值,SCSS 只负责编译时产出两套变量。
6.1 设计原则
- 颜色语义化:不写
--blue: #409eff,写--color-primary: #409eff,表达用途而非具体色值 - 分层定义:品牌色、功能色、中性色、背景色、边框色、阴影色分层管理
- 双主题全覆盖:每个颜色变量在亮色和暗色下都有对应值,不遗漏
6.2 _theme.scss —— 亮色 + 暗色完整配置
6.3 组件中消费主题变量
组件中统一使用 CSS 变量(var()),不直接引用 SCSS 变量:
为什么不用 SCSS 变量做主题?
SCSS 变量在编译时就被替换为具体色值,无法在运行时切换。CSS 自定义属性可以在运行时覆盖,配合 data-theme 属性实现无缝切换。
6.4 切换策略
方案一:属性切换(推荐)
方案二:跟随系统偏好
两个方案可以结合:用户手动选择优先,未选择时跟随系统。
6.5 过渡动画
给主题切换加过渡,避免切换时生硬闪烁:
七、Vite 全局配置
在 vite.config.ts 中配置全局 SCSS 变量注入,避免每个组件手动 @use:
additionalData 的注意事项:
- 只会注入变量和 mixin 这种「不产生实际 CSS 输出」的内容
_reset.scss和样式类文件不要注入,否则每个文件都会输出一份- 注入顺序:先
variables→functions→mixins,因为 mixin 可能依赖变量
八、入口文件与全局导入
8.1 styles/index.scss
用 @forward 统一导出,外部只需导入这一个文件:
8.2 main.ts 全局导入
九、命名规范 —— BEM
企业项目中推荐 BEM(Block Element Modifier)命名,配合 SCSS 的 & 嵌套,可读性和隔离性都很好。
生成 CSS:
BEM 的好处:组件间样式隔离,无类名冲突。配合 SCSS 嵌套不用手写完整类名,开发体验好。
十、完整集成示例
把所有文件串起来,在实际项目中的完整结构:
使用流程:
- 设计令牌 →
_variables.scss定义所有变量 - 工具层 →
_mixins.scss+_functions.scss封装复用逻辑 - Vite 注入 →
additionalData自动注入变量和 mixin - 组件使用 →
<style lang="scss">中直接使用变量和 mixin - 主题 → 通过
data-theme+ CSS 变量切换
写在最后
样式系统不是一蹴而就的。先搭好变量层和 mixin 层,后续有需要再加。关键是要守住两条底线:
- 所有样式值从变量取 —— 不写魔法数字
- 复用逻辑封装成 mixin —— 不复制粘贴
守住了这两条,项目大了之后样式就不会乱。

