Sass 是一种 CSS 的预编译语言,对css进行了扩展,提供了一系列内置的机制,方便以编程的方式编写css,大大提升了css的复用性和灵活性.
本章介绍At-rules,流程控制,操作符
1. At-Rules
sass里基本的At-rules
如下
@use
loads mixins, functions, and variables from other Sass stylesheets, and combines CSS from multiple stylesheets together.
@forward
loads a Sass stylesheet and makes its mixins, functions, and variables available when your stylesheet is loaded with the @use
rule.
@import
extends the CSS at-rule to load styles, mixins, functions, and variables from other stylesheets.
@mixin
and @include
makes it easy to re-use chunks of styles.
@function
defines custom functions that can be used in SassScript expressions.
@extend
allows selectors to inherit styles from one another.
@at-root
puts styles within it at the root of the CSS document.
@error
causes compilation to fail with an error message.
@warn
prints a warning without stopping compilation entirely.
@debug
prints a message for debugging purposes.
1.1. @use
1.1.1. @use和@import的区别
重要,提到模块化(module)就是指@use
@import
并不是真正意义的模块化
参考:
-
css - What’s the difference between @import and @use SCSS rules? - Stack Overflow
-
Sass: @use (sass-lang.com)
-
Sass: @import (sass-lang.com)
-
Introducing Sass Modules | CSS-Tricks
-
@use
有命名空间,不怕变量被污染,当然使用的时候也稍微麻烦点,要带上scope,@import
的变量如果和本地文件的变量重名会被覆盖,所以使用import的时候一般会推荐使用长一点的变量名
-
@use
不会污染其它文件,一个文件里的use相对于整个项目里的其它文件是不可见的,比如在一个文件里@use button
,这时候其它文件并不能使用这个button里的成员,而@import A
一次之后,其它被import的文件也可以使用A
里的变量
-
所有-
, _
开头的variables, mixins, functions被认为是私有成员,不能用@use
引入
-
Similarly, @extends
will only apply up the chain; extending selectors in imported files, but not extending files that import this one.(这个稍后翻译)
1.1.2. 读取成员Loading Members
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use "src/corners";
.button {
@include corners.rounded;
padding: 5px + corners.$radius;
}
|
1.1.2.1. 名称空间namespace
指定名称空间namespace
, 在use的时候加上as
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use "src/corners" as c;
.button {
@include c.rounded;
padding: 5px + c.$radius;
}
|
也可以用*
来导入到本地空间里
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use "src/corners" as *;
.button {
@include rounded;
padding: 5px + $radius;
}
|
1.1.2.2. 私有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// src/_corners.scss
$-radius: 3px;
@mixin rounded {
border-radius: $-radius;
}
// style.scss
@use "src/corners";
.button {
@include corners.rounded;
// This is an error! $-radius isn't visible outside of `_corners.scss`.
padding: 5px + corners.$-radius;
}
|
1.1.3. 重写默认值以及with的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
// style.scss
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);
|
输出
1
2
3
4
|
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}
|
1.1.4. 和mixins一起使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
// _library.scss
$-black: #000;
$-border-radius: 0.25rem;
$-box-shadow: null;
/// If the user has configured `$-box-shadow`, returns their configured value.
/// Otherwise returns a value derived from `$-black`.
@function -box-shadow() {
@return $-box-shadow or (0 0.5rem 1rem rgba($-black, 0.15));
}
@mixin configure($black: null, $border-radius: null, $box-shadow: null) {
@if $black {
$-black: $black !global;
}
@if $border-radius {
$-border-radius: $border-radius !global;
}
@if $box-shadow {
$-box-shadow: $box-shadow !global;
}
}
@mixin styles {
code {
border-radius: $-border-radius;
box-shadow: -box-shadow();
}
}
// style.scss
@use 'library';
@include library.configure(
$black: #222,
$border-radius: 0.1rem
);
@include library.styles;
|
1.1.5. 模块搜索
模块的加载总是在当前文件加载之前
1.1.5.1. 搜索路径
如果要@use moduleA
, 那么搜索路径为node_modules/moduleA/sass/moduleA.scss
在sass里相对路径总是可用的,不需要额外的写./
1.1.5.2. 模块的拆分
sass里只编一个文件,其它文件都是模块, 为了标识模块,都以_
开头,但是在引入的时候不需要加入_
比如有下列模块解构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
│ main.scss
│ postcss.config.js
│ tailwind.config.js
│ tailwind.scss
│
├─common
│ _global.scss
│ _variables.scss
│
├─components
│ _code.scss
│ _markdown.scss
│
├─pages
│ _index.scss
│
├─partial
│ _header.scss
│
└─vendor
_animation.scss
_normalize.scss
_utils.scss
|
引入的时候可以这样
1
2
3
4
5
6
7
8
9
10
11
12
|
@import "vendor/utils";
@import "vendor/animation";
@import "common/variables";
@import "common/global";
@import "components/code";
@import "components/markdown";
@import "partial/header";
@import "pages/index"
|
也可以建立一个_index
来索引整个子目录,示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// foundation/_code.scss
code {
padding: .25em;
line-height: 0;
}
// foundation/_lists.scss
ul, ol {
text-align: left;
& & {
padding: {
bottom: 0;
left: 0;
}
}
}
// foundation/_index.scss
@use 'code';
@use 'lists';
// style.scss
@use 'foundation';
|
可以看到,这样在外层引用的时候,省略了路径和子文件,更加简单,缺点就是不方便插拔子文件
1.1.6. 读取css
和scss一样
1
2
3
4
5
6
7
|
// code.css
code {
padding: .25em;
line-height: 0;
}
// style.scss
@use 'code';
|
1.2. @forward
我的理解是, @use
不能跨文件, 所以每个文件如果复用很麻烦, 但是用@import
会污染scope,于是折中的方案就是,利用@forward来进行组织.
所以@forward
是@import
的一个附带作用域的版本
另外要注意的是,@forward
和@use
同时出现,@forward
要放在前面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// src/_list.scss
@mixin list-reset {
margin: 0;
padding: 0;
list-style: none;
}
// bootstrap.scss
@forward "src/list";
// styles.scss
@use "bootstrap";
li {
@include bootstrap.list-reset;
}
|
1.2.1. 同样可以使用as来定义前缀
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// src/_list.scss
@mixin reset {
margin: 0;
padding: 0;
list-style: none;
}
// bootstrap.scss
@forward "src/list" as list-*;
// styles.scss
@use "bootstrap";
li {
@include bootstrap.list-reset;
}
|
1.2.2. 可见性
利用hide
,可以对部分进行屏蔽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// src/_list.scss
$horizontal-list-gap: 2em;
@mixin list-reset {
margin: 0;
padding: 0;
list-style: none;
}
@mixin list-horizontal {
@include reset;
li {
display: inline-block;
margin: {
left: -2px;
right: $horizontal-list-gap;
}
}
}
// bootstrap.scss
@forward "src/list" hide list-reset, $horizontal-list-gap;
|
1.2.3. 重写默认值
类似@use
,@forward
可以重写默认值, 所以也可以当做一个配置的方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
// _opinionated.scss
@forward 'library' with (
$black: #222 !default,
$border-radius: 0.1rem !default
);
// style.scss
@use 'opinionated' with ($black: #333);
|
1.2.4. 注意事项
非常重要的一点,@forward
不能用在含有样式代码的文件中
意思就是说@forward
只用在整合和暴露内部成员
也就是说如果像下面这样:
1
2
3
4
|
@forward "colors" as colors-*;
// ------ Fonts ------ //
$global-font-size: 16px !default;
$global-font-color: $colors-gray-800;
|
这样是拿不到变量的
@forward
一般就用来整合文件,暴露出去,当然也有特殊用法,就是上一节的重写默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
// _opinionated.scss
@forward 'library' with (
$black: #222 !default,
$border-radius: 0.1rem !default
);
|
1.3. @import
@import
应该是最常用的一个内置At-rules了, 没有import无法模块化
首先CSS本身就有@import
, 参考: @import - CSS(层叠样式表) | MDN (mozilla.org)
然而sass里的import和css里的import最大的不同就是sass是编译器执行的
@import在将来会被移除,所以从现在开始尽量少用或者不用, 原因很简单
- 语义上容易和前面的use等等产生分歧
- 并且css里也@import,重名了.
- 最重要的是import会对作用域进行污染,容易产生覆盖问题
- 定位一个变量的时候比较麻烦,因为都是全局的
- 每import一个文件,都会编译一次,会增加编译的时间
- 没有办法定义私有变量
1.3.1. 模块搜索路径
1.3.1.1. 如何搜索
如果是@import variables
, 那么会搜索./variables.scss
, ./variables.sass
, ./variables.css
一般@import
有以下几种形式
1.3.1.2. import相对路径
假设有如下文件树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
│ main.scss
│ postcss.config.js
│ tailwind.config.js
│ tailwind.scss
│
├─common
│ _global.scss
│ _variables.scss
│
├─components
│ _code.scss
│ _markdown.scss
│
├─pages
│ _index.scss
│
├─partial
│ _header.scss
│
└─vendor
_animation.scss
_normalize.scss
_utils.scss
|
1
2
|
// main.scss
@import 'common/variables'
|
1.3.1.3. import网络文件
1
|
@import url(https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/scss/bootstrap.scss);
|
1.3.1.4. import npm包的文件
和@use
一样,如果是npm包的,如果要@import moduleA
, 那么搜索路径为node_modules/moduleA/sass/moduleA.scss
拿bootstrap
举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// Custom.scss
// Option A: Include all of Bootstrap
// Include any default variable overrides here (though functions won't be available)
@import "../node_modules/bootstrap/scss/bootstrap";
// Custom.scss
// Option B: Include parts of Bootstrap
// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
@import "../node_modules/bootstrap/scss/functions";
// 2. Include any default variable overrides here
// 3. Include remainder of required Bootstrap stylesheets
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/mixins";
// 4. Include any optional Bootstrap components as you like
@import "../node_modules/bootstrap/scss/root";
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
@import "../node_modules/bootstrap/scss/images";
@import "../node_modules/bootstrap/scss/containers";
@import "../node_modules/bootstrap/scss/grid";
|
1.3.1.5. 注意事项
在任何操作系统上都用forward slashes - /
不要用back slashes - \
1.3.1.6. Partials
当以_
开头的文件被引入时, 意味着这些文件只是组合,不要去单独编译他们
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// foundation/_code.scss
code {
padding: .25em;
line-height: 0;
}
// foundation/_lists.scss
ul, ol {
text-align: left;
& & {
padding: {
bottom: 0;
left: 0;
}
}
}
// foundation/_index.scss
@import 'code', 'lists';
// style.scss
@import 'foundation';
|
1.3.2. 嵌套 -Nesting
1
2
3
4
5
6
7
8
9
|
// _theme.scss
pre, code {
font-family: 'Source Code Pro', Helvetica, Arial;
border-radius: 4px;
}
// style.scss
.theme-sample {
@import "theme";
}
|
官方推荐在使用第三方的样式的时候,为了限制作用域,可以结合mixin来引入第三方的样式
总之,import会很容易产生问题,即使是自己写的模块,也会容易出现作用域的问题,名称的问题等等,尽量少用import
替代方案如下
- 可以使用
@use
和@forward
来替代
- 可以将样式放在
@mixin
里,再进行import
1.3.3. import css
css文件也可以引入,只是文件中不要含有sass的语法
1.3.4. CSS的import
由于sass是要编译的,那么原始css文件如何保留不被编译,sass提供了下面几种机制
- 以
.css
结尾的import
- 以
http://
或者https://
开头的import
- 调用了url方法的import
- 带有media参数的import
例如:
1
2
3
4
|
@import "theme.css";
@import "http://fonts.googleapis.com/css?family=Droid+Sans";
@import url(theme);
@import "landscape" screen and (orientation: landscape);
|
1.3.5. 插值
import也可以使用差值
1
2
3
4
5
|
@mixin google-font($family) {
@import url("http://fonts.googleapis.com/css?family=#{$family}");
}
@include google-font("Droid Sans");
|
1.3.6. import和模块系统
如果@import
引入的文件包含了使用@use
导入的模块,那么模块里的成员是不能被访问到的(即便定义了namespace也不行),
@forward
可以传递作用域,使之变为可能
1.3.7. Import-Only文件
仅导入文件,@import
可用,@use
不可用,这样的文件有一定的兼容性,切无需增肌额外的namespace.
为什么这么做?
有些模块是为模块化系统设计的,所以名称很短,如果在用不了@use
的系统里就会导致一些覆盖,冲突等意想不到的结果
这时候定义一个文件名为xxx.import.scss
,为老的@import
引入方式提供支持
示例:
一个为模块化设计的短名称的模块,里面的名称在@use
的模块化系统里没有任何问题,因为可以@include reset.list()
1
2
3
4
5
6
7
8
9
10
|
// _reset.scss
// Module system users write `@include reset.list()`.
@mixin list() {
ul {
margin: 0;
padding: 0;
list-style: none;
}
}
|
但是如果被@import
了,那就容易乱套,这时候建立一个文件如下
1
2
3
4
|
// _reset.import.scss
// Legacy import users can keep writing `@include reset-list()`.
@forward "reset" as reset-*;
|
这时候,再去@import reset
, 就可以用@include reset-list()
, 这样就不会出现冲突和覆盖了
1.4. 关于@use,@forward和@import的使用总结
- 按照官方推荐,尽量的使用
@use
和@forward
- 层次清晰一点,同类型的模块放在一个文件夹下,可以设置
_index.scss
进行合并
- 由于
@use
无法传递成员,所以variables
,mixins
, functions
这类一定使用@forward
进行组合,并配合as
保留命名空间,不污染scope
- 项目里文件夹层次不宜过多,一般一层子目录就够了
- 文件夹的
_index.scss
里,含有variables
,mixins
, functions
的子模块要用@forward
,否则外层的sass访问不到
- 不是所有sass的实现都支持
@use
,目前只有DartSass
,所以使用前先确认编译环境
- 使用
@import
的时候尽量使用长名称,以免和业务发生冲突
@use
和@import
都有可能会使得文件编译后变得臃肿,所以使用前,一定规划好层次解构.避免不必要的重复导入
1.5. @mixin 和 @include
@mixin
用来混合属性或者混合计算属性, 和variables
一样,@mixin
的重要作用是用来复用, 也可以理解为更细粒度的模块化
定义mixin的方式有2种,带参数和不带参数
@mixin <name> { ... }
@mixin name(<arguments...>) { ... }
mixin里可以包含普通语句和顶级语句(@use @import @mixin @function
), 基本上和在外层写一个样式没有区别
如果要混合@mixin
,请使用@include
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
@mixin horizontal-list {
@include reset-list;
li {
display: inline-block;
margin: {
left: -2px;
right: 2em;
}
}
}
nav ul {
@include horizontal-list;
}
|
1.5.1. @mixin参数
1
2
3
4
5
6
7
8
9
10
11
|
@mixin rtl($property, $ltr-value, $rtl-value) {
#{$property}: $ltr-value;
[dir=rtl] & {
#{$property}: $rtl-value;
}
}
.sidebar {
@include rtl(float, left, right);
}
|
1.5.2. @mixin默认参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@mixin replace-text($image, $x: 50%, $y: 50%) {
text-indent: -99999em;
overflow: hidden;
text-align: left;
background: {
image: $image;
repeat: no-repeat;
position: $x $y;
}
}
.mail-icon {
@include replace-text(url("/images/mail.svg"), 0);
}
|
1.5.3. 按名称传递参数
1
2
3
4
5
6
7
8
9
10
11
12
|
@mixin square($size, $radius: 0) {
width: $size;
height: $size;
@if $radius != 0 {
border-radius: $radius;
}
}
.avatar {
@include square(100px, $radius: 4px);
}
|
1.5.4. 可变数量参数
类似python的*args
1
2
3
4
5
6
7
8
9
10
11
|
@mixin order($height, $selectors...) {
@for $i from 0 to length($selectors) {
#{nth($selectors, $i + 1)} {
position: absolute;
height: $height;
margin-top: $i * $height;
}
}
}
@include order(150px, "input.name", "input.address", "input.zip");
|
结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
input.name {
position: absolute;
height: 150px;
margin-top: 0px;
}
input.address {
position: absolute;
height: 150px;
margin-top: 150px;
}
input.zip {
position: absolute;
height: 150px;
margin-top: 300px;
}
|
1.5.5. 字典型可变参数
类似python的**kwargs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@use "sass:meta";
@mixin syntax-colors($args...) {
@debug meta.keywords($args);
// (string: #080, comment: #800, variable: #60b)
@each $name, $color in meta.keywords($args) {
pre span.stx-#{$name} {
color: $color;
}
}
}
@include syntax-colors(
$string: #080,
$comment: #800,
$variable: #60b,
)
|
输出
1
2
3
4
5
6
7
8
9
10
11
|
pre span.stx-string {
color: #080;
}
pre span.stx-comment {
color: #800;
}
pre span.stx-variable {
color: #60b;
}
|
注意:必须用meta.keywords
进行转换
1.5.6. 混用可变参数
1
2
3
|
$form-selectors: "input.name", "input.address", "input.zip" !default;
@include order(150px, $form-selectors...);
|
1.5.7. @content代码块
@mixin
还可以结合@content来指定一段样式代码传递的位置,其要点如下:
- 类似参数,但是无需写形参
- 实参必须是样式代码
@content
只能配合@mixin
使用,并且是在@mixin
里使用@content
@content
实际上就是占位符
1
2
3
4
5
6
7
8
9
10
11
12
|
@mixin hover {
&:not([disabled]):hover {
@content;
}
}
.button {
border: 1px solid black;
@include hover {
border-width: 2px;
}
}
|
输出
1
2
3
4
5
6
|
.button {
border: 1px solid black;
}
.button:not([disabled]):hover {
border-width: 2px;
}
|
另外,如果要传递多个代码块, @content
必须在@each
里使用
1.5.8. 更复杂的用法,给@content传递参数
这个写法实在晦涩难懂, 在函数里绕来绕去, 看示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@mixin media($types...) {
@each $type in $types {
@media #{$type} {
@content($type);
}
}
}
@include media(screen, print) using ($type) {
h1 {
font-size: 40px;
@if $type == print {
font-family: Calluna;
}
}
}
|
输出
1
2
3
4
5
6
7
8
9
10
11
12
|
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}
|
觉得这种不如直接写成这样, 简单直接
1
2
3
4
5
6
7
8
9
10
11
12
|
$medias: 'screen', 'print';
@each $sc in $medias {
@media #{$sc} {
h1 {
font-size: 40px;
@if $sc == print {
font-family: Calluna;
}
}
}
}
|
1.6. @function
@function
和@mixin
的区别在于
@function
主要用来计算,有返回值, @mixin
没有返回值,主要用来包含和混合样式
@mixin
需要用@include
来使用, @function
不需要
1.6.1. 使用原则
用来计算值,不要用来写样式
1.6.2. 参数
基本和@mixin
的使用方式相同,可以用可变参数,字典型参数,可变列表型参数和可变字典型参数,参数默认值这些特性都支持
可变列表型参数
1
2
3
4
5
6
7
8
9
10
11
|
@function sum($numbers...) {
$sum: 0;
@each $number in $numbers {
$sum: $sum + $number;
}
@return $sum;
}
.micro {
width: sum(50px, 30px, 100px);
}
|
1.6.3. @return
返回值使用@return
1
2
3
4
5
6
7
8
9
10
11
12
|
@function pow($base, $exponent) {
$result: 1;
@for $_ from 1 through $exponent {
$result: $result * $base;
}
@return $result;
}
.sidebar {
float: left;
margin-left: pow(4, 3) * 1px;
}
|
1.6.4. 注意事项
在sass里,很多地方-
和_
等同, 尤其是一些命名, @function的名称 larger-font
和larger_font
相同
1.6.5. 参考
1.7. @extend
@extend
类似于@mixin
都是用来混合样式的,但是用法有诸多不同, 稍微难理解一点
1.7.1. 原则和工作机制
1.7.1.1. @extend是会生成继承的组合代码的
他会使用类似,
进行样式选择器组合
1
2
3
4
5
6
7
8
|
.warning {
color: red;
}
p {
background-color: gray;
@extend .warning;
}
|
会生成
1
2
3
4
5
6
7
|
.warning, p {
color: red;
}
p {
background-color: gray;
}
|
同样的如果使用@mixin
1
2
3
4
5
6
7
8
|
@mixin warning {
color: red;
}
p {
background-color: gray;
@include warning;
}
|
输出如下
1
2
3
4
|
p {
background-color: gray;
color: red;
}
|
所以,@extend
和@mixin
的第一个区别是前者生成的是新的选择器以及相应的样式, 后者直接包含样式,不生成选择器
1.7.1.2. @extend生成的选择器是交错的
可以理解为:为达目的 不择手段, 看下面的示例:
1
2
3
4
5
6
7
8
|
.header .circle {
border-radius: 100%;
border-style: none;
}
.user-info .medium-avatar {
@extend .circle
}
|
想要继承.circle
,但是它不是单独存在,这个时候又无法确定顺序,所以生成的代码就比较臃肿了,输出如下:
1
2
3
4
|
.header .circle, .header .small-avatar .medium-avatar, .small-avatar .header .medium-avatar {
border-radius: 100%;
border-style: none;
}
|
1.7.2. 单纯的可继承样式
有些样式,我甚至都不想公开使用,只是内部继承, 可以理解为私有父类, 这种时候可以使用%
示例:
1
2
3
4
5
6
7
|
.alert:hover, %strong-alert {
font-weight: bold;
}
%strong-alert:hover {
color: red;
}
|
编译之后
1
2
3
|
.alert:hover {
font-weight: bold;
}
|
同样可以利用-
或者_
将可继承变得私有化
1.7.3. 范围
注意@use
,@forward
和@import
的区别,为了避免作用域的混乱,尽量用@use
和@forward
来限制继承的范围
1.7.4. 可选的继承
结尾加上!optional
, 因为在没有找到可用的选择器的时候,sass会发出警告,如果设置可选,那么就会在找不到的时候忽略.
这条规则在编写发布公共的sass组件时会比较有用.
1.7.5. 如何选择@extend和@mixin
参考以下区别和特性进行选择
-
@mixin
生成的代码比较简单,就是一个包含关系,@extend
涉及到一些选择器的计算和组合
-
选择器是可被服务器和编译器进行压缩优化的, @mixin
不会,所以@mixin
会比@extend
的代码要大(在某些情况下)
-
@extend
适合处理关系紧密,且语义明了的选择器
-
@mixin
是和处理需要参数传递,并进行定制的地方
-
@extend
即使是在编译以后,解构也很清晰,更接近原生的css
1.7.6. 限制
单独的选择器才能extend
当样式选择器错综复杂的时候, @extend基本上用处很小,因为它会假设选择器之间不会有任何交错(所以这种情况下,没有必要使用@extend了,及其不准确)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
header .warning li {
font-weight: bold;
}
aside .notice dd {
// Sass doesn't generate CSS to match the <dd> in
//
// <header>
// <aside>
// <div class="warning">
// <div class="notice">
// <dd>...</dd>
// </div>
// </div>
// </aside>
// </header>
//
// because matching all elements like that would require us to generate nine
// new selectors instead of just two.
@extend li;
}
|
输出
1
2
3
|
header .warning li, header .warning aside .notice dd, aside .notice header .warning dd {
font-weight: bold;
}
|
@extend
可以在其它At-Rules里使用,但是必须不能再被其它包裹
1
2
3
4
5
6
7
8
9
10
11
12
|
@media screen and (max-width: 600px) {
.error--serious {
@extend .error;
// ^^^^^^
// Error: ".error" was extended in @media, but used outside it.
}
}
.error {
border: 1px #f00;
background-color: #fdd;
}
|
1.8. @error
报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@mixin reflexive-position($property, $value) {
@if $property != left and $property != right {
@error "Property #{$property} must be either left or right.";
}
$left-value: if($property == right, initial, $value);
$right-value: if($property == right, $value, initial);
left: $left-value;
right: $right-value;
[dir=rtl] & {
left: $right-value;
right: $left-value;
}
}
.sidebar {
@include reflexive-position(top, 12px);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Error: Property top must be either left or right.
}
|
1.9. @warn
警告
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$known-prefixes: webkit, moz, ms, o;
@mixin prefix($property, $value, $prefixes) {
@each $prefix in $prefixes {
@if not index($known-prefixes, $prefix) {
@warn "Unknown prefix #{$prefix}.";
}
-#{$prefix}-#{$property}: $value;
}
#{$property}: $value;
}
.tilt {
// Oops, we typo'd "webkit" as "wekbit"!
@include prefix(transform, rotate(15deg), wekbit ms);
}
|
1.10. @debug
输出调试信息
1
2
3
4
5
6
7
8
9
10
|
@mixin inset-divider-offset($offset, $padding) {
$divider-offset: (2 * $padding) + $offset;
@debug "divider offset: #{$divider-offset}";
margin-left: $divider-offset;
width: calc(100% - #{$divider-offset});
}
// output
// test.scss:3 Debug: divider offset: 132px
|
1.11. @at-root
将嵌套的规则独立到外层
示例:
1
2
3
4
5
6
7
8
9
10
11
12
|
article .outside {
font-size: medium;
background-color: blue;
@at-root .inside {
font-size: small;
border: 0.2rem;
}
.inside2 {
font-size: x-small;
font-weight: 400;
}
}
|
很单纯的进行了分离没有任何覆盖和计算,输出如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
article .outside {
font-size: medium;
background-color: blue;
}
.inside {
font-size: small;
border: 0.2rem;
}
article .outside .inside2 {
font-size: x-small;
font-weight: 400;
}
|
如果不是想要单独定义新样式,而是将规则进行分离,可以这样
1
2
3
4
5
6
7
8
9
10
11
12
|
article .outside {
font-size: medium;
background-color: blue;
font-size: small;
border: 0.2rem;
font-size: x-small;
font-weight: 400;
}
article .outside .dark-bg {
background-color: black;
}
|
CSS的At-Rules
@support
@keyframes
2. 流程控制
2.1. @if and @else
这个没啥好说的, 条件为true
和false
, 还有个分支是@else if
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@use "sass:math";
@mixin triangle($size, $color, $direction) {
height: 0;
width: 0;
border-color: transparent;
border-style: solid;
border-width: math.div($size, 2);
@if $direction == up {
border-bottom-color: $color;
} @else if $direction == right {
border-left-color: $color;
} @else if $direction == down {
border-top-color: $color;
} @else if $direction == left {
border-right-color: $color;
} @else {
@error "Unknown direction #{$direction}.";
}
}
.next {
@include triangle(5px, black, right);
}
|
2.2. @for
以下两种方式, to
不包含末端, through
包含末端
1
2
|
@for <variable> from <expression> to <expression> { ... }
@for <variable> from <expression> through <expression> { ... }
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@for $i from 1 through 6 {
h#{$i} {
font-weight: 400;
font-family: $global-serif-font-family;
.anchor {
float: left;
line-height: 1;
margin-left: -20px;
padding-right: 4px;
&:hover {
border-bottom: initial;
}
.icon-link {
visibility: hidden;
font-size: 16px;
display: contents;
&:before {
vertical-align: middle;
}
}
}
&:hover {
.icon-link {
visibility: visible;
}
}
}
}
|
结果
1
2
3
4
5
6
7
8
|
.next {
height: 0;
width: 0;
border-color: transparent;
border-style: solid;
border-width: 2.5px;
border-left-color: black;
}
|
注意:
false的可能性
除此之外,全部都是true, 包括0, 空字符串, 空list
2.3. @each
2.3.1. 普通列表类型
1
|
@each <variable> in <expression> { ... }
|
1
2
3
4
5
6
7
8
9
|
$sizes: 40px, 50px, 80px;
@each $size in $sizes {
.icon-#{$size} {
font-size: $size;
height: $size;
width: $size;
}
}
|
2.3.2. 键值对类型
1
|
@each <variable>, <variable> in <expression> { ... }.
|
1
2
3
4
5
6
7
8
9
|
$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");
@each $name, $glyph in $icons {
.icon-#{$name}:before {
display: inline-block;
font-family: "Icon Font";
content: $glyph;
}
}
|
2.3.3. 解构
在已知列表包含了列表,且已知嵌套列表的长度的时候,我们可以简单的利用解构(Destructuring)来进行操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
$icons:
"eye" "\f112" 12px,
"start" "\f12e" 16px,
"stop" "\f12f" 10px;
@each $name, $glyph, $size in $icons {
.icon-#{$name}:before {
display: inline-block;
font-family: "Icon Font";
content: $glyph;
font-size: $size;
}
}
|
得到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@charset "UTF-8";
.icon-eye:before {
display: inline-block;
font-family: "Icon Font";
content: "";
font-size: 12px;
}
.icon-start:before {
display: inline-block;
font-family: "Icon Font";
content: "";
font-size: 16px;
}
.icon-stop:before {
display: inline-block;
font-family: "Icon Font";
content: "";
font-size: 10px;
}
|
2.4. @while
一种更自由的迭代方式, 只要条件为true就继续执行,表达式为
1
|
@while <expression> { ... }
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@use "sass:math";
/// Divides `$value` by `$ratio` until it's below `$base`.
@function scale-below($value, $base, $ratio: 1.618) {
@while $value > $base {
$value: math.div($value, $ratio);
}
@return $value;
}
$normal-font-size: 16px;
sup {
font-size: scale-below(20px, 16px);
}
|
结果
1
2
3
|
sup {
font-size: 12.36094px;
}
|
3. References