Sass学习笔记4 - 总结篇

CSS预处理器 —— Sass学习笔记4

注释

  1. 单行注释: // 不会被编译后的css文件中
  2. 多行注释:/* */ 会被完整输出到编译后的 CSS 文件中
    1. ! 作为多行注释的第一个字符:表示在压缩输出模式下保留这条注释并输出到 CSS 文件中,通常用于添加版权信息
    2. 多行注释中也可以写入 #{} 插值语句 (interpolation) ,用于输出变量值

嵌套规则

  1. 后代选择器

  2. 父选择器标识符 &

    1. & 的值:如果不存在父选择器,那么&的值就是空值null;若是存在,那么&的值就是一个用逗号分隔的数组列表(数组的知识可见变量类型——数组Lists一章
  3. 群组选择器

  4. 子组合选择器 >

  5. 同层组合选择器 + 和 ~

#main {
    color: #0ff0;  

    // 父选择器标识符 -- 在编译时, &会被替换成父选择器 #main
    &:hover{            // 用法1: 表示给#main元素添加hover样式
        color: #fff;
    }
    .ie &{  // 用法2:在父选择器标识符&之前添加选择器,表示在.ie下的#main样式
        color: #00f;
    } 
    &-bar { // 用法3:用作占位符,编译时会用父选择器的名称替换&,表示#main-bar
        border: 1px solid; 
    } 

    // 后代选择器 -- 在编译时,会在.redbox前面加上父选择器名,也就是 #main .redbox
    .redbox { 
        color: #000;
    }

    // 群组选择器 -- 编译时,会将#main和h1,h2,h3分别组合,然后将三者重新组合成一个群组选择器,也就是 #main h1, #main h2, #main h3{a{margin-bottom: .8em;}}
    h1, h2, h3 {
        // 后代选择器
        a {margin-bottom: .8em}
    }
    
    // 直接子元素 >,编译时会在前面加上父选择器名,编译为: #main > section
    > section { background: #eee }
    
    // 同层组合选择器+ 编译为 nav + #main
    // 父选择器标识符&会被替换成父选择器名,表示 nav元素紧跟其后的#main元素
    nav + & { margin-top: 0 } 
    
    // 同层全体组合选择器~ 编译为:#main ~ artical
    // 表示#main元素后面所有同一层级的article元素,不管它们中间是否隔着别的元素 
    ~ article { border-top: 1px dashed #ccc } 
}

编译结果

#main { color: #0ff0;}
// 父选择器标识符编译出来的结果
#main:hover { color: #fff; }
.ie #main { color: #00f; }
#main-bar { border: 1px solid; }
// 后代选择器编译出来的结果
#main .redbox { color: #000; }
// 群组选择器编译出来的结果
#main h1 a, #main h2 a, #main h3 a { margin-bottom: .8em; }
#main > section { background: #eee; } // 子组合选择器编译结果
nav + #main { margin-top: 0; } // 同层组合选择器编译结果
#main ~ article { border-top: 1px dashed #ccc; } // 同层全体组合选择器编译结果
@mixin does-parent-exist {
  @if & { // 如果父选择器存在,那么就给父元素添加hover样式
    &:hover { 
      /* 查看一下&的值: #{&} */ 
      color: red;
    }
  } @else {
    a {
      color: red;
    }
  }
}
div p{ @include does-parent-exist(); }
// div p:hover { /*parent: div p:hover*/ color: red; }

属性嵌套

嵌套属性的规则:把属性名从中划线 - 的地方断开在根属性后边添加一个冒号 :紧跟一个{ },把子属性部分写在这个{ }块中

编译规则:如果选择器嵌套一样,sass会把你的子属性一一解开,把根属性和子属性部分通过中划线 - 连接起来

#main{
    // 属性嵌套在编译时,会将根属性与子属性通过中划线【-】连接起来
    background:{
        color: #f00;
        image: url('1.jpg');
        repeat: no-repeat;
        position: 50% 50%;
    }
    // 命名空间也可以包含自己的属性值
    font: 12px/24px {
        weight: 600;
        family: 'Microsoft Yahei';
    }
    // 指明例外规则
    border: 1px solid #ddd{
        left: 0;
        right: 0;
    }
}

编译结果

#main { 
    background-color: #f00; 
    background-image: url("1.jpg"); 
    background-repeat: no-repeat; 
    background-position: 50% 50%;     
    font: 12px/24px; 
    font-weight: 600; 
    font-family: 'Microsoft Yahei'; 
    border: 1px solid #ddd; 
    border-left: 0; 
    border-right: 0; 
}

变量 $

$变量名: 变量值; // 变量值:css中所有合法的属性值都可以,还可以是属性名。。 // 全局变量
body{ 
    $变量名: 变量值; // 规则块内 -- 局部变量
    css属性: $变量名; // 变量引用。编译成css时,该$变量名会被其所指代的变量值替换
}

1. 变量作用域

1. 全局作用域:不在嵌套规则内定义的变量则可在任何地方使用(嵌套规则外 -- 全局变量)
2. 块级作用域:嵌套规则内定义的变量只能在嵌套规则内使用(嵌套规则内 -- 局部变量)
3. 将局部变量转换为全局变量可以添加 `!global` 声明(嵌套规则内 -- 全局变量 -- $变量名: 变量值 !global;)

2. 变量声明

  1. sass变量名以美元符号$开头,大小写敏感
  2. 可以用中划线和下划线两种形式命名,中划线方式更普遍
  3. 两种用法互相兼容,也就是说,用中划线声明的变量可以用下划线的方式引用,反之亦然
  4. 变量声明后还未生效,只有当变量被引用后才生效

