Skip to content

Sass 速查表

功能强大的 CSS 预处理器,支持变量、嵌套和混入。

01

Sass 基础

变量与嵌套

Sass 变量使用 $ 前缀。嵌套镜像 HTML 结构。& 引用父选择器。使用 lighten()/darken() 进行颜色操作。

sass
// 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 更流行且更易迁移。选择一种并在项目中保持一致。

sass
// 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 中。/*! */ 注释即使在压缩/生产模式下也会保留——用于许可证或关键声明。使用 // 进行内部文档注释。

sass
// 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 以减小文件大小。

sass
// 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
// 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; }
02

变量与数据类型

变量声明与作用域

在任何块外部声明的变量是全局的。在块内部声明的变量是该块的局部变量。局部变量会遮蔽同名的全局变量。使用局部变量以避免污染全局命名空间。

sass
$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 库和设计系统至关重要。

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——全局修改会使代码难以推理。

sass
$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
// 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,#{} 几乎可在任何地方使用。它将任何值转换为字符串表示。

sass
$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; }
}
03

嵌套

基础嵌套

嵌套让你编写镜像 HTML 层次结构的选择器,避免重复父选择器。编译后的 CSS 创建后代选择器。这提高了可读性,但过度使用会创建过于具体的选择器——嵌套保持在 3-4 层以内。

sass
// 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 风格类来扁平化深层结构以提高可维护性。

sass
// 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 等。

sass
.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 提升到顶层。这比在多个文件中分散媒体查询大大提高了可读性。

sass
.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 将每个项与父级组合。这比手动编写每个组合更简洁。& 也可在嵌套列表中使用,一次为多个父级追加类/修饰符。

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; }
04

父选择器(&)

基础父选择器

& 引用父选择器,允许伪类(:hover、:focus)、伪元素(::before)和修饰符嵌套。这使相关样式保持在一起。&:hover 编译为 .button:hover。没有 &,你会得到后代选择器。

sass
.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 并在块定义内保持一致。

sass
// 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 的复合选择器。这用于命名变体。注意——过度使用后缀可能在阅读源码时掩盖生成的类名。

sass
.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,组件适应其祖先上下文,将上下文特定覆盖保留在组件附近。

sass
.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 用于兄弟元素间距。这对于重复元素之间的间距和复杂的状态选择器非常强大。每个 & 都被完整的父选择器替换。

sass
.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;
  }
}
05

部分文件(_partial 文件)

创建部分文件

部分文件是以开头的下划线命名的 Sass 文件(如 _variables.scss)。下划线告诉 Sass 不要将它们编译为单独的 CSS 文件。部分文件用于导入到主文件中。这将代码模块化以便维护。

sass
// _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。一致的命名帮助团队快速定位代码。

sass
// 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 的部分文件。

sass
// 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
// 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。

sass
// 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
06

@import 与 @use

@import(已弃用)

@import 已弃用,因为它污染全局命名空间,可能多次执行文件,且不提供隐私或命名空间。Sass 建议迁移到 @use 和 @forward。现有的 @import 代码仍然有效,但新项目应迁移。

sass
// @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 出现多少次。

sass
// _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 * 将成员加载到当前作用域而不带前缀——方便但有冲突风险,请谨慎使用。命名命名空间更安全,在阅读代码时能清楚每个成员的来源。

sass
// _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 子句必须在对该模块的任何其他使用之前。这是配置库主题和设计令牌的标准方式。

sass
// _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 工具自动迁移大型代码库。

sass
// @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
07

混入(@mixin / @include)

定义与使用混入

@mixin 定义可重用的声明块(甚至是规则)。@include 在使用处插入混入的内容。混入非常适合厂商前缀、清除浮动和重复声明组。与 @extend 不同,混入每次包含时都会复制声明。

sass
// 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;
}

混入参数

混入像函数一样接受参数。按位置传递(顺序重要)或按名称传递(顺序无关,更清晰)。命名参数对于有许多可选参数的混入很有用。参数使混入灵活且可跨不同值复用。

sass
@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); }

默认参数

默认参数值使混入灵活——调用者可以省略参数以使用默认值。使用命名参数覆盖特定参数同时保持其他参数为默认值。默认值在每次包含混入时求值,而非定义时求值一次。

sass
@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 部分了解参数传递。

sass
// @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)的混入至关重要。

sass
// ... 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)...);
08

@extend 与继承

基础 @extend

