SCSS 3.5.5 简明上手指南
Sass
是成熟、稳定、强大的CSS
预处理器,截止到目前为止已经发展有 10 年,前当最新 release
版本为3.5.5
。而SCSS是Sass3版本当中引入的新语法特性,完全兼容
CSS3 的同时继承了Sass强大的动态功能。本文翻译自《Sass Guide》和《Sass
Syntactically Awesome
StyleSheets》两篇官方文档,讲解了现代化前端开发当中经常使用的 SCSS
语法特性,便于开发小组的同学快速上手。
基于 Gulp 完成前端自动化的年代,出于快速上手以及 npm 安装方便的考虑,开发团队一直沿用Less和gulp-less作为 CSS 预处理工具,但是 Sass 提供了更加丰富的动态语法特征,因此逐步淘汰基于 Gulp 的beaver前端项目脚手架以后,新项目全部基于 Webpack 的node-sass和sass-loader作为预处理工具。Sass 和 Less 的详细比较可以参考sass-vs-less和Sass 与 Less 比拼两篇文章,里面对两者的优劣做了非常详实的比较。
安装
如果正在使用的是 Linux 发行版,需要通过apt
安装Ruby以及build-essential
。
1 | ➜ sudo apt-get install ruby-dev |
命令行当中可以通过sass input.scss output.css
编译 Scss
文件,也可以添加--watch
选项监听 Scss 文件的变化:
1 | ➜ sass --watch input.scss:output.css |
如果多个 Scss 文件放置在一个目录当中,也可以对整个文件目录进行监听。
1 | ➜ sass --watch app/sass:public/stylesheets |
通过-i
或者--interactive
可以运行一个交互式的
SassScript 命令行。
1 | ➜ sass -i |
通过
sass --help
获取更多关于 SASS 在命令行的使用方法。
编码规则
SASS 首先会检查代码文件的 Unicode BOM(byte order
mark),然后是@charset
声明,再然后是底层运行 Ruby
的字符串编码,如果这些都未进行设置,将会默认以UTF-8编码输出
CSS 文件。
1 | @charset 'utf-8'; |
建议代码开头位置显式定义
@charset "encoding-name";
,让 SASS 能够按照给定的字符集编译输出。
特性概览
CSS 书写代码规模较大的 Web 应用时,容易造成选择器、层叠的复杂度过高,因此推荐通过 SASS 预处理器进行 CSS 的开发,SASS 提供的变量、嵌套、混合、继承等特性,让 CSS 的书写更加有趣与程式化。
变量
变量用来存储需要在 CSS 中复用的信息,例如颜色和字体。SASS
通过$
符号去声明一个变量。
1 | $font-stack: Helvetica, sans-serif; |
上面例子中变量$font-stack
和$primary-color
的值将会替换所有引用他们的位置。
1 | body { |
嵌套
SASS 允许开发人员以嵌套的方式使用 CSS,但是过度的使用嵌套会让产生的 CSS 难以维护,因此是一种不好的实践,下面的例子表达了一个典型的网站导航样式:
1 | nav { |
大家注意上面代码中的ul
、li
、a
选择器都被嵌套在nav
选择器当中使用,这是一种书写更高可读性
CSS 的良好方式,编译后产生的 CSS 代码如下:
1 | nav ul { |
引入
SASS 能够将代码分割为多个片段,并以 underscore
风格的下划线作为其命名前缀(_partial.scss),SASS
会通过这些下划线来辨别哪些文件是SASS
片段,并且不让片段内容直接生成为 CSS
文件,从而只是在使用@import
指令的位置被导入。CSS
原生的@import
会通过额外的 HTTP 请求获取引入的样式片段,而
SASS 的@import
则会直接将这些引入的片段合并至当前 CSS
文件,并且不会产生新的 HTTP
请求。下面例子中的代码,将会在base.scss
文件当中引入_reset.scss
片断。
1 | // _reset.scss |
SASS
中引入片断时,可以缺省使用文件扩展名,因此上面代码中直接通过@import 'reset'
引入,编译后生成的代码如下:
1 | html, |
SASS 片断使用下划线前缀命名,主要用于
SASS命令行工具
watch 指定目录源码的场景;如果使用 Webpack 等打包工具则毋须顾虑该问题,CSS 样式将会通过 Webpack 加载器,按照 ES6 风格的import
或 Webpack 插件extract-text-webpack-plugin
进行打包和模块化。
混合
混合(Mixin)用来分组那些需要在页面中复用的 CSS
声明,开发人员可以通过向 Mixin
传递变量参数来让代码更加灵活,该特性在添加浏览器兼容性前缀的时候非常有用,SASS
目前使用@mixin name
指令来进行混合操作。
1 | @mixin border-radius($radius) { |
上面的代码建立了一个名为border-radius
的
Mixin,并传递了一个变量$radius
作为参数,然后在后续代码中通过@include border-radius(10px)
使用该
Mixin,最终编译的结果如下:
1 | .box { |
继承
继承是 SASS
中非常重要的一个特性,可以通过@extend
指令在选择器之间复用
CSS 属性,并且不会产生冗余的代码,下面例子将会通过 SASS
提供的继承机制建立一系列样式:
1 | // 这段代码不会被输出到最终生成的CSS文件,因为它没有被任何代码所继承。 |
上面代码将.message
中的 CSS
属性应用到了.success
、.error
、.warning
上面,魔法将会发生在最终生成的
CSS 当中。这种方式能够避免在 HTML
元素上书写多个class
选择器,最终生成的 CSS
样式是下面这样的:
1 | .message, |
操作符
SASS
提供了标准的算术运算符,例如+
、-
、*
、/
、%
。在接下来的例子里,我们尝试在aside
和article
选择器当中对宽度进行简单的计算。
1 | .container { |
上面代码以960px
为基准建立了简单的流式网格布局,SASS
提供的算术运算符让开发人员可以更容易的将像素值转换为百分比,最终生成的
CSS 样式如下所示:
1 | .container { |
CSS 扩展
SASS 当中有两种可用的语法,一种是 Scss(即 SASS 化的
CSS),这是一种类 CSS
的语法,以.scss
作为源码文件后缀。另一种是
Sass,提供了比较简明的 CSS
书写方式,通过缩进而非括号来标识嵌套的选择器,以及使用换行而非分号来分隔各个属性,并使用.sass
作为代码文件的后缀。
Sass 和 Scss
两种代码风格可以通过sass-convert
命令行工具进行相互转换:
1 | ➜ sass-convert test.scss test.sass |
日常开发环境下,出于前端开发的使用习惯,更多会选择 Scss 语法,接下来的内容也将会以 Scss 风格为主。
嵌套规则
Scss 允许 CSS 规则嵌套使用,父子规则将会呈现包含选择器的关系,例如:
1 | /*===== SCSS =====*/ |
这样可以避免重复的使用父级选择器,从而达到简化 CSS 代码结构的目的,例如:
1 | /*===== SCSS =====*/ |
引用父级选择器&
Scss 使用$
关键字在 CSS
规则中引用父级选择器,例如在嵌套使用伪类选择器的场景下:
1 | /*===== SCSS =====*/ |
无论 CSS
规则嵌套的深度怎样,关键字&
都会使用父级选择器级联替换全部其出现的位置:
1 | /*===== SCSS =====*/ |
&
必须出现在复合选择器开头的位置,后面再连接自定义的后缀,例如:
1 | /*===== SCSS =====*/ |
如果在父级选择器不存在的场景使用
&
,Scss 预处理器会报出错误信息。
嵌套属性
CSS
许多属性都位于相同的命名空间(例如font-family
、font-size
、font-weight
都位于font
命名空间下),Scss
当中只需要编写命名空间一次,后续嵌套的子属性都将会位于该命名空间之下,请看下面的代码:
1 | /*===== SCSS =====*/ |
命令空间上可以直接书写CSS 简写属性,但是日常开发中建议直接书写CSS 简写属性,尽量保持 CSS 语法的一致性。
1 | .demo { |
占位符选择器%
占位符选择器与id
或者class
选择器的用法类似,只是#
和.
需要替换成为%
,占位符选择器必须通过@extend
指令进行调用。
关于占位符的讲解,请具体参考
@extend
章节。
注释
SASS 支持标准 CSS
的多行注释/* */
和单行注释//
,其中,多行注释会完整输出到编译后的
CSS 文件,而单行注释则不会。
1 | /* 多行注释 |
编译成为 CSS 的结果如下:
1 | @charset "UTF-8"; |
插值语句#{$variable}
可以应用在多行注释当中。
1 | $version: "3.5.5"; |
编译成为 CSS 的结果:
1 | @charset "UTF-8"; |
当注释中出现中文时,SASS 会自动在生成的 CSS 文件头部添加
@charset "UTF-8";
。
SassScript
SassScript 是 Sass 提供的一系列 CSS 语法扩展,允许在任意 CSS
属性上书写变量、计算以及函数。SassScript
还可以通过interpolation
插值生成选择器和属性名,这在编写mixins
时非常有用。
变量$
SassScript 通过$
关键字声明和使用一个变量。
1 | /*===== SCSS =====*/ |
SassScript
变量支持块级作用域,嵌套规则当中声明的变量都是局部变量,可以通过!global
将局部变量声明为全局变量。
1 | /*===== SCSS =====*/ |
数据类型
SassScript 支持 8 种数据类型,
- 数值 number:
1.2
,13
,10px
- 颜色
color:
blue
、#04a3f9
、rgba(255, 0, 0, 0.5)
- 布尔值
bollean:
true
、false
- 空值 null:
null
- 字符串
string:有或没有引号,
"menu"
、'sidebar'
、navbar
- 数组
list:由空格或逗号分隔,
1.5em 1em 0 2em, Helvetica, Arial, sans-serif
- 对象
map:
(key1: value1、key2: value2)
- 函数 function。
Scss 支持所有其它类型的 CSS 属性值,例如 Unicode 字符或者
!important
声明。Scss 并没有对这些类型进行特殊处理,仅仅将其视为没有使用引号的字符串。
String
CSS 支持两种类型的字符串:
- 使用单双引号的:
"Lucida Grande"
,'http://sass-lang.com'
; - 没有使用引号的:
sans-serif
,bold
;
SassScript 能够自动识别这 2 种字符串,Scss 当中使用了哪种类型字符串,就会在生成的 CSS 文件原样输出该类型字符串。
但是这里存在一个意外,当使用#{}
插值语法的时候,使用引号的字符串会失去引号,这样可以便于mixin
中使用选择器的名称。
1 | /*===== SCSS =====*/ |
List
SASS 当中可以通过 List 来表达 CSS
声明的值,例如margin: 10px 15px 0 0
或font-face: Helvetica, Arial, sans-serif
。SASS
中的 List 是一系列由空格或者逗号分隔的值,实际上单个值也可以作为一个
List,即只拥有一项元素的 List。
List 本身的作用不多,但是通过 SassScript 的 List
函数可以让它发挥更大的作用。nth
函数可以操作 List
中的每一项元素,join
函数能够合并多个 List
在一起,append
函数能够向 List
添加一项元素,而@each
指令可以为 List
当中每项元素添加样式。
除了包含简单的值,List 还可以包含其它的 List,例如拥有 2 项元素的
List1px 2px, 5px 6px
包含了1px 2px
和5px 6px
两个
List,如果内部的 List 和外部 List
拥有相同的分隔符,那么必须使用圆括号()
标识内部 List
的起始和结束位置,例如(1px 2px) (5px 6px)
。
当 List 被转换为普通 CSS 的时候,SASS
不会添加其它圆括号,这意味(1px 2px) (5px 6px)
和1px 2px 5px 6px
变成
CSS 的时候结果是相同的。但是无论如何,它们在 SASS
里是不相同的,(1px 2px) (5px 6px)
是一个拥有 2 个 List
元素的 List,1px 2px 5px 6px
则是拥有 4 个普通元素的
List。
List 里同样可以不包含任何元素,这样的 List
会被表现为()
(同样可以认为其是一个空的
map),它们并不能直接输出为 CSS。例如 SASS
编译font-family: ()
时将会发生错误。如果 List 包含一个空的
List()
或者其本身为空值null
,例如对于1px 2px () 3px
或1px 2px null 3px
当中的null
和()
将会在输出的
CSS 当中被移除。
逗号分隔的 List
尾部可能还跟着一个逗号,这在一些特殊的情况下比较有用,例如展现一个只有 1
个元素的 List,(1,)
就是一个包含了元素1
的
List,而(1 2 3,)
是一个逗号分隔的 List,这个 List
又包含着一个空格分隔的 List,空格分隔的 List
拥有着1
、2
、3
三个元素。
List 也可以通过方括号
[]
编写,方括号编写的 List用来在 CSS Grid 布局中表达行的名称,但是它同样也可以用于纯 SASS 代码,同样也可以通过逗号或者空格进行分隔。
Map
Map
提供了key
与value
之间的关联关系,并且能够通过key
找到相应的value
。他们可以容易的搜集value
到一个被命名的分组,然后动态的操作这些分组。它们在
CSS 中没有直接并行的概念,尽管在句法上与媒体查询表达式相似:
1 | $map: (key1: value1, key2: value2, key3: value3); |
不同与前面提到的 List,Map
必须总是被圆括号()
包裹,并且必须使用逗号,
进行分隔。Map
中的key
与value
可以是任意的 SassScript
对象,一个 Map 可以只拥有 1
个给定的key
和对应的value
(可以是一个
List),但是一个给定的value
可能会关联到多个key
。
和 List 一样,Map 大部分情况下也需要通过 SassScript
函数对其进行操作。其中map-get
函数负责查询 Map
中的value
,map-merge
函数添加value
至
Map,指令@each
可以被用来为 Map
中的每个key
和value
添加样式。Map
中键值对的顺序总是与其被建立的时候相同。
List 能够使用的位置,Map 也能正常使用。当通过一个 List
函数进行使用的时候,Map 被视为拥有键值对的
List。例如:(key1: value1, key2: value2)
在 List
函数当中将被视为一个嵌套的
List(即key1 value1, key2 value2
)。但是,值得注意的是,List
并不能相反的被视为一个 Map,尽管空的 List
将会抛出错误。()
即可以被视为一个没有key
和value
的
Map,也可以视为没有元素项的 List.
注意 Map 的key
可以是任意的 Sass 数据类型(即使是
Map),并且声明 Map 的语法允许任意的 SassScript
表达式,该表达式将会被解析以决定key
的值。
Map 并不能被转换为普通 CSS,在 CSS
函数中使用它作为变量和参数的值将会导致错误。这种情况下,建议使用inspect($value)
函数为
Map 的调试生成一个有用的字符串输出。
Color
任何 CSS 颜色表达式都返回一个 SassScript 色彩值,这包括大量被命名的颜色,它们与未引用的字符串是不可区分的。
在压缩输出模式下,Sass 将输出颜色的最小的 CSS
表示。例如,在压缩模式下,#FF0000
将输出为红色red
,而#FFEBCD
将会输出为白杏色blanchedalmond
。
用户使用被命名的颜色时,经常会遇到一个问题:在。。。SASS 喜欢在其他输出模式中键入相同的输出格式。。。之后,一个颜色会被插值到一个选择器,代码压缩时这将会变成不合法的语法。要避免这个问题,在被命名颜色用于构建选择器的时候,总是需要为它们添加引号。
First-Class Function
get-function($function-name)
将会返回一个函数引用,该函数能够被传递到call($function, $args...)
并得到调用。First-Class
函数不能直接用于 CSS 输出,任何这样的尝试都将会引发错误。
运算
SassScript
所有数据类型都支持==或!=
逻辑运算,但是每种数据类型还拥有自己的运算方式。
数值运算
数值类型支持常见的数值运算+
、-
、*
、/
、%
,并在必要时在不同的绝对数值单位之间转换。SASS
数学函数会在计算过程中保留单位,这意味不同单位不能在一起进行运算(例如px
和em
),两个相同单位的数值相乘后可能会造成运算结果的单位重复,例如:10px * 10px == 100px * px
,显然这里px * px
是一个无效单位,此时
SASS 会由于使用非法单位而报错。
关系运算符
<
、>
、<=
、>=
同样支持数值类型,而相等运算符==
,!=
则可以被 SASS 所有类型所支持。
除法与标识符/
CSS 允许在属性值中使用/
作为分隔数字的手段,SassScript 为
CSS 属性语法提供了一个扩展,允许/
作为除法运算符来使用。如果
SassScript 当中 2
个数值通过/
进行分隔,那么它们的计算结果将会出现在生成的 CSS
当中。
/
被解释为除法的情况主要有以下 3 种:
- 如果值及其部分被保存到一个变量,或者通过一个函数返回。
- 值被圆括号
()
包裹,除非这些圆括号在一个 List 的外面并且该地, - 如果指定值被用于其它算术表达式。
例如:
1 | /*===== SCSS =====*/ |
如果需要在普通 CSS
当中使用变量,可以通过#{}
去插入它们,例如:
1 | /*===== SCSS =====*/ |
减法、负数与标识符-
中划线-
在 CSS 和 SASS
中有许多不同的意义,可以表达一个减法运算符(5px - 3px
),一个负数的开始(-23px
),一元负值运算符(-$var
),或者一个标识符的组成部分(font-weight
),大部分情况下何时何地使用是比较清晰的,但也存在一些例外情况,为了避免这些例外需要遵循如下原则:
- 进行减法运算的时候,符号两则尽量保留空格。
- 作为负值标识或一元负值运算符时,需要在数值或变量前包含一个空格。
- 如果是在空格分隔的 List
中,就用括号将一元负值运算符括起来,就像
10px (-$var)
一样。
不同含义的表达遵循如下的优先次序:
- 表达式中使用字母时,
a-1
表示一个没有引号的字符串,其值为"a-1"
。但是唯一的例外在于单位的使用,比如5px-3px
与5px - 3px
是等效的,其执行结果为2px
。 - 两个数字之间没有空格意味着这是一个减法,所以
1-2
与1 - 2
作用相同。 - 将
-
放置在数字开头表示这是一个负数,所以1 -2
是一个包含了1
和-2
两个元素的 List。 - 两个数字之间不考虑空格表示这是减法,所以
1 -$var
和1 - $var
相同。 - 放置在一个值的前面表示这是一元负值运算符,该运算符会返回当前数值的负值。
1 | ➜ hank ✗ sass -i |
颜色运算
SASS
中所有的数学运算都分段的支持颜色值,即分别对红
、绿
、蓝
依次进行计算。
1 | /*===== SCSS =====*/ |
在执行完01 + 04 = 05
,02 + 05 = 07
,03 + 06 = 09
运算后编译为下面的
CSS:
1 | /*===== CSS =====*/ |
通常来说,使用颜色函数比使用颜色运算能更加有效的达到相同目的。颜色运算同样也可以作用于数值与颜色值,并且依然是分段进行的,例如:
1 | p { |
进行01 * 2 = 02
,02 * 2 = 04
,03 * 2 = 06
运算后编译为下面
CSS:
1 | p { |
注意,带有 alpha
通道的颜色(使用rgba
或hsla
函数创建的颜色)必须具有相同的
alpha 值单位,以便与其执行颜色运算。
1 | /*===== SCSS =====*/ |
颜色的 alpha
通道可以通过opacify
和transparentize
函数进行修改,例如:
1 | /*===== SCSS =====*/ |
IE 的 filter 滤镜要求所有颜色都包括 alpha
层,并且严格采用#AABBCCDD
格式,SASS
提供了ie_hex_str
函数能够更加简便的对颜色值进行转换,例如:
1 | /*===== SCSS =====*/ |
字符串运算
SassScript
使用+
运算符执行字符串连接操作。
1 | /*===== SCSS =====*/ |
注意,如果有引号的字符串被添加到没有引号的字符串(即带引号的字符串在+
的左侧),生成的字符串将会是有引号的。同理,如果将没有引号的字符串添加到有引号的字符串(即没有引号的字符串在+
的左侧),则结果为没有引号的字符串。
1 | /*===== SCSS =====*/ |
默认情况下,临近的两个 CSS 属性值可以通过空格进行连接。
1 | /*===== SCSS =====*/ |
字符串里可以通过#{}
插入动态的值,就像下面这样:
1 | /*===== SCSS =====*/ |
如果字符串插值#{}
中的变量是空值,则插值表达式的结果将被视为空字符串。
1 | /*===== SCSS =====*/ |
布尔运算
SassScript
当中布尔类型的值支持与and
、或or
、非not
运算。
1 | /*===== SCSS =====*/ |
List 运算
数组不支持任何特定的计算方式,只能通过list
函数进行操作,下面代码是一个使用了hsl($hue, $saturation, $lightness)
函数的例子:
1 | /*===== SCSS =====*/ |
圆括号
SASS 中可以通过圆括号{}
来调整运算的优先级。
1 | /*===== SCSS =====*/ |
函数
SassScript 内置了许多有用的函数,可以通过普通的 CSS 语句进行调用。
1 | /*===== SCSS =====*/ |
关键词参数
Sass 函数还允许通过关键词参数(即带有键名的参数)进行调用,上面的例子也可以改写为下面代码:
1 | /*===== SCSS =====*/ |
虽然这不是很简洁,但可以使样式表更加容易阅读。同时还能够让函数提供更灵活的接口,提供多个参数的同时,并不会变得难以调用。
命名参数能够以任何顺序进行传递,而带有默认值的参数可以省略掉。如果命名参数是变量名,那么下划线与破折号可以互换使用。
可以通过Sass::Script::Functions
查看
SASS 函数及其参数名称的完整清单,也可以通过 Ruby 进行自定义。
插值#{}
开发人员可以通过插值(interpolation)#{}
在选择器和属性名称中使用
SassScript 变量。
1 | /*===== SCSS =====*/ |
还可以使用#{}
将 SassScript
放入属性值,大多数情况下这种方式并不优于变量,但是使用#{}
意味着其附近的任意操作都将被视为普通
CSS,例如:
1 | /*===== SCSS =====*/ |
SassScript 中的&
如同使用选择器一样,SassScript
中的&
引用着当前的父选择器列表(一个由逗号分隔
List 作为元素再通过空格进行分隔的 List)。
1 | .navbar.text .sidebar.word, |
上面代码中$selector
的值为((".navbar.text" ".sidebar.word"), ".menu.item")
,这里的复合选择器使用了引号"
标识它是一个字符串,但是真实情况它可能是没有引号的。即使其父选择器并没有包含逗号和空格,&
依然会拥有两层嵌套,因此它能够被持续的访问。
当父级选择器列表不存在的时候,&
运算符的值为null
,使用mixin
当中可以通过该特性判断父选择器列表是否存在。
1 | @mixin does-parent-exist { |
变量默认值!default
可以在变量后添加!default
声明,如果变量已被赋值就不会再被重新进行赋值,如果变量未被赋值,则会被赋予新值。
1 | /*===== SCSS =====*/ |
如果变量的值是null
,则会被!default
视为没有赋值。
1 | /*===== SCSS =====*/ |
@规则与指令
SASS 支持所有 CSS3 的@-Rules
规则,以及 SASS
特有的#Directive
指令。
@import / #import
SASS 拓展了 CSS
的@import
允许其导入.scss
或.sass
文件,导入的文件将合并、编译到一个
CSS 文件,文件中的变量和 mixin 都可以在导入的主文件当中使用。
SASS 会基于当前目录查找其它文件,以及 Rack、Rails 或者 Merb 下的 SASS
文件目录。可以通过:load_paths
或--load-path
选项指定额外的搜索目录。
@import
通过指定路径以及文件名来引入 SASS
文件,但是在下面 4 种情况当中,@import
仅被编译为普通的 CSS
语句:
- 如果文件拓展名为
.css
。 - 如果文件名以
http://
开头。 - 如果文件名是
url()
- 如果
@import
包含媒体查询语句。
1 | /*===== SCSS =====*/ |
除上述四种情况外,文件拓展名为.scss
或.sass
的情况都会进行预编译处理,即使文件拓展名缺省依然能够正确的进行导入。
1 | // 下面2种导入方式等效 |
SASS 允许在一个@import
语句内同时导入多个文件。
1 | @import "base", "reset", "app"; |
@import
语句内可以有限制的使用#{}
插值,即不能动态的导入基于变量的
SASS 文件,只能用于标准 CSS
的@import url("");
导入方式。
1 | /*===== SCSS =====*/ |
Partial
如果你引入一个 SCSS 或 Sass 文件,但并不希望它们编译成 CSS
文件,那么可以在文件名开头添加一个下划线_
,这样 Sass
将不会把该文件编译成 CSS
文件,然后引入文件的时候不需要添加下划线。例如:你拥有一个_colors.scss
,但是
Sass
并不会创建_colors.css
文件,然后,不过仍然可以通过@import "colors";
语句引入_colors.scss
文件。
注意,不能在同一目录中包含相同名称的 Partial 和非 Partial;例如:
_colors.scss
不能与colors.scss
在相同目录下并存。
嵌套的@import
大多数情况下,在代码的顶层使用@import
导入是最为常用,但是我们依然可以在CSS规则
和@media规则
中使用;其效用如同基本的@import
,仍然会去包含引入文件的内容。
1 | /*===== SCSS =====*/ |
指令只能用于文档的基本级别,在一个嵌套上下文当中被
@import
引入的文件里,诸如@mixin
或者@charset
是不被允许的。
不能在
mixin
或者控制指令
当中嵌套使用@import
。
@media / #media
Sass 中@media
的用法与 CSS 相同,但是允许在 CSS
规则中嵌套使用。编译时@media
将被编译到文件最外层,包含嵌套的父级选择器。
1 | /*===== SCSS =====*/ |
@media
的查询条件可以嵌套使用,编译后 SASS
会自动添加and
关键字。
1 | /*===== SCSS =====*/ |
@media
的查询条件当中也可以使用 SassScript。
1 | /*===== SCSS =====*/ |
@extend / #extend
现实的 Web
页面开发场景当中,当一个class
属性拥有其它多个class
所具备样式的时候,通常的做法是在
HTML 上书写全部这些class
,如同下面的代码:
1 | <div class="error seriousError"> |
这样的代码并不利于维护,并且会带来非语义相关的样式到 HTML 上。
使用 SASS 提供的@extend
可以在不同 CSS
选择器之间继承样式,从而完美避免上述问题。
1 | /*===== SCSS =====*/ |
上面代码中.error
的样式会自动应用到.seriousError
,作用相当于同时使用了这两个class
上的样式。
通过.error
定义的其它样式规则,同样会在.seriousError
上工作,比如下面这样定义样式后。
1 | .error.danger { |
然后<div class="seriousError danger">
的背景图片同样会是warning.png
。
工作原理
@extend
是通过向被继承的 CSS 选择器,插入需要继承的 CSS
选择器来进行工作,例如上面例子的代码转换如下:
1 | /*===== SCSS =====*/ |
合并选择器的时候,
@extend
将会避开不必要的样式规则重复,并且不会生成不能匹配任何东西的选择器。
继承复杂的选择器
class
选择器并不是唯一能被继承的选择器,其它选择器也可以被继承,甚至可以去继承一个
CSS 元素,接着看下面的代码:
1 | /*===== SCSS =====*/ |
就像前面内容中的.error.intrusion
一样,a:hover
上定义的规则也会在.hoverlink
上工作,即使它们拥有着其它的选择器。
1 | /*===== SCSS =====*/ |
多重继承
一个选择器可以同时继承多个选择器的样式,例如:
1 | /*===== SCSS =====*/ |
上面代码中,每个.seriousError
旁边都会伴随.error
和.attention
选择器。通常,文档后面定义的样式将会采取这样的优先级规则:.seriousError
的背景色是#ff0
而非#fdd
,因为.attention
定义的位置比.error
更加靠后.
多重继承时还可以使用逗号,
分隔的选择器列表,例如:@extend .error, .attention
作用与@extend .error; @extend .attention;
等效。
链式继承
一个选择器可以继承另外一个选择器,另外一个选择器又继承了第三个选择器,从而形成链式的继承结构。
1 | /*===== SCSS =====*/ |
上面代码中,所有使用.error
的位置也会使用.seriousError
,同时使用.seriousError
和.error
的位置则都会使用到.criticalError
。
选择器序列
诸如.foo .bar or .foo + .bar
这样的选择器队列目前还不能被继承,但是嵌套选择器本身是可以使用@extend
实现继承的,例如:
1 | /*===== SCSS =====*/ |
有时候选择器序列会继承出现在另一个序列中的选择器,这种情况下就需要合并两个序列,例如:
1 | /*===== SCSS =====*/ |
由上面代码在技术上可能匹配 2 个选择器序列当中的每个序列,可能导致生成的 CSS 样式体积较大。因此如果例子中需要 10 个选择器,Sass 将会只会生成一个有效的选择器。
当被合并的 2 个选择器序列没有共同的选择器时,那么将会生成 2 个新的选择器:第 1 个选择器序列在第 2 个之前,以及第 2 个选择器序列在第 1 个之前,就像下面这样:
1 | /*===== CSS =====*/ |
如果需要合并的 2
个选择器序列共享了部分选择器,那么这些共享的选择器将会被合并,只有不同的选择器会发生变化。下面的例子中,两个选择器序列都包含#admin
,所以编译结果会合并这两个
ID 选择器。
1 | /*===== SCSS =====*/ |
只能@extend 的选择器
有些时候,你只想为需要去@extend
继承的 Class
选择器编写样式,并且不希望直接使用到 HTML。在编写 Sass
库的时候,你可能要为使用者提供@exnted
继承的样式,并且使用者如果不需要可以选择忽略。
如果使用普通的 Class 选择器,会生成更多额外的 CSS。而且可能与其它
Class 选择器发生样式冲突,因此 Sass
提供了占位选择器(placeholder
selectors),例如%foo
。
占位选择器的外观看起来与 Class 或者 ID
选择器类似,只是#
和.
被%
所替代。占位选择器可以出现在任意
Class 和 ID 选择器能够使用的位置,并能够防止其中定义的规则集被渲染为
CSS。
1 | /*===== SCSS =====*/ |
!optional
当继承一个选择器的时候,如果@extend
没有工作将会发生错误,例如对于a.important {@extend .notice}
,如果当前文档没有包含.notice
选择器就会发生错误。如果被包含的这个选择器.notice
是h1.notice
,由于h1
与a
具有冲突,因此没有新的选择器将会被创建。
如果希望@extend
不生成任何选择器,可以在选择器后面添加!optional
标志,例如:
1 | a.important { |
在指令中使用@extend
在@media
之类的指令中使用@extend
会存在一些限制,Sass
不能让@media
代码块之外的 CSS
规则应用到其内部的选择器,从而避免四处拷贝样式并新建大量的样式块。这意味如果在@media
或其它
CSS
指令中使用@extend
,那么就只能继承当前指令块内出现的选择器。
例如下面的代码工作状态良好:
1 | @media print { |
但是这样的代码就是错误的:
1 | .error { |
希望有一天浏览器能够原生支持
@extend
,并允许它用于@media
或其它指令当中。
@at-root / #at-root
某些需要放置在文档根元素上的样式,可以就近的放置在其父选择器上,该特性同样可用于行内 CSS 选择器。
1 | /*===== SCSS =====*/ |
@at-root
可以通过代码块同时包含多个选择器。
1 | /*===== SCSS =====*/ |
@at-root
默认情况下仅仅是排除掉父级选择器,但是实际开发过程中也有可能需要将@media
之类的指令选择性的过滤、移动到外面,这里可以通过@at-root (without: ...)
和@at-root (with: ...)
语法实现该操作。
1 | /*===== SCSS =====*/ |
可以向@at-root
传递 2
个特殊值,rule
表示普通的 CSS
规则,因此@at-root (without: rule)
作用类似于没有任何查询条件的@at-root
;all
代码全部
CSS
规则,@at-root (without: all)
代表样式将会被移动到所有指令和
CSS 规则的外侧。
@debug
@debug
指令打印 SassScript
表达式的值到标准错误输出流,可以用来调试具有复杂 SassScript 的 SASS
文件,例如:
1 | /*===== SCSS =====*/ |
@warn
@warn
指令打印 SassScript
表达式的值到标准错误输出流,在警告用户弃用库或者修复mixin
轻微错误的场景比较有用,@warn
和@debug
存在
2 个主要区别:
可以使用
--quiet
命令行选项或 SASS 的:quiet
选项来关闭@warn
警告。 样式表 trace 将与消息一起被打印,开发人员可以方便的看到@warn
警告发生在样式表的哪里个位置。
1 | /*===== SCSS =====*/ |
@error
@error
指令抛出一个 SassScript
表达式的值作为致命错误,其中包含了友好的堆栈
trace,可以用来验证mixin
和function
的参数,例如:
1 | @mixin adjust-location($x, $y) { |
SASS 当前还没有提供捕获
@error
的方式。
控制指令和表达式
SassScript 支持一些基本的控制指令和表达式,例如只在某些条件下包含样式,或是在多次变化中包含相同的样式。
控制指令属于高级特性,并不经常使用,主要用于
mixins
当中,尤其是对于 Compass 这样的库。
if()
内置的if()
函数允许在一个条件处理分支返回两种可能的结果,它可以用于任意的脚本上下文。if()
函数只能判断相应的那个参数并且返回,这允许引用已经被定义或者计算的变量,否则将会导致错误发生(例如除以零)。
1 | /*===== SCSS =====*/ |
@if
@if
指令接收一个 SassScript
表达式,当表达式返回false
或者null
之外的数据时,会选择使用接下来的嵌套语句,
1 | /*===== SCSS =====*/ |
@if
语句后面可以跟随多个@else if
语句和一个@else
语句,如果@if
语句失败,SASS
将逐个尝试@else if
语句,直至其中一个成功;如果全部失败,则会执行@else
语句。例如:
1 | /*===== SCSS =====*/ |
@for
@for
指令用于重复输出一组样式,每次重复时都会存在一个计数器变量用于调整输出结果。该指令拥有@for $var from <start> through <end>
和@for $var from <start> to <end>
两种形式。
$var
可以是任意变量名(例如$i
),<start>
和<end>
是返回整数的
SassScript
表达式,如果<start>
大于<end>
,那么计数器将会进行递减而非递增。
@for
指令会设置$var
为指定范围内的连续数值,对于from...through
数值范围包括<start>
和<end>
的值,对于from...to
会从<start>
开始运行,但不会包括<end>
的值。
1 | /*===== SCSS =====*/ |
@each
@each
指令的使用格式为@each $var in <list or map>
,其中$var
可以是任意变量名称(例如$length
或$name
),而<list or map>
是一个返回list
或者map
的
SassScript 表达式。
@each
规则设置$var
为list
或map
当中的每一项,输出样式中将会使用$var
的实际值。
1 | /*===== SCSS =====*/ |
多重赋值
@each
指令能够以@each $var1, $var2, ... in <list>
的格式使用多个变量,如果<list>
是一个列表中的列表元素,那么子列表中的每个元素将会被分配至各自的变量,例如:
1 | /*===== SCSS =====*/ |
由于maps
被视为一个键值对的列表,所以多重赋值也能够正常的工作,例如:
1 | /*===== SCSS =====*/ |
@while
@while
指令可以用来重复输出嵌套的样式,直至 SassScript
表达式返回结果为false
,可以用于实现比@for
语句更复杂的循环,但日常开发较少使用。
1 | /*===== SCSS =====*/ |
Mixin 指令
混入(Mixin)可以用来定义 Web
应用当中需要复用的样式,避免出现.float-left
这样的无语义
class。混入里可以包含 CSS 规则以及 SASS
语法,甚至可以携带参数和引入变量,从而方便的生成各种灵活的样式。
定义混入:@mixin
混入(Mixin)通过 SASSR
提供的@mixin
指令来定义,后面跟随混入的名称与参数以及内容块,例如下面代码定义了一个名为large-text
的
Mixin:
1 | @mixin large-text { |
混入 Mixin 当中可以同时包含选择器和 CSS 属性,其中选择器还可以包含父级的选择器,例如:
1 | @mixin clearfix { |
因为历史原因,混入 Mixin 的名字以及其它 SASS 标识符可以互换连字符
-
和下划线_
,例如对于名称为add-column
的混入,也可以将其视为add_column
,反之亦然。
包含混入:@include
@include
指令通过混入的名称和可选的参数,可以引入特定的混入
Mixin 至文档,从而包含并复用其样式,例如下面代码:
1 | /*===== SCSS =====*/ |
混入 Mixin 可以包含在其它规则之外,只要它们不直接定义 CSS 属性或使用父选择器引用,例如:
1 | /*===== SCSS =====*/ |
混入 Mixin 也可以包含其它的混入,接下来看下面的代码:
1 | @mixin compound { |
事实上,混入 Mixin 还可以包含自己,但是在 SASS 3.3 版本之前,这种递归调用的行为是被禁止的。另外,只有定义了后代选择器的混入 Mixin 可以安全的混入源文件的顶层。
参数
混入 Mixin 可以使用 SassScript
的值作为参数,参数被包括在混入当中并作为为变量提供给其内部使用。多个参数通过逗号,
分隔,然后通过传递对应顺序的参数进行调用,例如:
1 | /*===== SCSS =====*/ |
混入 Mixin 可以通过变量赋值语法为参数指定默认值,调用的时候如果缺省参数,则使用默认值代替,例如:
1 | /*===== SCSS =====*/ |
关键字参数
混入 Mixin
在通过@include指令
进行引入时也可以使用显式的关键字参数,例如可以将上面例子改写为:
1 | p { |
虽然上面的方式不够简明,但能够使 Scss 代码更加容易阅读,并让函数接口更加灵活,并方便的进行多参数的混入。命名参数可以按照任意顺序进行传递,如果有默认值可以省略。并且由于命名参数本质上是变量名称,因此下划线
_
和连字符-
可以互换使用。
尾部逗号
如果传入混入 Mixin或者函数
Function的最后一个参数是位置性的或关键字风格的,那么可以在这个参数后面跟随一个逗号,
,这种编码风格可以更加简洁的进行重构,并且减少语法错误。
可变参数
有些场景下,混入 Mixin或者函数
Function的参数个数是不确定的,例如创建一个可以接收任意数量参数的box-shadow
混入。SASS
里可以通过...
符号添加可变参数支持,将所有剩余的参数包装到一个列表
List。
1 | /*===== SCSS =====*/ |
可变参数可以包含任意需要传递给混入
Mixin或者函数
Function的关键字参数,它们可以通过keywords($args)
函数
进行访问,该函数将会返回一个
Map,将它们作为一个从字符串(不包含$
)到值的映射返回。
可变参数也可以在调用 Mixin 的时候使用,使用相同的语法能够扩展一个值的列表 List,以便每个值作为单独的参数传入;或者扩展值的 Map,从而使每个键值对都会作为一个关键字参数来进行处理。例如:
1 | /*===== SCSS =====*/ |
可以同时传递一个 List 和一个 Map 参数,只要 List 参数位于 Map 类型参数之前,例如
@include colors($values..., $map...)
。
我们可以通过可变参数包装 Mixin 并添加额外样式,而不改变 Mixin 的参数签名。这样参数将会通过被包装的 Mixin 直接进行传递,例如:
1 | @mixin wrapped-stylish-mixin($args...) { |
传入内容块到 Mixin
样式块可以传递到混入 Mixin
所包含样式的位置,它们会出现在 Mixin
内任意@content
指令的位置,从而能够定义关联到选择器和指令的构造的抽象。
1 | /*===== SCSS =====*/ |
同样的 Mixin
可以在简写语法的.sass
文件中完成(@mixin
和@include
分别用=
和+
表示)。
1 | =apply-to-ie6-only |
注意: 当
@content
指令被指定多次或者位于一个循环当中,样式块将会在每次调用中被复制并引用。
有些 Mixin
可能需要传入一个内容块,或者根据是否传入内容块而具备不同的行为。当内容块传递至当前
Mixin 的时候,content-exists()
函数将会返回true
,从而该函数可以用于帮助实现这类行为。
变量作用域与内容块
传递给 Mixin 的内容块会在其定义的作用域中进行运算,而非混入 Mixin 的作用域。这意味 Mixin 当中的局部变量不能传递至样式块,并将其解析为全局值使用。
1 | /*===== SCSS =====*/ |
此外,这还清楚的表明,在被传递的内容块中使用的变量与 Mixin,会关联到内容块定义位置的其它样式。
1 | /*===== SCSS =====*/ |
函数指令
SASS 支持开发人员自定义函数,并且在任意值或脚本上下文里使用,例如:
1 | /*===== SCSS =====*/ |
正如上面代码所展示的,函数可以访问任意全局变量并接收参数,其行为类似一个混入
Mixin。函数可以包含语句,并且必须调用@return
来设置函数的返回值。
与 Mixin 一样,SASS 可以通过关键字参数调用自定义函数,因此可以像下面这样调用上面例子中的函数:
1 | #sidebar { |
建议在函数前面添加前缀以避免命名冲突,同时也能够有效区分哪些是自定义的函数,例如为自定义函数添加公司名称作为前缀-uinika-grid-width
。自定义函数同样也支持可变参数,使用方式与混入
Mixin 相同。
同样由于历史原因,函数名以及其它 SASS 标识符当中的连字符
-
和下划线_
都是可以互换的,如果定义了一个名为grid-width
的函数,那么同样可以通过grid_width
进行调用,反之亦然。
输出类型
虽然 SASS 默认输出的 CSS
格式是良好的,但是可以根据开发人员的个人习惯,使用 SASS 提供的其它 4
种输出格式。即可以通过:style
选项进行设定,也可以在命令行中直接使用--style
标志。
:nested
嵌套 nested是 SASS 默认的输出格式,每个属性都会占据一行,但缩进不是固定的,每个规则都基于其嵌套深度进行缩进。例如:
1 | #main { |
嵌套格式在阅读较大 CSS 工程文件时能够帮助开发人员快速的了解代码的结构。
:expanded
扩展 expanded格式类似于人工手写的 CSS 样式,每个属性和规则都独占用一行。规则当中的 CSS 属性会进行缩进,但规则本身并不会进行任何特殊的缩进,例如:
1 | #main { |
:compact
紧凑 compact格式比起上述两种格式占据的空间更小,这种格式将重点聚焦在选择器而非 CSS 属性上。每个 CSS 规则独自占据一行,该行还包括全部的 CSS 属性。每个嵌套的规则都会另起新行,不嵌套的选择器会输出空白行作为分隔,例如:
1 | #main { |
:compressed
压缩 compressed格式会尽可能减小所生成文件的体积,并在该文件末尾产生一个换行。除了必要的选择器分隔之外,几乎没有多余的空格。这种格式会让颜色等 CSS 属性值以最简短的方式来进行表示,由于代码可读性极差,因此主要用于生产环境。
1 | #main { |
扩展 SASS
对于前端开发人员的特殊需求,SASS 为提供了多项高阶定制功能,但是使用这些功能需要非常熟悉Ruby。例如自定义 SASS 函数、缓存存储、自定义导入器等等,这些功能在日常前端开发工作当中并不常用,可以根据需要查阅 SASS 的源码 API 文档。
SCSS 3.5.5 简明上手指南