3. 变量赋值

变量的赋值与css的赋值是一样的

  1. 任何可以用作css属性值的赋值都可以用作sass的变量值
  2. 在声明变量时,变量值也可以引用其他变量
  3. 可以在变量值后面使用 !global 声明,将局部变量 变成 全局变量
  4. 可以在变量第一次赋值时在后面使用 !default 指定变量默认值
    1. 只在第一次赋值时有用。也就是说,如果变量已经被赋值,就不会再被重新赋值,但是如果变量还没有被赋值,则会被赋予新的值。
    2. 变量是 null 空值时将视为未被 !default 赋值。

4. 变量引用

  1. 凡是css属性的标准值可存在的地方,变量就可以使用。css生成时,变量会被它们的值所替代。
  2. 变量可以修改,但一经修改,所有引用此变量的地方(同一作用域)生成的值都会随之改变
  3. 变量有一种特殊的引用方法:插值语句 #{变量}

插值语句 #{}

插值 (interpolation) —— 将一个占位符,替换成一个值。

  • 通过 #{} 插值语句可以在选择器属性名中使用变量 —— 其实就是字符串连接的语法糖而已。
  • #{} 插值语句也可以在属性值中插入 SassScript —— 可以避免 Sass 运行运算表达式,直接编译 CSS。
$name: foo;
$attr: border;
p.#{$name} { // 在选择器中使用变量
  #{$attr}-color: blue; // 在属性名中使用变量
}
// p.foo { border-color: blue; }

p {
  $font-size: 12px;
  $line-height: 30px;
  font: #{$font-size}/#{$line-height};  // 避免将/用作除法运算符
}
// p { font: 12px/30px; }

1. 用于css函数

  • Sass会把CSS函数(包括伪类)认为是字符串,所以想要在最后获得它们的值,要求你转义所有同它们一起使用的变量。否则结果会与你的预期大不相同
    • 比如:calc()url()linear-gradient()radial-gradient()cubic-bezier()
$sidebar-width: 250px;
$max: 3;
// for语句循环
@for $i from 1 through $max {
    .el:nth-of-type(#{$i}) { // 与css伪类一起使用,需要转义
        width: calc(100% - #{$sidebar-width}); // 与css函数calc一起使用,需要转义
    }
}
// 编译结果
.el:nth-of-type(1) { width: calc(100% - 250px * 1); }
.el:nth-of-type(2) { width: calc(100% - 250px * 2); }
.el:nth-of-type(3) { width: calc(100% - 250px * 3); }

2. 用于css指令

  • 与css指令一起使用,比如@support@page,最重要的是@media
    • 如果@media后面紧跟 变量,那么就必须使用插值才可以
    • 如果@media后面紧跟(()),就不再需要插值变量了,因为Sass会求出所有在这些括号里面的值
$screen: screen;
@media #{$screen} { // @media字符串后面紧跟着变量,那么就必须使用插值进行转义
    // ...
}

$value: 1336px;
@media (max-width: $value) { // @media字符串后面紧跟着(),那么()中的变量就无需插值,sass会先求出括号中的值
    // ...
}

变量类型

SassScript 支持 6 种主要的数据类型(变量的6中主要数据类型):

  • 数字1, 2, 13, 10px
  • 字符串,有引号字符串与无引号字符串,"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)

SassScript 也支持其他 CSS 属性值,比如 Unicode 字符集,或 !important 声明。然而Sass 不会特殊对待这些属性值,一律视为无引号字符串

可以使用 type-of() 检查变量或者一段表达式的类型

1. 字符串类型

  • 有引号字符串 (quoted strings) :如 "Lucida Grande" 'Microsoft Yahei'
  • 无引号字符串 (unquoted strings),如 sans-serifbold
  • 一般情况下,变量字符串在编译时类型不会有变化,sass中有引号,编译的css中也就有引号,sass中无引号,编译的css中也就没有引号。但是有一种例外情况,就是在使用插值语句#{}时,有引号字符串会被编译成无引号字符串。
$font-family: 'Microsoft Yahei', sans-serif; // 一个有引号字符串,一个无引号字符串
body { 
    font: 14px/24px $font-family;
    font-family: #{$font-family}; // 使用插值语句
}
// 编译后. 可发现在编译css文件时不会改变字符串的类型
body { 
    font: 14px/24px "Microsoft Yahei", sans-serif; // 在编译 CSS 文件时不会改变字符串类型
    font-family: Microsoft Yahei, sans-serif;   // 有引号字符串被编译为无引号字符串
}

2. 数组 Lists

sass 中的数组(或者叫列表)指的是 通过 空格 或者 逗号 分隔的一系列的值。事实上,独立的值也被视为数组 —— 只包含一个值的数组。数组中也可以包含子数组。如用逗号分隔:1px 2px, 5px 6px ,还有用空格分隔 (1px 2px) (5px 6px)

数组的规则:

  • 除非列表太长不能写在80字符宽度的单行中,否则应该始终单行显示。 —— 最好单号显示
  • 除非适用于CSS,否则应该始终使用逗号作为分隔符。 —— 建议使用逗号分隔
  • 除非为空或者嵌套在另一个数组中,否则始终不要使用括号。
  • 始终不要添加尾部的逗号 —— 基于逗号分隔的数组允许保留结尾的逗号,但建议不要保留

注意:

  • 列表第一项的索引是1,而不是0,这一点和javascript数组不同
$list: ();  // 空数组。若被引用,编译会报错 —— 空数组不可以直接编译成 CSS

$list-space: "item-1" "item-2" "item-3"; // 使用空格进行分隔
$list-space: "item-1","item-2","item-3"; // 使用逗号进行分隔

