Skip to content

Sass

第一章:走进 Sass

一、Sass 简介

1. CSS 预处理器的概念

CSS 预处理器用一种专门的编程语言,进行 Web 页面样式设计,然后再编译成正常的 CSS 文件,以供项目使用。CSS 预处理器为 CSS 增加一些编程的特性,无需考虑浏览器的兼容性问题。例如说:Sass(SCSS)、LESS、Stylus、Turbine、Switch CSS、CSS Cacheer、DT CSS 等,均属于 CSS 预处理器。其中比较优秀的有 Sass、LESS、Stylus。

2. Sass 是什么

Sass 是世界上最成熟、最稳定、最强大的专业级 CSS 扩展语言!

简单来说就是 CSS 预处理器。

文档地址

Sass 的官方网站:sass-lang.com

中文文档

Sass 和 SCSS 区别

Sass 从第三代开始,加入了缩进式风格,并且完全向下兼容普通的 CSS 代码,这一代的 Sass 也被称为 SCSS(Sassy CSS)。

总结:目前 Sass 新版支持两种写法。Sass 是旧版,语法采用缩进。SCSS 是新版,语法采用 {}。目前官方推荐语法使用 SCSS。

3. Sass 编译器实现版本

  • Dart Sass (包名 sass)
    • 目前的官方实现,也是唯一被积极维护和推荐的版本。
    • 由 Sass 官方团队用 Dart 语言编写,然后可以编译成纯 JavaScript 在 Node.js 环境中运行。
    • 优点是安装简单、功能最新、跨平台兼容性好。
    • 通过安装 sass 这个 npm 包 (也就是 Dart Sass) 来进行编译。
    • GitHub 仓库:https://github.com/sass/dart-sass
  • LibSass (包名 node-sass) :LibSass 版本说明
    • 已经停止维护,不推荐使用。
    • 用 C/C++ 编写的高性能编译库。
    • 曾经因为速度快而流行,但由于安装困难、跟不上 Sass 语言新特性等问题已被废弃。
    • 2025 年 10 月结束了生命周期。
    • GitHub 仓库:https://github.com/sass/libsass

二、环境变量配置安装

方法一:sass 包

npm sass 介绍:sass

bash
npm install -g sass
# 推荐
npm install --save-dev sass

不推荐这种使用方式学习 sass,这种使用方式都是在脚手架中。推荐使用 Live Sass Compiler 插件。

bash
# 采用单个 Sass 文件 input.scss,并将该文件编译为 output.css
sass input.scss output.css

# 每次保存了 input.scss 会自动重新编译
sass --watch input.scss output.css

# Sass 会监视 app/sass 文件夹中的所有文件是否发生更改,并将 CSS 编译到 public/stylesheets 文件夹
sass --watch app/sass:public/stylesheets

方法二:VScode 集成

1)Live Sass Compiler

2)点击管理(设置的小符号)

3)点击扩展设置

4)点击【在 settings.json 中编辑】

配置选项参考:VScode Live sass - compiler/docs/settings.md

json
{
  "liveSassCompile.settings.formats": [
    {
      /*
        expanded - 展开格式
        compressed - 压缩格式
      */
      "format": "expanded", // 指定输出的css格式
      "extensionName": ".css", // 添加到输出文件的扩展名后缀 (必须以 .css 结尾)
      /*
        ~ 代表当前正在被编译的 Sass 文件所在的目录
        null 表示当前目录
      */
      "savePath": "~/../css"
    }
  ],
  // 排除目录
  "liveSassCompile.settings.excludeList": [
    "/**/node_modules/**",
    "/.vscode/**",
    "/.history/**"
  ],
  // 是否生成对应的map
  "liveSassCompile.settings.generateMap": false,
  // 是否添加兼容前缀, 例如:-webkit- -moz- ... 等
  "liveSassCompile.settings.autoprefix": [
    "> 1%",
    "last 2 versions"
  ],
  "explorer.confirmDelete": false // 在文件资源管理器中删除文件时,不再弹出确认提示框
}

5)复制上面代码到第四步打开的 settings.json 文件中。

6)点击底部状态栏的 Watch Sass 来开启此插件。

备注:相关的 VSCode 插件还有 scss-to-css。

第二章:基础语法

一、注释

语法

单行注释:编译后,单行注释不在 CSS 中

scss
// 单行注释

多行注释

scss
/*
  多行注释
*/

文档注释

scss
/// 文档注释

保留注释

如果是压缩输出模式,那么注释也会被去掉,这个时候可以在多行注释的第一个字符书写一个 ! ,此时即便是在压缩模式,这条注释也会被保留,通常用于添加版权信息。

scss
/*!
  该 CSS 作者 XXX
  创建于 xxxx年xx月xx日
*/
.test{
  width: 300px;
}

二、选择器的书写

1. 选择器嵌套

1)后代选择器 / 基础选择器
scss
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

SCSS 选择器嵌套它是一层层的父元素里面套着子元素,这样有助于后期代码的维护。