@extend 使一个选择器继承另一个的所有样式,共享规则而非复制。编译后的 CSS 将选择器分组在一起。@extend 比混入更 DRY 地共享基础样式,但可能创建意外的选择器链——请谨慎使用。

sass
.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 可能以意想不到的方式层叠并使输出膨胀。

sass
// 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 更可预测。

sass
.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 会使编译失败。

sass
// !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 主要用于单个文件内的语义关系。

sass
// @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)
09

占位符选择器(%placeholder)

%placeholder 基础

%placeholder 选择器(如 %base-message)是仅为扩展而存在的规则——它们本身永远不会输出到 CSS。这避免了在真实类上使用 @extend 可能导致的未使用基类问题。占位符是共享仅供继承的样式的最简洁方式。

sass
// %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 中使用时使用真实类;当基类纯粹用于继承时使用占位符。

sass
// 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 多个占位符,组合共享样式集。这组合了可重用的构建块而不输出未使用的基类。占位符在设计系统原语(卡片、按钮、输入框)中表现出色,跨许多具体组件共享。

sass
%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 类膨胀。

sass
%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)和设计系统原语。它们将这些模式保留在一处,避免除非使用否则不输出。这是一种共享不可配置工具样式的简洁方式。

sass
// 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; }
10

函数(@function)

定义函数

@function 定义一个可重用的计算,@return 一个值。函数在属性值中调用(不同于输出声明的混入)。使用函数进行单位转换、颜色数学和响应式缩放等计算。返回值可以是任何 Sass 数据类型。

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

函数参数与默认值

函数支持默认参数、命名参数和可变参数(...),与混入一样。默认值使函数调用方便。命名参数提高了有许多可选参数的函数的可读性。可变参数让函数接受任意数量的值。

sass
@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 加载。全局函数名仍然有效但已弃用,推荐使用命名空间模块函数。

sass
// 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 和设计令牌查找。将这些集中在函数中确保一致性,并使更改(如更新基础字体大小)自动传播到各处。

sass
// 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)。

sass
// 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
11

控制指令

@if / @else if / @else

@if/@else if/@else 根据比较条件输出样式。Sass 使用 == 和 != 进行相等判断(不是 ===)。布尔运算符是关键字 and、or、not(不是 &&、||、!)。用于混入和函数中的主题切换、功能标志和响应式逻辑。

sass
@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 是排除的(排除结束值)。常用于生成网格列、错峰动画和编号工具类。使用 #{} 插值将计数器插入选择器。

sass
// @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 遍历列表,将每个项绑定到一个变量。它非常适合从配置列表生成主题变体、图标类和工具类。列表的列表可以在每次迭代中解包为多个变量,用于如(名称,颜色)元组的成对数据。

sass
// 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 中映射迭代遵循插入顺序。

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 不适合的非线性递进(如每步翻倍)。始终确保循环终止——忘记递增会导致无限循环,挂起编译。

sass
// @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;
}
12

列表与映射

列表

列表是 Sass 的数组类型,由空格或逗号分隔。索引从 1 开始(不同于大多数语言)——nth($list, 1) 返回第一项。负索引从末尾计数。单个值被视为单元素列表。列表可以嵌套。

sass
// 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 模块中。

sass
$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() 检查存在性。映射是建模设计令牌、断点和主题调色板的标准方式。

sass
// 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 模块下命名空间化。

sass
$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——设计系统的强大模式。结合映射,你可以从单个配置映射驱动整个工具类框架。

sass
$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; }
}
13

颜色函数

lighten / darken

lighten() 和 darken() 按百分比移动颜色的亮度。它们已弃用,推荐使用 sass:color 的 color.adjust(),后者明确更改哪个通道。纯 lighten/darken 在已经极端的颜色上可能产生意外结果——优先使用基于 HSL 的调整。

sass
$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 中也有效。

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 是现代命名空间等价物。

sass
// 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 是已弃用的别名。

sass
@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 值构造颜色。这些实现了程序化调色板生成。

sass
@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%); }
14

数学运算

基础算术

Sass 支持 +、-、*、/、% 对数字进行运算并处理单位。由于 CSS 使用 / 作为分隔符(font: 16px/1.5),普通 / 作为除法已弃用——使用 sass:math 的 math.div() 或括号。+ 和 - 的单位必须兼容。两个带单位值相乘通常是错误。

sass
$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) 函数除以基础字体大小。集中单位转换以保持项目间距比例一致。

sass
$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。

