Sass “始解”

之前学过Stylus,但是Stylus似乎并没有Sass这么流行,很多CSS框架并不支持Stylus。所以学习下Sass,以便更好地阅读CSS框架。

一. 安装

首先你得安装Ruby,这个Stylus需要NodeJS是一个道理

ruby -v // 检测下是否安装好

接着用Ruby的包管理工具gem,安装Sass,以及Compass(一个有用的Sass工具库)

gem install sass
gem install compass

如果下载过慢,可以考虑换镜像

sass -v // 查看是否安装好

二、语法

Sass的语法有两种,一种是花括号的格式,一种是以缩进代替花括号的格式(和Stylus一样哦)。前者的文件扩展名是.scss,后者则是.sass。这里我们以scss来介绍Sass语法。

我将先入门SassScript,这是最接近编程语言的部分,这部分对Javascript工程师来说,是很好理解的

0. 命令行

Sass通过sass命令可以对源文件进行编译,并且同时还有不同的输出格式

sass input.sass output.css

也可以自动监听某个文件,自动编译

sass --watch input.scss:output.css

你甚至可以监控文件夹下所有文件

sass --watch app/sass:public/stylesheets

Sass还可以改变输入格式

默认的格式是nested

大致的格式是这样的

.huge {
font-size: 10em;
font-weight: bold;
text-decoration: underline; }

还有expanded格式

sass input.scss output.css --style expanded

.huge {
font-size: 10em;
font-weight: bold;
text-decoration: underline;
}

另外还有compact格式

sass input.scss output.css --style compact

.huge { font-size: 10em; font-weight: bold; text-decoration: underline; }

最后还有压缩格式compressed

sass input.scss output.css --style compressed

.huge{font-size:10em;font-weight:bold;text-decoration:underline}

和普通编程语言一样,Sass也提供交互命令行

sass -i
>> 1px + 2px + 3px
6px
>> "hello world"
"hello world"
>>

1. 变量

Sass的变量以$开头,命名方式如下:

$highlight-color: #F90;

引用也十分简单

nav {
color: $highlight-color;
}

如果是Stylus中,我们可能是这样用的

highlight-color = #F90
nav
color: $highlight-color

说实话其实相差不多。。。

同时Sass中的变量也支持块级作用域

nav {
$width: 100px;
width: $width;
color: $highlight-color;
}

当然你也可以将块级作用域中的变量提升为全局变量

nav {
$width: 100px !global;
width: $width;
color: $highlight-color;
}

header {
width: $width
}

另外当你给变量添加!default时, 当前变量并不会覆盖之前定义的变量

$link-color: yellow;
$link-color: blue !default;
a.disable {
color: $link-color;
}

编译成

a.disable {
color: yellow;
}

这在模块化Sass中十分有用

2. 数据类型