css
nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
nav li {
  display: inline-block;
}
nav a {
  display: block;
  padding: 6px 12px;
  text-decoration: none;
}
2)并集选择器 / 列表选择器
scss
.alert, .warning {
  ul, p {
    margin-right: 0;
    margin-left: 0;
    padding-bottom: 0;
  }
}
css
.alert ul, .alert p, .warning ul, .warning p {
  margin-right: 0;
  margin-left: 0;
  padding-bottom: 0;
}
3)结构选择器 / 选择器组合器
scss
ul > {
  li {
    list-style-type: none;
  }
}

h2 {
  + p {
    border-top: 1px solid gray;
  }
}

p {
  ~ span {
    opacity: 0.8;
  }
}
css
ul > li {
  list-style-type: none;
}

h2 + p {
  border-top: 1px solid gray;
}

p ~ span {
  opacity: 0.8;
}

2. 父选择器 &

在嵌套 CSS 规则时,有时也需要直接使用嵌套外层的父选择器,例如,当给某个元素设定 hover 样式时,或者当 body 元素有某个 classname 时,可以用 & 代表嵌套规则外层的父选择器。

总结:& 将被替换成嵌套外层的父选择器,如果含有多层嵌套,最外层的父选择器会一层一层向下传递。

例如有这么一段样式:

css
.container {width: 1200px;margin: 0 auto}
.container a {color: #333;}
.container a:hover {text-decoration: underline;color: #f00;}
.container .top {border: 1px #f2f2f2 solid;}
.container .top-left {float: left; width: 200px;}

用 SCSS 编写:

带伪类选择器的元素我们可以把它放到它的父类下面,但不能直接放,我们需要在伪类的前面放 & 。

以下面代码为例,.top 下的 &-left 代表的 .top-left,这是 & 代表 .top

scss
.container {
  width: 1200px;
  margin: 0 auto;
  a {
    color: #333;
    &:hover {
      text-decoration: underline;
      color: #f00;
    }
    .top {
      border: 1px #f2f2f2 solid;
      &-left {
        float:left;
        width: 200px;
      }
    }
  }
}

3. 属性选择器

有些 CSS 属性遵循相同的命名空间(namespace),比如 font-family,font-size,font-weight 都以 font 作为属性的命名空间。为了便于管理这样的属性,同时也为了避免了重复输入,Sass 允许将属性嵌套在命名空间中。

CSS 写法:

css
.container a {
  color: #333;
  font-size: 14px;
  font-family: sans-serif;
  font-weight: bold;
}

SCSS 写法:

scss
.container {
  a {
    color: #333;
    font: {
      size: 14px;
      family: sans-serif;
      weight: bold;
    }
  }
}

命名空间也可以包含自己的属性值,例如:

scss
.funky {
  font: 20px/24px {
    family: fantasy;
    weight: bold;
  }
}

编译为:

css
.funky {
  font: 20px/24px;
  font-family: fantasy;
  font-weight: bold;
}

注意:font: 后面要加一个空格。

4. 占位符选择器 %foo 必须通过 @extend

有时,需要定义一套样式并不是给某个元素用,而是只通过 @extend 指令使用。尤其是在制作 Sass 样式库的时候,希望 Sass 能够忽略用不到的样式。定义的样式在不使用的情况下不会编译出来。

例如有这样一段编译前样式:

scss
.buttom%base {
  display: inline-block;
  margin-bottom: 0;
  border: 1px solid red; 
}

.btn-default {
  @extend %base;
  color: #faa;
  width: 60px;
}

.btn-success {
  @extend %base;
  color: #faa;
  width: 60px;
}

编译后

css
.buttom.btn-default, .buttom.btn-success {
  display: inline-block;
  margin-bottom: 0;
  border: 1px solid red;
}

.btn-default {
  color: #faa;
  width: 60px;
}

.btn-success {
  color: #faa;
  width: 60px;
}

三、插值语句 #{}

类似 ES6 的模板字符串。

css
p {
  font: 16px/30px Arial,Helvetia,sans-serif;
}

如果需要使用变量,同时又要确保不做除法运算,而是完整的编译到 CSS 文件中,只需要用 #{} 插值语句将变量包裹。

scss
$font-size: 16px;
$height: 30px;

p {
  font: #{$font-size}/#{$height};
}

四、流程控制指令

其他指令见第四章。

1. @if 和 @else

scss
.container{
  // 第一种
  @if(/* 条件 */){
    // ...
  }

  // 第二种
  @if(/* 条件 */){
    // ...
  }@else{
    // ...
  }

  // 第三种
  @if(/* 条件 */){
    // ...
  }@else if(/* 条件 */){
    // ...
  }@else{
    // ...
  }
}

例如:

scss
%triangle {
  width: 0px;
  height: 0px;
  display: inline-block;
}

@mixin triangle($direction: top, $size: 30px, $border-color: black) {
  border-width: $size;
  border-#{$direction}-width: 0;
  @if ($direction==top) {
    border-color: transparent transparent $border-color transparent;
    border-style: dashed dashed solid dashed;
  } @else if ($direction==right) {
    border-color: transparent transparent transparent $border-color;
    border-style: dashed dashed dashed solid;
  } @else if ($direction==bottom) {
    border-color: $border-color transparent transparent transparent;
    border-style: solid dashed dashed dashed;
  } @else if ($direction==left) {
    border-color: transparent $border-color transparent transparent;
    border-style: dashed solid dashed dashed;
  }
}

.p0 {
  @extend %triangle;  // 继承
  @include triangle(right,30px,#aff); // 混入
}
.p1 {
  @extend %triangle;
  @include triangle(bottom,30px,#afa);
}
.p2 {
  @extend %triangle;
  @include triangle(left,30px,#faa);
}
.p3 {
  @extend %triangle;
  @include triangle(right,30px,#aaf);
}

使用

html
<p class="p0"></p>
<p class="p1"></p>
<p class="p2"></p>
<p class="p3"></p>

2. @for

1)语法

@for 指令可以在限制的范围内重复输出格式,每次按照要求(变量的值)对输出结果做出变动。这个指令包含两种格式:

  • @for <$var> from <start> to <end> { ... }

  • @for <$var> from <start> through <end> { ... }

区别在于 through 与 to 的含义:

  • 当使用 through 时,遍历范围 [start, end]
  • 而是用 to 时,遍历范围 [start, end)
  • <start><end> 必须是整数值。
2)例子

例 1

scss
@for $i from 1 to 4 { // 生成 .p1 到 .p3
  .p#{$i} {
    width: 10 * $i;
    height: 30px;
    background-color: red;
  }
}

@for $i from 1 through 3 { // 生成 .p1 到 .p3
  .p#{$i} {
    width: 10 * $i;
    height: 30px;
    background-color: red;
  }
}

例 2

scss
@keyframes loading {
  0% {
    opacity: 0.3;
    transform: translateY(0px);
  }
  50% {
    opacity: 1;
    transform: translateY(-20px);
    background: green;
  }
  100% {
    opacity: 0.3;
    transform: translateY(0px);
  }
}
#loading {
  position: fixed;
  top: 200px;
  left: 46%;
}
#loading span {
  position: absolute;
  width: 20px;
  height: 20px;
  background: #3498db;
  opacity: 0.5;
  border-radius: 50%;
  animation: loading 1s infinite ease-in-out;
}

@for $i from 1 to 6 {
  #loading span:nth-child(#{$i}) {
    left: 20 * ($i - 1) + px;
    // animation-delay: 20 * ($i - 1) / 100 + s;
    animation-delay: unquote(
      $string: "0." +
        (
          $i - 1,
        ) *
        2 +
        s
    );
  }
}

