Sass 基础
变量与嵌套
Sass 变量使用 $ 前缀。嵌套镜像 HTML 结构。& 引用父选择器。使用 lighten()/darken() 进行颜色操作。
// SCSS syntax ($var)
$primary-color: #3498db;
$font-stack: -apple-system, sans-serif;
body {
font-family: $font-stack;
color: $primary-color;
a {
color: lighten($primary-color, 20%);
&:hover {
color: darken($primary-color, 10%);
}
}
}SCSS 与缩进语法
SCSS(.scss)是 CSS 的超集——每个有效的 CSS 文件都是有效的 SCSS。Sass(.sass)去除大括号和分号,依赖缩进。SCSS 更流行且更易迁移。选择一种并在项目中保持一致。
// SCSS (.scss) - uses braces and semicolons
.box {
width: 100px;
height: 100px;
}
// Sass (.sass) - indentation-based, no braces/semicolons
.box
width: 100px
height: 100px
// Both compile to the same CSS
// .box { width: 100px; height: 100px; }注释
// 注释是静默的(输出时移除)。/* */ 注释保留在编译后的 CSS 中。/*! */ 注释即使在压缩/生产模式下也会保留——用于许可证或关键声明。使用 // 进行内部文档注释。
// Single-line comment - NOT compiled to CSS
/* Multi-line comment - compiled to CSS */
/*! Important comment - always kept even in compressed mode */
// Silent comment for developers
$base-font: 16px;
/*! Keep this copyright notice */
body { font-size: $base-font; }编译
Sass 将 .scss/.sass 编译为 .css。--watch 在保存时自动重新编译。输出样式:expanded(默认可读)、nested(缩进)、compact(每行一条规则)、compressed(生产环境压缩)。生产环境使用 compressed 以减小文件大小。
// Compile a single file
// $ sass input.scss output.css
// Watch for changes
// $ sass --watch input.scss:output.css
// Watch a directory
// $ sass --watch scss/:css/
// Compressed output
// $ sass --style=compressed input.scss output.css
// Four output styles: expanded, nested, compact, compressed
$width: 100px;
.box { width: $width; }选择器继承概述
Sass 是一个 CSS 预处理器,添加了变量、嵌套、混入、继承、函数和控制流。这些功能减少重复(DRY 原则)并使大型样式表易于维护。所有 Sass 特性最终编译为标准 CSS。
// Sass adds programming features to CSS:
// 1. Variables ($var)
// 2. Nesting
// 3. Mixins (@mixin / @include)
// 4. Inheritance (@extend)
// 5. Functions (@function)
// 6. Control directives (@if, @for, @each, @while)
// 7. Partials & Modules (@use, @forward)
// These features help keep stylesheets DRY and maintainable.
$radius: 4px;
.card { border-radius: $radius; }变量与数据类型
变量声明与作用域
在任何块外部声明的变量是全局的。在块内部声明的变量是该块的局部变量。局部变量会遮蔽同名的全局变量。使用局部变量以避免污染全局命名空间。
$color: #333; // global scope
$padding: 10px; // global
.block {
$local-color: #fff; // local to .block
color: $local-color;
padding: $padding; // can read globals
}
// $local-color is NOT available here
// .other { color: $local-color; } // ERROR!default 标志
!default 仅在变量为 null 或未定义时赋值。这让用户可以在导入库之前设置变量来覆盖库的默认值。这对于构建可配置的 Sass 库和设计系统至关重要。
// Only assign if the variable is not already defined
$base-color: #333 !default;
$base-color: #fff; // this wins (already? no - assigned above first)
// Useful in libraries/partials for user overrides
// _theme.scss
$primary: blue !default;
.button { background: $primary; }
// User config BEFORE importing
// $primary: red;
// @use 'theme'; -> button is red!global 标志
!global 从块内部(混入、函数、控制指令)显式赋值到全局作用域。自 Sass 3+ 起,块内赋值默认是局部的。谨慎使用 !global——全局修改会使代码难以推理。
$count: 0;
@for $i from 1 through 3 {
$count: $count + $i !global;
}
// $count is now 6 (1+2+3)
.total { content: "#{$count}"; }
// Without !global, the assignment inside @for
// would create a local variable instead数据类型
Sass 有 7 种数据类型:数字(带单位)、字符串、颜色、布尔值、null、列表和映射。type-of() 以字符串形式返回类型。理解类型对于正确使用函数和控制指令至关重要。
// Sass has 7 data types:
$number: 16px; // number (with optional unit)
$string: "Helvetica"; // string (quoted or unquoted)
$color: #3498db; // color
$boolean: true; // boolean (true/false)
$null: null; // null (no value)
$list: 10px 20px 30px; // list (space or comma separated)
$map: (key: value); // map (key-value pairs)
// Type checking
type-of($number) // number
type-of($list) // list
type-of($map) // map插值 #{}
#{} 插值将变量注入选择器、属性名、字符串和其他不允许普通变量的地方。不同于只在值中有效的 $var,#{} 几乎可在任何地方使用。它将任何值转换为字符串表示。
$name: "user";
$version: 2;
// Use #{} to insert variables into selectors and strings
.#{$name}-avatar { background: url("/img/#{$name}.png"); }
// In property names and values
.icon-#{$name} {
content: "v#{$version}";
}
// Interpolation in @media and @include
@media (max-width: #{$version * 100}px) {
body { font-size: 14px; }
}嵌套
基础嵌套
嵌套让你编写镜像 HTML 层次结构的选择器,避免重复父选择器。编译后的 CSS 创建后代选择器。这提高了可读性,但过度使用会创建过于具体的选择器——嵌套保持在 3-4 层以内。
// SCSS nesting mirrors HTML structure
.nav {
background: #333;
ul {
list-style: none;
margin: 0;
li {
display: inline-block;
}
}
a {
color: white;
text-decoration: none;
}
}
// Compiles to: .nav {...} .nav ul {...} .nav ul li {...} .nav a {...}深层嵌套(反模式)
深层嵌套创建过长、过于具体的选择器,难以覆盖且浏览器匹配速度慢。经验法则:嵌套不要超过 3-4 层。使用显式的 BEM 风格类来扁平化深层结构以提高可维护性。
// AVOID: overly deep nesting
.header {
.nav {
> ul {
li {
a {
span { // 5 levels deep!
color: red;
}
}
}
}
}
}
// Compiles to: .header .nav > ul li a span
// Too specific - hard to override, slow to match
// BETTER: flatten with classes
.header-nav-link-text { color: red; }嵌套属性
嵌套属性在属性命名空间(font:、border:、margin:)后使用冒号来分组相关子属性。这避免了重复前缀,纯粹是语法糖。它编译为标准 CSS 属性如 font-family、border-top 等。
.icon {
// Shorthand for font-family, font-size, font-weight...
font: {
family: "Arial";
size: 14px;
weight: bold;
}
// border-top, border-right, etc.
border: {
top: 1px solid #ccc;
bottom: 2px dashed #999;
}
}
// Compiles to: font-family, font-size, font-weight, border-top, border-bottom嵌套媒体查询
在规则内嵌套 @media 查询使响应式样式与组件共存,提高可维护性。编译后的 CSS 将 @media 提升到顶层。这比在多个文件中分散媒体查询大大提高了可读性。
.container {
width: 100%;
// Media query nested inside the rule
@media (min-width: 768px) {
width: 720px;
}
@media (min-width: 1024px) {
width: 960px;
}
}
// Compiles to:
// .container { width: 100%; }
// @media (min-width: 768px) { .container { width: 720px; } }
// @media (min-width: 1024px) { .container { width: 960px; } }嵌套中的选择器列表
当嵌套逗号分隔的选择器列表时,Sass 将每个项与父级组合。这比手动编写每个组合更简洁。& 也可在嵌套列表中使用,一次为多个父级追加类/修饰符。
.btn {
// Comma creates a selector list
.icon,
.label {
color: white;
}
}
// Compiles to:
// .btn .icon, .btn .label { color: white; }
// Nesting a list with &
.card,
.panel {
&.active { border-color: blue; }
}
// .card.active, .panel.active { border-color: blue; }父选择器(&)
基础父选择器
& 引用父选择器,允许伪类(:hover、:focus)、伪元素(::before)和修饰符嵌套。这使相关样式保持在一起。&:hover 编译为 .button:hover。没有 &,你会得到后代选择器。
.button {
background: blue;
color: white;
// & refers to the parent (.button)
&:hover {
background: darkblue;
}
&:focus {
outline: 2px solid blue;
}
&::after {
content: "→";
}
}
// Compiles to: .button, .button:hover, .button:focus, .button::after使用 & 实现 BEM
& 简洁地实现 BEM(块元素修饰符)命名。&__title 将 __title 追加到父级,创建 .card__title。&--featured 创建修饰符类。这使 BEM 类生成保持 DRY 并在块定义内保持一致。
// BEM methodology with parent selector
.card {
background: white;
&__title { // .card__title
font-size: 18px;
}
&__body { // .card__body
padding: 16px;
}
&--featured { // .card--featured
border-color: gold;
}
&--featured &__title { // .card--featured .card__title
color: gold;
}
}父选择器后缀
将 & 放在嵌套选择器开头会将后缀追加到父级,生成如 .button-primary 的复合选择器。这用于命名变体。注意——过度使用后缀可能在阅读源码时掩盖生成的类名。
.button {
background: blue;
// Append suffix to parent
&-primary { background: darkblue; } // .button-primary
&-secondary { background: gray; } // .button-secondary
&-danger { background: red; } // .button-danger
// Combining with state
&-primary.is-active { font-weight: bold; }
}
// Generates: .button, .button-primary, .button-secondary, .button-danger反向嵌套
将 & 放在嵌套选择器末尾会反转顺序——父级成为后代。这非常适合上下文/主题样式如 .dark-theme .modal,组件适应其祖先上下文,将上下文特定覆盖保留在组件附近。
.modal {
// & at the end - parent as descendant
.dark-theme & {
background: #222;
color: #eee;
}
.sidebar & {
width: 200px;
}
}
// Compiles to:
// .dark-theme .modal { background: #222; color: #eee; }
// .sidebar .modal { width: 200px; }
// Useful when parent is styled differently in a context单个选择器中的多个 &
可以在单个选择器中多次使用 &。& + & 生成 .button + .button 用于兄弟元素间距。这对于重复元素之间的间距和复杂的状态选择器非常强大。每个 & 都被完整的父选择器替换。
.button {
// Multiple & references in one selector
& + & { // .button + .button (adjacent sibling)
margin-left: 8px;
}
& ~ & { // .button ~ .button (general sibling)
margin-top: 4px;
}
&.active &__icon { // .button.active .button__icon
color: white;
}
.parent &.is-open &__content {
display: block;
}
}部分文件(_partial 文件)
创建部分文件
部分文件是以开头的下划线命名的 Sass 文件(如 _variables.scss)。下划线告诉 Sass 不要将它们编译为单独的 CSS 文件。部分文件用于导入到主文件中。这将代码模块化以便维护。
// _variables.scss - partial file (underscore prefix)
$primary: #3498db;
$secondary: #2ecc71;
$font-base: 16px;
// _buttons.scss
.button {
background: $primary;
padding: $font-base / 2;
}
// Partials are NOT compiled to their own CSS file.
// The underscore tells Sass this is a partial.
// Import them into a main file:
// main.scss: @use 'variables'; @use 'buttons';部分文件命名约定
部分文件使用带前导下划线的描述性名称。@use 或 @import 语句省略下划线和扩展名——Sass 自动将 'variables' 解析为 _variables.scss。一致的命名帮助团队快速定位代码。
// Common partial naming patterns:
// _variables.scss - variables
// _mixins.scss - mixins
// _functions.scss - functions
// _reset.scss - CSS reset
// _base.scss - base styles
// _typography.scss - typography
// _buttons.scss - button component
// _header.scss - header component
// _footer.scss - footer component
// _mediaqueries.scss - media queries
// Import without the underscore or extension:
// @use 'variables';
// @use 'mixins';7-1 架构模式
7-1 模式是广泛采用的 Sass 架构:7 个文件夹(abstracts、base、components、layout、pages、themes、vendors)加上一个 main.scss 入口点。这通过分离关注点在大型项目中扩展良好。每个文件夹包含导入到 main.scss 的部分文件。
// 7-1 architecture: 7 folders, 1 main file
// sass/
// abstracts/ - variables, functions, mixins
// base/ - reset, typography, base styles
// components/ - buttons, cards, nav
// layout/ - header, footer, grid
// pages/ - home, about specific styles
// themes/ - theme definitions
// vendors/ - third-party
// main.scss - imports everything
// main.scss
// @use 'abstracts/variables';
// @use 'base/reset';
// @use 'components/buttons';
// @use 'layout/header';索引部分文件(_index.scss)
_index.scss(或 _all.scss)部分文件作为文件夹的公共 API,转发其所有部分文件。这让你用一条 @use 语句导入整个文件夹。添加新部分文件时只需更新索引——主文件保持不变。这是 Sass 模块系统模式。
// sass/components/_index.scss
// Forward all component partials from one entry point
@forward 'buttons';
@forward 'cards';
@forward 'nav';
@forward 'modal';
// Now in main.scss, one line imports everything:
// @use 'components';
// (resolves to components/_index.scss)
// This keeps the main file clean and lets
// you reorganize partials without touching main.scss部分文件最佳实践
最佳实践:每个部分文件一个关注点,保持文件小(50-200 行),使用 @use 进行显式依赖管理,避免循环导入。部分文件提高可维护性和复用性。@use 语句使依赖显式,不同于旧的 @import。
// 1. One concern per partial
// _buttons.scss -> only buttons
// _forms.scss -> only forms
// 2. Keep partials small and focused
// (50-200 lines is a good target)
// 3. Use @use (not @import) for explicit dependencies
// _card.scss
@use '../abstracts/variables' as *;
@use '../abstracts/mixins' as *;
.card {
background: $white;
@include card-shadow;
}
// 4. Avoid circular dependencies@import 与 @use
@import(已弃用)
@import 已弃用,因为它污染全局命名空间,可能多次执行文件,且不提供隐私或命名空间。Sass 建议迁移到 @use 和 @forward。现有的 @import 代码仍然有效,但新项目应迁移。
// @import - the OLD way (deprecated in Sass 1.80+)
@import 'variables';
@import 'mixins';
@import 'buttons';
// Problems with @import:
// 1. Globals leak across files (no namespacing)
// 2. Each @import of the same file runs it again
// 3. No way to make members private
// 4. Extends and mixins can conflict
// Use @use instead (the modern module system)
$primary: blue;@use 基础(命名空间)
@use 加载模块一次并通过命名空间(默认为文件名)暴露其成员。用 namespace.$var 或 namespace.mixin-name() 访问成员。这防止全局污染和命名冲突。每个模块只加载一次,无论 @use 出现多少次。
// _variables.scss
$primary: #3498db;
$radius: 4px;
// main.scss - @use creates a namespace (filename by default)
@use 'variables';
.button {
// Access via namespace::member
background: variables.$primary;
border-radius: variables.$radius;
}
// The namespace prevents naming collisions
// and makes dependencies explicit@use 配合 as
as v 将命名空间重命名为更短的名称。as * 将成员加载到当前作用域而不带前缀——方便但有冲突风险,请谨慎使用。命名命名空间更安全,在阅读代码时能清楚每个成员的来源。
// _variables.scss
$primary: blue;
$secondary: green;
// Rename the namespace with 'as'
@use 'variables' as v;
.btn {
background: v.$primary;
color: v.$secondary;
}
// Or remove the namespace entirely (use with caution)
@use 'variables' as *;
.card {
background: $primary; // direct access, no prefix
}
// 'as *' can cause collisions - prefer named namespaces@use 配置(with)
with (...) 在加载时配置模块的 !default 变量。这取代了 @import 的全局变量配置模式。with 子句必须在对该模块的任何其他使用之前。这是配置库主题和设计令牌的标准方式。
// _theme.scss
$primary: blue !default;
$radius: 4px !default;
.button {
background: $primary;
border-radius: $radius;
}
// main.scss - configure variables on import
@use 'theme' with (
$primary: red,
$radius: 8px
);
// The 'with' clause sets variables marked !default
// This is how libraries are configured by users@use 与 @import 对比
从 @import 迁移到 @use:成员变为命名空间化,文件只加载一次,以 - 或 _ 开头的成员是私有的(不暴露)。@use 使依赖显式并防止意外的全局污染。使用 sass-migrator 工具自动迁移大型代码库。
// @import (old) - global, no namespace
@import 'variables';
.button { background: $primary; } // global $primary
// @use (new) - namespaced, loaded once
@use 'variables';
.button { background: variables.$primary; }
// Key differences:
// @import: global scope, runs multiple times, no privacy
// @use: namespaced, runs once, supports private (-prefix)
// _mixins.scss with a private member
@mixin -internal { /* private - not exposed */ }
@mixin public { @include -internal; }
// @use only exposes non-private (no dash prefix) members混入(@mixin / @include)
定义与使用混入
@mixin 定义可重用的声明块(甚至是规则)。@include 在使用处插入混入的内容。混入非常适合厂商前缀、清除浮动和重复声明组。与 @extend 不同,混入每次包含时都会 复制声明。
// Define a mixin with @mixin
@mixin center {
display: flex;
justify-content: center;
align-items: center;
}
@mixin card-shadow {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
// Use with @include
.hero {
@include center;
height: 200px;
}
.modal {
@include center;
@include card-shadow;
}混入参数
混入像函数一样接受参数。按位置传递(顺序重要)或按名称传递(顺序无关,更清晰)。命名参数对于有许多可选参数的混入很有用。参数使混入灵活且可跨不同值复用。
@mixin button($bg, $color) {
background: $bg;
color: $color;
padding: 8px 16px;
border: none;
border-radius: 4px;
}
// Pass arguments by position
.btn-primary { @include button(blue, white); }
.btn-danger { @include button(red, white); }
// Pass arguments by name (clearer, order-independent)
.btn-info { @include button($color: white, $bg: teal); }默认参数
默认参数值使混入灵活——调用者可以省略参数以使用默认值。使用命名参数覆盖特定参数同时保持其他参数为默认值。默认值在每次包含混入时求值,而非定义时求值一次。
@mixin border($width: 1px, $style: solid, $color: #ccc) {
border: $width $style $color;
}
// Use all defaults
.box { @include border; }
// border: 1px solid #ccc;
// Override one - use named args to skip earlier ones
.card { @include border($color: blue); }
// border: 1px solid blue;
// Override by position
.banner { @include border(2px, dashed); }
// border: 2px dashed #ccc;带 @content 的混入
@content 注入通过 @include 传递的样式块。这对于媒体查询混入、关键帧和包装器上下文非常强大。包含的块在调用作用域中求值,因此可以使用调用者的变量。参见 @content 部分了解参数传递。
// @content lets you pass a block of styles to a mixin
@mixin respond-to($breakpoint) {
@media (min-width: $breakpoint) {
@content; // injected styles go here
}
}
.sidebar {
width: 100%;
@include respond-to(768px) {
width: 250px; // this block replaces @content
float: left;
}
}
// Compiles to:
// .sidebar { width: 100%; }
// @media (min-width: 768px) { .sidebar { width: 250px; float: left; } }可变参数(...)
... 运算符有两个用途:在混入定义中,它将额外参数收集为列表(可变参数);在调用处,它将列表展开为单独的参数。这对于包装接受可变数量值的 CSS 属性(如 box-shadow 和 transition)的混入至关重要。
// ... captures multiple args into a list (or expands a list)
@mixin box-shadow($shadows...) {
box-shadow: $shadows;
}
.card { @include box-shadow(0 1px 2px black, 0 4px 8px gray); }
// box-shadow: 0 1px 2px black, 0 4px 8px gray;
// Expanding a list into arguments
$shadows: 0 1px 2px black, 0 4px 8px gray;
.modal { @include box-shadow($shadows...); }
// Also works for @include with maps as keyword args
@mixin theme($colors) { /* ... */ }
@include theme(("bg": red, "fg": white)...);@extend 与继承
基础 @extend
@extend 使一个选择器继承另一个的所有样式,共享规则而非复制。编译后的 CSS 将选择器分组在一起。@extend 比混入更 DRY 地共享基础样式,但可能创建意外的选择器链——请谨慎使用。
.message {
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
// .success inherits all .message styles
.success {
@extend .message;
background: #d4edda;
border-color: #c3e6cb;
}
.error {
@extend .message;
background: #f8d7da;
border-color: #f5c6cb;
}
// Compiles to: .message, .success, .error { padding: 10px; ... }扩展复杂选择器
@extend 适用于任何选择器——类、元素选择器、伪类、复合选择器。扩展 .icon.large 会继承匹配 .icon 和 .icon.large 的规则。扩展复杂选择器要小心;生成的 CSS 可能以意想不到的方式层叠并使输出膨胀。
// You can extend any selector, not just classes
a:hover {
text-decoration: underline;
}
.link-card:hover {
@extend a:hover; // inherits :hover rule
color: blue;
}
// Extending with compound selectors
.icon {
display: inline-block;
}
.icon.large {
font-size: 24px;
}
.icon-x { @extend .icon.large; } // extends BOTH rules链式 @extend
@extend 链:如果 B 扩展 A 且 C 扩展 B,则 C 继承两者。编译后的 CSS 相应地分组选择器。深层链可能产生大型选择器列表和意外的特异性。对于大多数复用场景,混入比链式 @extend 更可预测。
.base {
display: block;
padding: 8px;
}
.intermediate {
@extend .base;
background: #eee;
}
.final {
@extend .intermediate;
color: red;
}
// .final inherits from .intermediate which inherits from .base
// Compiles to: .base, .intermediate, .final { display: block; padding: 8px; }
// .intermediate, .final { background: #eee; }
// .final { color: red; }@extend !optional
向 @extend 添加 !optional 会在目标选择器不存在时静默跳过扩展,而不是抛出错误。这在扩展可选或条件加载的选择器时很有用。没有 !optional,如果目标缺失,@extend 会使编译失败。
// !optional prevents errors if the selector doesn't exist
.fluent {
@extend .nonexistent !optional; // no error, just skipped
color: blue;
}
// Without !optional, extending a missing selector throws:
// ".nonexistent failed to @extend"
// Use !optional when the extension target may or may not exist
// (e.g., optional theme classes from a third-party library)@extend 与 @include 混入对比
@extend 分组选择器(大量复用时 CSS 更小)但创建隐藏耦合。@include 混入复制声明(CSS 更大)但自包含且可配置。新代码优先使用混 入——它们更可预测。@extend 主要用于单个文件内的语义关系。
// @extend - shares one rule, groups selectors
.btn { padding: 8px; }
.primary { @extend .btn; background: blue; }
// -> .btn, .primary { padding: 8px; }
// @mixin - duplicates declarations each use
@mixin btn { padding: 8px; }
.primary { @include btn; background: blue; }
// -> .primary { padding: 8px; background: blue; }
// When to use which:
// @extend: share base styles semantically (DRY selectors)
// @mixin: configurable repeated declarations (with args)
// Avoid @extend across module boundaries (can cause side effects)占位符选择器(%placeholder)
%placeholder 基础
%placeholder 选择器(如 %base-message)是仅为扩展而存在的规则——它们本身永远不会输出到 CSS。这避免了在真实类上使用 @extend 可能导致的未使用基类问题。占位符是共享仅供继承的样式的最简洁方式。
// %placeholder selectors are NOT output in CSS
// They only appear when @extended
%base-message {
padding: 10px;
border-radius: 4px;
}
.success { @extend %base-message; background: green; }
.error { @extend %base-message; background: red; }
// Compiles to (notice %base-message is gone):
// .success, .error { padding: 10px; border-radius: 4px; }
// .success { background: green; }
// .error { background: red; }占位符与类对比
当你 @extend 一个真实类时,该类即使从未在 HTML 中使用也会出现在输出中。占位符解决了这个问题:%base 只在扩展处输出。当基类也直接在 HTML 中使用时使用真实类;当基类纯粹用于继承时使用占位符。
// Class as extend target - the class IS output
.message { padding: 10px; } // appears in CSS even if unused
.success { @extend .message; }
// Placeholder as extend target - NOT output
%message { padding: 10px; } // never appears in CSS
.success { @extend %message; }
// Result is identical for .success, but the placeholder
// version doesn't pollute the CSS with an unused .message rule.
// Use placeholders for internal/inheritance-only base styles.占位符配合 @extend
一个选择器可以 @extend 多个占位符,组合共享样式集。这组合了可重用的构建块而不输出未使用的基类。占位符在设计系统原语(卡片、按钮、输入框)中表现出色,跨许多具体组件共享。
%card-base {
background: white;
border: 1px solid #eee;
border-radius: 8px;
padding: 16px;
}
%card-shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
// Combine multiple placeholders
.featured-card {
@extend %card-base;
@extend %card-shadow;
border-color: gold;
}多个占位符共享
当多个选择器 @extend 同一个占位符时,Sass 将它们分组在输出中,生成紧凑的 CSS。这是 Sass 中最高效的样式共享形式。占位符实现了一种继承/组合形式,而不会导致运行时 CSS 类膨胀。
%button { padding: 8px 16px; border: none; cursor: pointer; }
%rounded { border-radius: 4px; }
%shadow { box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
// Many components share the same placeholders
.btn-primary { @extend %button; @extend %rounded; @extend %shadow; background: blue; }
.btn-secondary { @extend %button; @extend %rounded; background: gray; }
.btn-ghost { @extend %button; background: transparent; }
// Sass deduplicates: %button's rule is shared across all three占位符使用场景
占位符非常适合可重用的工具模式:居中、清除浮动、无障碍(visually-hidden)和设计系统原语。它们将这些模式保留在一处,避免除非使用否则不输出。这是一种共享不可配置工具样式的简洁方式。
// 1. Design system primitives
%abs-center {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
}
// 2. Clearfix
%clearfix::after {
content: "";
display: table;
clear: both;
}
// 3. Visually hidden (accessibility)
%visually-hidden {
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.modal { @extend %abs-center; }
.container { @extend %clearfix; }
.sr-only { @extend %visually-hidden; }函数(@function)
定义函数
@function 定义一个可重用的计算,@return 一个值。函数在属性值中调用(不同于输出声明的混入)。使用函数进行单位转换、颜色数学和响应式缩放等计算。返回值可以是任何 Sass 数据类型。
// @function defines a function that returns a value
@function rem($px) {
$base: 16px;
@return ($px / $base) * 1rem;
}
// Use it like a CSS function
body { font-size: rem(16); } // 1rem
h1 { font-size: rem(32); } // 2rem
.small { font-size: rem(12); } // 0.75rem
// Functions return values; mixins output CSS rules函数参数与默认值
函数支持默认参数、命名参数和可变参数(...),与混入一样。默认值使函数调用方便。命名参数提高了有许多可选参数的函数的可读性。可变参数让函数接受任意数量的值。
@function spacing($factor: 1, $base: 8px) {
@return $base * $factor;
}
// Use defaults
.card { padding: spacing(); } // 8px
.card-lg { padding: spacing(2); } // 16px
// Override by name
.section { padding: spacing($base: 10px); } // 10px
.hero { padding: spacing(3, 10px); } // 30px
// Variable arguments work too
@function max-of($nums...) {
@return ...; // implement with reduce
}内置函数概述
Sass 提供了丰富的内置函数用于颜色、数学、字符串、列表和映射。现代 Sass 将这些组织成模块(sass:math、sass:color、sass:string、sass:list、sass:map、sass:meta),通过 @use 加载。全局函数名仍然有效但已弃用,推荐使用命名空间模块函数。
// Color functions
lighten(#000, 50%) // gray
rgba(#fff, 0.5) // semi-transparent white
mix(red, blue) // purple
// Math functions
percentage(0.5) // 50%
round(3.6) // 4
min(10px, 20px) // 10px
// String functions
to-upper-case("abc") // "ABC"
str-length("hello") // 5
// List/Map functions
length(1 2 3) // 3
map-get(("a": 1), "a") // 1
// Modern Sass uses modules: @use 'sass:math'; math.div(10, 2)实用函数示例
函数擅长封装可重用的计算:单位转换(px/em/rem)、通过映射管理 z-index 和设计令牌查找。将这些集中在函数中确保一致性,并使更改(如更新基础字体大小)自动传播到各处。
// Convert px to em relative to a context
@function em($px, $context: 16px) {
@return ($px / $context) * 1em;
}
// Generate a z-index from a scale map
$z-indexes: (
"base": 0,
"dropdown": 100,
"modal": 200,
"toast": 300,
);
@function z($name) {
@return map-get($z-indexes, $name);
}
// Usage
body { font-size: em(18px); } // 1.125em
.modal { z-index: z("modal"); } // 200
.toast { z-index: z("toast"); } // 300递归函数
Sass 函数通过 @if 条件和自调用支持递归。这实现了阶乘、斐波那契和其他计算序列。然而,Sass 递归没有尾调用优化且速度慢——避免在实际样式表中使用深层递归。迭代计算优先使用循环(@for)。
// Functions can call themselves (recursion)
@function factorial($n) {
@if $n <= 1 {
@return 1;
} @else {
@return $n * factorial($n - 1);
}
}
.power { width: factorial(5) * 1px; } // 120px
// Fibonacci
@function fib($n) {
@if $n < 2 { @return $n; }
@return fib($n - 1) + fib($n - 2);
}
// Note: deep recursion is slow in Sass - use sparingly控制指令
@if / @else if / @else
@if/@else if/@else 根据比较条件输出样式。Sass 使用 == 和 != 进行相等判断(不是 ===)。布尔运算符是关键字 and、or、not(不是 &&、||、!)。用于混入和函数中的主题切换、功能标志和响应式逻辑。
@mixin theme($mode) {
@if $mode == dark {
background: #222;
color: #eee;
} @else if $mode == light {
background: #fff;
color: #333;
} @else {
background: gray;
color: black;
}
}
.dark { @include theme(dark); }
.light { @include theme(light); }
.auto { @include theme(auto); }
// Comparison operators: ==, !=, <, >, <=, >=, and, or, not@for 循环
@for 从起始值到结束值迭代变量。through 是包含的(包含结束值);to 是排除的(排除结束值)。常用于生成网格列、错峰动画和编号工具类。使用 #{} 插值将计数器插入选择器。
// @for $var from <start> through <end> (inclusive)
@for $i from 1 through 3 {
.col-#{$i} { width: 100% / $i; }
}
// .col-1 { width: 100%; } .col-2 { width: 50%; } .col-3 { width: 33.333%; }
// 'to' is EXCLUSIVE (excludes the end)
@for $i from 1 to 3 {
.item-#{$i} { order: $i; }
}
// .item-1, .item-2 (no .item-3)
// Use 'through' for inclusive, 'to' for exclusive ranges@each 循环(列表)
@each 遍历列表,将每个项绑定到一个变量。它非常适合从配置列表生成主题变体、图标类和工具类。列表的列表可以在每次迭代中解包为多个变量,用于如(名称,颜色)元组的成对数据。
// Iterate over a list
$icons: user, home, settings, search;
@each $name in $icons {
.icon-#{$name} {
background-image: url("/icons/#{$name}.svg");
}
}
// .icon-user, .icon-home, .icon-settings, .icon-search
// Multiple values (unpacking)
@each $name, $color in (success green), (error red) {
.alert-#{$name} { color: $color; }
}@each 配合映射
@each 遍历映射,每次迭代绑定键和值。这是从配置映射生成响应式工具、主题变体和断点感知类的标准方式。现代 Sass 中映射迭代遵循插入顺序。
$breakpoints: (
"sm": 576px,
"md": 768px,
"lg": 1024px,
"xl": 1280px,
);
// Iterate key-value pairs of a map
@each $name, $size in $breakpoints {
.container-#{$name} {
max-width: $size;
margin: 0 auto;
}
}
// .container-sm, .container-md, .container-lg, .container-xl
// Also destructuring lists of maps for richer config
$buttons: (("primary", blue), ("danger", red));@while 循环
@while 循环直到条件变为假;你必须手动更新计数器。用于 @for 不适合的非线性递进(如每步翻倍)。始终确保循环终止——忘记递增会导致无限循环,挂起编译。
// @while repeats while a condition is true
$i: 1;
@while $i <= 5 {
.mt-#{$i} { margin-top: $i * 4px; }
$i: $i + 1;
}
// .mt-1 { margin-top: 4px; } ... .mt-5 { margin-top: 20px; }
// Unlike @for, you control the increment
// WARNING: always increment/decrement to avoid infinite loops!
$i: 6;
@while $i > 0 {
.delay-#{$i} { transition-delay: $i * 100ms; }
$i: $i - 1;
}列表与映射
列表
列表是 Sass 的数组类型,由空格或逗号分隔。索引从 1 开始(不同于大多数语言)——nth($list, 1) 返回第一项。负索引从末尾计数。单个值被视为单元素列表。列表可以嵌套。
// Lists are ordered collections (like arrays)
// Separated by spaces or commas
$sizes: 10px 20px 30px; // space-separated
$colors: red, green, blue; // comma-separated
$mixed: (1px 2px) (3px 4px); // nested lists
// Single value is also a list of length 1
$one: 5px;
// Indexes are 1-based!
$list: a b c;
nth($list, 1); // a (NOT 0-indexed)
nth($list, -1); // c (negative = from end)
length($list); // 3列表函数
列表函数:length()、nth()、index() 用于访问;append()、join()、insert-nth() 返回新列表(列表是不可变的)。zip() 从多个列表配对元素。separator() 返回列表的分隔符。使用 @each 迭代。现代 API 位于 sass:list 模块中。
$list: a b c;
// Access
length($list); // 3
nth($list, 2); // b
index($list, b); // 2 (position of b, or null)
// Modify (return NEW list - lists are immutable)
append($list, d); // a b c d
join(a b, c d); // a b c d
join((a b), (c d), comma); // a, b, c, d
// Insert
insert-nth($list, 2, x); // a x b c
// Misc
zip(1 2, a b); // (1 a) (2 b) - pairs elements映射
映射是键值集合(关联数组)。键必须唯一;值可以是任何 Sass 类型,包括嵌套映射。使用 map-get() 读取值,map-has-key() 检查存在性。映射是建模设计令牌、断点和主题调色板的标准方式。
// Maps are key-value pairs (like objects/dictionaries)
$colors: (
"primary": #3498db,
"secondary": #2ecc71,
"danger": #e74c3c,
"warning": #f39c12,
);
// Access values
map-get($colors, "primary"); // #3498db
// Check existence
map-has-key($colors, "success"); // false
// Keys must be unique; values can be any type (even nested maps)
$breakpoints: (
"mobile": (max: 767px),
"desktop": (min: 768px),
);映射函数
映射函数:map-get() 读取值,map-keys()/map-values() 返回列表,map-merge() 合并映射(非常适合扩展主题),map-remove() 删除键。映射是不可变的——函数返回新映射。现代 API 在 sass:map 模块下命名空间化。
$colors: ("a": 1, "b": 2);
// Access
map-get($colors, "a"); // 1
map-has-key($colors, "c"); // false
// Keys & values as lists
map-keys($colors); // ("a", "b")
map-values($colors); // (1, 2)
// Merge (combines two maps; second wins on conflicts)
$base: ("x": 1, "y": 2);
$override: ("y": 99, "z": 3);
map-merge($base, $override); // ("x": 1, "y": 99, "z": 3)
// Remove keys
map-remove($colors, "a"); // ("b": 2)迭代与解构
@each 解构映射(键、值)和列表的列表,每次迭代绑定多个变量。这将配置数据转换为生成的 CSS——设计系统的强大模式。结合映射,你可以从单个配置映射驱动整个工具类框架。
$grid: (
"sm": 576px,
"md": 768px,
"lg": 1024px,
);
// @each destructures map into key+value
@each $name, $size in $grid {
@media (min-width: $size) {
.container-#{$name} { max-width: $size; }
}
}
// Nested destructuring with lists of lists
$config: (("primary", blue, white), ("danger", red, white));
@each $name, $bg, $fg in $config {
.btn-#{$name} { background: $bg; color: $fg; }
}颜色函数
lighten / darken
lighten() 和 darken() 按百分比移动颜色的亮度。它们已弃用,推荐使用 sass:color 的 color.adjust(),后者明确更改哪个通道。纯 lighten/darken 在已经极端的颜色上可能产生意外结果——优先使用基于 HSL 的调整。
$base: #3498db;
.light { background: lighten($base, 20%); } // lighter blue
.dark { background: darken($base, 20%); } // darker blue
// Note: lighten/darken are deprecated in modern Sass
// Prefer sass:color adjust() for clarity:
@use 'sass:color';
.adjusted {
// same effect, explicit
background: color.adjust($base, $lightness: 20%);
border-color: color.adjust($base, $lightness: -20%);
}rgba / rgb / 透明度
rgba($color, $alpha) 为任何颜色添加透明度——最常见的颜色函数。rgba() 也接受 4 个数字参数。现代 sass:color 模块提供 color.adjust($alpha: ...) 进行显式 alpha 控制。CSS Color 4 空格语法(rgb(r g b / a%))在现代 Sass 中也有效。
$brand: #3498db;
// Add alpha to any color
.semi { background: rgba($brand, 0.5); } // #3498db at 50%
.fade { background: rgba($brand, 0.1); } // very transparent
// rgba() also accepts 4 numeric args
.box { background: rgba(52, 152, 219, 0.8); }
// Modern alternative: color.adjust for alpha
@use 'sass:color';
.glass { background: color.adjust($brand, $alpha: -0.5); }
// rgb() and rgba() with space syntax (CSS Color 4)
.modern { background: rgb(52 152 219 / 80%); }mix / 混合颜色
mix($color1, $color2, $weight) 混合两种颜色——$weight 是 $color1 的百分比(默认 50%)。使用 mix($color, white) 创建色调,mix($color, black) 创建阴影,这通常比 lighten()/darken() 看起来更自然。color.mix 是现代命名空间等价物。
// mix() blends two colors by a weight (default 50%)
$mix: mix(red, blue); // purple (50/50)
$mix30: mix(red, blue, 30%); // 30% red, 70% blue (bluer)
// Useful for generating tints and shades
$base: #3498db;
.tint { background: mix($base, white, 20%); } // 20% blue, 80% white
.shade { background: mix($base, black, 20%); } // 20% blue, 80% black
// sass:color module
@use 'sass:color';
.blended { background: color.mix(red, blue, 50%); }adjust-color / scale-color / change-color
三个精确的颜色工具:color.adjust() 向通道添加增量,color.scale() 按百分比缩放通道朝向最小/最大值(更安全——不会超调),color.change() 将通道设置为绝对值。全局 adjust-color/scale-color/change-color 是已弃用的别名。
@use 'sass:color';
$base: #3498db;
// adjust: ADDS to channels (absolute change)
.a { background: color.adjust($base, $red: 30, $alpha: -0.3); }
// scale: SCALES channels by % (relative change, stays in range)
.s { background: color.scale($base, $lightness: 50%); }
// change: SETS channels to exact values (replaces)
.c { background: color.change($base, $lightness: 50%, $hue: 0); }
// adjust moves by a delta, scale shrinks/grows toward min/max,
// change overwrites with a fixed value.hue / saturation / complement
color.hue/saturation/lightness 读取 HSL 通道——用于条件逻辑。color.complement() 返回相反色相的颜色(非常适合强调色),color.grayscale() 去饱和,color.invert() 进行 RGB 反转。color.hsl() 从 HSL 值构造颜色。这些实现了程序化调色板生成。
@use 'sass:color';
$c: #3498db;
// Read HSL channels
color.hue($c); // 204deg
color.saturation($c); // 70%
color.lightness($c); // 53%
// Derive new colors
.complement { background: color.complement($c); } // opposite hue
.grayscale { background: color.grayscale($c); } // desaturated
.invert { background: color.invert($c); } // RGB inverse
// Build from HSL
.from-hsl { background: color.hsl(204, 70%, 53%); }数学运算
基础算术
Sass 支持 +、-、*、/、% 对数字进行运算并处理单位。由于 CSS 使用 / 作为分隔符(font: 16px/1.5),普通 / 作为除法已弃用——使用 sass:math 的 math.div() 或括号。+ 和 - 的单位必须兼容。两个带单位值相乘通常是错误。
$base: 16px;
// Sass does math on numbers (with unit handling)
width: 100% - 20px; // ERROR - incompatible units
width: 100px + 20px; // 120px
width: 100px * 2; // 200px
width: 100px / 2; // 50px (use math.div in modern Sass)
width: (100px / 2); // 50px (parenthesized = math, not CSS)
// Modern division (the / operator is deprecated for division)
@use 'sass:math';
width: math.div(100px, 2); // 50px
width: math.div(100, 3); // 33.333...单位转换
Sass 自动转换物理单位(px、in、cm、mm、pt、pc),但不转换 px/em/rem/%。你必须自己编写转换函数。常见模式是 rem($px) 函数除以基础字体大小。集中单位转换以保持项目间距比例一致。
$base-font: 16px;
// Convert px to rem
@function rem($px) {
@return math.div($px, $base-font) * 1rem;
}
h1 { font-size: rem(32px); } // 2rem
// Convert px to em (relative to context)
@function em($px, $context: 16px) {
@return math.div($px, $context) * 1em;
}
// Sass does NOT auto-convert between px/em/rem/%
// You must compute the ratio yourself.
// 1in = 96px, 1cm = 37.8px, 1pt = 1.33px (these DO auto-convert)数学函数
sass:math 提供 round()、ceil()、floor()、abs()、min()、max()、percentage() 以及(较新的)三角和根函数及常量如 math.$pi。此处的 min()/max() 是 Sass 函数(不同于 CSS min()/max()——使用不带引号的 CSS 函数获取实际 CSS min/max)。始终显式导入 math。
@use 'sass:math';
// Rounding
math.round(3.6); // 4
math.ceil(3.1); // 4
math.floor(3.9); // 3
// Absolute & bounds
math.abs(-5); // 5
math.min(10px, 20px); // 10px
math.max(10px, 20px); // 20px
// Percentage conversion
math.percentage(0.5); // 50% (math.percentage, or global percentage)
// Trig & constants (Sass 1.65+)
math.sin(math.$pi); // ~0
math.cos(0); // 1
math.sqrt(16); // 4取模与除法
math.div() 是现代除法函数——/ 运算符保留给 CSS 列表分隔符(如 font: 16px/1.5)。math.mod() 返回余数。math.compatible() 检查两个数字是否可以相加。始终 @use 'sass:math' 进行算术;全局 / 除法已弃用并将被移除。
@use 'sass:math';
// Modulo (remainder)
math.unit(10px); // px
math.compatible(10px, 5px); // true (can add)
math.is-unitless(5); // true
// Division MUST use math.div (the / operator is for lists/CSS)
$result: math.div(100, 3); // 33.333...
// Modulo
$rem: math.mod(10, 3); // 1
// Generating a grid with modulo
@for $i from 1 through 12 {
.col-#{$i} { width: math.div($i, 12) * 100%; }
}比较与逻辑运算符
Sass 使用 == 和 != 进行相等判断。布尔运算符是关键字 and、or、not——不是 C 风格的 &&/||/!。使用括号明确优先级。这些运算符驱动混入、函数和控制指令内的 @if 条件。真值:除 false 和 null 外的所有值。
// Comparison: ==, !=, <, >, <=, >=
@if $width > 100px { /* ... */ }
@if $count == 3 { /* ... */ }
@if $mode != "dark" { /* ... */ }
// Boolean logic: and, or, not (NOT &&, ||, !)
$enabled: true;
$mobile: false;
@if $enabled and not $mobile {
.desktop-only { display: block; }
}
@if $mode == "dark" or $mode == "night" {
background: black;
}
// Use parentheses to group
@if ($a > 1 and $b < 10) or $c == 5 { /* ... */ }字符串函数
quote / unquote
quote() 为字符串添加引号;unquote() 移除引号。Sass 区分带引号字符串(用于 content、字体名称)和不带引号字符串(用于标识符、类名)。许多函数接受两者,但区别对输出格式和选择器生成很重要。
// quote() forces quotes around a string
$unquoted: Helvetica;
$quoted: quote($unquoted); // "Helvetica"
// unquote() removes quotes
$quoted: "Arial";
$plain: unquote($quoted); // Arial
// Useful when a string needs to be quoted for output
.url { background: url(quote("/img/x.png")); }
// url("/img/x.png")
// Quoted vs unquoted matters for some CSS values
// Class names and property names are usually unquoted大小写函数
to-upper-case() 和 to-lower-case() 转换字符串大小写——用于生成一致的类名、content 值或规范化输入。结合 #{} 插值,它们让你从变量构建动态选择器和内容。现代命名空间版本位于 sass:string 中。
// Change string case
$upper: to-upper-case("hello"); // "HELLO"
$lower: to-lower-case("WORLD"); // "world"
// Practical: generate uppercase class names
$states: success, warning, error;
@each $s in $states {
.badge-#{to-upper-case($s)} {
content: "#{to-upper-case($s)}";
}
}
// .badge-SUCCESS { content: "SUCCESS"; } etc.str-length / str-slice / str-index
sass:string 函数:string.length() 返回字符数,string.slice($str, $start, $end) 返回子字符串(从 1 开始,负数从末尾),string.index() 查找子字符串位置(从 1 开始,缺失返回 null)。Sass 没有内置 split——用 index() 和 slice() 循环实现。
@use 'sass:string';
$str: "Hello, World";
// Length (character count)
string.length($str); // 12
// Slice (substring) - 1-based, supports negatives
string.slice($str, 1, 5); // "Hello"
string.slice($str, 8); // "World"
string.slice($str, -5); // "World" (from end)
// Index of substring (1-based, or null if not found)
string.index($str, "World"); // 8
string.index($str, "xyz"); // null
// Split is not built-in - use a custom function with index+slicestr-insert / str-replace
string.insert($string, $insert, $position) 在从 1 开始的位置插入子字符串(用大数字追加)。string.replace($string, $to-replace, $replace-with, $limit?) 替换子字符串(默认全部,或限制次数)。这些对于程序化类名和内容生成至关重要。
@use 'sass:string';
// str-insert: insert at position (1-based)
string.insert("Hello", "!", 6); // "Hello!"
string.insert("World", "Hello ", 1); // "Hello World"
// Modern Sass (1.55+) has string.replace
string.replace("a-b-c", "-", "_"); // "a_b_c" (replace all)
string.replace("a-b-c", "-", "_", 1); // "a_b-c" (limit 1)
// Build dynamic strings
$prefix: "icon";
$name: string.insert($prefix, "-user", 100); // "icon-user"字符串插值模式
#{} 插值将任何 Sass 值(数字、列表、颜色、布尔值)转换为其字符串表示并嵌入。使用它构建动态选择器、属性名、content 值和 URL。插值是 Sass 值与不允许普通变量的 CSS 文本输出之间的桥梁。
$prefix: "btn";
$modifier: "primary";
// #{} converts any value to a string and embeds it
.#{$prefix}-#{$modifier} { /* ... */ } // .btn-primary
// Interpolating numbers, lists, booleans
$count: 3;
.item-#{$count} { order: $count; } // .item-3 { order: 3; }
// Interpolating in @include and @media
@mixin respond-to($bp) {
@media (max-width: $bp) { @content; }
}
// Interpolating function calls
.content::after {
content: "#{to-upper-case('hello')}";
}@at-root
基础 @at-root
@at-root 退出当前嵌套上下文,将嵌套规则输出到样式表的顶层。没有 @at-root,.parent 内的 .child 编译为 .parent .child(后代)。@at-root 使其仅为 .child。这在源码中保持相关规则分组的同时输出扁平选择器。
.parent {
color: blue;
// @at-root jumps out of the nesting
@at-root .child {
color: red;
}
}
// Compiles to (note: .child is NOT nested under .parent):
// .parent { color: blue; }
// .child { color: red; } <- top-level, not .parent .child
// Useful for defining sibling/related rules inside a block
// without creating a descendant selector.@at-root 配合选择器
将 @at-root 与 & 结合使用可选择性地保留父选择器的部分。@at-root .wrapper & 输出 .wrapper .component(前置上下文)而非 .component .wrapper。这是编写主题/上下文覆盖同时将其与组件代码共存于同一位置的简洁方式。
.component {
width: 100px;
// Combine @at-root with & to keep part of the parent
@at-root .wrapper & {
background: yellow;
}
// -> .wrapper .component { background: yellow; }
// Useful for context overrides without deep nesting
@at-root .dark-theme & {
color: white;
}
// -> .dark-theme .component { color: white; }
}@at-root 用于 BEM
@at-root 帮助将 BEM 修饰符类(.block--active)生成为兄弟而非后代。没有 @at-root,在 .block 内嵌套 .block--active 会创建 .block .block--active(错误——表示块内的活动块)。@at-root 在顶层输出修饰符,这是正确的 BEM 输出。
.block {
// State classes as siblings, not descendants
@at-root {
.block--active { border-color: blue; }
.block--disabled { opacity: 0.5; }
}
// Or inline form
@at-root .block--hidden { display: none; }
}
// Compiles to top-level siblings:
// .block { ... }
// .block--active { border-color: blue; }
// .block--disabled { opacity: 0.5; }
// .block--hidden { display: none; }
// Avoids accidentally generating .block .block--active@at-root 配合媒体查询
@at-root (with: media) 将规则提升出选择器嵌套但保留在 @media 块内。@at-root (without: media) 做相反的事。with/without 子句控制保留哪些上下文(rule、media、supports)。这是高级功能但对构建稳健的响应式混入库至关重要。
.component {
width: 100%;
// @at-root lifts media queries to the top level
@at-root (with: media) {
@media (min-width: 768px) {
& { width: 50%; }
}
}
// -> @media (min-width: 768px) { .component { width: 50%; } }
}
// Default @at-root (without: rule) lifts OUT of everything
// @at-root (with: media) keeps the @media context but exits rules
// Useful in complex nested mixins to control scope precisely@at-root 的 without(with)
(without: ...) 和 (with: ...) 子句控制 @at-root 从哪些嵌套上下文逃逸。常见上下文:rule(选择器)、media(@media)、supports(@supports)和 all。(without: rule) 保留 @media 但退出选择器。这种精确的作用域控制实现了高级混入和库模式。
.parent {
@media (min-width: 768px) {
.child {
width: 50%;
// Exit only the rule, keep the @media
@at-root (without: rule) {
.sibling { width: 25%; }
}
// -> @media (min-width: 768px) { .sibling { width: 25%; } }
// Exit everything (default)
@at-root .top { color: red; }
// -> .top { color: red; } (no @media, no parent)
}
}
}@content
@content 基础
@mixin 内的 @content 是通过 @include { ... } 传递的样式块的占位符。包含的块在调用处替换 @content。这让混入包装自定义样式——对于媒体查询、关键帧和任何内部内容因使用而异的包装器模式至关重要。
// @content marks where the included block is injected
@mixin highlighted {
background: yellow;
font-weight: bold;
@content; // the block passed to @include goes here
}
.title {
@include highlighted {
color: red; // injected where @content was
font-size: 20px;
}
}
// Compiles to:
// .title { background: yellow; font-weight: bold; color: red; font-size: 20px; }带参数的 @content
@content ($args) 从混入向包含的块传递值,后者用 using ($vars) 接收。这让混入向调用者的块提供上下文(如当前断点名称)。对于构建需要向消费者公开循环状态的迭代/包装混入很强大。
// Pass values back to the included block (Sass 3.3+)
@mixin each-breakpoint {
$breakpoints: (sm: 576px, md: 768px, lg: 1024px);
@each $name, $size in $breakpoints {
@media (min-width: $size) {
@content ($name, $size);
}
}
}
// Receive via #{...} - actually use the block like a function
@include each-breakpoint using ($name, $size) {
.container-#{$name} { max-width: $size; }
}@content 用于媒体查询
最常见的 @content 用例:一个 respond-to 混入将样式包装在正确的 @media 查询中。调用者传递响应式样式块;混入将其放入正确的媒体查询中。这使响应式代码 可读并将断点定义集中在一处。
// Classic responsive mixin pattern
@mixin respond-to($breakpoint) {
@if $breakpoint == phone {
@media (max-width: 600px) { @content; }
} @else if $breakpoint == tablet {
@media (min-width: 601px) and (max-width: 900px) { @content; }
} @else if $breakpoint == desktop {
@media (min-width: 901px) { @content; }
}
}
.sidebar {
width: 300px;
@include respond-to(phone) {
width: 100%;
float: none;
}
@include respond-to(desktop) {
width: 350px;
}
}@content 模式
@content 驱动许多可重用模式:@supports 包装器、关键帧生成器和 BEM 的修饰符类生成器。每个都让调用者提供主体而混入处理样板(选择器、at-rule、命名空间)。这就是结构良好的 Sass 库提供灵活、可组合 API 的方式。
// Wrapper for @supports (feature queries)
@mixin supports-flex { @supports (display: flex) { @content; } }
// Keyframes generator
@mixin keyframes($name) {
@keyframes #{$name} { @content; }
}
@include keyframes(fade-in) {
from { opacity: 0; }
to { opacity: 1; }
}
// Modifier generator
@mixin modifier($name) {
&--#{$name} { @content; }
}
.button {
@include modifier(primary) { background: blue; }
@include modifier(danger) { background: red; }
}@content 使用场景
@content 启用上下文包装器(media/supports/keyframes)、扩展钩子(基础样式 + 自定义添加)和条件输出。它是 Sass 的高阶函数等价物:混入是框架,@content 是回调。掌握 @content 以构建灵活、可重用的样式工具。
// 1. Context wrappers (@media, @supports, @keyframes)
@mixin mobile { @media (max-width: 767px) { @content; } }
// 2. Reset/normalize hooks
@mixin base-styles {
box-sizing: border-box;
margin: 0;
@content; // allow components to add more
}
// 3. Debugging/profiling wrappers
@mixin timed {
$start: systime();
@content;
// log elapsed...
}
// 4. Conditional output
@mixin if-dark($is-dark) {
@if $is-dark { @content; }
}模块系统(@use / @forward)
@use 与 @import
@use 用适当的模块系统取代 @import:命名空间防止冲突,模块只加载一次(缓存),以 - 或 _ 为前缀的成员是私有的(不暴露),with 显式配置默认值。这修复了 @import 的所有结构性问题。所有新 Sass 代码使用 @use。
// @use is the modern module system (replaces @import)
// Key improvements:
// 1. Members are namespaced (no global pollution)
// 2. Modules load ONCE (cached, no duplicate execution)
// 3. Privacy: members starting with - or _ are private
// 4. Explicit configuration with 'with'
// _shapes.scss
$radius: 4px;
@mixin rounded { border-radius: $radius; }
@mixin -internal { /* private */ } // not exposed
// app.scss
@use 'shapes'; // namespace: shapes
@use 'shapes' as s; // namespace: s
@use 'shapes' as *; // no namespace (global)
.card { @include shapes.rounded; } // via namespace@forward
@forward 从另一个模块重新导出成员,而不使其成为自己的——非常适合聚合文件夹内容的桶/索引文件。@forward 支持 hide 排除成员和 as prefix-* 在重新导出时重命名。这让你为库或文件夹制作干净的公共 API。
// @forward re-exports members from another module
// Useful for building index/barrel files
// sass/abstracts/_index.scss
@forward 'variables'; // exposes _variables.scss members
@forward 'functions';
@forward 'mixins';
// Now consumers can import the whole folder at once:
// app.scss
@use 'abstracts' as *; // gets variables + functions + mixins
// @forward can rename or hide members
@forward 'buttons' hide button-styles;
@forward 'forms' as form-*; // prefix all members with 'form-'私有成员(- 前缀)
名称以 - 或 _ 开头的成员是私有的——它们在自己的文件内可用但不会通过 @use 或 @forward 暴露。这让模块隐藏实现细节并仅暴露公共 API。使用 - 前缀标记内部辅助函数,保持模块接口干净稳定。
// _math-helpers.scss
@function -internal-double($n) {
@return $n * 2;
}
@function public-times-two($n) {
@return -internal-double($n); // OK - same file
}
// Public function usable via @use
@function scale($n, $factor: 2) {
@return -internal-double($n) * $factor;
}
// app.scss
@use 'math-helpers';
.val { width: math-helpers.scale(10); } // OK
// .val { width: math-helpers.-internal-double(10); } // ERROR - private配置模块(with)
with ($var: value, ...) 在加载时配置模块的 !default 变量。这是 @import 全局变量覆盖模式的现代、作用域化替代。配置仅适用于模块的第一次 @use——后续 @use 语句复用已配置的模块。这是库主题化的方式。
// _theme.scss
$primary: blue !default;
$radius: 4px !default;
@mixin button {
background: $primary;
border-radius: $radius;
}
// app.scss - configure on @use
@use 'theme' with (
$primary: #ff6600,
$radius: 8px,
);
.btn { @include theme.button; }
// background: #ff6600; border-radius: 8px;
// 'with' only works for variables marked !default in the module.
// Configuration must happen at the FIRST @use of that module.模块最佳实践
模 块最佳实践:使用 @use(而非 @import),优先使用简短的显式命名空间,用 @forward 在 _index.scss 中聚合文件夹,用 - 标记内部辅助函数为私有,通过 with 配置 !default 变量配置库,使用内置 sass:* 模块的标准函数。这使代码模块化且可维护。
// 1. Always use @use, never @import (deprecated)
// 2. Use explicit namespaces for clarity
@use 'sass:math'; // built-in module namespace: math
@use 'variables' as v; // short custom namespace
// 3. Use @forward in _index.scss barrel files
// components/_index.scss
@forward 'buttons';
@forward 'cards';
// 4. Keep members private with - prefix
@function -helper() { @return ...; }
// 5. Configure libraries with 'with', set !default on library vars
$brand: blue !default;
// 6. Use built-in modules: sass:math, sass:color, sass:string,
// sass:list, sass:map, sass:meta, sass:selector, sass:css调试(@debug / @warn / @error)
@debug
@debug 在编译期间将值打印到终端,附带源文件和行号。它不会停止构建。使用 @debug 在开发期间检查变量、跟踪执行流程和验证计算。发布前移除或注释掉 @debug 语句。输出到 stderr,不在 CSS 中。
// @debug prints a message to the console during compilation
$base: 16px;
$size: 24px;
@debug "Calculating size...";
@debug "base is #{$base}, size is #{$size}";
@debug "ratio: " #{math.div($size, $base)};
// Console output during compile:
// _file.scss:5 DEBUG: Calculating size...
// _file.scss:6 DEBUG: base is 16px, size is 24px
// @debug does NOT stop compilation - just logs
// Use it to trace values while developing@warn
@warn 在编译期间向控制台打印非致命警告——构建继续。用于库中的弃用通知、回退行为和潜在误用警报。@warn 默认包含堆栈跟踪,用户可定位警告来源。与 @debug 一样,它不出现在 CSS 输出中。
// @warn prints a warning to the console (does NOT stop compilation)
@mixin deprecated-mixin {
@warn "deprecated-mixin is deprecated; use new-mixin instead.";
/* ... styles ... */
}
// Useful for deprecation notices, potential issues
@warn "Variable $primary is not set, falling back to default.";
// With stack trace for locating the call site
@warn "Invalid value: #{$value}" {
// optional stack trace via meta.module-functions
}
// @warn is non-fatal - compilation continues.@error
@error 抛出致命错误,立即停止编译。用于验证函数和混入的输入——及早用清晰消息捕获误用胜过发出损坏的 CSS。@error 包含堆栈跟踪。将 @error 保留给真正无效的状态;可恢复问题使用 @warn,检查使用 @debug。
// @error stops compilation with an error message
@function divide($a, $b) {
@if $b == 0 {
@error "Cannot divide by zero: divide(#{$a}, #{$b})";
}
@return math.div($a, $b);
}
// In a mixin validating arguments
@mixin set-font-size($size) {
@if not $size {
@error "set-font-size requires a non-null $size, got #{$size}";
}
font-size: $size;
}
// @error halts the build - use for invalid inputs
// that would produce broken CSS.meta 模块函数
sass:meta 提供内省:type-of() 返回值的类型,inspect() 生成任何值的可读字符串(非常适合调试),*-exists 函数按名称检查变量/混入/函数。meta.get-function() 返回可调用引用,启用高阶模式。将这些用于防御性库代码。
@use 'sass:meta';
// Inspect values
meta.type-of(10px); // number
meta.type-of("hi"); // string
meta.type-of((a: 1)); // map
meta.inspect((a: 1, b: 2)); // (a: 1, b: 2) - readable representation
// Variable existence (within current scope)
meta.variable-exists("base"); // true if $base exists
meta.global-variable-exists("primary");
// Mixin/function existence
meta.mixin-exists("my-mixin");
meta.function-exists("my-func");
// Feature queries
meta.feature-exists("at-error"); // true
// Get a function reference (for higher-order use)
$fn: meta.get-function("lighten");检查函数
type-of()(全局或 meta.type-of)标识值的数据类型——对防御性函数/混入逻辑至关重要。inspect() 将任何 Sass 值(包括映射和嵌套列表)渲染为可读字符串,非常适合 @debug 日志。math.unit()/is-unitless() 检查数字。这些工具使 Sass 内部在开发期间可观察。
@use 'sass:meta';
@use 'sass:math';
// type-of: get the type of any value
$value: 16px;
type-of($value); // number
type-of("hello"); // string
type-of(#fff); // color
type-of(true); // bool
type-of(null); // null
type-of(1 2 3); // list
type-of((a: 1)); // map
// inspect: readable debug output of complex values
$map: (primary: blue, secondary: green);
debug: inspect($map); // (primary: blue, secondary: green)
// unit / unitless for numbers
math.unit(16px); // px
math.is-unitless(16); // true
// Combine with @debug for tracing
@debug "got: #{inspect($map)}";Sass 与 SCSS 语法
SCSS 语法(.scss)
SCSS(.scss)是最流行的 Sass 语法。它是 CSS 的严格超集——任何有效的 CSS 文件都是有效的 SCSS,因此你可以将 .css 重命名为 .scss 并逐步添加 Sass 特性。SCSS 像 CSS 一样使用大括号和分号。新项目和团队采用时选择 SCSS。
// SCSS - Sassy CSS
// Uses braces {} and semicolons ;
// Superset of CSS - all CSS is valid SCSS
$primary: #3498db;
.button {
background: $primary;
&:hover {
background: darken($primary, 10%);
}
.icon {
width: 16px;
height: 16px;
}
}
// Pros: familiar to CSS devs, easy migration, widely adoptedSass 缩进语法(.sass)
Sass 缩进语法(.sass)去除大括号和分号,仅依赖缩进(如 Python 或 Stylus)。它更简洁,但你不能将原始 CSS 直接粘贴到 .sass 文件中,且缩进错误会导致 bug。不如 SCSS 常见,但因其简洁外观被一些人偏爱。两者编译为相同的 CSS。
// Sass - indented syntax
// NO braces, NO semicolons - uses indentation only
// Removes CSS punctuation for a cleaner look
$primary: #3498db
.button
background: $primary
&:hover
background: darken($primary, 10%)
.icon
width: 16px
height: 16px
// Pros: less typing, cleaner visually
// Cons: can't paste CSS directly, less popular, error-prone indentation语法间转换
sass-convert(随 Sass 提供)在 .scss 和 .sass 之间转换。在 Sass 语法中,@mixin 变为 =,@include 变为 + 以求简洁。除语法外,两者相同——相同特性、相同输出 CSS。大多数团队因其 CSS 兼容性和更广泛的工具支持而标准化使用 SCSS。
// Convert SCSS to Sass (and vice versa) with the sass CLI:
// $ sass-convert style.scss style.sass
// $ sass-convert style.sass style.scss
// Bulk convert a folder:
// $ sass-convert --recursive --from scss --to sass scss/ sass/
// $ sass-convert --recursive --from sass --to scss sass/ scss/
// Key syntax differences:
// 1. SCSS uses { } and ; | Sass uses indentation
// 2. SCSS: @include mixin() | Sass: +mixin()
// 3. SCSS: @mixin name {} | Sass: =name
// 4. SCSS: @import "x" | Sass: same
// 5. Multi-line: SCSS needs ; | Sass just newlines何时使用哪种
SCSS 是更安全的默认选择——它是行业标准,每个 CSS 文件都是有效的 SCSS,大多数文档/示例使用它。缩进 .sass 语法吸引不喜欢标点的开发者,但工具支持较少且更难让新团队成员上手。每个项目选择一种并保持一致。
// Use SCSS (.scss) when:
// - Starting a new project (most common choice)
// - Migrating an existing CSS codebase (CSS is valid SCSS)
// - Working on a team (most developers know SCSS)
// - Using CSS frameworks/libraries (they're usually SCSS)
// Use Sass (.sass) when:
// - You prefer minimal punctuation / Python-like syntax
// - Writing from scratch with no CSS to migrate
// - Personal preference for cleaner-looking source
// Both produce identical CSS. The choice is stylistic.
// IMPORTANT: don't mix .scss and .sass in the same project.常见陷阱
现代 Sass 弃用了几个遗留特性:使用 math.div() 代替 / 进行除法,@use/@forward 代替 @import,sass:color/sass:math 模块函数代替全局 lighten()/darken()/mix() 等。sass-migrator 工具自动完成这些迁移。关注编译期间的弃用警告以保持代码面向未来。
// 1. Division: / is deprecated for math, use math.div()
@use 'sass:math';
width: math.div(100px, 2); // CORRECT
// width: 100px / 2; // DEPRECATED (may be CSS list separator)
// 2. @import is deprecated - use @use/@forward
@use 'variables'; // CORRECT
// @import 'variables'; // DEPRECATED
// 3. lighten()/darken() deprecated - use color.adjust
@use 'sass:color';
color.adjust($c, $lightness: 10%); // CORRECT
// lighten($c, 10%); // DEPRECATED
// 4. Global color functions deprecated - use sass:color module
color.mix(red, blue); // CORRECT (via @use 'sass:color')
// mix(red, blue); // DEPRECATED global function相关 Sass 代码片段
Copy-paste ready code for common tasks.
Variables
Store colors, spacing, and breakpoints in Sass variables.
Nesting
Mirror HTML structure and use the parent selector with &.
Mixins
Create reusable style groups with arguments and defaults.
Extend & Placeholders
Share styles across selectors with @extend and %placeholders.
Partials & @use
Split stylesheets into partials and load them with @use.
Functions
Define custom functions that return computed values.
Control Directives
Generate styles with @for, @each, and @if.
Operators
Use arithmetic, relational, and equality operators in Sass.
这篇内容对您有帮助吗?