// 数组嵌套推荐使用小括号
$list: ( 
    ("item-1.1", "item-1.2", "item-1.3"), 
    ("item-2.1", "item-2.2", "item-2.3"),
    ("item-3.1", "item-3.2", "item-3.3")
);

// nth获取元素中指定的某个值
$list:"antzone",2,"softwhy.com";
.div {
    content: nth($list,1);  // content: length($list) 求数组的长度
}

操作数组的函数:

  1. length($list):返回一个列表的长度值。
  2. index(list,value):返回一个值在列表中的位置值
  3. nth(list,index):返回一个列表中指定的某个标签值。
  4. append(list1,val, [$separator]):将某个值放在列表的最后。
  5. join(list1,list2, [$separator]):将两个列给连接在一起,变成一个列表。
  6. zip($lists…):将几个列表结合成一个多维的列表。。

3. Maps

​ Maps可视为键值对的集合,键被用于定位值。和Lists不同,Maps必须被圆括号包围键值对被逗号分割

​ Maps可用于任何Lists可用的地方,在List函数中 Map会被自动转换为List , 如 (key1: value1, key2: value2)会被List函数转换为 key1 value1, key2 value2 ,反之则不能。

操作Maps的函数:

  1. map-has-key(map,key) 判断某个key是否存在于map中
  2. map-get(map,key) 获取map中键key对应的值
  3. map-merge(map1,map2) 将两个maps合并成一个新的maps,不影响原来的maps对象
  4. map-keys(map) 和 map-values(map)
  5. map-remove(map,key) 从map中删除一个key,返回一个新的map
  6. keywords($args) 返回一个函数的阐述,这个参数可以动态设置key和value
  7. @each name,value in $map 循环maps

示例:搭配函数使用

// 声明一个Maps变量:Maps必须被圆括号包围,键值对被逗号分割
$layer: (
  offcanvas: 1,
  lightbox: 500,
  dropdown: 10,
  tooltip: 15
);

// 定义函数,参数$name对应$layer中的key
@function layer($name) {
  @if map-has-key($layer, $name) { // 判断传入的参数是否存在于$layer
    @return map-get($layer, $name); // 根据$name获取$layer中对应的值
  }

  @warn "The key #{$name} is not in the map '$layer'";
  @return null;
};
// 使用
.m-lightbox {
  z-index: layer(lightbox); // 调用方法,并传入对应的值
} 
// 编译后
.m-lightbox { z-index: 500; }

搭配列表一起使用