使用

html
<div id="loading">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

3. @each

@each 指令的格式是 $var in <list>$var 可以是任何变量名,比如 $length 或者 $name,而 <list> 是一连串的值,也就是值列表。

例如做如下效果

普通 CSS 的写法

css
p {
  width: 10px;
  height: 10px;
  display: inline-block;
  margin: 10px;
  z-index: 5;
}
.p0 {
  background-color: red;
}
.p1 {
  background-color: green;
}
.p2 {
  background-color: blue;
}
.p3 {
  background-color: turquoise;
}
.p4 {
  background-color: darkmagenta;
}

Sass 的写法:

scss
$var: 10;

p {
  width: 10px;
  height: 10px;
  display: inline-block;
  margin: 10px;
  z-index: $var - 5;
}

$color-list: red green blue turquoise darkmagenta;

@each $color in $color-list {
  $index: index($color-list, $color);
  .p#{$index - 1} {
    background-color: $color;
  }
}

4. @while

1)语法

@while 指令重复输出格式直到表达式返回结果为 false。这样可以实现比 @for 更复杂的循环。格式:@while <expression> { ... }

2)例子

用 scss 实现 bootstrap 中 css 的这么一段代码。

http://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.css

css
.col-sm-12 {
  width: 100%;
}
.col-sm-11 {
  width: 91.66666667%;
}
.col-sm-10 {
  width: 83.33333333%;
}
.col-sm-9 {
  width: 75%;
}
.col-sm-8 {
  width: 66.66666667%;
}
.col-sm-7 {
  width: 58.33333333%;
}
.col-sm-6 {
  width: 50%;
}
.col-sm-5 {
  width: 41.66666667%;
}
.col-sm-4 {
  width: 33.33333333%;
}
.col-sm-3 {
  width: 25%;
}
.col-sm-2 {
  width: 16.66666667%;
}
.col-sm-1 {
  width: 8.33333333%;
}

用 @while 实现。

scss
@use "sass:string";

$column: 12;

@while $column>0 {
  .col-sm-#{$column} {
    width: $column / 12 * 100%;
    width: $column / 12 * 100#{"%"};
    // width: $column / 12 * 100 + %; 会报错
    // width: unquote($string: $column / 12 * 100 + "%"); 过时
    width: string.unquote($string: calc($column / 12) * 100 + "%");
  }
  $column: $column - 1;
}

5. 元运算符

if(expression, value1, value2)

例子:

scss
p {
  color: if(1+1==2, green, yellow);
}

div {
  color: if(1+1==3, green, yellow);
}
css
p {
  color: green;
}

div {
  color: yellow;
}

第三章:Sass Script

一、变量