无非也就是

  1. 数字(1, 2, 5px)
  2. 字符串("MIT")
  3. 布尔型
  4. null
  5. 数组
  6. map(key1: value1, key2: value2)
  7. 颜色值(#fff, blue, rgb(0, 0, 0))

主要需要掌握是基础用法及其各种内置函数

这个地址列出了Sass所有内置函数

http://sass-lang.com/documentation/Sass/Script/Functions.html

颜色

颜色的操作函数主要是rgb,rgba,hsl,hsla

这些基本是在CSS颜色函数的基础上实现的,但rgb和hsl值会转换为hex值

另外还有一些实用的颜色函数

比如降低颜色透明度的函数

transparentize($color, 0.1);

这个函数将会使得颜色的透明度降低0.1

而opacify则可以提高颜色的透明度

opacify($color, 0.1)

数字

js中的操作很像

>> round(0.6)  // 四舍五入
1
>> ceil(1.2) // 向上取整
2
>> floor(1.8) // 向下取整
1
>> abs(-1) // 取绝对值
1
>> min(1, 2) // 取最小值
1
>> max(1, 2) //取最大值
2
>> percentage(0.1) // 小数转百分比
10%

字符串

比较有趣的是quoteunquote,两者正好相反

一个为字符串加上引号,一个为字符串删除引号

font-family: quote(Arial)

将会被编译成

font-family: "Arial";

font-family: unquote("Arial");

将会被编译成

font-family: Arial;

其他的字符串操作基本上是字符串长度,切割字符串,插入字符串,大小写转换等,和编程语言中大同小异。

list

list在Sass中是比较有用的,例如,定义一个font-family列表

$font-face: Helvetica, Arial, sans-serif;

nth函数可以得到列表的某个序列的字体,注意是从1开始获取的

nth($font-face, 1)

join则可以将两个数组列表连接起来

假设现在还有一个字体列表,我们要应用在.title上,我们可以这样做

$title-font: "Unica One", "Vollkorn";
.title {
font-family: join($title-font, $font-face)
}

那么就会转换为

.title {
font-family: "Unica One", "Vollkorn", Helvetica, Arial, sans-serif;
}

还有append函数可以添加数组元素,zip可以构建多维数组, index的作用则和javascriptindexOf相似

map

map的概念和javascriptObject概念差不多,表示方法如下

$map: (color: #fff, font-family: "Safi")

而其操作也很简单

map-get($map, color) // #fff
map-keys($map) // ("color", "font-family")
map-values($map) // (#ffffff, "Safi")
map-remove($map, font-family) // (color: #fff)
$map1: (font-family: "Safi")
map-merge($map, $map1) // (color: #fff, font-family: "Safi")
map-has-key($map, font-size) // false

嗯,比JavaScript还更加健全,简便

3. 运算

Sass支持数字的+,-,*,/,%,并且会自动进行单位的转换

>> 3px + 2pt
5.6666666667px

另外由于原生CSS支持除法,所以为了兼容,Sass必须将表达式括在括号中才能进行运算

>> 1px/2
1px/2
>> (1px/2)
0.5px

但是当我们使用变量时,又不需要括号

>> $a: 5px
5px
>> $b: 2
2
>> $a/$b
2.5px

如果我们需要保持运算在原生CSS中怎么办?

利用插值可以很好做到这一点

>> #{$a}/#{$b}
"5px/2"

Sass字符串的运算也有些小技巧

p:before {
content: "Foo " + Bar;
font-family: sans- + "serif";
}

会被编译为

p:before {
content: "Foo Bar";
font-family: sans-serif; }

你可能已经发现了,有引号的字符串连接无引号的,则编译成有引号的字符串;反之,则编译成无引号的字符串

Sass的布尔运算则和其他编程语言也很类似

and -> &&
or -> ||
not -> !

基本用语义化的方式代表符号

4. 插值

我认为插值是预处理器中最好用的一个属性之一,刚用Stylus的时候,想向动态改变属性名称、类名,变量啥的都不行,最后就用到了插值。

Sass当中当然也有这个特性

$name: foo;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}


以下部分就不属于SassScript了


5. 函数

之前我们已经介绍过不少内置函数,对于函数的用法,我们已经不陌生了。但是如何自定义函数呢?

这里我们需要用到一个指令@function

$grid-width: 40px;
$gutter-width: 10px;

@function grid-width($n) {
@return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }

编译成

#sidebar {
width: 240px; }

6. 混入与继承

混入和继承这两个概念都有在其他编程语言中应用。在Sass中这两种模式也得到了很好的利用,可以大大提升写CSS的效率。

混入其实就是把重复代码抽取出来,在需要的时候插入

@mixin border-radius($size: 5px) { // 混入函数可以设置默认值
-moz-border-radius: $size;
-webkit-border-radius: $size;
border-radius: $size;
}
.notice {
@include border-radius;
}

@mixin是混入指令,@include则是插入指令

将会编译成

.notice {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}

可以看到使用了默认值

我们还可以传入参数

.notice {
@include border-radius(6px);
}

这将会编译成

.notice {
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
}

关键词参数和参数变量我们也可以使用

@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}

a {
@include link-colors(
$hover: #fff,
$normal: #000,
$visited: #000
); // 关键词参数
}


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

如果还想在混入器之外导入其他的内容,还可以用@content占位

$color: white;
@mixin colors($color: blue) {
background-color: $color;
@content;
border-color: $color;
}
.colors {
@include colors { color: $color; }
}

这将会编译成

.colors {
background-color: white;
color: white;
border-color: white;
}

那么,怎么判断是否构成一个混入器呢?一个官方经验法则是

判断一组属性是否应该组合成一个混合器,一条经验法则就是你能否为这个混合器想出一个好的名字。如果你能找到一个很好的短名字来描述这些属性修饰的样式,比如rounded-corners,fancy-font或者no-bullets,那么往往能够构造一个合适的混合器

最后,为了方便混入的使用,@mixin可以简化为=,@include可以简化为+。


继承@extend则比混入的规则更加复杂

但是继承也十分好理解,就是继承父选择器所有样式,并且还拥有自己的实现。

不过,继承父选择器所有样式并不容易。

简单的@extend应用可能是下面这样的:

.dialog {
border-radius: 10px;
}

.small-dialog {
width: 200px;
@extend .dialog;
}

这将会编译成

.dialog, .small-dialog {
border-radius: 10px; }

.small-dialog {
width: 200px; }

但是当你的父选择器变得复杂的时候,那么Sass的工作就多了很多了

例如下方这样例子

ul .disable {
color: #eee;
}

nav .btn {
@extend .disable;
font-size: 30px;
}

将会编译成这样