// 定义maps变量,每个key的值都是一个列表,表示(背景颜色, 字体颜色)
$buttons: (
    error: (#d82d2d, #666), 
    success: (#52bf4a, #fff),
    warning: (#c23435, #fff)
);
// 使用
.m-button {
    display: inling-block;
    padding: .5em; 
    // 遍历maps遍历
    @each $name, $colors in $buttons {
        $bgcolor: nth($colors, 1);  // 通过nth获取数组中的第一项
        $fontcolor: nth($colors, 2);// 通过nth获取数组中的第二项
        // 父选择器标识符&的第三种用法:用在选择器中作为占位符
        &--#{$name} {
            background-color: $bgcolor;
            color: $fontcolor;
        }
    }
} 
// 编译后
.m-button { display: inling-block; padding: .5em;}
.m-button--error { background-color: #d82d2d; color: #666; }
.m-button--success { background-color: #52bf4a; color: #fff; }
.m-button--warning { background-color: #c23435; color: #fff; }

运算

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

1. 数字运算

SassScript 支持数字的运算有:

  1. 数字的加减乘除、取整等运算 (+, -, *, /, %),如果必要会在不同单位间转换值。
  2. 关系运算 <, >, <=, >=
  3. 可用于所有数据类型的相等运算 **==, != **

1.1 除法运算

在 SassScript 中, / 有两个作用:分隔数字、除法运算符

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

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

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

p { 
    $width: 1000px;
    width: $width/2;            // 值的一部分是变量 —— 除法运算符
    width: round(1.5)/2;        // 值是算术表达式的一部分 —— 除法运算符
    height: (500px/2);          // 值被圆括号包裹 —— 除法运算符
    margin-left: 5px + 8px/2px; // 加减乘除运算
    // 要使用变量,又要确保 `/` 不做除法运算 而是 完整地编译到 CSS 文件中 —— 使用插值语句
    $font-size: 12px;
    $line-height: 30px;
    font: #{$font-size}/#{$line-height};    // font: 12px/30px;
}

1.2 内置函数

  • percentage($number) 将一个不带单位的数值转成百分比
  • round($number)$number 四舍五入为整数,$number可带单位
  • ceil($number) 大于 $number ,向上取整
  • floor($number)ceil()相反,去除 $number 小数,向下取整
  • abs($number),返回 $number 的绝对值
  • min($numbers…),返回 $number... 的最小值
  • max($numbers…),返回 $number... 的最大值
  • random([$limit]),返回一个随机数

2. 颜色值运算

颜色的值(RGB)是分段计算进行的,也就是分别计算红色,绿色,以及蓝色的值

  1. 颜色与颜色进行算术运算; color: #010203 + #040506; // color: #050709;
  2. 颜色与数字进行算术运算;color: #010203 * 2; // color: #020406;
  3. 若颜色值中包含 alpha channel(rgba 或 hsla 两种颜色值),则必须拥有相等的 alpha 值才能进行运算
    1. 也就是说:rgba + rgba ; hsla + hsla ... 这两者的透明度 a 必须相同才可以进行运算。因为术运算不会作用于 alpha 值
    2. 颜色值的 alpha channel 可以通过 opacifytransparentize 两个函数进行调整
  4. IE 滤镜要求所有的颜色值包含 alpha 层,而且格式必须固定 #AABBCCDD,使用 ie_hex_str 函数可以很容易地将颜色转化为 IE 滤镜要求的格式
p {
    color: #010203 + #040506; // 编译为 color: #050709;
    // 计算 01 + 04 = 05、02 + 05 = 07、03 + 06 = 09 

    color: #010203 * 2; // 编译为 color: #020406;
    // 计算 01 * 2 = 02、02 * 2 = 04、03 * 2 = 06
    
    color: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75); // 两者的alpha必须相同,都为0.75
    // 编译为:color: rgba(255, 255, 0, 0.75);
} 
$red: rgba(255, 0, 0, 0.5);
$green: #00ff00;
p {
    color: opacify($red, 0.3); // 使用 opacity 函数 -- color: rgba(255, 0, 0, 0.8);
    background-color: transparentize($red, 0.25); // 使用 transparentize 函数 
    // 编译结果: background-color: rgba(255, 0, 0, 0.25); 
} 
div { // 使用ie-hex-str函数将颜色转化未符合 IE 滤镜要求的格式  #RRGGBBAA
  filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($red)}');
}
// 编译结果
div {
  filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=#FF00FF00, endColorstr=#80FF0000);
}

2.1 内置函数

对于颜色的操作,scss提供了大量内置函数,非常方便。

  • rgba() 能省掉手工转换 hex 到 rgb 格式的工作
  • opacify () 增加透明度
  • transparentize () 减少透明度值
  • lighten / darken 是基于 HSL 明度变换,这个比较适合 button 按钮的 normal 态和 hover 态变换,
  • saturate / desaturate 是基于 HSL 饱和度 变换,
  • tint / shade 色彩算法,通过增加 白色(tint) 和 黑色(shade) 的占比来生成系列色
  • complement 补色
    • 在色彩理论中,如果一种颜色与另一种颜色混合后,呈现中性的灰黑色,那么这两种颜色就互为补色

3. 字符串运算

  1. 使用 + 可用于连接字符串
    1. 有引号字符串+无引号字符串=有引号字符串; 'a' + b = 'ab'
    2. 无引号字符串+有引号字符串=无引号字符串;a + 'b' = ab
  2. 运算表达式与其他值连用时,用空格做连接符。 margin: 3px + 4px auto;
  3. 有引号的文本字符串中使用 #{} 插值语句可以添加动态的值。 contnet: 'welcome, #{username}'
  4. 空的值被视作插入了空字符串。 $value: null; content: 'I ate #{value} pies';

3.1 内置函数

  • unquote($string) 删除 $string 前后的引号。
  • quote($string)$string前后添加引号
  • str-length($string) 返回 $string 的长度
  • str-insert($string, $insert, $index) 在指定位置插入字符
  • str-index($string, $substring) 返回指定字符在字符串的位置
  • to-upper-case($string)$string小写字母转成大写字母
  • to-lower-case($string)$string大写字母转成小写字母

4. 布尔运算

SassScript 支持布尔型的 andor 以及 not 运算。

5. 数组运算

数组不支持任何运算方式,只能使用 list functions 控制(见变量类型——数组一章)。

5.1 内置函数

  1. length($list):返回一个列表的长度值。
  2. index(list,value):返回一个值在列表中的位置值
  3. nth(list,index):返回一个列表中指定的某个标签值。
  4. append(list1,val, [$separator]):将某个值放在列表的最后。
  5. join(list1,list2, [$separator]):将两个列给连接在一起,变成一个列表。
  6. zip($lists…):将几个列表结合成一个多维的列表。。

Sass高级 —— 规则和指令

@-Rules 与指令

1. @import

Sass 拓展了 @import 的功能,允许其导入 SCSS 或 Sass 文件。

1. 例外情况

通常,@import 寻找 Sass 文件并将其导入,但在以下情况下,@import 仅作为普通的 CSS 语句,不会导入任何 Sass 文件。

  • 文件拓展名是 .css
  • 文件名以 http:// 开头;
  • 文件名是 url()
  • @import 包含 media queries。
2. 基础知识

Sass 拓展了 @import 的功能:

  • 若不存在上面的4种情况,文件的拓展名是 .scss.sass,则导入成功
    • 就算没有指定拓展名,Sass 也会试着寻找文件名相同,拓展名为 .scss.sass 的文件并将其导入
  • Sass 允许同时导入多个文件,使用逗号 , 进行分隔
  • 导入文件也可以使用 #{ } 插值语句,但只能作用于 CSS 的 url() 导入方式,而不是通过变量动态导入 Sass 文件
  • 被导入的文件将合并编译到同一个 CSS 文件
  • 被导入的文件中所包含的变量或者混合指令 (mixin) 都可以在导入的文件中使用 —— 变量和mixin可通用
  • Sass 在当前地址寻找 Sass 文件
    • 如果需要设定其他地址,可以用 :load_paths 选项,或者在命令行中输入 --load-path 命令
/** 4种情况 @import仅作为普通的 CSS 语句 */
@import "foo.css";                      // 文件拓展名是 .css
@import "foo" screen;                   // @import 包含 media queries
@import "http://foo.com/bar";   // 文件名以 http:// 开头
@import url(foo);                           // 文件名是 url()

/** 正确示例 */
@import "foo.scss";     // 1- 文件的拓展名是 .scss 或 .sass 
@import "foo";              // 2- 没有扩展名,Sass会试着找 foo.scss 或者 foo.sass 进行导入
@import "rounded-corners", "text-shadow";           // 同时导入多个文件,使用逗号进行分隔

/** 3- 使用插值语句 -- 只能作用域 CSS 的 url() 导入方式*/
$family: unquote("Droid+Sans");     // 字符串内置函数unquote(str)去除引号
@import url("http://fonts.googleapis.com/css?family=\#{$family}"); // CSS url()方式导入
// 编译结果: @import url("http://fonts.googleapis.com/css?family=Droid+Sans");
3. 局部 (Partials)

​ 如果需要导入 SCSS 或者 Sass 文件,但又不希望将其编译为 CSS,只需要在文件名前添加下划线,这样会告诉 Sass 不要编译这些文件,但导入语句中却不需要添加下划线

—— 需要导入 + 不希望被编译 ---- 将文件命名时,前面加上下划线。
—— 如:文件名为 _colors.scss; 导入 @import colors.scss; 其实导入的就是 _colors.scss文件
—— 注意:不可存在同名文件,即不能再有 colors.scss 文件 ---- 因为导入语句中不需要添加下划线

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

4. 嵌套 @import

​ 大多数情况下,一般在文件的最外层(不在嵌套规则内)使用 @import,其实,也可以将 @import 嵌套进 CSS 样式或者 @media 中,与平时的用法效果相同,只是这样导入的样式只能出现在嵌套的层中。

可以理解成作用域:

  • 在文件最外层使用 @import ---- 导入的样式、变量、mixin等全局可用 ---- 全局
  • 在嵌套规则内,或者@media中 ---- 导入的样式只能出现在嵌套的层中 ---- 局部

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

2. @media

CSS中的 @media 规则指定一组规则的目标媒体类型(以逗号分隔)

Sass 中 @media 指令与 CSS 中用法一样,只是增加了一点额外的功能:允许其在 CSS 规则中嵌套

  1. 如果 @media 嵌套在 CSS 规则内,编译时,@media 将被编译到文件的最外层,包含嵌套的父选择器
  2. @mediaqueries 允许互相嵌套使用,编译时,Sass 自动添加 and
  3. @media 甚至可以使用 SassScript(比如变量,函数,以及运算符)代替条件的名称或者值
$media: screen;
$feature: -webkit-min-device-pixel-ratio;
$value: 1.5;

.navbar {
  width: 300px;
  // 1- @media嵌套在 CSS 规则内
  @media #{$media} { // 3-使用 SassScript 如果@media后面直接跟变量,就需要用插值语句;
    .sidebar {
      // 2- 嵌套使用
      @media ($feature: $value) {// 3-使用 SassScript 如果跟着圆括号,则不需要用插值语句
        width: 500px;
      }
    }
  }
}
// 编译结果
.navbar { width: 300px; }
// 嵌套在 CSS 规则内的@media,编译时,将被编译到文件的最外层,包含嵌套的父选择器
@media screen and (-webkit-min-device-pixel-ratio: 1.5) { 
  .navbar .sidebar { width: 500px; } 
}

3. @extend

​ 情形:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式。

​ 通常做法:在 HTML 中给元素定义两个 class,一个通用样式,一个特殊样式。

​ 不便之处:必须时刻记住特殊样式需要参考通用样式

​ 解决之法:使用 @extend ,告诉 Sass 将一个选择器下的所有样式(包括其他使用到该选择器的样式)继承给另一个选择器

  1. 如何工作@extend继承它后面选择器的样式,但并不是简单的将样式插入到被继承选择器所在的位置,而是智能的进行了合并,比如避免无谓的重复,不能匹配任何元素的选择器也会删除
    1. 比如 a:hover{...} .error{ @extend a:hover; } 将a的所有样式继承给.error
    2. 编译时,使用.error替换a:hover,然后合并到一起,也就是 a:hover, .error{....}
  2. @extend-Only 选择器】通过 %占位符名称 标记的样式不会被编译,通过 @extend
  3. 延伸复杂的选择器】Sass 允许延伸任何定义给 单个元素 的选择器,不止是简单的Class 选择器,比如 .special.coola:hover 或者 a.user[href^="http://"]
  4. 多重延伸】同一个选择器可以一次继承多个选择器,它所包含的属性将继承给所有被延伸的选择器
    1. 比如 .warn{...} .error{ ... } .success{ @extend .warn; @extend .error; } 也可以写成 .success{ @extend .warn , .error; }
  5. 继续链】当一个选择器延伸给第二个后,可以继续将第二个选择器延伸给第三个。。。
    1. 比如 .warn{...} .error{ @extend .warn; } .success{ @extend .error; } 那么success也会继承.warn
// .error以及其他使用到.error 的样式
.error {
  border: 1px solid #f00;
}
.error.intrusion { // 其他使用到 .error 的样式也会同样继承给 .seriousError 
  background-image: url("./image/hacked.png");
}
// a:hover以及其他使用到a:hover的样式
a:hover {
  text-decoration: underline;
}
.comment a.user:hover { // 合并规则:合并选择器列
  font-weight: bold;
}
// @extend-Only 选择器 -- 该选择器不能被编译
.success %place-s{  
  font-size: 14px;
}

// @extend的使用
.seriousError {             // 【多重延伸】 也可以写成 @extend .error, a:hover;
  @extend .error;       // 【延伸】使用 @extend 继承.error的所有样式
  @extend a:hover;      // 【延伸复杂的选择器】使用 @extend 继承a:hover 的所有样式
  border-width: 3px;    // 单独给 .seriousError 设定特殊样式
}
.criticalError {
  @extend .seriousError;    // 【继续延伸】将.seriousError的所有样式延伸给.criticalError
  color: #fff;                      // 单独给 .criticalError 设定特殊样式
}
.font{
  @extend %place-s;
}

编译为

.error, .seriousError, .criticalError { 
  border: 1px solid #f00; 
}
.error.intrusion, .seriousError.intrusion, .criticalError.intrusion { 
  background-image: url("./image/hacked.png"); 
}
a:hover, .seriousError, .criticalError{ 
  text-decoration: underline; 
}
.comment a.user:hover, .comment .user.seriousError, .comment .user.criticalError { 
  font-weight: bold; 
}
.success .font{ 
  font-size: 14px;
}
.seriousError, .criticalError { 
  border-width: 3px; 
}
.criticalError { 
  color: #fff; 
}
  1. 选择器列】暂时不可以将选择器列延伸给其他元素,但是,却可以将其他元素延伸给选择器列

    1. 比如 不可以 @extend .foo .bar@extend .foo + .bar;但是可以:.foo .bar{ @extend a; }
  2. 合并选择器列】有时会遇到复杂的情况,比如选择器列中的某个元素需要延伸给另一个选择器列,这种情况下,两个选择器列需要合并。当两个列 (sequence) 合并时:

    1. 没有包含相同的选择器,将生成两个新选择器:第一列出现在第二列之前,或者第二列出现在第一列之前
    2. 包含了相同的选择器,相同部分将会合并在一起,其他部分交替输出
    3. 注意:按照全排列的方式,生成可能选择器组合的数目也许很庞大,scss只会生成最可能的选择器组合
#admin .tabbar a {
  font-weight: bold;
}

// 1-【合并选择器列】没有包含相同的选择器
#demo .overview .fakelink { 
  @extend a;                                        // 延伸选择器列 #admin .tabbar a 中的单个元素a
}
/** 编译结果 */
#admin .tabbar a, 
#admin .tabbar #demo .overview .fakelink,   // 生成新的选择器1:第一列在第二列之前
#demo .overview #admin .tabbar .fakelink {  // 生成新的选择器2:第二列在第一列之前
  font-weight: bold; 
}