1. 声明

  • 变量以美元符号 ($) 开头,后面跟变量名;
  • 变量名是不以数字开头的,可包含字母、数字、下划线、横线(连接符);
  • 变量值写法同 css,即变量名和值之间用冒号 (😃 分隔;
  • 变量一定要先定义后使用;
scss
$color: #faa;
$border-color: #afa;
$border-width: 1px;

.container {
  color: $color;
  border-color: $border-color;
}

注意

通过连接符与下划线定义的同名变量为同一变量,建议使用连接符。

scss
$font-size: 14px;
$font_size: 16px;
.container{font-size: $font-size;} // 16px

2. 作用域

1)局部变量

定义:在选择器内容定义的变量,只能在选择器范围内使用。

scss
.container {
  $font-size:14px;
  font-size: $font-size;
}
2)全局变量

定义:定义后能全局使用的变量。

第一种:在选择器外面的最前面定义的变量

scss
$font-size: 16px;

.container {
  font-size: $font-size;
}
.footer {
  font-size: $font-size;
}

第二种:使用 !global 标志定义全局变量

scss
.container {
  $font-size: 16px !global;
  font-size: $font-size;
}

.footer {
  font-size: $font-size;
}

3. 数据类型

SCSS 支持 7 种主要的数据类型。

  • 数字:16, 10px, 30%
  • 字符串:有引号字符串与无引号字符串,"foo", 'bar', baz
  • 颜色:blue, #04a3f9, rgba(255,0,0,0.5)
  • 布尔型:true, false
  • 空值:null
  • 数组(list):用空格或逗号做分隔符,1.5em 1em 0 2em, Helvetica, Arial, sans-serif
  • 字典(maps):相当于 JavaScript 的 object,(key1: value1, key2: value2)
scss
$layer-index: 10; // 数字不带单位
$border-width: 3px; // 数字带单位

$font-base-family: "Open Sans", 'Helvetica', Sans-Serif; // 字符串

$top-bg-color: rbga(255, 147, 29, 0.6); // 颜色

$blank-mode: true;	//布尔

$var: null; // 值 null 是其类型的唯一通道。它表示缺少值,通常由函数返回,以指示缺少结果。

// 数组
$color-map: 1px 2px, 5px 6px; // 包含 1px 2px 与 5px 6px 两个数组的数组

$fonts: ( // map
  color1: #fa0000,
  color2: #fbe200,
  color3: #95d7eb,
  serif: "Helvetica Neue",
  monospace: "Consolas",
);

使用

scss
.container {
  $font-size: 16px !global;
  font-size: $font-size;
  @if $blank-mode {
    background-color: map-get($color-map, color1);
  } @else {
    background-color: map-get($color-map, color2);
  }
  content: type-of($var);
  content: length($var);
  color: map-get($color-map, color3);
}

.footer {
  font-size: $font-size;
}

// 如果列表中包含空值,则生成的 CSS 中将忽略该空值。
.wrap {
  font: 18px bold map-get($fonts, "sans");
}

值列表:可以用空格或逗号分隔,并且可以用方括号括起来或根本不加括号。例如 1.5em 1em 0 2emHelvetica, Arial, sans-serif[col1-start]

4. 默认值

scss
$color: #333;
// 如果 $color 之前没定义就使用如下的默认值
$color: #666 !default;

.container {
  border-color: $color;
}

二、运算符

1. 等号操作符

所有数据类型均支持相等运算 ==!=,此外,每种数据类型也有其各自支持的运算方式。

符号说明
==等于
!=不等于

比较规则

  • 如果数字具有相同的值和相同的单位,或者它们的单位相互转换后的值相等,则数字相等。
  • ……

2. 关系或比较运算符

符号说明
< (lt)小于
> (gt)大于
<= (lte)小于等于
>= (gte)大于等于

例子

scss
// 单位相同或无单位
@debug 100 > 50; // true
@debug 10px < 17px; // true

// 无单位的会转为参与比较的单位
@debug 100 > 50px; // true
@debug 10px < 17; // true

// 单位不兼容的会报错

3. 逻辑运算符

符号说明
and逻辑与
or逻辑或
not逻辑非

例 1:

scss
@debug not true; // false
@debug not false; // true

@debug true and true; // true
@debug true and false; // false

@debug true or false; // true
@debug false or false; // false

4. 数字操作符

符号说明
+
-
*
%取模

注意:Sass 中的除法是通过 math.div() 函数完成的。

注意

数字类型包括:纯数字、百分号、css 部分单位(px、pt、in...)

% 与 单位不能一起运算

纯数字与百分号或单位运算时会自动转换成相应的百分号与单位值

以下三种情况将被视为除法运算符号

  • 如果值或值的一部分,是变量或者函数的返回值
  • 如果值被圆括号包裹
  • 如果值是算数表达式的一部分

scss
/* ========== / 运算 ============ */
$width: 100px;
div{
  font: 16px/30px Arial,Helvetica, sans-serif; //不运算
  width: (10/5); // 使用了小括号
  width: $width / 10; //使用变量与括号
  width: round(50) /2; //使用了函数
  width: 50px / 10 + 50px; // 使用了 + 表达式
}

如果需要使用变量,同时又要确保/不做除法运算而是完整的编译到 CSS 文件中,只需要用 #{} 插值语句将变量包裹。