ul .disable, ul nav .btn, nav ul .btn {
color: #eee; }

nav .btn {
font-size: 30px; }

Sass充分考虑了ul和nav之前所有关系,使得你可能写的所有html结构都能应用所继承的样式

所以你的父子选择器的层级越深,所产生的CSS就越复杂

因此在使用@extend时注意父子选择器的层级,不要过深

@extend还有一个特殊用法,有的类似混入

如果你想写一个样式,但是不想被使用在HTML中,这个功能将会有些作用。

%dialog {
border-radius: 10px;
}

.small-dialog {
width: 200px;
@extend %dialog;
}
.mid-dialog {
width: 450px;
@extend %dialog;
}

将会编译成

.small-dialog, .mid-dialog {
border-radius: 10px; }

.small-dialog {
width: 200px; }

.mid-dialog {
width: 450px; }

最后,使用@extend需要注意在指令中是不能引用外部的样式的

以下样式编译会出错

.error {
border: 1px #f00;
background-color: #fdd;
}

@media print {
.seriousError {
// INVALID EXTEND: .error is used outside of the "@media print" directive
@extend .error;
border-width: 3px;
}
}

7. 模块化(@import)

@import可以导入外部样式表,不仅是.scss文件(不需要加后缀),而且可以是原生css文件(只要你加上.css)

另外Sass的模块化有个约定的规则。

如果外部Sass文件名为_color.scss,那么你可以直接@import "color"引入,不需要加上额外的_

Sass模块还可以嵌套,且模块内变量将是局部变量


.list {
@import "item"
}

怎么样,Sass的模块化是不是很简单^_^

8. 流程控制和表达式

if else的判断

和普通语言中的if else别无二致 看下面的例子

@mixin fl($type) {
@if $type == lf {
float: left;
}
@else if $type == rt {
float: right;
}
@else {
float: none;
}
}

div {
@include fl(rt)
}

将会编译成

div {
float: right;
}

@for @each @while 之循环

@for有两种表示方法


@for $i from 1 to 3 {
.item:nth-of-type(#{$i}) {
font-size: $i + 10px;
}
}

会被编译为

.item:nth-of-type(1) {
font-size: 11px; }

.item:nth-of-type(2) {
font-size: 12px; }

而另一种方式

@for $i from 1 through 3 {
.item:nth-of-type(#{$i}) {
font-size: $i + 10px;
}
}

则会被编译成以下的css

.item:nth-of-type(1) {
font-size: 11px; }

.item:nth-of-type(2) {
font-size: 12px; }

.item:nth-of-type(3) {
font-size: 13px; }

from tofrom through的差别还是很明显的

@each遍历数组,个人认为应用比@for广泛得多

我们可以写一段和输出样式相同的代码

@each $i in 1, 2, 3 {
.item:nth-of-type(#{$i}) {
font-size: $i + 10px;
}
}

当然@each还可以更强大

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

这将会编译成

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

另外@each还可以遍历map

@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}

嗯, @each还是相当6的

@while也是相当简单,和普通语言类似

$i:1;
@while $i > 4 {
.item:nth-of-type(#{$i}) {
font-size: $i + 10px;
}
}

这段代码会输出和@for,@each一样的代码

最后介绍一个函数,类似三元表达式?:

>> if(true, 1px, 2px)
1px

Sass还是挺正经的编译型语言啊。

9. 注释

Sass中提供两种注释

多行注释//和单行注释/* */ 和其他编程语言中一样

但是//并不会留存在编译后的css中,而/* */则会留存在编译后的css中,而且如果多行注释的第一个字符为!,那么多行注释即使在压缩模式下也会保存下来(这种方式和Stylus类似)

另外Sass中的注释也是动态的,可以进行插值

$version:1.0
/* author jeff version:#{$version} */

这将会编译成

/* author jeff version:1.0 */

10. CSS扩展

CSS的扩展其实很简单

嵌套是Sass的重要特性之一

.parent {
color: #fff;
.child {
font-size: 18px;
}
>.direct-child {
font-size: 19px;
}
+ .sibling {
font-size: 20px;
}
~ .siblings {
font-size: 21px;
}
}

将会编译成

.parent {
color: #fff; }
.parent .child {
font-size: 18px; }
.parent > .direct-child {
font-size: 19px; }
.parent + .sibling {
font-size: 20px; }
.parent ~ .siblings {
font-size: 21px; }

嵌套使得代码结构化程度,可读性都大大提高

引用父选择器也简化了代码

.item {
&-icon {
font-size: 12px;
}
}

将会编译成

.item {
width: 200px; }
.item-icon {
width: 30%;
font-size: 12px; }

需要注意的是&只能出现在选择器的开头,不能作为后缀。

11. 其它

@at-root @debug @warn @error

参考资料

分享