内容简介:基于Gulp完成前端自动化的年代,出于快速上手以及npm安装方便的考虑,开发团队一直沿用
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 。
➜ sudo apt-get install ruby-dev ➜ gem install sass
命令行当中可以通过 sass input.scss output.css 编译Scss文件,也可以添加 --watch 选项监听Scss文件的变化:
➜ sass --watch input.scss:output.css
如果多个Scss文件放置在一个目录当中,也可以对整个文件目录进行监听。
➜ sass --watch app/sass:public/stylesheets
通过 -i 或者 --interactive 可以运行一个交互式的SassScript命令行。
➜ sass -i >> 5px + 8px 13px
通过 sass --help 获取更多关于SASS在命令行的使用方法。
编码规则
SASS首先会检查代码文件的Unicode BOM( byte order mark ),然后是 @charset 声明,再然后是底层运行 Ruby 的字符串编码,如果这些都未进行设置,将会默认以 UTF-8 编码输出CSS文件。
@charset 'utf-8';
#app {
background: url('../assets/背景图片.png');
}
建议代码开头位置显式定义 @charset "encoding-name"; ,让SASS能够按照给定的字符集编译输出。
特性概览
CSS书写代码规模较大的Web应用时,容易造成选择器、层叠的复杂度过高,因此推荐通过SASS预处理器进行CSS的开发,SASS提供的 变量 、 嵌套 、 混合 、 继承 等特性,让CSS的书写更加有趣与程式化。
变量
变量用来存储需要在CSS中复用的信息,例如颜色和字体。SASS通过 $ 符号去声明一个变量。
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
上面例子中变量 $font-stack 和 $primary-color 的 值 将会替换所有引用他们的位置。
body {
font: 100% Helvetica, sans-serif;
color: #333; }
嵌套
SASS允许开发人员以 嵌套 的方式使用CSS,但是 过度的使用嵌套会让产生的CSS难以维护,因此是一种不好的实践 ,下面的例子表达了一个典型的网站导航样式:
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
大家注意上面代码中的 ul 、 li 、 a 选择器都被嵌套在 nav 选择器当中使用,这是一种书写更高可读性CSS的良好方式,编译后产生的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; }
引入
SASS能够将代码分割为多个片段,并以underscore风格的下划线作为其命名前缀( _partial.scss ),SASS会通过这些下划线来辨别哪些文件是 SASS片段 ,并且不让片段内容直接生成为CSS文件,从而只是在使用 @import 指令的位置被导入。CSS原生的 @import 会通过额外的HTTP请求获取引入的样式片段,而SASS的 @import 则会直接将这些引入的片段合并至当前CSS文件,并且不会产生新的HTTP请求。下面例子中的代码,将会在 base.scss 文件当中引入 _reset.scss 片断。
// _reset.scss
html, body, ul, ol {
margin: 0;
padding: 0;
}
// base.scss
@import 'reset';
body {
font: 100% Helvetica, sans-serif;
background-color: #efefef;
}
SASS中引入片断时,可以 缺省使用文件扩展名 ,因此上面代码中直接通过 @import 'reset' 引入,编译后生成的代码如下:
html, body, ul, ol {
margin: 0;
padding: 0; }
body {
font: 100% Helvetica, sans-serif;
background-color: #efefef; }
SASS片断使用下划线前缀命名,主要用于 SASS命令行工具 watch指定目录源码的场景;如果使用Webpack等打包 工具 则毋须顾虑该问题,CSS样式将会通过Webpack加载器,按照ES6风格的 import 或Webpack插件 extract-text-webpack-plugin 进行打包和模块化。
混合
混合( Mixin )用来分组那些需要在页面中复用的CSS声明,开发人员可以通过向Mixin传递变量参数来让代码更加灵活,该特性在添加浏览器兼容性前缀的时候非常有用,SASS目前使用 @mixin name 指令来进行混合操作。
@mixin border-radius($radius) {
border-radius: $radius;
-ms-border-radius: $radius;
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
}
.box {
@include border-radius(10px);
}
上面的代码建立了一个名为 border-radius 的Mixin,并传递了一个变量 $radius 作为参数,然后在后续代码中通过 @include border-radius(10px) 使用该Mixin,最终编译的结果如下:
.box {
border-radius: 10px;
-ms-border-radius: 10px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px; }
继承
继承是SASS中非常重要的一个特性,可以通过 @extend 指令在选择器之间复用CSS属性,并且不会产生冗余的代码,下面例子将会通过SASS提供的继承机制建立一系列样式:
// 这段代码不会被输出到最终生成的CSS文件,因为它没有被任何代码所继承。
%other-styles {
display: flex;
flex-wrap: wrap;
}
// 下面代码会正常输出到生成的CSS文件,因为它被其接下来的代码所继承。
%message-common {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.message {
@extend %message-common;
}
.success {
@extend %message-common;
border-color: green;
}
.error {
@extend %message-common;
border-color: red;
}
.warning {
@extend %message-common;
border-color: yellow;
}
上面代码将 .message 中的CSS属性应用到了 .success 、 .error 、 .warning 上面,魔法将会发生在最终生成的CSS当中。这种方式能够避免在HTML元素上书写多个 class 选择器,最终生成的CSS样式是下面这样的:
.message, .success, .error, .warning {
border: 1px solid #ccc;
padding: 10px;
color: #333; }
.success {
border-color: green; }
.error {
border-color: red; }
.warning {
border-color: yellow; }
操作符
SASS提供了标准的算术运算符,例如 + 、 - 、 * 、 / 、 % 。在接下来的例子里,我们尝试在 aside 和 article 选择器当中对宽度进行简单的计算。
.container { width: 100%; }
article[role="main"] {
float: left;
width: 600px / 960px * 100%;
}
aside[role="complementary"] {
float: right;
width: 300px / 960px * 100%;
}
上面代码以 960px 为基准建立了简单的流式网格布局,SASS提供的算术运算符让开发人员可以更容易的将像素值转换为百分比,最终生成的CSS样式如下所示:
.container {
width: 100%; }
article[role="main"] {
float: left;
width: 62.5%; }
aside[role="complementary"] {
float: right;
width: 31.25%; }
CSS扩展
SASS当中有两种可用的语法,一种是Scss( 即SASS化的CSS ),这是一种类CSS的语法,以 .scss 作为源码文件后缀。另一种是Sass,提供了比较简明的CSS书写方式,通过缩进而非括号来标识嵌套的选择器,以及使用换行而非分号来分隔各个属性,并使用 .sass 作为代码文件的后缀。
Sass和Scss两种代码风格可以通过 sass-convert 命令行工具进行相互转换:
➜ sass-convert test.scss test.sass ➜ sass-convert test.sass test.scss
日常开发环境下,出于前端开发的使用习惯,更多会选择Scss语法,接下来的内容也将会以Scss风格为主。
嵌套规则
Scss允许CSS规则嵌套使用,父子规则将会呈现 包含选择器 的关系,例如:
/*===== SCSS =====*/
#main p {
color: #00ff00;
width: 97%;
.redbox {
background-color: #ff0000;
color: #000000;
}
}
/*===== CSS =====*/
#main p {
color: #00ff00;
width: 97%; }
#main p .redbox {
background-color: #ff0000;
color: #000000; }
这样可以避免重复的使用父级选择器,从而达到简化CSS代码结构的目的,例如:
/*===== SCSS =====*/
#main {
width: 97%;
p, div {
font-size: 2em;
a { font-weight: bold; }
}
pre { font-size: 3em; }
}
/*===== CSS =====*/
#main {
width: 97%; }
#main p, #main div {
font-size: 2em; }
#main p a, #main div a {
font-weight: bold; }
#main pre {
font-size: 3em; }
引用父级选择器&
Scss使用 $ 关键字在CSS规则中引用 父级选择器 ,例如在嵌套使用伪类选择器的场景下:
/*===== SCSS =====*/
a {
font-weight: bold;
text-decoration: none;
&:hover { text-decoration: underline; }
body.firefox & { font-weight: normal; }
}
/*===== CSS =====*/
a {
font-weight: bold;
text-decoration: none; }
a:hover {
text-decoration: underline; }
body.firefox a {
font-weight: normal; }
无论CSS规则嵌套的深度怎样,关键字 & 都会使用父级选择器 级联替换 全部其出现的位置:
/*===== SCSS =====*/
#main {
color: black;
a {
font-weight: bold;
&:hover { color: red; }
}
}
/*===== CSS =====*/
#main {
color: black; }
#main a {
font-weight: bold; }
#main a:hover {
color: red; }
& 必须出现在复合选择器开头的位置,后面再连接自定义的后缀,例如:
/*===== SCSS =====*/
#main {
color: black;
&-sidebar { border: 1px solid; }
}
/*===== CSS =====*/
#main {
color: black; }
#main-sidebar {
border: 1px solid; }
如果在父级选择器不存在的场景使用 & ,Scss预处理器会报出错误信息。
嵌套属性
CSS许多属性都位于相同的命名空间( 例如 font-family 、 font-size 、 font-weight 都位于 font 命名空间下 ),Scss当中只需要编写命名空间一次,后续嵌套的子属性都将会位于该命名空间之下,请看下面的代码:
/*===== SCSS =====*/
.demo {
// 命令空间后带有冒号:
font: {
family: fantasy;
size: 30em;
weight: bold;
}
}
/*===== CSS =====*/
.demo {
font-family: fantasy;
font-size: 30em;
font-weight: bold; }
命令空间上可以直接书写 CSS简写属性 ,但是日常开发中建议直接书写 CSS简写属性 ,尽量保持CSS语法的一致性。
.demo {
font: 20px/24px fantasy {
weight: bold;
}
}
.demo {
font: 20px/24px fantasy;
font-weight: bold;
}
占位符选择器%
占位符选择器与 id 或者 class 选择器的用法类似,只是 # 和 . 需要替换成为 % ,占位符选择器必须通过 @extend 指令进行调用。
关于占位符的讲解,请具体参考章节。
注释
SASS支持标准CSS的多行注释 /* */ 和单行注释 // ,其中,多行注释会完整输出到编译后的CSS文件,而单行注释则不会。
/* 多行注释
* 会原样输出到CSS文件 */
body { color: black; }
// 单行注释不会出现在输出的CSS当中
a { color: green; }
编译成为CSS的结果如下:
@charset "UTF-8";
/* 多行注释
* 会原样输出到CSS文件 */
body {
color: black; }
a {
color: green; }
插值语句 #{$variable} 可以应用在多行注释当中。
$version: "3.5.5";
/* 这段CSS是通过SASS #{$version}生成的。*/
编译成为CSS的结果:
@charset "UTF-8"; /* 这段CSS是通过SASS 3.5.5生成的。*/
当注释中出现中文时,SASS会自动在生成的CSS文件头部添加 @charset "UTF-8"; 。
SassScript
SassScript是Sass提供的一系列CSS语法扩展,允许在任意CSS属性上书写变量、计算以及函数。SassScript还可以通过 interpolation 插值生成选择器和属性名,这在编写 mixins 时非常有用。
变量$
SassScript通过 $ 关键字声明和使用一个变量。
/*===== SCSS =====*/
$width: 8rem; // 声明变量
#main {
width: $width; // 使用变量
}
/*===== CSS =====*/
#main {
width: 8rem; }
SassScript变量支持块级作用域,嵌套规则当中声明的变量都是 局部变量 ,可以通过 !global 将局部变量声明为 全局变量 。
/*===== SCSS =====*/
#menu {
$width: 5rem !global;
width: $width;
}
#sidebar {
width: $width;
}
/*===== CSS =====*/
#menu {
width: 5rem; }
#sidebar {
width: 5rem; }
数据类型
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 中使用选择器的名称。
/*===== SCSS =====*/
@mixin firefox-message($selector) {
body.firefox #{$selector}:before {
content: "Hi, Firefox users!";
}
}
@include firefox-message(".header");
/*===== CSS =====*/
body.firefox .header:before {
content: "Hi, Firefox users!"; }
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项元素的List 1px 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中没有直接并行的概念,尽管在句法上与媒体查询表达式相似:
$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种:
()
例如:
/*===== SCSS =====*/
p {
font: 10px/8px; // 普通CSS,未进行除法运算。
$width: 1000px;
width: $width/2; // 使用一个变量,进行了除法运算。
width: round(1.5)/2; // 使用一个函数,进行了除法运算。
height: (500px/2); // 使用圆括号, 进行了除法运算。
margin-left: 5px + 8px/2px; // 使用了加号+, 进行了除法运算。
font: (italic bold 10px/8px); // 在List当中,圆括号不会进行计算。
}
/*===== CSS =====*/
p {
font: 10px/8px;
width: 500px;
height: 250px;
margin-left: 9px; }
如果需要在普通CSS当中使用变量,可以通过 #{} 去插入它们,例如:
/*===== SCSS =====*/
p {
$font-size: 12px;
$line-height: 30px;
font: #{$font-size}/#{$line-height};
}
/*===== CSS =====*/
p {
font: 12px/30px; }
减法、负数与标识符 -
中划线 - 在CSS和SASS中有许多不同的意义,可以表达一个减法运算符( 5px - 3px ),一个负数的开始( -23px ),一元负值运算符( -$var ),或者一个标识符的组成部分( font-weight ),大部分情况下何时何地使用是比较清晰的,但也存在一些例外情况,为了避免这些例外需要遵循如下原则:
10px (-$var)
不同含义的表达遵循如下的优先次序:
- 表达式中使用字母时,
a-1表示一个没有引号的字符串,其值为"a-1"。但是唯一的例外在于单位的使用,比如5px-3px与5px - 3px是等效的,其执行结果为2px。 - 两个数字之间没有空格意味着这是一个减法,所以
1-2与1 - 2作用相同。 - 将
-放置在数字开头表示这是一个负数,所以1 -2是一个包含了1和-2两个元素的List。 - 两个数字之间不考虑空格表示这是减法,所以
1 -$var和1 - $var相同。 - 放置在一个值的前面表示这是一元负值运算符,该运算符会返回当前数值的负值。
➜ hank ✗ sass -i >> a-1 "a-1" >> 5px-3px 2px >> 1 -2 (1 -2) >> 1-2 -1
颜色运算
SASS中所有的数学运算都分段的支持颜色值,即分别对 红 、 绿 、 蓝 依次进行计算。
/*===== SCSS =====*/
p {
color: #010203 + #040506;
}
在执行完 01 + 04 = 05 , 02 + 05 = 07 , 03 + 06 = 09 运算后编译为下面的CSS:
/*===== CSS =====*/
p {
color: #050709; }
通常来说,使用 颜色函数 比使用 颜色运算 能更加有效的达到相同目的。 颜色运算 同样也可以作用于数值与颜色值,并且依然是分段进行的,例如:
p {
color: #010203 * 2;
}
进行 01 * 2 = 02 , 02 * 2 = 04 , 03 * 2 = 06 运算后编译为下面CSS:
p {
color: #020406; }
注意,带有alpha通道的颜色( 使用 rgba 或 hsla 函数创建的颜色 )必须具有相同的alpha值单位,以便与其执行颜色运算。
/*===== SCSS =====*/
p {
color: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75);
}
/*===== CSS =====*/
p {
color: rgba(255, 255, 0, 0.75); }
颜色的alpha通道可以通过 opacify 和 transparentize 函数进行修改,例如:
/*===== SCSS =====*/
$translucent-red: rgba(255, 0, 0, 0.5);
p {
color: opacify($translucent-red, 0.3); // 增加alpha通道值
background-color: transparentize($translucent-red, 0.25); // 减少alpha通道值
}
/*===== CSS =====*/
p {
color: rgba(255, 0, 0, 0.8);
background-color: rgba(255, 0, 0, 0.25); }
IE的filter滤镜要求所有颜色都包括alpha层,并且严格采用 #AABBCCDD 格式,SASS提供了 ie_hex_str 函数能够更加简便的对颜色值进行转换,例如:
/*===== SCSS =====*/
$translucent-red: rgba(255, 0, 0, 0.5);
$green: #00ff00;
div {
filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}');
}
/*===== CSS =====*/
div {
filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#FF00FF00', endColorstr='#80FF0000'); }
字符串运算
SassScript使用 + 运算符执行 字符串连接 操作。
/*===== SCSS =====*/
p {
cursor: e + -resize;
}
/*===== CSS =====*/
p {
cursor: e-resize; }
注意,如果有引号的字符串被添加到没有引号的字符串( 即带引号的字符串在 + 的左侧 ),生成的字符串将会是 有引号 的。同理,如果将没有引号的字符串添加到有引号的字符串( 即没有引号的字符串在 + 的左侧 ),则结果为 没有引号 的字符串。
/*===== SCSS =====*/
p:before {
content: 'Foo ' + Bar; // 有引号字符串 + 无引号字符串 = 有引号字符串
font-family: sans- + 'serif'; // 无引号字符串 + 有引号字符串 = 无引号字符串
}
/*===== CSS =====*/
p:before {
content: "Foo Bar";
font-family: sans-serif; }
默认情况下,临近的两个CSS属性值可以通过 空格 进行连接。
/*===== SCSS =====*/
p {
margin: 3px + 4px auto;
}
/*===== CSS =====*/
p {
margin: 7px auto; }
字符串里可以通过 #{} 插入动态的值,就像下面这样:
/*===== SCSS =====*/
p:before {
content: "I ate #{5 + 10} pies!";
}
/*===== CSS =====*/
p:before {
content: "I ate 15 pies!"; }
如果字符串插值 #{} 中的变量是空值,则插值表达式的结果将被视为空字符串。
/*===== SCSS =====*/
$value: null; // 空值
p:before {
content: "I ate #{$value} pies!";
}
/*===== CSS =====*/
p:before {
content: "I ate pies!"; }
布尔运算
SassScript当中布尔类型的值支持 与and 、 或or 、 非not 运算。
/*===== SCSS =====*/
$year: 2018;
$moth: 8;
$day: 6;
$name: Hank;
#app {
@if ($year > 2010 and $moth==8 or $day==6 not $name==Hank) {
color: blue;
} @else {
color: red;
}
}
/*===== CSS =====*/
#app {
color: blue; }
List运算
数组不支持任何特定的计算方式,只能通过 list函数 进行操作,下面代码是一个使用了 hsl($hue, $saturation, $lightness) 函数的例子:
/*===== SCSS =====*/
a {
color: hsl(120deg, 100%, 50%)
}
/*===== CSS =====*/
a {
color: lime; }
圆括号
SASS中可以通过圆括号 {} 来调整运算的优先级。
/*===== SCSS =====*/
p {
width: 1em + (2em * 3);
}
/*===== CSS =====*/
p {
width: 7em; }
函数
SassScript内置了许多有用的函数,可以通过普通的CSS语句进行调用。
/*===== SCSS =====*/
p {
color: hsl(0, 100%, 30%);
}
/*===== CSS =====*/
p {
color: #990000; }
关键词参数
Sass函数还允许通过 关键词参数 (即 带有键名的参数 )进行调用,上面的例子也可以改写为下面代码:
/*===== SCSS =====*/
p {
color: hsl($hue: 0, $saturation: 100%, $lightness: 30%);
}
/*===== CSS =====*/
p {
color: #990000; }
虽然这不是很简洁,但可以使样式表更加容易阅读。同时还能够让函数提供更灵活的接口,提供多个参数的同时,并不会变得难以调用。
命名参数能够以任何顺序进行传递,而带有默认值的参数可以省略掉。如果命名参数是变量名,那么下划线与破折号可以互换使用。
可以通过 Sass::Script::Functions 查看SASS函数及其参数名称的完整清单,也可以通过Ruby进行自定义。
插值 #{}
开发人员可以通过 插值 ( interpolation ) #{} 在选择器和属性名称中使用SassScript变量。
/*===== SCSS =====*/
$name: uinika;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}
/*===== CSS =====*/
p.uinika {
border-color: blue; }
还可以使用 #{} 将SassScript放入属性值,大多数情况下这种方式并不优于变量,但是使用 #{} 意味着其附近的任意操作都将被视为普通CSS,例如:
/*===== SCSS =====*/
p {
$font-size: 12px;
$line-height: 30px;
font: #{$font-size}/#{$line-height};
}
/*===== CSS =====*/
p {
font: 12px/30px; }
SassScript中的 &
如同使用选择器一样,SassScript中的 & 引用着当前的 父选择器列表 ( 一个由逗号分隔List作为元素再通过空格进行分隔的List )。
.navbar.text .sidebar.word,
.menu.item {
$selector: &;
}
上面代码中 $selector 的值为 ((".navbar.text" ".sidebar.word"), ".menu.item") ,这里的复合选择器使用了引号 " 标识它是一个字符串,但是真实情况它可能是没有引号的。即使其父选择器并没有包含逗号和空格, & 依然会拥有两层嵌套,因此它能够被持续的访问。
当 父级选择器列表 不存在的时候, & 运算符的值为 null ,使用 mixin 当中可以通过该特性判断 父选择器列表 是否存在。
@mixin does-parent-exist {
@if & {
&:hover {
color: red;
}
}
@else {
a {
color: red;
}
}
}
变量默认值 !default
可以在变量后添加 !default 声明,如果变量已被赋值就不会再被重新进行赋值,如果变量未被赋值,则会被赋予新值。
/*===== SCSS =====*/
$content: "First content";
$content: "Second content?" !default;
$new_content: "First time reference" !default;
#main {
content: $content;
new-content: $new_content;
}
/*===== CSS =====*/
#main {
content: "First content";
new-content: "First time reference"; }
如果变量的值是 null ,则会被 !default 视为没有赋值。
/*===== SCSS =====*/
$content: null;
$content: "Non-null content" !default;
#main {
content: $content;
}
/*===== CSS =====*/
#main {
content: "Non-null content"; }
@规则与指令
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
/*===== SCSS =====*/ @import "app.css"; @import "pp" screen; @import "https://uinika.github.io/app"; @import url(app); /*===== CSS =====*/ @import url(app.css); @import "pp" screen; @import "https://uinika.github.io/app"; @import url(app);
除上述四种情况外,文件拓展名为 .scss 或 .sass 的情况都会进行预编译处理,即使 文件拓展名缺省 依然能够正确的进行导入。
// 下面2种导入方式等效 @import 'base'; @import 'base.scss;
SASS允许在一个 @import 语句内同时导入多个文件。
@import 'base', 'reset', 'app';
@import 语句内可以有限制的使用 #{} 插值,即不能动态的导入基于变量的SASS文件,只能用于标准CSS的 @import url(""); 导入方式。
/*===== SCSS =====*/
$family: unquote("Droid+Sans");
@import url("http://fonts.googleapis.com/css?family=#{$family}");
/*===== CSS =====*/
@import url("http://fonts.googleapis.com/css?family=Droid+Sans");
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 ,仍然会去包含引入文件的内容。
/*===== SCSS =====*/
// 文件demo.scss包含如下代码
.demo {
color: red;
}
#app {
@import "demo"; // 在文件app.scss中引入demo.scss
}
/*===== CSS =====*/
#app .demo {
color: red;
}
指令只能用于文档的基本级别,在一个嵌套上下文当中被 @import 引入的文件里,诸如 @mixin 或者 @charset 是不被允许的。
不能在 mixin 或者 控制指令 当中嵌套使用 @import 。
@media / #media
Sass中 @media 的用法与CSS相同,但是允许在CSS规则中嵌套使用。编译时 @media 将被编译到文件最外层,包含嵌套的父级选择器。
/*===== SCSS =====*/
.sidebar {
width: 300px;
@media screen and (orientation: landscape) {
width: 500px;
}
}
/*===== CSS =====*/
.sidebar {
width: 300px;
}
@media screen and (orientation: landscape) {
.sidebar {
width: 500px;
}
}
@media 的查询条件可以嵌套使用,编译后SASS会自动添加 and 关键字。
/*===== SCSS =====*/
@media screen {
.sidebar {
@media (orientation: landscape) {
width: 500px;
}
}
}
/*===== CSS =====*/
@media screen and (orientation: landscape) {
.sidebar {
width: 500px;
}
}
@media 的查询条件当中也可以使用SassScript。
/*===== SCSS =====*/
$media: screen;
$feature: -webkit-min-device-pixel-ratio;
$value: 1.5;
@media #{$media} and ($feature: $value) {
.sidebar {
width: 500px;
}
}
/*===== CSS =====*/
@media screen and (-webkit-min-device-pixel-ratio: 1.5) {
.sidebar {
width: 500px;
}
}
@extend / #extend
现实的Web页面开发场景当中,当一个 class 属性拥有其它多个 class 所具备样式的时候,通常的做法是在HTML上书写全部这些 class ,如同下面的代码:
<div class="error seriousError">
Oh no! You've been hacked!
</div>
<style>
.error {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
border-width: 3px;
}
</style>
这样的代码并不利于维护,并且会带来非语义相关的样式到HTML上。
使用SASS提供的 @extend 可以在不同CSS选择器之间继承样式,从而完美避免上述问题。
/*===== SCSS =====*/
.error {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}
/*===== CSS =====*/
.error, .seriousError {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
border-width: 3px;
}
上面代码中 .error 的样式会自动应用到 .seriousError ,作用相当于同时使用了这两个 class 上的样式。
通过 .error 定义的其它样式规则,同样会在 .seriousError 上工作,比如下面这样定义样式后。
.error.danger {
background-image: url("/image/warning.png");
}
然后 <div class="seriousError danger"> 的背景图片同样会是 warning.png 。
工作原理
@extend 是通过向被继承的CSS选择器,插入需要继承的CSS选择器来进行工作,例如上面例子的代码转换如下:
/*===== SCSS =====*/
.error {
border: 1px #f00;
background-color: #fdd;
}
.error.danger {
background-image: url("/image/warning.png");
}
.seriousError {
@extend .error;
border-width: 3px;
}
/*===== CSS =====*/
.error, .seriousError {
border: 1px #f00;
background-color: #fdd; }
.error.danger, .seriousError.danger {
background-image: url("/image/warning.png"); }
.seriousError {
border-width: 3px; }
合并选择器的时候, @extend 将会避开不必要的样式规则重复,并且不会生成不能匹配任何东西的选择器。
继承复杂的选择器
class 选择器并不是唯一能被继承的选择器,其它选择器也可以被继承,甚至可以去继承一个CSS元素,接着看下面的代码:
/*===== SCSS =====*/
.hoverlink {
@extend a:hover;
}
a:hover {
text-decoration: underline;
}
/*===== CSS =====*/
a:hover, .hoverlink {
text-decoration: underline; }
就像前面内容中的 .error.intrusion 一样, a:hover 上定义的规则也会在 .hoverlink 上工作,即使它们拥有着其它的选择器。
/*===== SCSS =====*/
.hoverlink {
@extend a:hover;
}
.comment a.user:hover {
font-weight: bold;
}
/*===== CSS =====*/
.comment a.user:hover, .comment .user.hoverlink {
font-weight: bold; }
多重继承
一个选择器可以同时继承多个选择器的样式,例如:
/*===== SCSS =====*/
.error {
border: 1px #f00;
background-color: #fdd;
}
.attention {
font-size: 3em;
background-color: #ff0;
}
.seriousError {
@extend .error;
@extend .attention;
border-width: 3px;
}
/*===== CSS =====*/
.error, .seriousError {
border: 1px #f00;
background-color: #fdd;
}
.attention, .seriousError {
font-size: 3em;
background-color: #ff0;
}
.seriousError {
border-width: 3px;
}
上面代码中,每个 .seriousError 旁边都会伴随 .error 和 .attention 选择器。通常,文档后面定义的样式将会采取这样的优先级规则: .seriousError 的背景色是 #ff0 而非 #fdd ,因为 .attention 定义的位置比 .error 更加靠后.
多重继承时还可以使用逗号 , 分隔的选择器列表,例如: @extend .error, .attention 作用与 @extend .error; @extend .attention; 等效。
链式继承
一个选择器可以继承另外一个选择器,另外一个选择器又继承了第三个选择器,从而形成链式的继承结构。
/*===== SCSS =====*/
.error {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}
.criticalError {
@extend .seriousError;
position: fixed;
top: 10%;
bottom: 10%;
left: 10%;
right: 10%;
}
/*===== CSS =====*/
.error, .seriousError, .criticalError {
border: 1px #f00;
background-color: #fdd; }
.seriousError, .criticalError {
border-width: 3px; }
.criticalError {
position: fixed;
top: 10%;
bottom: 10%;
left: 10%;
right: 10%; }
上面代码中,所有使用 .error 的位置也会使用 .seriousError ,同时使用 .seriousError 和 .error 的位置则都会使用到 .criticalError 。
选择器序列
诸如 .foo .bar or .foo + .bar 这样的选择器队列目前还不能被继承,但是嵌套选择器本身是可以使用 @extend 实现继承的,例如:
/*===== SCSS =====*/
a {
color: blue;
&:hover {
text-decoration: underline;
}
}
#fake-links .link {
@extend a;
}
/*===== CSS =====*/
a, #fake-links .link {
color: blue; }
a:hover, #fake-links .link:hover {
text-decoration: underline; }
有时候选择器序列会继承出现在另一个序列中的选择器,这种情况下就需要合并两个序列,例如:
/*===== SCSS =====*/
#admin .tabbar a {
font-weight: bold;
}
#demo .overview .fakelink {
@extend a;
}
由上面代码在技术上可能匹配2个选择器序列当中的每个序列,可能导致生成的CSS样式体积较大。因此如果例子中需要10个选择器,Sass将会只会生成一个有效的选择器。
当被合并的2个选择器序列没有共同的选择器时,那么将会生成2个新的选择器:第1个选择器序列在第2个之前,以及第2个选择器序列在第1个之前,就像下面这样:
/*===== CSS =====*/
#admin .tabbar a,
#admin .tabbar #demo .overview .fakelink,
#demo .overview #admin .tabbar .fakelink {
font-weight: bold; }
如果需要合并的2个选择器序列共享了部分选择器,那么这些共享的选择器将会被合并,只有不同的选择器会发生变化。下面的例子中,两个选择器序列都包含 #admin ,所以编译结果会合并这两个ID选择器。
/*===== SCSS =====*/
#admin .tabbar a {
font-weight: bold;
}
#admin .overview .fakelink {
@extend a;
}
/*===== CSS =====*/
#admin .tabbar a,
#admin .tabbar .overview .fakelink,
#admin .overview .tabbar .fakelink {
font-weight: bold; }
只能@extend的选择器
有些时候,你只想为需要去 @extend 继承的Class选择器编写样式,并且不希望直接使用到HTML。在编写Sass库的时候,你可能要为使用者提供 @exnted 继承的样式,并且使用者如果不需要可以选择忽略。
如果使用普通的Class选择器,会生成更多额外的CSS。而且可能与其它Class选择器发生样式冲突,因此Sass提供了 占位选择器 ( placeholder selectors ),例如 %foo 。
占位选择器的外观看起来与Class或者ID选择器类似,只是 # 和 . 被 % 所替代。占位选择器可以出现在任意Class和ID选择器能够使用的位置,并能够防止其中定义的规则集被渲染为CSS。
/*===== SCSS =====*/
// This ruleset won't be rendered on its own.
#context a%extreme {
color: blue;
font-weight: bold;
font-size: 2em;
}
.notice {
@extend %extreme;
}
/*===== CSS =====*/
#context a.notice {
color: blue;
font-weight: bold;
font-size: 2em; }
!optional
当继承一个选择器的时候,如果 @extend 没有工作将会发生错误,例如对于 a.important {@extend .notice} ,如果当前文档没有包含 .notice 选择器就会发生错误。如果被包含的这个选择器 .notice 是 h1.notice ,由于 h1 与 a 具有冲突,因此没有新的选择器将会被创建。
如果希望 @extend 不生成任何选择器,可以在选择器后面添加 !optional 标志,例如:
a.important {
@extend .notice !optional;
}
在指令中使用@extend
在 @media 之类的指令中使用 @extend 会存在一些限制,Sass不能让 @media 代码块之外的CSS规则应用到其内部的选择器,从而避免四处拷贝样式并新建大量的样式块。这意味如果在 @media 或其它CSS指令中使用 @extend ,那么就只能继承当前指令块内出现的选择器。
例如下面的代码工作状态良好:
@media print {
.error {
border: 1px #f00;
background-color: #fdd;
}
.seriousError {
@extend .error;
border-width: 3px;
}
}
但是这样的代码就是 错误 的:
.error {
border: 1px #f00;
background-color: #fdd;
}
@media print {
.seriousError {
// 无效的继承,因为.error定义在@media print指令的外面
@extend .error;
border-width: 3px;
}
}
希望有一天浏览器能够原生支持 @extend ,并允许它用于 @media 或其它指令当中。
@at-root / #at-root
某些需要放置在文档根元素上的样式,可以就近的放置在其父选择器上,该特性同样可用于行内CSS选择器。
/*===== SCSS =====*/
.parent {
...
@at-root .child { ... }
}
/*===== CSS =====*/
.parent { ... }
.child { ... }
@at-root 可以通过 代码块 同时包含多个选择器。
/*===== SCSS =====*/
.parent {
...
@at-root {
.child1 { ... }
.child2 { ... }
}
.step-child { ... }
}
/*===== CSS =====*/
.parent { ... }
.child1 { ... }
.child2 { ... }
.parent .step-child { ... }
@at-root 默认情况下仅仅是排除掉父级选择器,但是实际开发过程中也有可能需要将 @media 之类的指令选择性的过滤、移动到外面,这里可以通过 @at-root (without: ...) 和 @at-root (with: ...) 语法实现该操作。
/*===== SCSS =====*/
@media print {
.page {
width: 8in;
@at-root (without: media) {
color: red;
}
}
}
/*===== CSS =====*/
@media print {
.page {
width: 8in;
}
}
.page {
color: red;
}
可以向 @at-root 传递2个特殊值, rule 表示普通的CSS规则,因此 @at-root (without: rule) 作用类似于没有任何查询条件的 @at-root ; all 代码全部CSS规则, @at-root (without: all) 代表样式将会被移动到所有指令和CSS规则的外侧。
@debug
@debug 指令打印SassScript表达式的值到标准错误输出流,可以用来调试具有复杂SassScript的SASS文件,例如:
/*===== SCSS =====*/ @debug 10em + 12em; /*===== CSS =====*/ Line 1 DEBUG: 22em
@warn
@warn 指令打印SassScript表达式的值到标准错误输出流,在警告用户弃用库或者修复 mixin 轻微错误的场景比较有用, @warn 和 @debug 存在2个主要区别:
可以使用 --quiet 命令行选项或SASS的 :quiet 选项来关闭 @warn 警告。
样式表trace将与消息一起被打印,开发人员可以方便的看到 @warn 警告发生在样式表的哪里个位置。
/*===== SCSS =====*/
@mixin adjust-location($x, $y) {
@if unitless($x) {
@warn "Assuming #{$x} to be in pixels";
$x: 1px * $x;
}
@if unitless($y) {
@warn "Assuming #{$y} to be in pixels";
$y: 1px * $y;
}
position: relative;
left: $x;
top: $y;
}
@error
@error 指令抛出一个SassScript表达式的值作为致命错误,其中包含了友好的堆栈trace,可以用来验证 mixin 和 function 的参数,例如:
@mixin adjust-location($x, $y) {
@if unitless($x) {
@error "$x may not be unitless, was #{$x}.";
}
@if unitless($y) {
@error "$y may not be unitless, was #{$y}.";
}
position: relative;
left: $x;
top: $y;
}
SASS当前还没有提供捕获 @error 的方式。
控制指令和表达式
SassScript支持一些基本的控制指令和表达式,例如只在某些条件下包含样式,或是在多次变化中包含相同的样式。
控制指令属于高级特性,并不经常使用,主要用于 mixins 当中,尤其是对于Compass这样的库。
if()
内置的 if() 函数允许在一个条件处理分支返回两种可能的结果,它可以用于任意的脚本上下文。 if() 函数只能判断相应的那个参数并且返回,这允许引用已经被定义或者计算的变量,否则将会导致错误发生( 例如除以零 )。
/*===== SCSS =====*/
div {
width: if(true, 1px, 2px);
height: if(false, 1px, 2px);
}
/*===== CSS =====*/
div {
width: 1px;
height: 2px; }
@if
@if 指令接收一个SassScript表达式,当表达式返回 false 或者 null 之外的数据时,会选择使用接下来的嵌套语句,
/*===== SCSS =====*/
p {
@if 1+1==2 {
border: 1px solid;
}
@if 5 < 3 {
border: 2px dotted;
}
@if null {
border: 3px double;
}
}
/*===== CSS =====*/
p {
border: 1px solid; }
@if 语句后面可以跟随多个 @else if 语句和一个 @else 语句,如果 @if 语句失败,SASS将逐个尝试 @else if 语句,直至其中一个成功;如果全部失败,则会执行 @else 语句。例如:
/*===== SCSS =====*/
$type: monster;
p {
@if $type==ocean {
color: blue;
}
@else if $type==matador {
color: red;
}
@else if $type==monster {
color: green;
}
@else {
color: black;
}
}
/*===== CSS =====*/
p {
color: green; }
@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> 的值。
/*===== SCSS =====*/
@for $i from 1 through 3 {
.item-#{$i} {
width: 2em * $i;
}
}
/*===== CSS =====*/
.item-1 {
width: 2em; }
.item-2 {
width: 4em; }
.item-3 {
width: 6em; }
@each
@each 指令的使用格式为 @each $var in <list or map> ,其中 $var 可以是任意变量名称( 例如 $length 或 $name ),而 <list or map> 是一个返回 list 或者 map 的SassScript表达式。
@each 规则设置 $var 为 list 或 map 当中的每一项,输出样式中将会使用 $var 的实际值。
/*===== SCSS =====*/
@each $animal in puma, sea-slug, egret, salamander {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
/*===== CSS =====*/
.puma-icon {
background-image: url("/images/puma.png"); }
.sea-slug-icon {
background-image: url("/images/sea-slug.png"); }
.egret-icon {
background-image: url("/images/egret.png"); }
.salamander-icon {
background-image: url("/images/salamander.png"); }
多重赋值
@each 指令能够以 @each $var1, $var2, ... in <list> 的格式使用多个变量,如果 <list> 是一个列表中的列表元素,那么子列表中的每个元素将会被分配至各自的变量,例如:
/*===== SCSS =====*/
@each $animal, $color, $cursor in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
border: 2px solid $color;
cursor: $cursor;
}
}
/*===== CSS =====*/
.puma-icon {
background-image: url("/images/puma.png");
border: 2px solid black;
cursor: default; }
.sea-slug-icon {
background-image: url("/images/sea-slug.png");
border: 2px solid blue;
cursor: pointer; }
.egret-icon {
background-image: url("/images/egret.png");
border: 2px solid white;
cursor: move; }
由于 maps 被视为一个键值对的列表,所以多重赋值也能够正常的工作,例如:
/*===== SCSS =====*/
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}
/*===== CSS =====*/
h1 {
font-size: 2em; }
h2 {
font-size: 1.5em; }
h3 {
font-size: 1.2em; }
@while
@while 指令可以用来重复输出嵌套的样式,直至SassScript表达式返回结果为 false ,可以用于实现比 @for 语句更复杂的循环,但日常开发较少使用。
/*===== SCSS =====*/
$i: 6;
@while $i>0 {
.item-#{$i} {
width: 2em * $i;
}
$i: $i - 2;
}
/*===== CSS =====*/
.item-6 {
width: 12em; }
.item-4 {
width: 8em; }
.item-2 {
width: 4em; }
Mixin指令
混入( Mixin )可以用来定义Web应用当中需要 复用 的样式,避免出现 .float-left 这样的无语义class。 混入 里可以包含CSS规则以及SASS语法,甚至可以携带参数和引入变量,从而方便的生成各种灵活的样式。
定义混入: @mixin
混入( Mixin )通过SASSR提供的 @mixin 指令来定义,后面跟随混入的名称与参数以及内容块,例如下面代码定义了一个名为 large-text 的Mixin:
@mixin large-text {
font: {
family: Arial;
size: 20px;
weight: bold;
}
color: #ff0000;
}
混入Mixin当中可以同时包含选择器和CSS属性,其中选择器还可以包含父级的选择器,例如:
@mixin clearfix {
display: inline-block;
&:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
* html & {
height: 1px
}
}
因为历史原因,混入Mixin的名字以及其它SASS标识符可以互换连字符 - 和下划线 _ ,例如对于名称为 add-column 的混入,也可以将其视为 add_column ,反之亦然。
包含混入: @include
@include 指令通过混入的名称和可选的参数,可以引入特定的混入Mixin至文档,从而包含并复用其样式,例如下面代码:
/*===== SCSS =====*/
@mixin large-text {
font: {
family: Arial;
size: 20px;
weight: bold;
}
color: #ff0000;
}
.page-title {
@include large-text;
padding: 4px;
margin-top: 10px;
}
/*===== CSS =====*/
.page-title {
font-family: Arial;
font-size: 20px;
font-weight: bold;
color: #ff0000;
padding: 4px;
margin-top: 10px; }
混入Mixin可以包含在其它规则之外,只要它们不直接定义CSS属性或使用父选择器引用,例如:
/*===== SCSS =====*/
@mixin silly-links {
a {
color: blue;
background-color: red;
}
}
@include silly-links;
/*===== CSS =====*/
a {
color: blue;
background-color: red; }
混入Mixin也可以包含其它的混入,接下来看下面的代码:
@mixin compound {
@include highlighted-background;
@include header-text;
}
@mixin highlighted-background {
background-color: #fc0;
}
@mixin header-text {
font-size: 20px;
}
事实上,混入Mixin还可以包含自己,但是在SASS 3.3版本之前,这种递归调用的行为是被禁止的。另外,只有定义了后代选择器的混入Mixin可以安全的混入源文件的顶层。
参数
混入Mixin可以使用SassScript的值作为参数,参数被包括在混入当中并作为为变量提供给其内部使用。多个参数通过逗号 , 分隔,然后通过传递对应顺序的参数进行调用,例如:
/*===== SCSS =====*/
@mixin sexy-border($color, $width) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p {
@include sexy-border(blue, 1in);
}
/*===== CSS =====*/
p {
border-color: blue;
border-width: 1in;
border-style: dashed; }
混入Mixin可以通过变量赋值语法为参数指定默认值,调用的时候如果缺省参数,则使用默认值代替,例如:
/*===== SCSS =====*/
// 定义参数$width的默认值为1
@mixin sexy-border($color, $width: 1in) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p {
@include sexy-border(blue);
}
h1 {
@include sexy-border(blue, 2in);
}
/*===== CSS =====*/
p {
border-color: blue;
border-width: 1in;
border-style: dashed; }
h1 {
border-color: blue;
border-width: 2in;
border-style: dashed; }
关键字参数
混入Mixin在通过 @include指令 进行引入时也可以使用显式的关键字参数,例如可以将上面例子改写为:
p {
@include sexy-border($color: blue);
}
h1 {
@include sexy-border($color: blue, $width: 2in);
}
虽然上面的方式不够简明,但能够使Scss代码更加容易阅读,并让函数接口更加灵活,并方便的进行多参数的混入。命名参数可以按照任意顺序进行传递,如果有默认值可以省略。并且由于命名参数本质上是变量名称,因此下划线 _ 和连字符 - 可以互换使用。
尾部逗号
如果传入 混入Mixin 或者 函数Function 的最后一个参数是位置性的或关键字风格的,那么可以在这个参数后面跟随一个逗号 , ,这种编码风格可以更加简洁的进行重构,并且减少语法错误。
可变参数
有些场景下, 混入Mixin 或者 函数Function 的参数个数是不确定的,例如创建一个可以接收任意数量参数的 box-shadow 混入。SASS里可以通过 ... 符号添加 可变参数 支持,将所有剩余的参数包装到一个列表List。
/*===== SCSS =====*/
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
/*===== CSS =====*/
.shadows {
-moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
-webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; }
可变参数可以包含任意需要传递给 混入Mixin 或者 函数Function 的关键字参数,它们可以通过 keywords($args) 函数 进行访问,该函数将会返回一个Map,将它们作为一个从字符串( 不包含 $ )到值的映射返回。
可变参数也可以在调用Mixin的时候使用,使用相同的语法能够扩展一个值的列表List,以便每个值作为单独的参数传入;或者扩展值的Map,从而使每个键值对都会作为一个关键字参数来进行处理。例如:
/*===== SCSS =====*/
@mixin colors($text, $background, $border) {
color: $text;
background-color: $background;
border-color: $border;
}
// 使用List作为参数
$values: #ff0000, #00ff00, #0000ff;
.primary {
@include colors($values...);
}
// 使用Map作为参数
$value-map: (text: #00ff00, background: #0000ff, border: #ff0000);
.secondary {
@include colors($value-map...);
}
/*===== CSS =====*/
.primary {
color: #ff0000;
background-color: #00ff00;
border-color: #0000ff; }
.secondary {
color: #00ff00;
background-color: #0000ff;
border-color: #ff0000; }
可以同时传递一个List和一个Map参数,只要List参数位于Map类型参数之前,例如 @include colors($values..., $map...) 。
我们可以通过可变参数包装Mixin并添加额外样式,而不改变Mixin的参数签名。这样 参数 将会通过被包装的Mixin直接进行传递,例如:
@mixin wrapped-stylish-mixin($args...) {
font-weight: bold;
@include stylish-mixin($args...);
}
.stylish {
// $width参数将会以关键字的形式传递到stylish-mixin
@include wrapped-stylish-mixin(#00ff00, $width: 100px);
}
传入内容块到Mixin
样式块可以传递到混入Mixin所包含样式的位置,它们会出现在Mixin内任意 @content 指令的位置,从而能够定义关联到选择器和指令的构造的抽象。
/*===== SCSS =====*/
@mixin apply-to-ie6-only {
* html {
@content;
}
}
@include apply-to-ie6-only {
#logo {
background-image: url(/logo.gif);
}
}
/*===== CSS =====*/
* html #logo {
background-image: url(/logo.gif); }
同样的Mixin可以在简写语法的 .sass 文件中完成( @mixin 和 @include 分别用 = 和 + 表示 )。
=apply-to-ie6-only
* html
@content
+apply-to-ie6-only
#logo
background-image: url(/logo.gif)
注意: 当 @content 指令被指定多次或者位于一个循环当中,样式块将会在每次调用中被复制并引用。
有些Mixin可能需要传入一个内容块,或者根据是否传入内容块而具备不同的行为。当内容块传递至当前Mixin的时候, content-exists() 函数将会返回 true ,从而该函数可以用于帮助实现这类行为。
变量作用域与内容块
传递给Mixin的内容块会在其定义的作用域中进行运算,而非混入Mixin的作用域。这意味Mixin当中的局部变量不能传递至样式块,并将其解析为全局值使用。
/*===== SCSS =====*/
$color: white;
@mixin colors($color: blue) {
background-color: $color;
@content;
border-color: $color;
}
.colors {
@include colors {
color: $color;
}
}
/*===== CSS =====*/
.colors {
background-color: blue;
color: white;
border-color: blue; }
此外,这还清楚的表明,在被传递的内容块中使用的变量与Mixin,会关联到内容块定义位置的其它样式。
/*===== SCSS =====*/
@mixin smartphone {
@content;
color: gold;
}
#sidebar {
$sidebar-width: 300px;
width: $sidebar-width;
@include smartphone {
width: $sidebar-width / 3;
}
}
/*===== CSS =====*/
#sidebar {
width: 300px;
width: 100px;
color: gold; }
函数指令
SASS支持开发人员自定义函数,并且在任意值或脚本上下文里使用,例如:
/*===== SCSS =====*/
$grid-width: 40px;
$gutter-width: 10px;
@function grid-width($n) {
@return $n * $grid-width + ($n - 1) * $gutter-width;
}
#sidebar {
width: grid-width(5);
}
/*===== CSS =====*/
#sidebar {
width: 240px; }
正如上面代码所展示的,函数可以访问任意全局变量并接收参数,其行为类似一个混入Mixin。函数可以包含语句,并且必须调用 @return 来设置函数的返回值。
与Mixin一样,SASS可以通过关键字参数调用自定义函数,因此可以像下面这样调用上面例子中的函数:
#sidebar {
width: grid-width($n: 5);
}
建议在函数前面添加前缀以避免命名冲突,同时也能够有效区分哪些是自定义的函数,例如为自定义函数添加公司名称作为前缀 -uinika-grid-width 。自定义函数同样也支持可变参数,使用方式与混入Mixin相同。
同样由于历史原因,函数名以及其它SASS标识符当中的连字符 - 和下划线 _ 都是可以互换的,如果定义了一个名为 grid-width 的函数,那么同样可以通过 grid_width 进行调用,反之亦然。
输出类型
虽然SASS默认输出的CSS格式是良好的,但是可以根据开发人员的个人习惯,使用SASS提供的其它4种输出格式。即可以通过 :style 选项进行设定,也可以在命令行中直接使用 --style 标志。
:nested
嵌套nested是SASS默认的输出格式,每个属性都会占据一行,但缩进不是固定的,每个规则都基于其嵌套深度进行缩进。例如:
#main {
color: #fff;
background-color: #000; }
#main p {
width: 10em; }
.huge {
font-size: 10em;
font-weight: bold;
text-decoration: underline; }
嵌套格式在阅读较大CSS工程文件时能够帮助开发人员快速的了解代码的结构。
:expanded
扩展expanded格式类似于人工手写的CSS样式,每个属性和规则都独占用一行。规则当中的CSS属性会进行缩进,但规则本身并不会进行任何特殊的缩进,例如:
#main {
color: #fff;
background-color: #000;
}
#main p {
width: 10em;
}
.huge {
font-size: 10em;
font-weight: bold;
text-decoration: underline;
}
:compact
紧凑compact格式比起上述两种格式占据的空间更小,这种格式将重点聚焦在选择器而非CSS属性上。每个CSS规则独自占据一行,该行还包括全部的CSS属性。 每个嵌套的规则都会另起新行,不嵌套的选择器会输出空白行作为分隔 ,例如:
#main { color: #fff; background-color: #000; }
#main p { width: 10em; }
.huge { font-size: 10em; font-weight: bold; text-decoration: underline; }
:compressed
压缩compressed格式会尽可能减小所生成文件的体积,并在该文件末尾产生一个换行。除了必要的选择器分隔之外,几乎没有多余的空格。这种格式会让颜色等CSS属性值以最简短的方式来进行表示,由于代码可读性极差,因此主要用于生产环境。
#main{color:#fff;background-color:#000}#main p{width:10em}.huge{font-size:10em;font-weight:bold;text-decoration:underline}
扩展SASS
对于前端开发人员的特殊需求,SASS为提供了多项高阶定制功能,但是使用这些功能需要非常熟悉 Ruby 。例如自定义SASS函数、缓存存储、自定义导入器等等,这些功能在日常前端开发工作当中并不常用,可以根据需要查阅SASS的源码API文档。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
MATLAB智能算法/科学与工程计算技术丛书
编者:温正//孙华克 / 清华大学 / 2017-09-01 / 89.0
一起来看看 《MATLAB智能算法/科学与工程计算技术丛书》 这本书的介绍吧!