5. 字符串运算符

  • <expression> + <expression> 返回包含两个表达式值的字符串。

    scss
    @debug "Helvetica" + " Neue"; // "Helvetica Neue"
    @debug "Helvetica" + Neue; // "HelveticaNeue"
    @debug Helvetica + " Neue"; // Helvetica Neue
    @debug Helvetica + Neue; // HelveticaNeue

    注意:如果有引用字符串(位于 + 左侧)连接无引号字符串,运算结果是有引号的;无引号字符串(位于 + 左侧)连接有引号字符串,运算结果则没有引号。

  • <expression> - <expression> 返回一个不带引号的字符串,其中包含两个表达式的值,并用 - 分隔。这是一个旧版运算符,通常应使用插值法来代替。

    scss
    @debug sans- + serif; // sans-serif
    @debug sans- + "serif" // sans-serif
    @debug sans - serif; // sans-serif

6. 布尔运算符

scss
@debug not true; // false
@debug not false; // true

@debug true and true; // true
@debug true and false; // false

@debug true or false; // true
@debug false or false; // false

第四章:@-Rules 与指令

一、导入样式

1. @use

@use 加载的样式表称为“模块”。

scss
// 若文件后缀为 scss、sass、css 时, 可省略
@use 'src/corners.scss'; // 默认命名空间为文件名 corners
@use "src/corners.scss" as c; // 命名空间为 c
@use "src/corners.scss" as *; // 无命名空间, 直接就能用里面的内容

2. @forward

scss
// 用法与 @use 大致一致, 就是不能在本文件中使用, 需要在其他文件通过 @use 引入本文件才行
@forward 'src/corners.scss';
@forward "src/list" as list-*;

例子参考地址

3. @import

从 Dart Sass 1.80.0 开始,@import 规则是已弃用,并将在 Dart Sass 3.0.0 中从语言中删除。

1)基本使用

Sass 扩展了 @import 的功能,允许其导入 Scss 或 Sass 文件。被导入的文件将合并编译到同一个 CSS 文件中,另外,被导入的文件中所包含的变量或混合指令 (mixin) 都可以再导入的文件中使用。

例子

public.scss

scss
$font-base-color: #333;

index.scss 里面使用

scss
@import "public";

$color:#666;

.container {
  border-color: $color;
  color:$font-base-color;
}

注意:跟普通 css 里面 @import 的区别。

如以下几种方式,都将作为普通的 css 语句,不会导入任何 SCSS 文件

  1. 文件扩展名是 .CSS;
  2. 文件名以 http:// 开头;
  3. 文件名是 url();
  4. @import 包含 media queries。
scss
@import "public.css";
@import url(public);
@import "http://xxx.com/xxx";
@import 'landscape' screen and (orientation:landscape);
2)局部文件 (Partials)

SCSS 文件夹下的 SCSS 文件都会被监听且自动编译,取消编译的方法在文件夹名称前加下划线 (_) ,否则文件会被自动编译。

scss
@import "_public.scss" // 导入的时候可以不加下划线和文件后缀 例:@import "public"

注意:不可以同时存在添加下划线与未添加下划线的同名文件,添加下划线的文件将会被忽略。

3)嵌套 @import

把局部的 SCSS 文件嵌套在选择器里面。

假设 example.scss 文件包含以下样式:

scss
.example {
  color: red;
}

然后导入到 #main 样式内

scss
#main {
  @import "example";
}

将会被编译为

css
#main .example {
  color: red;
}

注意:不可以在混合指令 (mixin) 或控制指令 (control directives) 中嵌套 @import

二、混合指令

混合指令 (Mixin Directives) 用于定义可重复使用的样式。混合指令可以包含所有的 CSS 规则、绝大部分 SCSS 规则,甚至通过参数功能引入变量,输出多样化的样式。

1. 定义混合指令 @mixin

1)语法

混合指令的用法是在 @mixin 后添加名称与样式。

scss
@mixin mixin-name() {
  /* CSS 声明 */
}
2)使用例子

标准形式

scss
// 定义页面一个区块基本的样式
@mixin block () {
  width: 96%;
  margin-left: 2%;
  border-radius: 8px;
  border: 1px #f6f6f6 solid;
}

// 使用混入
.container {
  .block {
    @include block;
  }
}

参数形式

scss
// 定义 flex 布局元素纵轴的排列方式
@mixin flex-align($aitem) {
  --webkit-box-align: $aitem;
  --webkit-align-items: $aitem;
  --ms-flex-align: $aitem;
  align-items: $aitem;
}

// 使用
.container {
  @include flex-align(center)
}

可选参数 / 默认值

scss
@mixin replace-text($image, $x: 50%, $y: 50%) {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;

  background: {
    image: $image;
    repeat: no-repeat;
    position: $x $y;
  }
}

.mail-icon {
  @include replace-text(url("/images/mail.svg"), 0);
}
css
.mail-icon {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;
  background-image: url("/images/mail.svg");
  background-repeat: no-repeat;
  background-position: 0 50%;
}

关键字参数 / 指定参数

scss
// 定义块元素内边距
@mixin block-padding($top,$right,$bottom,$left) {
  padding-top: $top;
  padding-right: $right;
  padding-bottom: $bottom;
  padding-left: $left;
}

// 使用
.container {
  @include block-padding($top:12px,$right:50px,$bottom:30px,$left:0px)
}