// 2-【合并选择器列】包含相同的选择器-#admin
#admin .overview .fakelink {    
  @extend a;                                        // 延伸选择器列 #admin .tabbar a 中的单个元素a
}
/** 分析
 * 由于它们具有共同的祖辈#admin选择器,所以将其放在所有选择器的开头毫无疑问。
 * 将.fakelink选择器插入到被继承的a选择器位置。
 * 但是我们无法判断.tabbar和.overview谁包含谁,所以两者就相互包含一下
 */

/** 编译结果 */
#admin .tabbar a, 
#admin .tabbar .overview .fakelink,  
#admin .overview .tabbar .fakelink {  
  font-weight: bold; 
}
  1. !optional 声明】如果要求 @extend 不生成新选择器,可以通过 !optional 声明达到这个目的
p{
  color:red;
  // @extend .notice;       // 由于并不存在一个名为notice的样式类,所以会报错
  @extend .notice !optional;    // 由于是可选的,即便notice样式类不存在也不会报错
}
// 编译结果
p{ color: red; }
  1. 在指令中延伸】在指令中使用 @extend 时 有一些限制
    1. 比如在 @media (或者其他 CSS 指令)中使用 @extend,必须延伸给相同指令层中的选择器
.warn{ color: yellow; }
@media print {
  .error { color: #fdd; }
  .seriousError {
    @extend .error;                 // OK
    @extend .warn;                  // ERROR    .warn在"@media print"指令外层
    border-width: 3px;
  }
}

4. @at-root

  1. @at-root指令可以使一个或多个规则被限定输出在文档的根层级上,而不是被嵌套在其父选择器下
  2. 默认情况下,@at-root只会跳出选择器嵌套,而不能跳出指令,比如@media或@support指令
  3. 如果要跳出指令,可以搭配 without 和 with
    1. without 的作用是指定跳出哪些指令
    2. with 的作用是指定不跳出哪些指令,其他的指令都跳出
      1. @at-root (without/with: all); 表示所有
      2. @at-root (without/with: rule); 表示常规css
      3. @at-root (without/with: media); 表示 @media 指令
      4. @at-root (without/with: support); 表示 @support 指令
.parent{
  color:red;
  @at-root .child { // 使用@at-root 将.child规则输出到文档的根层级上
    color: #fff;
  }
  // 多个规则
  @at-root {
    .child-1 { color: yellow; }
    .child-2 { color: red; }
  }
}
// 编译结果
.parent { color: red; }
.child { color: #fff; }
.child-1 { color: yellow; }
.child-2 { color: red; }
// 默认情况下@at-root并不会使指定的规则或则选择器跳出指令
@media print { 
  @at-root{
    .foo {  color: green;  } 
  }
}
// 编译结果 @media print { .foo { color: green; } }

// 搭配without
@media print {
  .parent{
    color:red;
    @at-root (without: media) { // 指定只跳出media,但并不会跳出.parent
      .child { color: #0ff; }
    }
  }
}
// 编译结果 @media print { .parent { color: red; } } .parent .child { color: #0ff; }

// 搭配 without:all
@media print {
  .parent{
    color:red;
    @at-root (without: all) {   // all标识要跳出所有
      .child { color: #0ff; }
    }
  }
}
// 编译结果  @media print { .parent { color: red; } }  .child { color: #0ff; }

// 搭配 with
@media print { 
  @supports ( transform-origin: 5% 5% ) { 
    @at-root (with: supports){ // 除了supports,其他指令都跳出
      .foo { color: green; }
    }
  } 
}
// 编译结果  @supports (transform-origin: 5% 5%) { .foo { color: green; } }

5. @debug

@debug伪指令检测错误,并将SassScript表达式值显示到标准错误输出流

$font-sizes: 10px + 20px
.container{ 
  @debug $font-sizes;
}

6. @warn

Sass @warn指令在出现问题并希望向用户提供警告性建议时使用。它将SassScript表达式的值显示到标准错误输出流。

$main-color:  #bdc3c7;
@warn "Darker: " darken($main-color, 30%);

控制指令

​ SassScript 提供了一些基础的控制指令,比如在满足一定条件时引用样式,或者设定范围重复输出格式。控制指令是一种高级功能,日常编写过程中并不常用到,主要与混合指令 (mixin) 配合使用,尤其是用在 Compass 等样式库中。

if(exp, value1, value2)

语法:if( expression, value1, value2 )

说明:内置函数,基于条件expression,如果表达式结果为真,则返回 value1;为假则返回 value2。函数的结果可以参考可能未被定义的变量或具有进一步的计算。

h2{
   color: if( 1 + 1 == 2 , green , red);
}
// 编译结果
h2 { color: green; }

@if exp {}

  1. @if 的表达式返回值不是 false 或者 null 时,条件成立,输出 {} 内的代码

  2. @if 声明后面可以跟多个 @else if 声明,或者一个 @else 声明。如果 @if 声明失败,Sass 将逐条执行 @else if 声明,如果全部失败,最后执行 @else 声明

$type: monster;
p {
  @if 1 + 1 == 2 { border: 1px solid; } // 表达式的值是 true -- 真
  @if 5 < 3 { border: 2px dotted; }     // 表达式的值是 false  -- 假
  @if null  { border: 3px double; }     // 表达式的值是 null  -- 假

  @if $type == ocean {
    color: blue;
  } @else if $type == matador {
    color: red;
  } @else if $type == monster {
    color: green;
  } @else {
    color: black;
  }
}

@for

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

两种格式:

  1. @for $var from <start> through <end>
  2. @for $var from <start> to <end>

区别在于 throughto 的含义:

  1. 当使用 through 时,条件范围包含 <start><end> 的值; start <= $val <= end ----- [start, end]
  2. 使用 to 时条件范围只包含 <start> 的值不包含 <end> 的值; start <= $val < end ----- [start, end)

$var 可以是任何变量,比如 $i<start><end> 必须是整数值

@for $index from 1 through 3 {
  .item-#{$index} { width: 2em * $index; }
}
// 编译结果
.item-1 { width: 2em; } .item-2 { width: 4em; } .item-3 { width: 6em; }

@each

语法: @each $var in <list or map>

说明:$var 可以是任何变量名,比如 $length$name

<list or map> 是一连串的值,也就是值列表(数组、Maps ...)

@each 将变量 $var 作用于值列表中的每一个项目,然后输出结果,例如:

// 声明变量 $animal   循环列表: puma, sea-slug, egret, salamander
@each $animal in puma, egret, sea-slug, salamander {
  .#{$animal}-icon {
    background-image: url('/images/#{$animal}.png');
  }
}
// 编译结果
.puma-icon { background-image: url('/images/puma.png'); }
.egret-icon { background-image: url('/images/egret.png'); }
.sea-slug-icon { background-image: url('/images/sea-slug.png'); }
.salamander-icon { background-image: url('/images/salamander.png'); }

1. @each 多个分配

语法: @each $var1, $var2, $var3 ... in <list>

说明:<list> 表示列表的列表,每个变量将保存子列表的元素

$mul-list: (puma, black, default), (slug, blue, pointer), (egret, white, move);
@each $animal, $color, $cursor in $mul-list {
  .#{$animal}-icon {
    background: url('/images/#{$animal}.png');
    border-color: 2px solid $color;
    cursor: $cursor;
    }
}
// 编译结果
.puma-icon { background: url('/images/puma.png'); border-color: black; cursor: default; }
.slug-icon { background: url('/images/slug.png'); border-color: blue; cursor: pointer; }
.egret-icon { background: url('/images/egret.png'); border-color: white; cursor: move; }

2. @each 多个分配与映射

语法: @each $var1, $var2 in <map>

说明:<map> 表示键值对的列表

$mul-map: (h1: red, h2: green, h3: blue);
@each $header, $color in $mul-map {
  #{$header} {
    color: $color;
  }
}
// 编译结果
h1 { color: red; } h2 { color: green; } h3 { color: blue; }

@while

@while 指令重复输出格式直到表达式返回结果为 false。这样可以实现比 @for 更复杂的循环,只是很少会用到。

语法: while(condition) { // CSS codes... }

说明:要注意,计数器变量需要在每次迭代时递增/递减。

$i: 30;
@while $i > 0 {
  .paddding-#{$i} { padding-left: 1px * $i; }
  $i: $i - 10;  // 计数器变量在每次迭代时递减
}
// 编译结果 
.paddding-30 { padding-left: 30px; }
.paddding-20 { padding-left: 20px; }
.paddding-10 { padding-left: 10px; }

混合指令

Mixins允许创建一组可以在整个样式表中重复使用的样式,而不需要重新创建非语义类。混合指令可以包含所有的 CSS 规则绝大部分 Sass 规则,甚至通过参数功能引入变量,输出多样化的样式。

​ 在CSS中,mixin可以存储多个值或参数和调用函数; 它有助于避免编写重复的代码。混合名称可以交替使用下划线和连字符。

1 定义 @mixin

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

语法: @mixin 混合指令名称 { // css code, sass code ... }

附:为便于书写,@mixin 可以用 = 表示;即: =混合指令名称 { // css code, sass code ... }

  1. 混合样式中也可以包含其他混合样式
  2. 混合样式中应该只定义后代选择器,这样可以安全的导入到文件的任何位置。
// 定义混合指令 clearfix
@mixin clearfix {
  display: inline-block;
  &:after {                     // 使用父选择器标识符
    content: "."; 
    height: 0;
    clear: both;
    visibility: hidden;
  }
  * html & { height: 1px }
}

2 引用 @include

@include 指令用于在文档中引用混合样式mixin。格式是在其后添加混合名称,并传递需要的参数(可选)。由mixin定义的样式可以包含在当前规则中。

语法: @include 混合指令名称( 参数[可选] )

附:为便于书写,@include 可以用 + 表示;即: +混合指令名称( 参数[可选] )

  1. 可在规则块中引用mixin;也可在最外层引用混合样式,不会直接定义属性,也不可以使用父选择器

  2. 混合样式中也可以包含其他混合样式

    @mixin highlighted-background { background-color: #fc0; }  
    @mixin does-parent-exist {
      @if & {            // 如果父选择器存在,那么就给父元素添加hover样式
        &:hover {
          /* 查看一下&的值: #{&} */
          color: red;
        }
      } @else {
        a {
          @include highlighted-background;   // 混合样式中也可以包含其他混合样式
          color: red;
        }
      }
    }
    div p{ @include does-parent-exist(); }   // 在嵌套规则块中引用mixin
    @include does-parent-exist();                        // 直接在代码最外层引用mixin 
    // 编译结果
    div p:hover { /* 查看一下&的值: div p:hover */ color: red; }
    a { background-color: #fc0; color: red; }
    

3 参数 (Arguments)

​ 参数用于给混合指令中的样式设定变量并且赋值使用。在定义混合指令的时候,按照变量的格式,通过逗号分隔,将参数写进圆括号里。引用指令时,按照参数的顺序,再将所赋的值对应写进括号:

定义: @mixin 混合指令名称( param1:默认值,param2, ... ) { // css code, sass code... }

引用: @include 混合指令名称( val1,val2, ... )

两种参数类型:关键字参数,可变参数

  1. 【关键字参数】引用mixin时使用 @include mixin-name($param1: $val1, $param2: $val2, ... )

    1. 关键词参数可以打乱顺序使用,如果使用默认值也可以省缺
    2. 参数名被视为变量名,下划线、短横线可以互换使用
  2. 【可变参数变量】

    1. 用于声明mixin @mixin mixin-name( $args...)
    2. 用于引用mixin @include mixin-name( $vals...)
// 定义了两个参数$color和$width, $width的缺省值为 1in
@mixin sexy-border($color, $width: 1in) { 
  border: { 
    color: $color; 
    width: $width; 
    style: dashed;
  }
}
h1 { @include sexy-border($color: blue, $width: 2in); } // 【关键字参数】
p { @include sexy-border(blue); } // 只传入一个参数$color, 使用$width的默认值

// 【可变参数变量】 用于引用mixin
$values: #ff0000, 2in;
h2{ @include sexy-border($values...); }

// 编译结果
h1 { border-color: blue; border-width: 2in; border-style: dashed; }
p { border-color: blue; border-width: 1in; border-style: dashed; }
h2 { border-color: blue; border-width: 2in; border-style: dashed; }

可变参数变量:用于声明mixin

// 用于声明mixin
@mixin box-shadow($shadows...) {    // 参数变量 用于定义mixin
  -moz-box-shadow: $shadows;
  -webkit-box-shadow: $shadows;
  box-shadow: $shadows;
}
.shadows {
  @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
// 编译为
.shadowed {
  -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 wrapped-stylish-mixin($args...) {
  font-weight: bold;
  @include stylish-mixin($args...);
}
@mixin stylish-mixin($args...){
  /* #{$args} */
  color: nth($args, 1);
  width: nth($args, 2);
}
.stylish {
  @include wrapped-stylish-mixin(#00ff00, $width: 100px);
}
// .stylish { font-weight: bold; /* #00ff00, 100px */ color: #00ff00; width: 100px; }

4 导入代码块

​ 在引用混合样式的时候,可以先将一段代码导入到混合指令中,然后再输出混合样式,额外导入的部分将出现在 @content 标志的地方。

  1. 若传入的代码块中有用到变量,该变量一定是引用时能够访问到的,而不能是mixin内部的
    1. 也就是说,在引用时 —— 先计算再传递给mixin
  2. @content 在指令中出现过多次或者出现在循环中时,额外的代码将被导入到每一个地方。
$color-1: white;
$color: red;
@mixin colors($color: blue) {
  background-color: $color;
  @content;
  border-color: $color;
}
.colors {
  @include colors { color: $color-1; } //传递的内容块在引用时就计算,所以引用的变量不能时mixin内的
}
// 编译结果
.colors { background-color: blue; color: white; border-color: blue; }

函数指令

@function 自定义函数名(param1,param2 ...){
@return 返回值
}

调用: 自定义函数名(val1, val2 ...); 或者 自定义函数名($param1: val1, $param2: val2 ...);

Sass 支持自定义函数,并能在任何属性值或 Sass script 中使用

  1. 【命名约定】为了避免命名冲突,函数名称可以带前缀,以便可以轻松区分;函数和其他Sass标识符可以交替使用下划线(_)和连字符( - )
  2. 就像mixin一样,函数也可以访问全局定义的变量,也可以接受参数,也支持变量参数。
  3. 应该使用 @return 来调用函数的返回值。
  4. 可以使用关键字参数调用SASS定义的函数 fn-name( $param1: val1, $param2: val2 ... )
$grid-width: 40px;
$gutter-width: 10px;

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

#sidebar { width: grid-width(5); }  // 使用关键字参数调用 width: grid-width($n: 5);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275