Sass/SCSS 的嵌套
作用
- 减少重复:不再处处写
.card
前缀。 - 表达结构:一眼看出父子/状态/修饰的关系。
- 就地维护:媒体查询、状态样式和主体样式放在一起,改组件更集中。
- BEM 更顺手:
&__el
、&--mod
、&.is-xxx
自然生成。
下面我们来看下它嵌套编译的效果
scss
.card {
.title { color: #333; } // 后代
&:hover { box-shadow: ...; } // 父的伪类
.actions > .btn { ... } // 组合器保留
}
编译后:
css
.card .title{color:#333}
.card:hover{box-shadow:...}
.card .actions > .btn{...}
变量与类型
php
$size: 16px;
$brand: #0b81ff;
$title: "Hello";
$on: true;
$none: null;
// 列表 & 映射
$spaces: 4px 8px 16px;
$palette: (primary: #0b81ff, success: #22c55e);
null
的属性会被跳过。map.get($palette, primary)
取值。
选择器嵌套与父选择器 &
css
.card {
padding: 16px;
&--active { box-shadow: 0 4px 12px rgba(0,0,0,.08); } // BEM 修饰
&:hover { transform: translateY(-1px); } // 伪类
.title { font-weight: 600; } // 子元素
}
嵌套尽量 ≤ 3 层,避免选择器过长。
插值 #{}
(拼接变量)
css
$base: btn;
$radius: 8;
.#{$base} { border-radius: #{$radius}px; } // → .btn { border-radius: 8px }
Mixin / Include(可复用片段)
less
@mixin flex-center($gap: 8px) {
display: flex; align-items: center; justify-content: center; gap: $gap;
}
.toolbar { @include flex-center(12px); }
// 带内容插槽
@mixin layer($z: 1) { position: relative; z-index: $z; @content; }
.badge { @include layer(10) { pointer-events: none; } }
Function(返回计算值)
less
@use "sass:math";
@function px2rem($px, $base: 16) { @return math.div($px, $base) * 1rem; }
.title { font-size: px2rem(20); }
Dart Sass 用
math.div
代替/
除法。
控制指令
css
$theme: dark;
@if $theme == dark { body { background:#0f172a; color:#e2e8f0; } }
@else { body { background:#fff; color:#111; } }
@each $gap in (4px, 8px, 12px) { .gap-#{$gap} { gap: $gap; } }
@for $i from 1 through 3 { .col-#{$i} { width: (100%/3)*$i; } }
$i: 0;
@while $i < 3 { .ring-#{$i} { outline-width: $i+1px; } $i: $i + 1; }
占位选择器与继承(谨慎用)
css
%btn-base { font: inherit; padding: .5em 1em; border: 1px solid transparent; }
.primary { @extend %btn-base; background: #0b81ff; color: #fff; }
@extend
可能引发"选择器膨胀",组件库更推荐用 mixin 复用样式。
模块化导入(现代写法)
less
/* tokens/_color.scss */
$primary: #0b81ff !default;
@mixin btn() { padding: 8px 12px; }
/* design/_index.scss ------ 聚合出口 */
@forward "../tokens/color";
/* app.scss ------ 使用与配置 */
@use "./design/index" as d with ($primary: #0052d9);
.button { color: d.$primary; @include d.btn(); }
@use
:有命名空间、成员只读;用with
配置带!default
的变量。@forward
:做"总入口"(barrel),对外转发变量/函数/mixin。- 旧式
@import
已弃用;@import url(...)
是 CSS 导入(不会带来 Sass 变量)。
调试与错误
less
@use "sass:meta";
@debug meta.type-of((a: b));
@warn "Deprecated var, will be removed.";
@error "Invalid token";
全局方法(旧写法)
颜色相关
写法 | 说明 | 示例/结果 |
---|---|---|
darken(#fff, 10%) |
调暗亮度 10%(不是透明度) | #fff → #e6e6e6 |
lighten(#fff, 30%) |
调亮亮度 30%(纯白几乎无变化;也不是透明度) | #fff → #fff |
hsl(0, 100%, 50%) |
由 色相/饱和度/亮度 构造颜色(红) | #ff0000 |
adjust-hue(#fff, 180deg) |
色相旋转 180°(对白/灰无可见变化) | #fff |
saturate(#fff, 10%) |
提高饱和度(对白/灰无效) | #fff |
desaturate(#fff, 10%) |
降低饱和度 | #fff |
transparentize(#fff, 0.1) |
增加透明度(α 减 0.1) | rgba(255,255,255,0.9) |
opacify(#fff, 0.1) |
增加不透明度(α 加 0.1) | rgba(255,255,255,1) |
注:
transparentize/opacify
的第二个参数是 0--1 的无单位数,不是百分比。
计算
写法 | 说明 | 示例/结果 |
---|---|---|
abs(-10px) |
绝对值 | 10px |
ceil(-12.5px) |
向上取整(朝 +∞) | -12px |
round(12.3px) |
四舍五入 | 12px |
floor(12.8px) |
向下取整 | 12px |
percentage(650px / 1000px) |
比例转百分比 | 65% (因为 px/px → 无单位 0.65) |
min(1, 2, 3) |
取最小值(编译期) | 1 |
max(1, 2, 3) |
取最大值(编译期) | 3 |
提醒:
min/max
比较带单位 时需同维度可换算(如px
与in
);px
vsrem/vw
等不同维度请用 CSS 的min()/max()/clamp()
在浏览器端计算。
字符串相关
写法 | 说明 | 示例/结果 |
---|---|---|
to-upper-case("hello") |
转大写 | "HELLO" |
to-lower-case("HELLO") |
转小写 | "hello" |
str-length("hello") |
字符串长度 | 5 |
str-index("hello", "h") |
返回首次出现位置(1 基;找不到为 null ) |
1 |
str-insert("hello", "world", 5) |
在索引处插入子串(1 基;负数为倒数) | "hellworldo" |
内置模块Api(新写法)
这里主要介绍 sass:color、sass:math、sass:string,其他有需要的可以自行去了解
sass:color(调色/透明度/混色)
less
@use "sass:color";
$brand: #1677ff;
/* 1) 按比例靠近极值(推荐,变化更自然) */
.bg-hover { background: color.scale($brand, $lightness: 10%); } // 变亮
.bg-active { background: color.scale($brand, $lightness: -12%); } // 变暗
.bg-ghost { background: color.scale($brand, $alpha: -30%); } // 更透明
/* 2) 加/减固定量(严格步长) */
.more-sat { background: color.adjust($brand, $saturation: 15%); }
.rotate { background: color.adjust($brand, $hue: 30deg); } // 旋转色相
/* 3) 设定绝对值(直接锁定到目标) */
.fixed-a { background: color.change($brand, $alpha: 0.6); } // α=0.6
.fixed-l { background: color.change($brand, $lightness: 40%); } // L=40%
/* 4) 混色(做浅/深阶) */
.light-1 { background: color.mix(#fff, $brand, 20%); } // 更浅
.dark-1 { background: color.mix(#000, $brand, 15%); } // 更深
/* 5) 反相(生成对比色) */
.invert { color: color.invert($brand, 100%); }
小抄:
- 变亮/变暗 →
scale($lightness: ±x%)
;透明度 →scale($alpha: ±x%)
- 固定步长 →
adjust
;锁定目标值 →change
;两色过渡 →mix
sass:math(数值/单位运算)
less
@use "sass:math";
/* 1) 除法一定用 math.div */
.title { font-size: math.div(20, 16) * 1rem; } // 1.25rem
/* 2) 取整/比较/钳制(编译期) */
.box { margin: math.round(4.6px); } // 5px
.maxw { max-width: math.min(960px, 1in); } // 单位可换算才行
/* 混单位(px vs rem/vw)请用 CSS 的 min()/clamp() 在运行时算 */
/* 3) 常用函数 */
$pi: math.$pi; // 3.14159...
$len: math.hypot(3, 4); // 5(向量长度)
$angle: math.atan2(1, -1); // 135deg(可直接放到 rotate())
/* 4) 小工具:px→rem */
@function px2rem($px, $base: 16) { @return math.div($px, $base) * 1rem; }
.btn { padding: px2rem(10) px2rem(14); }
要点:
min/max/clamp
仅比较同维度可换算单位 (px↔in 等);混单位用 CSS 的min()/max()/clamp()
。- 乘除尽量"有单位 × 无单位",避免产生非法复合单位。
sass:string(拼接/切片/大小写/引号)
less
@use "sass:string";
/* 1) 拼接请用插值 #{} */
$ns: "app"; $block: btn;
.selector { content: "#{$ns}-#{$block}"; } // "app-btn"
/* 2) 长度/索引/切片/插入(索引从 1 开始,负数从尾部数) */
@debug string.length("hello"); // 5
@debug string.index("btn--primary", "--"); // 4
@debug string.slice("abcdef", 2, 4); // "bcd"
@debug string.insert("color", "-primary", 6); // "color-primary"
/* 3) 大小写/引号 */
@debug string.to-upper-case("btn"); // "BTN"
@debug string.quote(btn); // "btn"
@debug string.unquote("bold"); // bold(变标识符)
/* 4) 唯一 id(避免命名冲突) */
$uid: string.unique-id(); // 比如 "u5ab9"
@keyframes fade-#{$uid} { from{opacity:0} to{opacity:1} }
.fade { animation: fade-#{$uid} .2s ease; }
小抄:
- 串联字符串 → **插值
#{}**
;结构化处理再用length/index/slice/insert
。 quote/unquote
控制是否带引号;unique-id()
做不冲突的 keyframes/变量名。