注意:参数名和参数的数量要对应,参数顺序可调整。

可变参数

参数不固定的情况

scss
/*
  定义线性渐变
  @param $direction  方向
  @param $gradients  颜色过渡的值列表
*/
@mixin linear-gradient($direction,$gradients...) {
  background-color: nth($gradients, 1);
  background-image: linear-gradient($direction,$gradients);
}

.container {
  @include linear-gradient(to right, orange, yellow)
}

2. 引用混合样式 @include

3. @content

@content 表示占位的意思,在使用混合指令的时候,会将指令大括号里面的内容放置到 @content 的位置,有点类似于插槽。

例子:

scss
@mixin button-theme($color) {
  background-color: $color;
  border: 1px solid darken($color, 15%);

  &:hover {
    background-color: lighten($color, 5%);
    border-color: darken($color, 10%);
  }

  @content
};


.button-primary {
  @include button-theme(#007bff){
    width: 500px;
    height: 400px;
  }
}

.button-secondary {
  @include button-theme(#6c757d){
    width: 300px;
    height: 200px;
  }
}
css
.button-primary {
  background-color: #007bff;
  border: 1px solid #0056b3;
  width: 500px;
  height: 400px;
}
.button-primary:hover {
  background-color: #1a88ff;
  border-color: #0062cc;
}

.button-secondary {
  background-color: #6c757d;
  border: 1px solid #494f54;
  width: 300px;
  height: 200px;
}
.button-secondary:hover {
  background-color: #78828a;
  border-color: #545b62;
}

最后我们需要说一先关于 @content 的作用域的问题。

在混合指令的局部作用域里面所定义的变量不会影响 @content 代码块中的变量,同样,在 @content 代码块中定义的变量不会影响到混合指令中的其他变量,两者之间的作用域是隔离的

scss
@mixin scope-test {
  $test-variable: "mixin";

  .mixin{
    content: $test-variable
  }

  @content
};

.test {
  $test-variable: "test";
  @include scope-test {
    .content {
      content : $test-variable
    }
  }
}
css
.test .mixin {
  content: "mixin";
}
.test .content {
  content: "test";
}

三、@extend

在设计网页的时候通常遇到这样的情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式。通常会在 HTML 中给元素定义两个 class,一个通用样式,一个特殊样式。

1. 问题引入

标记说明
info信息!请注意这个信息。
success成功!很好地完成了提交
warning警告!请不要提交。
danger错误!请进行一些更改。

所有按钮的基本样式(风格、字体大小、内边距、边框等 ...),我们通常会定义一个通用 alert 样式。

css
.alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 12px;
}

不同警告单独风格

css
.alert-info {
  color: #31708f;
  background-color: #d9edf7;
  border-color: #bce8f1;
}

.alert-success {
  color: #3c763d;
  background-color: #dff0d8;
  border-color: #d6e9c6;
}

.alert-warning {
  color: #8a6d3b;
  background-color: #fcf8e3;
  border-color: #faebcc;
}

.alert-danger {
  color: #a94442;
  background-color: #f2dede;
  border-color: #ebccd1;
}

2. 使用继承 @extend 改进

scss
.alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 12px;
}

.alert-info {
  @extend .alert;
  color: #31708f;
  background-color: #d9edf7;
  border-color: #bce8f1;
}

.alert-success {
  @extend .alert;
  color: #3c763d;
  background-color: #dff0d8;
  border-color: #d6e9c6;
}

.alert-warning {
  @extend .alert;
  color: #8a6d3b;
  background-color: #fcf8e3;
  border-color: #faebcc;
}

.alert-danger {
  @extend .alert;
  color: #a94442;
  background-color: #f2dede;
  border-color: #ebccd1;
}

3. 使用多个 @extend

定义两个类

scss
.alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 12px;
}

.important {
  font-weight: bold;
  font-size: 14px;
}

使用

scss
.alert-danger {
  @extend .alert;
  @extend .important;
  color: #afa;
  background-color: #aaf;
  border-color: #faa;
}

4. @extend 多层继承

一个类可以继承另外一个类,这个类还可以被另外一个类继承。

scss
.alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 12px;
}

.important {
  @extend .alert;
  font-weight: bold;
  font-size: 14px;
}

.alert-danger {
  @extend .important;
  color: #afa;
  background-color: #aaf;
  border-color: #faa;
}

5. 占位符 %

你可能发现被继承的 CSS 父类并没有被实际应用,也就是说 HTML 代码中没有使用该类,他的唯一目的就是扩展其他选择器。

对于该类,可能不希望被编译输出到最终的 CSS 文件中,它只会增加 CSS 文件的大小,永远不会被使用。

这就是占位符选择器的作用。

占位符选择器类似于类选择器,但是,它们不是以句点 (.) 开头,而是一百分号 (%) 开头。

当在 Sass 文件中使用占位符选择器时,它可以用于扩展其他选择器,但不会编译成最终的 CSS。

改写

scss
%alert {
  padding: 15px;
  margin-bottom: 20px;
  border: 1px solid transparent;
  border-radius: 4px;
  font-size: 12px;
}

.alert-info {
  @extend %alert;
  color: #31708f;
  background-color: #d9edf7;
  border-color: #bce8f1;
}

