概念
SCSS(Sassy CSS)是 Sass 预处理器的主流语法形式,是原生 CSS 的超集(类似于 JS 与 TS 的关系)。这也就意味着:
- 所有合法的 CSS 代码直接复制到 SCSS 文件中都能正常运行
- 在此基础上,SCSS 增加了变量、嵌套、Mixin、继承等编程化特性
- 浏览器无法直接识别 SCSS,必须通过编译工具(如 dart-sass)转换成普通 CSS 才能生效
环境
SCSS 编译依赖 Node.js,所以需要终端上具备该环境
# 安装编译工具
npm install -g sass
# 编译文件:sass 源文件.scss 输出文件.css
sass src/style.scss dist/style.css
# 实时监听(修改 SCSS 自动编译)
sass --watch src/style.scss:dist/style.css # 监听文件
sass --watch src/styles:dist/styles # # 监听整个目录目录结构
scss/
├── _variables.scss // 变量
├── _mixins.scss // 混合
├── _buttons.scss // 按钮样式
└── index.scss // 入口文件(对外暴露)用于把不同的数据分割出去,不至于让一个文件变得臃肿。再局部文件写完数据后再入口文件进行导入
// scss/index.scss
@forward "variables"; // 转发变量
@forward "mixins"; // 转发混合
@forward "buttons"; // 转发按钮样式此时别的地方只需要导入这一个 @use "./scss" as ui; 即可使用
SCSS 中,文件名以 _ 开头(如 _variables.scss),表示这是一个局部文件(Partial File);它的核心特性是:
- 不会被编译器单独编译成 CSS 文件
- 只能被其他 SCSS 文件通过
@use/@forward/@import导入使用
在处理导入路径时,会自动匹配带 _ 前缀的文件,此时就可以省略下划线的书写
注释
// 单行注释 —— 编译后会被删除(仅保留在 SCSS 源码)
/* 多行注释 —— 编译后保留在 CSS 文件中(可用于版权声明) */
/*! 强制保留注释 —— 即使压缩 CSS 也不会删除 */嵌套
核心作用是让 CSS 选择器的层级和 HTML 结构一一对应,不用重复写父选择器
原生 CSS:
/* HTML 结构:<nav><ul><li><a></a></li></ul></nav> */
nav {
background: #f5f5f5;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav ul li {
float: left;
padding: 0 15px;
}
nav ul li a {
color: #333;
text-decoration: none;
}
nav ul li a:hover {
color: #165DFF;
}SCSS 嵌套:
nav {
background: #f5f5f5;
// 嵌套 ul(父选择器是 nav)
ul {
list-style: none;
margin: 0;
padding: 0;
// 嵌套 li(父选择器是 nav ul)
li {
float: left;
padding: 0 15px;
// 嵌套 a(父选择器是 nav ul li)
a {
color: #333;
text-decoration: none;
// 嵌套 hover 伪类
&:hover {
color: #165DFF;
}
}
}
}
}编译过后最终结果会跟 原生CSS 一摸一样。子选择器会自动拼接父选择器 + 空格 + 子选择器
在嵌套的时候,选择器跟原生的 CSS 选择器是一致的,如:类选择器、标签选择器、ID选择器...
& 符号
& 是嵌套里用于替换父选择器的占位符,它会完全替代父选择器
.btn {
padding: 8px 16px;
background: #165DFF;
color: #fff;
// & 代表 .btn → 编译后:.btn:hover
&:hover {
background: darken(#165DFF, 8%);
}
// & 代表 .btn → 编译后:.btn:active
&:active {
background: lighten(#165DFF, 8%);
}
}也可以用来拼接 BEM 格式修饰符
// 块:card
.card {
width: 300px;
// 元素:card__header(& 代表 .card)
&__header {
padding: 10px;
border-bottom: 1px solid #eee;
}
// 修饰符:card--large(& 代表 .card)
&--large {
width: 400px;
}
// 修饰符+元素:card--large .card__header
&--large &__header {
padding: 15px;
}
}
// ---------- 编译后 -----------
.card { width: 300px; }
.card__header { padding: 10px; border-bottom: 1px solid #eee; }
.card--large { width: 400px; }
.card--large .card__header { padding: 15px; }BEM 格式:
块__元素--修饰符。一种常用的命名规范
插值语法
语法:#{}
// 选择器插值
$prefix: "ui";
.#{$prefix}-btn { /* .ui-btn */ }
// 属性名插值
$prop: "color";
.box {
#{$prop}: #333; /* color: #333 */
}
// 数值+单位插值
$unit: "px";
.box {
width: 100#{$unit}; /* 100px */
}属性嵌套
对于拥有相同前缀的 CSS 属性也可以进行嵌套
.box {
// 字体属性嵌套(编译后:font-size: 14px; font-weight: bold;)
font: {
size: 14px;
weight: bold;
}
// 内边距嵌套(编译后:padding-top: 10px; padding-bottom: 10px;)
padding: {
top: 10px;
bottom: 10px;
left: 20px;
right: 20px;
}
// 边框嵌套(编译后:border-width: 1px; border-style: solid; border-color: #eee;)
border: {
width: 1px;
style: solid;
color: #eee;
}
}父选择器前置
本质是把原本的子选择器变成 “父选择器”,原本的父选择器变成 “子选择器”
// 对于 .box 提前定义了基础样式
.box {
width: 100px;
height: 100px;
}
// 但是再某些情况下,我们需要对嵌套在特定容器中的 .box 进行特殊样式设置
.container {
.box {
width: 200px;
}
}上述写法的问题:.box 的基础样式和 “容器限定样式” 分开了,代码不集中。用 & 前置可以把容器限定样式写在 .box 自己的层级里
.box {
// 基础样式(所有 .box 都生效)
width: 100px;
height: 100px;
// 父选择器前置:.container &;编译后:.container .box
.container & {
width: 200px; // 只有在 .container 里的 .box 宽度变成 200px
}
}跳出嵌套
语法:@at-root [选择器]
// 场景:深层嵌套中,需要给某个选择器单独提级
.card {
width: 300px;
.body {
padding: 10px;
// @at-root 让 .card-link 跳出嵌套,编译后直接是 .card-link,而非 .card .body .card-link
@at-root .card-link {
color: #165DFF;
}
}
}
// 编译后结果
.card { width: 300px; }
.card .body { padding: 10px; }
.card-link { color: #165DFF; }
// @at-root with #{}(带父选择器跳出)
.card {
.body {
// 编译后是 .card .card-special,而非 .card .body .card-special
@at-root #{&}-special {
border: 1px solid #eee;
}
}
}变量
语法:$变量名: 变量值;
// 颜色(最常用:主题色、辅助色)
$primary-color: #165DFF; // 主色调
// 尺寸(间距、圆角、字体大小)
$base-font-size: 14px; // 基础字体大小
// 字体(字体族)
$base-font-family: "Microsoft Yahei", sans-serif;
// 布尔值/数字(用于逻辑判断)
$is-dark-mode: false;
$max-width: 1200;定义好的变量,在任何需要写值的地方直接用 $变量名 替代即可
background-color: $primary-color; // 主色调作用域
作用域决定了变量能在什么被使用,分为全局和局部两个作用域
全局
定义位置:在所有选择器、Mixin 之外(最好写在单独的 _variables.scss 文件)
作用范围:整个 SCSS 文件(及导入该文件的其他文件)都能使用
$global-color: #165DFF;
.box1 {
color: $global-color;
}
.box2 {
color: $global-color;
}局部
定义位置:在选择器、Mixin 内部
作用范围:仅在定义它的 “大括号内” 生效,外部无法使用
.box1 {
// 父级不可用
color: $global-color;
.box2 {
// 定义局部变量
$global-color: #165DFF;
// 同级可用
color: $global-color;
.box3 {
// 子集可用
color: $global-color;
}
}
}特殊(全局)
若是想让局部变量能够被全局使用,则只需要再变量末尾加入 !global 即可
$global-color: #165DFF !global;尽量避免使用 !global 避免造成全局变量污染
特殊(默认)
!default 作用为给变量设置默认值,若该变量已被定义,则使用已定义的值;若未定义,则使用默认值
语法:$变量名: 默认值 !default;
// _button.scss(按钮组件的变量文件)
$button-primary-color: #165DFF !default; // 默认主色调
$button-padding: 8px 16px !default; // 默认内边距
// 按钮样式
.btn-primary {
background: $button-primary-color;
padding: $button-padding;
}
// ----------- 分割 -----------------
// 使用者的 index.scss
// 第一步:自定义变量(覆盖默认值)
$button-primary-color: #F53F3F; // 改成红色
$button-padding: 10px 20px; // 加大内边距
// 第二步:导入按钮组件
@import "_button";
// 最终按钮的主色调是红色,内边距是10px 20pxMap
语法:$map名: (键1: 值1, 键2: 值2, ...);
// 示例:主题色 Map
$theme-colors: (
primary: #165DFF,
success: #00B42A,
warning: #FF7D00,
danger: #F53F3F
);访问 Map 中的值
.btn-primary {
background: map-get($theme-colors, primary); // #165DFF
}常用函数:
| 函数名 | 作用 |
|---|---|
map-get($map, $key) | 获取指定键的值 |
map-keys($map) | 获取所有键(返回列表) |
map-values($map) | 获取所有值(返回列表) |
map-has-key($map, $key) | 判断是否有某个键 |
map-merge($map1, $map2) | 合并两个 Map |
列表
定义:直接通过空格或者逗号来创建
$list1: 10px 20px 30px; // 空格分隔
$list2: (10px, 20px, 30px); // 逗号分隔| 函数名 | 作用 |
|---|---|
length($list) | 获取列表长度(值的个数) |
nth($list, $index) | 获取列表中第 N 个值(索引从 1 开始,而非 0) |
set-nth($list, $index, $value) | 修改列表中第 N 个值 |
append($list, $value) | 向列表末尾添加值 |
prepend($list, $value) | 向列表开头添加值 |
join($list1, $list2) | 合并两个列表 |
index($list, $value) | 查找值在列表中的索引(无则返回 null) |
list-separator($list) | 获取列表的分隔符(comma/space) |
zip($list1, $list2) | 按索引配对多个列表(生成嵌套列表) |
原生变量
// SCSS 变量转 CSS 变量(支持运行时动态修改)
$primary-color: #165DFF;
:root {
--primary-color: #{$primary-color}; // 插值注入 CSS 变量
--font-size-base: #{$base-font-size};
}
// 使用 CSS 变量
.btn {
background: var(--primary-color);
// 结合 SCSS 运算 + CSS 变量
padding: calc(var(--font-size-base) * 2);
}混合
Mixin(混合)核心作用是封装可复用的样式片段
使用 @mixin 混合名(形参) { 样式表 } 来定义混合,然后使用 @include 混合名(实参) 来调用。参数不是必填项;如果有多个参数中间使用 逗号 分割,并且参数可以使用 冒号 来定义默认参数
场景:需要三种按钮,他们的基础样式都是一样的,仅仅是背景颜色有所不同
// 定义 Mixin:封装基础按钮样式,接收“背景色”参数
@mixin button-style($bg-color) {
padding: 8px 16px;
border: none;
border-radius: 4px;
color: #fff;
background: $bg-color;
}
// 调用 Mixin:传不同背景色,一行搞定
.btn-primary {
@include button-style(#165DFF);
}
.btn-success {
@include button-style(#00B42A);
}
.btn-warning {
@include button-style(#FF7D00);
}可变参数
通过再最后一个变量的末尾添加 ... 来实现可变参数
// 定义:... 表示可变参数
@mixin box-shadow($color, $shadows...) {
color: $color;
box-shadow: $shadows;
}
// 调用:传任意数量参数
.box1 {
@include box-shadow(#165DFF, 0 2px 4px rgba(0, 0, 0, 0.1));
}
.box2 {
@include box-shadow(#165DFF, 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.2));
}传递样式块
@content 允许在调用 Mixin 时,额外传入一段样式,相当于给 Mixin 加 “自定义扩展”
// 定义:响应式 Mixin,用 @content 接收自定义样式
@mixin responsive($breakpoint) {
@if $breakpoint ==mobile {
@media (max-width: 768px) {
@content; // 接收调用时传入的样式
}
}
@else if $breakpoint ==tablet {
@media (min-width: 769px) and (max-width: 992px) {
@content;
}
}
@else if $breakpoint ==pc {
@media (min-width: 993px) {
@content;
}
}
}
// 调用:传入自定义样式(不同设备的宽度)
.box {
width: 100%;
// 移动端:宽度 90%
@include responsive(mobile) {
width: 90%;
padding: 10px;
}
// 平板:宽度 80%
@include responsive(tablet) {
width: 80%;
padding: 15px;
}
// PC:宽度 1200px
@include responsive(pc) {
width: 1200px;
padding: 20px;
}
}继承
@extend(继承)是 SCSS 中另一种样式复用方式,核心作用是让一个选择器完全继承另一个选择器的所有样式
// 定义被继承的通用样式(基础文本样式)
.text-common {
font-family: "Microsoft Yahei";
line-height: 1.5;
color: #333;
margin: 0;
padding: 0;
}
// 继承通用样式(标题1)
.title-h1 {
@extend .text-common; // 继承 .text-common 的所有样式
font-size: 24px;
font-weight: bold;
}
// 继承通用样式(标题2)
.title-h2 {
@extend .text-common; // 继承 .text-common 的所有样式
font-size: 20px;
font-weight: 600;
}
// ----------- 分割(编译后结果) ----------
.text-common,
.title-h1,
.title-h2 {
font-family: "Microsoft Yahei";
line-height: 1.5;
color: #333;
margin: 0;
padding: 0;
}
.title-h1 {
font-size: 24px;
font-weight: bold;
}
.title-h2 {
font-size: 20px;
font-weight: 600;
}占位符选择器
如果被继承的选择器(如 .text-common)只是 “复用模板”,不需要在 HTML 中直接使用,用 占位符选择器 % 替代类选择器,编译后不会生成这个 “模板选择器”,减少冗余
// 占位符选择器:仅用于继承,编译后不会生成 .text-common
%text-common {
font-family: "Microsoft Yahei";
line-height: 1.5;
color: #333;
}
.title-h1 {
@extend %text-common;
font-size: 24px;
}如果被继承的是嵌套选择器,会把所有子选择器也继承。也就是说,最终的编译结果会连嵌套的子集也生成一份继承数据
运算
SCSS 的运算功能的核心作用是对数值、颜色、字符串等进行计算,动态生成样式值。原生 CSS 也能够使用 calc() 进行计算,但是语法受限(如单位必须明确);而 SCSS 则是能够直接进行运算,还能结合变量、函数进行辅助
$base-width: 100px;
.box {
width: $base-width - 20; // 自动补单位,结果 80px
}
// 原生 CSS calc()
.box {
width: calc(100px - 20px); // 必须写全单位
}语法
SCSS 支持 +、-、*、/、% 五种基础运算符,还支持比较运算(>、<、>=、<=、==、!=)和逻辑运算(and、or、not)
规则:
- 单位自动适配:只要一个值有单位,结果会自动继承单位(如
100px - 20 = 80px) - 运算优先级:和数学一致(先乘除后加减,括号优先)
- 除法注意:
/在 SCSS 中默认是 “分隔符”(如:font: 14px/1.5;表示字体大小14px,行高1.5),需满足以下条件才会被识别为除法:- 数值被括号包裹:
(100px / 2) - 数值是变量 / 函数返回值:
$width / 2 - 配合其他运算符:
100px + 20px / 2
- 数值被括号包裹:
数值
// 定义变量
$base-width: 1200px;
$gutter: 20px;
$column-count: 4;
// 宽度计算:总宽度 - 左右间距
.container {
width: $base-width - $gutter * 2; // 1200 - 40 = 1160px
margin: 0 auto;
}
// 列宽计算:均分列数(除法需括号)
.column {
width: ($base-width / $column-count); // 1200 / 4 = 300px
margin-right: $gutter;
// 最后一列去掉右边距(逻辑判断+运算)
&:last-child {
margin-right: 0;
}
}
// 百分比运算
.box {
width: 100% - 20%; // 80%
height: (500px / 2); // 250px
}如果是不同单位之间进行运算,那么 SCSS 会直接报错。所以得使用 calc(),SCSS 会保留 calc(),交给浏览器计算
.box {
width: calc(100px + 20em); // SCSS 会保留 calc(),交给浏览器计算
}颜色
$primary-color: #165DFF; // 十六进制颜色
// 直接运算(RGB 分量加减)
.btn-hover {
background: $primary-color + #111; // 加深颜色(#175EFF)
}
.btn-light {
background: $primary-color - #222; // 减淡颜色(#155CDE)
}
// 用内置颜色函数(精准控制)
.btn-dark {
background: darken($primary-color, 10%); // 加深10%
}
.btn-bright {
background: lighten($primary-color, 10%); // 减淡10%
}字符串
// 拼接图片路径
$img-path: "../images/";
.logo {
background: url($img-path + "logo.png"); // url("../images/logo.png")
}逻辑
// 定义变量
$is-dark-mode: true;
$max-width: 1200;
// 条件运算
.container {
@if $is-dark-mode {
background: #333;
color: #fff;
}
@else {
background: #fff;
color: #333;
}
// 逻辑判断(and/or/not)
@if $max-width >1000 and $is-dark-mode {
padding: 30px;
}
}内置函数
数学
| 函数名 | 作用 | 示例 | 结果 |
|---|---|---|---|
round($n) | 四舍五入 | round(3.2) | 3 |
ceil($n) | 向上取整 | ceil(3.1) | 4 |
floor($n) | 向下取整 | floor(3.9) | 3 |
abs($n) | 取绝对值 | abs(-20) | 20 |
min($n1, $n2) | 取最小值 | min(10, 20) | 10 |
max($n1, $n2) | 取最大值 | max(10, 20) | 20 |
random($limit) | 随机数 | random()random($limit) | 无参:生成 0-1 之间的随机数(不含 1) 有参:生成 1 到 N 之间的随机整数(包含两端) |
颜色
| 函数名 | 作用 | 示例 |
|---|---|---|
darken($color, $n) | 加深颜色($n 是百分比) | darken(#165DFF, 10%) |
lighten($color, $n) | 减淡颜色($n 是百分比) | lighten(#165DFF, 10%) |
rgba($color, $a) | 给颜色加透明度($a 是 0-1) | rgba(#165DFF, 0.5) |
mix($c1, $c2, $n) | 混合两种颜色($n 是占比) | mix(#165DFF, #fff, 50%) |
类型判断与转换
SCSS 提供 type-of() 等函数判断值类型,避免运算 / 传参错误
// 类型判断
$val1: 100px;
$val2: #fff;
$val3: (1, 2, 3);
@debug type-of($val1); // number(数值)
@debug type-of($val2); // color(颜色)
@debug type-of($val3); // list(列表)
// 类型转换(常用)
$num: 100;
$px-num: $num * 1px; // 100px(数字转带单位数值)
$color-hex: #165DFF;
$color-rgb: rgb($color-hex); // rgb(22, 93, 255)(十六进制转RGB)拆分/导入
核心是 @use(导入)、@forward(导出 / 转发),以及弃用的 @import,它能把样式拆分成多个文件(如变量、Mixin、工具函数),按需导入 / 对外暴露,避免代码冗余和变量污染
| 指令 | 核心作用 | 描述 |
|---|---|---|
@use | 导入其他 SCSS 文件,生成独立命名空间 | 现代 SCSS 首选(替代 @import) |
@forward | 转发 / 导出其他 SCSS 文件的内容 | 封装公共模块时用(对外暴露接口) |
@import | 导入文件,变量全局污染 | 官方弃用(仅兼容老项目) |
导出
// 语法1:基础转发
@forward "variables"; // 转发 _variables.scss 的所有内容
// 语法2:转发时隐藏指定内容(对外不暴露)
@forward "variables" hide $private-color, @private-mixin;
// 语法3:转发时添加前缀(避免命名冲突)
@forward "buttons" as btn-*; // 所有 Mixin/变量加 btn- 前缀导入 use
@use 导入:
// 默认别名(文件名)
@use "文件路径"; // 如 @use "variables" → 别名是 variables
// 自定义别名
@use "文件路径" as 别名; // 如 @use "variables" as v;
// 简化别名
@use "variables" as *; // 省略别名,直接调用变量(但会失去命名空间保护,易冲突)
// 导入时覆盖默认变量
@use "variables" with (
$primary-color: #F53F3F,
$button-padding: 10px 20px
);路径规则:
- 省略文件后缀(
.scss)和开头的_(如_variables.scss→ 写variables) - 相对路径:
@use "./src/variables"(当前目录下的 src 文件夹)
使用时则是 别名.xxx 来调用
导入 import
@import "_variables.scss"; // 可省略 _ 和 .scss → @import "variables";问题:
- 变量全局污染:导入后变量 / Mixin 直接全局可用,多个文件重名会覆盖;
- 重复编译:多次导入同一文件,会重复编译样式;
- 无命名空间:无法隔离不同文件的内容。
指令
for 循环
// 列表:多个值的集合
$sizes: 12px, 14px, 16px, 18px;
// For 循环列表,生成字体类
@for $i from 1 through length($sizes) {
.text-#{$i} {
font-size: nth($sizes, $i); // nth:取列表第n个值
}
}
// ---------- 分割 -----------
// 编译后生成
.text-1 { font-size: 12px; }
.text-2 { font-size: 14px; }
.text-3 { font-size: 16px; }
.text-4 { font-size: 18px; }each 循环
// 遍历列表
$sizes: small 12px, medium 16px, large 20px;
@each $name, $size in $sizes {
.text-#{$name} {
font-size: $size;
}
}
// 遍历 Map(主题色)
$theme-colors: (
primary: #165DFF,
success: #00B42A
);
@each $key, $color in $theme-colors {
.btn-#{$key} {
background: $color;
}
}while 循环
$i: 1;
@while $i <=4 {
.item-#{$i} {
margin-left: $i * 10px;
}
$i: $i + 1;
}函数
// 自定义函数:计算响应式字体大小(vw 适配)
@function rem($px, $base: 16px) {
@return ($px / $base) * 1rem;
}
@function vw($px, $viewport: 375px) {
@return ($px / $viewport) * 100vw;
}
.box {
font-size: rem(24px); // 1.5rem
width: vw(300px); // 80vw(基于 375px 视口)
}