sass
@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' 进行算术;全局 / 除法已弃用并将被移除。

sass
@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 外的所有值。

sass
// 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 { /* ... */ }
15

字符串函数

quote / unquote

quote() 为字符串添加引号;unquote() 移除引号。Sass 区分带引号字符串(用于 content、字体名称)和不带引号字符串(用于标识符、类名)。许多函数接受两者,但区别对输出格式和选择器生成很重要。

sass
// 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 中。

sass
// 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() 循环实现。

sass
@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+slice

str-insert / str-replace

string.insert($string, $insert, $position) 在从 1 开始的位置插入子字符串(用大数字追加)。string.replace($string, $to-replace, $replace-with, $limit?) 替换子字符串(默认全部,或限制次数)。这些对于程序化类名和内容生成至关重要。

sass
@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 文本输出之间的桥梁。

sass
$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')}";
}
16

@at-root

基础 @at-root

@at-root 退出当前嵌套上下文,将嵌套规则输出到样式表的顶层。没有 @at-root,.parent 内的 .child 编译为 .parent .child(后代)。@at-root 使其仅为 .child。这在源码中保持相关规则分组的同时输出扁平选择器。

sass
.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。这是编写主题/上下文覆盖同时将其与组件代码共存于同一位置的简洁方式。

sass
.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 输出。

sass
.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)。这是高级功能但对构建稳健的响应式混入库至关重要。

sass
.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 但退出选择器。这种精确的作用域控制实现了高级混入和库模式。

sass
.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)
    }
  }
}
17

@content

@content 基础

@mixin 内的 @content 是通过 @include { ... } 传递的样式块的占位符。包含的块在调用处替换 @content。这让混入包装自定义样式——对于媒体查询、关键帧和任何内部内容因使用而异的包装器模式至关重要。

sass
// @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) 接收。这让混入向调用者的块提供上下文(如当前断点名称)。对于构建需要向消费者公开循环状态的迭代/包装混入很强大。

sass
// 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 查询中。调用者传递响应式样式块;混入将其放入正确的媒体查询中。这使响应式代码可读并将断点定义集中在一处。

sass
// 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 的方式。

sass
// 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 以构建灵活、可重用的样式工具。

sass
// 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; }
}
18

模块系统(@use / @forward)

@use 与 @import

@use 用适当的模块系统取代 @import:命名空间防止冲突,模块只加载一次(缓存),以 - 或 _ 为前缀的成员是私有的(不暴露),with 显式配置默认值。这修复了 @import 的所有结构性问题。所有新 Sass 代码使用 @use。

sass
// @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。

sass
// @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。使用 - 前缀标记内部辅助函数,保持模块接口干净稳定。

sass
// _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 语句复用已配置的模块。这是库主题化的方式。

sass
// _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:* 模块的标准函数。这使代码模块化且可维护。

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
19

调试(@debug / @warn / @error)

@debug

@debug 在编译期间将值打印到终端,附带源文件和行号。它不会停止构建。使用 @debug 在开发期间检查变量、跟踪执行流程和验证计算。发布前移除或注释掉 @debug 语句。输出到 stderr,不在 CSS 中。

sass
// @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 输出中。

sass
// @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。

sass
// @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() 返回可调用引用,启用高阶模式。将这些用于防御性库代码。

sass
@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 内部在开发期间可观察。

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)}";
20

Sass 与 SCSS 语法

SCSS 语法(.scss)

SCSS(.scss)是最流行的 Sass 语法。它是 CSS 的严格超集——任何有效的 CSS 文件都是有效的 SCSS,因此你可以将 .css 重命名为 .scss 并逐步添加 Sass 特性。SCSS 像 CSS 一样使用大括号和分号。新项目和团队采用时选择 SCSS。

sass
// 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 adopted

Sass 缩进语法(.sass)

Sass 缩进语法(.sass)去除大括号和分号,仅依赖缩进(如 Python 或 Stylus)。它更简洁,但你不能将原始 CSS 直接粘贴到 .sass 文件中,且缩进错误会导致 bug。不如 SCSS 常见,但因其简洁外观被一些人偏爱。两者编译为相同的 CSS。

sass
// 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。

sass
// 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 语法吸引不喜欢标点的开发者,但工具支持较少且更难让新团队成员上手。每个项目选择一种并保持一致。

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 工具自动完成这些迁移。关注编译期间的弃用警告以保持代码面向未来。

sass
// 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

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。