.alert-success {
  @extend %alert;
  color: #3c763d;
  background-color: #dff0d8;
  border-color: #d6e9c6;
}

.alert-warning {
  @extend %alert;
  color: #8a6d3b;
  background-color: #fcf8e3;
  border-color: #faebcc;
}

.alert-danger {
  @extend %alert;
  color: #a94442;
  background-color: #f2dede;
  border-color: #ebccd1;
}

四、@at-root

有些时候,我们可能会涉及到将嵌套规则移动到根级别(声明的时候并没有写在根级别)。这个时候就可以使用 @at-root

scss
.parent{
  color: red;

  @at-root .child{
    color: blue;
  }
}
css
.parent {
  color: red;
}
.child {
  color: blue;
}

如果你想要移动的是一组规则,这个时候需要在 @at-root 后面添加一对大括号,将想要移动的这一组样式放入到大括号里面

scss
.parent {
  color: red;

  @at-root {
    .child {
      color: blue;
    }
    .test {
      color: pink;
    }
    .test2 {
      color: purple;
    }
  }
}
css
.parent {
  color: red;
}
.child {
  color: blue;
}

.test {
  color: pink;
}

.test2 {
  color: purple;
}

第五章:函数指令

一、自定义函数

在 Sass 里面自定义函数的语法如下:

scss
@function fn-name($params...){
  @return XXX;
}

具体示例如下:

scss
@function divide($a, $b){
  @return $a / $b
};

.container {
  width: divide(100px, 2)
}
.container {
  width: 50px;
}

函数可以接收多个参数,如果不确定会传递几个参数,那么可以使用前面介绍过的不定参数的形式。

scss
@function sum($nums...) {
  $sum: 0;
  @each $n in $nums {
    $sum: $sum + $n;
  }
  @return $sum;
}

.box1 {
  width: sum(1, 2, 3) + px;
}

.box2 {
  width: sum(1, 2, 3, 4, 5, 6) + px;
}
.box1 {
  width: 6px;
}

.box2 {
  width: 21px;
}

最后我们还是来看一个实际开发中的示例:

scss
// 根据传入的 $background-color 返回适当的文字颜色
@function contrast-color($background-color) {
  // 计算背景颜色的亮度
  $brightness: red($background-color) * 0.299 + green($background-color) * 0.587 + blue($background-color) * 0.114;

  // 根据亮度来返回黑色或者白色的文字颜色
  @if $brightness > 128 {
    @return #000;
  } @else {
    @return #fff;
  }
}

.button {
  $background-color: #007bff;
  background-color: $background-color;
  color: contrast-color($background-color);
}

在上面的代码示例中,我们首先定义了一个名为 contrast-color 的函数,该函数接收一个背景颜色参数,函数内部会根据这个背景颜色来决定文字应该是白色还是黑色。

scss
.button {
  background-color: #007bff;
  color: #fff;
}

二、内置函数

更多函数列表参见:https://sass-lang.com/documentation/modules

中文文档:https://sass.nodejs.cn/documentation/modules

1. color (颜色函数)

RGB 函数

函数名和参数类型函数作用
rgb(red, green, blue)返回一个 16 进制颜色值
rgba(red, green, blue, alpha)返回一个 rgba;red, green 和 blue 可被当作一个整体以颜色单词、hsl、rgb 或 16 进制形式传入
red($color)从 $color 中获取其中红色值
green($color)从 $color 中获取其中绿色值
blue($color)从 $color 中获取其中蓝色值
mix(color1, color2, weight?)按照 weight 比例,将 color1 和 color2 混合为一个新颜色

HSL 函数

函数名和参数类型函数作用
hsl(hue,saturation,$lightness)通过色相(hue)、饱和度(saturation)和亮度(lightness)的值创建一个颜色
hsla(hue,saturation,lightness,alpha)通过色相(hue)、饱和度(saturation)、亮度(lightness)和透明(alpha)的值创建一个颜色
saturation($color)从一个颜色中获取饱和度(saturation)值
lightness($color)从一个颜色中获取亮度(lightness)值
adjust-hue(color,degrees)通过改变一个颜色的色相值,创建一个新的颜色
lighten(color,amount)通过改变颜色的亮度值,让颜色变亮,创建一个新的颜色
darken(color,amount)通过改变颜色的亮度值,让颜色变暗,创建一个新的颜色
hue($color)从一个颜色中获取亮度色相(hue)值

Opacity 函数

函数名和参数类型函数作用
alpha(color) / opacity(color)获取颜色透明度值
rgba(color, alpha)改变颜色的透明度
opacify(color, amount) / fade-in(color, amount)使颜色更不透明
transparentize(color, amount) / fade-out(color, amount)使颜色更加透明

Sass 包含很多操作颜色的函数。例如:lighten() 与 darken() 函数可用于调亮或调暗颜色

例如:

scss
p {
  height: 30px;
}
.p0 {
  background-color: #5c7a29;
}
.p1 {
  /* 
    让颜色变亮
    lighten($color, $amount)
    $amount 的取值在 0%-100% 之间
  */
  background-color: lighten(#5c7a29, 30%);
}
.p2 {
  // 让颜色变暗, 通常使用color.scale()代替该方案
  background-color: darken($color: #5c7a29, $amount: 15%);
}
.p3 {
  // 降低颜色透明度, 通常使用color.scale(5c7a29)代替该方案
  // background-color:opacify(#5c7a29,0.5);
  background-color: opacify(rgba(#5c7a29, 0.1), 0.5);  // 里面小数相加不能 >=1 否则无效
}

使用

html
<p class="p0"></p>
<p class="p1"></p>
<p class="p2"></p>
<p class="p3"></p>

2. String (字符串函数)

函数名和参数类型函数作用
quote($string)添加引号
unquote($string)除去引号
to-lower-case($string)变为小写
to-upper-case($string)变为大写
str-length($string)返回 $string 的长度 (汉字算一个)
str-index(string,substring)返回 substring 在 string 的位置
str-insert(string,insert, $index)在 string 的 index 处插入$insert
str-slice(string,start-at, $end-at)截取 string 的 start-at 和 $end-at 之间的字符串

注意:索引是从 1 开始的,如果书写 -1,那么就是倒着来的;两边都是闭区间。

例子

scss
$str: "Hello world!";

.slice1{
  content: str-slice($str, 1, 5)
}

.slice2{
  content:str-slice($str, -1)
}

编译为:

css
.slice1 {
  content: "Hello";
}

.slice2 {
  content: "!";
}

3. Math (数学函数)

函数名和参数类型函数作用
percentage($number)转换为百分比形式
round($number)四舍五入为整数
ceil($number)数值向上取整
floor($number)数值向下取整
abs($number)获取绝对值
min($number...)获取最小值
max($number...)获取最大值
random($number?:number)不传入值:获得 0-1 的随机数;传入正整数 n:获得 0-n 的随机整数(左开右闭)

例子:

scss
p {
  z-index: abs($number: -16); // 取整 16
  z-index: max(6,12,8,35); // 最大值 35
  z-index: ceil(6.8); // 向上取整 7
  opacity: random();  // 随机数 0-1
}

4. List (数组函数)

函数名和参数类型函数作用
length($list)获取数组长度
nth($list, n)获取指定下标的元素
set-nth(list, n, $value)向 list 的 n 处插入$value
join(list1, list2, $separator)拼接 list1 和 list2;separator 为新 list 的分隔符,默认为 auto,可选择 comma、space
append(list, val, $separator)向 list 的末尾添加 val;$separator 为新 list 的分隔符,默认为 auto,可选择 comma、space
index(list, value)返回 value 值在 list 中的索引值
zip($lists…)将几个列表结合成一个多维的列表;要求每个的列表个数值必须是相同的

例子:

scss
p {
  z-index: length(12px); // 1
  z-index: length(12px 5px 8px); // 3
  z-index: index(a b c d, c); // 3
  padding: append(10px 20px, 30px); // 10px 20px 30px
  color: nth($list: red blue green, $n: 2); // blue
}

5. Map (字典函数)

函数名和参数类型函数作用
map-get(map, key)获取 map 中 key 对应的 $value
map-merge(map1, map2)合并 map1 和 map2,返回一个新 $map
map-remove(map, key)从 map 中删除 key,返回一个新 $map
map-keys($map)返回 map 所有的 key
map-values($map)返回 map 所有的 value
map-has-key(map, key)判断 map 中是否存在 key,返回对应的布尔值
keywords($args)返回一个函数的参数,并可以动态修改其值

例子:

scss
$font-size: (
  "small": 12px,
  "normal": 18px,
  "large": 24px,
);
$padding: (
  top: 10px,
  right: 20px,
  bottom: 10px,
  left: 30px,
);

p {
  font-size: map-get($font-size, "normal"); // 18px
  @if map-has-key($padding, "right") {
    padding-right: map-get($padding, "right");
  }
  &:after {
    content: map-keys($font-size) + " " + map-values($padding) + "";
  }
}

6. selector (选择器函数)

选择符相关函数可对CSS选择进行一些相应的操作,例如:selector-append()可以把一个选择符附加到另一个选择符,selector-unify()将两组选择器合成一个复合选择器。

例子:

scss
.header {
  background-color: #000;
  content: '' + selector-append(".a",".b",".c");
  content: selector-unify("a",".disabled") + '';
}

7. meta (自检函数)

函数名和参数类型函数作用
type-of($value)返回 $value 的类型
unit($number)返回 $number 的单位
unitless($number)判断 $number 是否没用带单位,返回对应的布尔值,没有带单位为 true
comparable($number1, $number2)判断 $number1$number2 是否可以做加、减和合并,返回对应的布尔值
variable-exists()检查当前作用域中是否存在某个变量
mixin-exists()检查某个 mixin 是否存在

例子:

scss
$color:#f00;
@mixin padding($left:0,$top:0,$right:0,$bottom:0) {
  padding: $top $right $bottom $left;
}

.container{
  @if variable-exists(color){
    color: $color;
  }
  @else{
    content: "$color不存在";
  }
  @if mixin-exists(padding) {
    @include padding($left:10px, $right:10px)
  }
}

自检函数通常用在代码的调试上

preview
图片加载中
预览

Released under the MIT License.