本文作为 2020年的css周边:preprocessor、postcss及css-in-js(一周内发布,编写中) 的一部分用来说明预处理器的用法,欢迎阅读
sass实现
sass是一个样式表语言,只有语法和定义,需要对应的实现才能根据sass源码转化为css,sass对应于实现就类似于ecma262对应于v8。
现在最新的实现是dart-sass,之前还有一个实现是Node-Sass,后者已经废弃,引入dart实现版本的原因请参考Announcing Dart Sass,两种实现的具体区别可以参考Node-Sass or Dart-Sass : The CSS Preprocessor Dilemma。
一般我们只需下载sass,其是Dart Sass编译成的纯js版本。下载完就可以直接用sass命令编译sass文件,需要告诉sass在哪个文件输入和输出,并可以添加--watch选项自动编译
sass --watch input.scss output.css
也可以指定目录
sass --watch app/sass:public/stylesheets
其他用法请参考dart-sass-cli
sass语言规范
语法
sass有两种语法,两种语法特性一致。
一种语法使用.scss扩展名的文件,是css语法的超级,这里的scss之于css就相当于ts之于js。
另一种语法是sass最初的语法,以.sass扩展名,使用缩进代替了第一种的花括号。
样式表结构
就像css一样,大部分sass样式表也是由包含属性声明的样式规则组成,但是sass的样式表同时还有其他。
- 声明,不同声明之间用分号分隔
- 通用声明,可以在样式表的任何地方
- 变量声明,比如
$var: value
- 流程控制at-rules
@error
,@warn
, and@debug
规则
- 变量声明,比如
- css声明,这些声明生成css,除了不能用在
@function
,可以用在任何地方- style规则,比如
h1 { /* ... */ }
- CSS at-rules,比如
@media
and@font-face
@include
使用Mixin@at-root
- style规则,比如
- 顶层声明,用在样式表顶层或者嵌套在顶层的css声明中
@use
加载模块@import
@mixin
定义mixin@function
定义函数
- 其他声明
- 属性声明,比如
width: 100px
,只能用于style规则和一些css at-rules @extend
,用在样式规则
- 属性声明,比如
- 通用声明,可以在样式表的任何地方
- 表达式,一个表达式是在一个属性或变量声明的右侧的任何东西。每个表达式生成一个value,我们将sass的表达式语法称为sassScript。
- 字面量 最简单的表达式只代表静态值
- Number,单位可选,比如
12
或100px
- String,引号可选,比如
"Helvetica Neue"
或bold
- Colors,可以用16进制表达式或者颜色名,比如
#c6538c
或blue
- boolean,
true
或false
- null
- Lists of values,用空格或逗号分隔,可能用方括号包围或者没有括号,比如
1.5em 1em 0 2em, Helvetica, Arial, sans-serif
或[col1-start]
- Maps,比如
("background": red, "foreground": pink)
- Number,单位可选,比如
- 操作符 数字操作符
==
和!=
用来检查是否相等+
,-
,*
,/
, and%
<
,<=
,>
, and>=
and
,or
, andnot
用来进行布尔判断+
,-
,/
也可以用来连接字符串(
和)
用于显式控制操作顺序
- 其他表达式
- 变量,比如
$var
- 函数调用,比如
nth($list, 1)
- 特殊函数,比如
calc(1px + 100%)
- 父级选择器,比如
&
- 值
!important
- 变量,比如
- 字面量 最简单的表达式只代表静态值
特殊函数
css定义的大部分函数都在scss正常使用,会被解析为一个函数调用,并按原样编译成css.但有一些例外,它们不能仅解析为sassScript表达式.
这里指的是url()等函数,因为在css中的语法和sassScript中不同,需要在sass中特殊处理,这里不展开,具体参考Special Functions
style规则
基本用法和css一样
属性声明
sass中声明的值可以是任何sassScript表达式。
- 插值,可以用来动态生成任何需要的属性
//scss
@mixin define-emoji($name, $glyph) {
span.emoji-#{$name} {
font-family: IconFont;
font-variant: normal;
font-weight: normal;
content: $glyph;
}
}
@include define-emoji("women-holding-hands", "?");
//css
@charset "UTF-8";
span.emoji-women-holding-hands {
font-family: IconFont;
font-variant: normal;
font-weight: normal;
content: "?";
}
- 嵌套,可以用于处理父子选择器,和带相同前缀的属性
//scss
.enlarge {
font-size: 14px;
transition: {
property: font-size;
duration: 4s;
delay: 2s;
}
&:hover { font-size: 36px; }
}
//css
.enlarge {
font-size: 14px;
transition-property: font-size;
transition-duration: 4s;
transition-delay: 2s;
}
.enlarge:hover {
font-size: 36px;
}
- 隐藏声明,如果一个声明的值是null或空字符串,则不会编译到css
- 自定义属性,css自定义属性,也叫css变量,当sass在编译自定义属性时会把相关字段原样传给css,唯一的个例是插值(#{}),后者可以注入动态的值。
//scss
SCSS SYNTAX
$primary: #81899b;
$accent: #302e24;
$warn: #dfa612;
:root {
--primary: #{$primary};
--accent: #{$accent};
--warn: #{$warn};
// Even though this looks like a Sass variable, it's valid CSS so it's not
// evaluated.
--consumed-by-js: $primary;
}
//css
:root {
--primary: #81899b;
--accent: #302e24;
--warn: #dfa612;
--consumed-by-js: $primary;
}
注意插值会移除字符串的引号,如果要保留引号需要使用 meta.inspect()
函数
@use "sass:meta";
$string: "dd";
:root {
--consumed-by-js: #{meta.inspect($string)};
}
父选择器
父选择器&
用在嵌套的选择器中代表外部的选择器,使得可以用更复杂的方式复用外部的选择器,比如添加伪类。具体使用时会被外部的选择器替换,而不是正常的嵌套行为
//scss
SCSS SYNTAX
.alert {
// The parent selector can be used to add pseudo-classes to the outer
// selector.
&:hover {
font-weight: bold;
}
// It can also be used to style the outer selector in a certain context, such
// as a body set to use a right-to-left language.
[dir=rtl] & {
margin-left: 0;
margin-right: 10px;
}
// You can even use it as an argument to pseudo-class selectors.
:not(&) {
opacity: 0.8;
}
}
//css
.alert:hover {
font-weight: bold;
}
[dir=rtl] .alert {
margin-left: 0;
margin-right: 10px;
}
:not(.alert) {
opacity: 0.8;
}
- 添加后缀,可以用来给外部的选择器添加后缀,当使用BEM等方法时效果显著,比如
&__copy
- 用于sassScript,返回父选择器,比如判断有没有父选择器
//scss
@mixin app-background($color) {
#{if(&, '&.app-background', '.app-background')} {
background-color: $color;
color: rgba(#fff, 0.75);
}
}
@include app-background(#036);
.sidebar {
@include app-background(#c6538c);
}
//css
.app-background {
background-color: #036;
color: rgba(255, 255, 255, 0.75);
}
.sidebar.app-background {
background-color: #c6538c;
color: rgba(255, 255, 255, 0.75);
}
还可以把&
作为一个表达式传递给函数或者用作插值,当结合selector函数和@at-root规则使用时,可以嵌套出强大的效果。
比如
//scss
@use "sass:selector";
@mixin unify-parent($child) {
@at-root #{selector.unify(&, $child)} {
@content;
}
}
.wrapper .field {
@include unify-parent("input") {
/* ... */
}
@include unify-parent("select") {
/* ... */
}
}
//css
.wrapper input.field {
/* ... */
}
.wrapper select.field {
/* ... */
}
占位选择器
占位选择器以%
开始,不会输出到css任何东西,但是可以用来extended
//scss
SCSS SYNTAX
%toolbelt {
box-sizing: border-box;
border-top: 1px rgba(#000, .12) solid;
padding: 16px 0;
width: 100%;
&:hover { border: 2px rgba(#000, .5) solid; }
}
.action-buttons {
@extend %toolbelt;
color: #4285f4;
}
.reset-buttons {
@extend %toolbelt;
color: #cddc39;
}
//css
.action-buttons, .reset-buttons {
box-sizing: border-box;
border-top: 1px rgba(0, 0, 0, 0.12) solid;
padding: 16px 0;
width: 100%;
}
.action-buttons:hover, .reset-buttons:hover {
border: 2px rgba(0, 0, 0, 0.5) solid;
}
.action-buttons {
color: #4285f4;
}
.reset-buttons {
color: #cddc39;
}
变量
sass的变量名以$
开始,可以用变量名代替对应的值。
一个变量声明像一个属性声明: <variable>: <expression>;
,但和属性只能在样式规则或at-rule中使用不同,变量可以在任何地方声明。
$base-color: #c6538c;
$border-dark: rgba($base-color, 0.88);
.alert {
border: 1px solid $border-dark;
}
注意css本身也有变量,但是和sass中的表现不一样,区别包括
- sass变量会被编译掉,css变量会保留在输出
- css变量对于不同元素可以有不通知,但是sass变量同时只能有一个值
- sass变量是命令式的,当使用该变量然后改动时,之前使用的变量将不变,css变量是声明式的,如果改变会影响到之前的使用
- 默认值,可以在变量的值后面添加
!default
表示默认值,当作为一个模块时,允许用户使用with
重写,如果没有标记默认值,则不允许with
重写 - 内置变量,sass内置模块的变量不可改变
@use "sass:math" as math;
// This assignment will fail.
math.$pi: 0;
- 作用域,在顶级作用域声明的变量是全局的,在块(花括号包围的)中定义的是本地的,可以在块中使用
!global
来设置全局变量,注意只能用于之前在全局声明过的变量。
$variable: first global value;
.content {
$variable: second global value !global;
value: $variable;
}
.sidebar {
value: $variable;
}
在控制流中的变量声明只用于分配给外部作用域已经存在的变量以值,不能声明新的
$dark-theme: true !default;
$primary-color: #f8bbd0 !default;
$accent-color: #6a1b9a !default;
@if $dark-theme {
$primary-color: darken($primary-color, 60%);
$accent-color: lighten($accent-color, 60%);
}
.button {
background-color: $primary-color;
border: 1px solid $accent-color;
border-radius: 3px;
}
- 高级的变量函数,
meta.variable-exists()
用于检查当前作用域给定变量是否存在,meta.global-variable-exists()
用于检查全局作用域。
注意不能使用插值来基于一个变量定义另一个变量,但是可以使用映射。
@use "sass:map";
$theme-colors: (
"success": #28a745,
"info": #17a2b8,
"warning": #ffc107,
);
.alert {
// Instead of $theme-color-#{warning}
background-color: map.get($theme-colors, "warning");
}
插值
插值用#{}
包裹,可以用在以下地方
- 样式规则中的选择器
- 声明中的属性名
- 自定义属性值
- css at-rules
- @extends
- @import
- 带引号或者不带引号的字符串
- 特殊函数
- 一般函数名
- 注释
//scss
SCSS SYNTAX
@mixin corner-icon($name, $top-or-bottom, $left-or-right) {
.icon-#{$name} {
background-image: url("/icons/#{$name}.svg");
position: absolute;
#{$top-or-bottom}: 0;
#{$left-or-right}: 0;
}
}
@include corner-icon("mail", top, left);
//css
.icon-mail {
background-image: url("/icons/mail.svg");
position: absolute;
top: 0;
left: 0;
}
用在sassScript中
可以用来向不带引号的字符串注入一个值,解析后返回不带引号的字符串(正因如此)。但作为属性名时不需要插值,不然#{$accent}
会被编译成$accent
,如果包含的是带引号的字符串,将去掉字符串后返回。
string.unquote()
也可以将带引号的字符串转化为不带引号的字符串。
at-rules
大部分sass另外加的函数都是以at-rules的形式添加的,at-rules指的是以@开头的规则。
@use和@forward
用于导入和转发module和module 成员(mixins, functions, and variables),原有的@import废弃。
所有源文件模块及其属性默认导出,且编译成对应css文件,因此不需要显式导出。如果只想用于导出而不编译,可以在文件名前加下划线_
前缀,其中的成员可以添加下划线前缀设为私有。
当导入模块时,会将所有样式规则导入,如果要访问其成员,可默认使用文件名(不带扩展名)作为命名空间,也可以使用as
显式指定,如果指定为*
,则可以直接使用其公共成员。
另外可以对标记默认值得变量进行重写,语法@use <url> with (<variable>: <value>, <variable>: <value>)
@use "src/corners" as c;
当转发模块时,执行转发的文件内不可以使用被转发的内容,可以用于将多个模块统一对外暴露。另外还可以
- 利用
@forward "<url>" as <prefix>-*
为成员增加前缀 - 利用
@forward "<url>" hide <members...>
或@forward "<url>" show <members...>
处理可见性,其中<members...>
是逗号分隔的成员。 - 也可以用
with
重写默认值
@mixin and @include
分别用函数的方式来创建@mixin name(<arguments...>) { ... }.
和使用@include <name>(<arguments...>)
可复用的样式规则。
- 可选参数
@mixin replace-text($image, $x: 50%, $y: 50%){}
- 任意参数,在最后一个参数后面添加
...
,比如@mixin order($height, $selectors...)
- 内容块,除了可以通过参数传递数据,还可以通过内容块,即通过
@content
获取调用时的样式内容
//scss
@mixin hover {
&:not([disabled]):hover {
@content;
}
}
.button {
border: 1px solid black;
@include hover {
border-width: 2px;
}
}
//css
.button {
border: 1px solid black;
}
.button:not([disabled]):hover {
border-width: 2px;
}
像内容块传递参数,@content
也是一个函数,使用using
传递参数
//scss
@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;
}
}
}
//css
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}
控制流
sass提供了一些控制流用在mixin和function中,包括
@if
和@else
,语法分别为@if <expression> { ... }
,@else { ... }
,后者也可以变体为@else if permalink@else if
//scss
SCSS SYNTAX
@mixin triangle($size, $color, $direction) {
height: 0;
width: 0;
border-color: transparent;
border-style: solid;
border-width: $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);
}
//css
.next {
height: 0;
width: 0;
border-color: transparent;
border-style: solid;
border-width: 2.5px;
border-left-color: black;
}
@each
,用于遍历列表(用逗号分隔的表达式)生成一串样式,语法@each <variable> in <expression> { ... }
//scss
$sizes: 40px, 50px, 80px;
@each $size in $sizes {
.icon-#{$size} {
font-size: $size;
height: $size;
width: $size;
}
}
//css
.icon-40px {
font-size: 40px;
height: 40px;
width: 40px;
}
.icon-50px {
font-size: 50px;
height: 50px;
width: 50px;
}
.icon-80px {
font-size: 80px;
height: 80px;
width: 80px;
}
遍历map@each <variable>, <variable> in <expression> { ... }
$icons: ("eye": "\f112", "start": "\f12e", "stop": "\f12f");
@each $name, $glyph in $icons {
.icon-#{$name}:before {
display: inline-block;
font-family: "Icon Font";
content: $glyph;
}
}
遍历一个列表的列表@each <variable...> in <expression> { ... }
//scss
$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;
}
}
//css
@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;
}
@for
递增数字, 语法是@for <variable> from <expression> to <expression> { ... }
或@for <variable> from <expression> through <expression> { ... }
,前者不包括最后一个数字,后者包含.
//scss
$base-color: #036;
@for $i from 1 through 3 {
ul:nth-child(3n + #{$i}) {
background-color: lighten($base-color, $i * 5%);
}
}
//css
ul:nth-child(3n + 1) {
background-color: #004080;
}
ul:nth-child(3n + 2) {
background-color: #004d99;
}
ul:nth-child(3n + 3) {
background-color: #0059b3;
}
@while
循环,@while <expression> { ... }
//scss
/// Divides `$value` by `$ratio` until it's below `$base`.
@function scale-below($value, $base, $ratio: 1.618) {
@while $value > $base {
$value: $value / $ratio;
}
@return $value;
}
$normal-font-size: 16px;
sup {
font-size: scale-below(20px, 16px);
}
//css
sup {
font-size: 12.36094px;
}
@function
声明自定义函数, @function <name>(<arguments...>) { ... }
,用@return
返回
//scss
@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;
}
//css
.sidebar {
float: left;
margin-left: 64px;
}
@extend
用来扩展另一个选择器的样式@extend <selector>
,只能扩展简单的选择器(比如.info
),可以用逗号分隔扩展多个选择器的交集。
mixin和extend都可以复用样式,但是前者可以加参数,后者适合具有上下级关系的选择器。
@error和@warn、@debug
用在mixin和function中时用于提醒。
@at-root
@at-root <selector> { ... }
,对应选择器就像写在嵌套外一样。
//scss
div {
@at-root p {
color: aqua;
}
}
//css
p {
color: aqua;
}
也可以丢弃或选择嵌套中某些规则@at-root (with: <rules...>) { ... }
或@at-root (without: <rules...>) { ... }
来自css的at-rules
css中的at规则可以写作@<name> <value>
,@<name> { ... }
或@<name> <value> { ... }
,比如
@namespace svg url(http://www.w3.org/2000/svg);
@font-face {
font-family: "Open Sans";
src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}
@counter-style thumbs {
system: cyclic;
symbols: "\1F44D";
}
如果一个at规则被嵌套,输出时会提到最外层
内置模块
sass提供了好多内置模块(以sass:
开头),包含很多函数,可以使用@use
引入。
介绍各个模块的函数之前,先介绍全局函数
hsl($hue $saturation $lightness)
hsl($hue $saturation $lightness / $alpha)
hsl($hue, $saturation, $lightness, $alpha: 1)
hsla($hue $saturation $lightness)
hsla($hue $saturation $lightness / $alpha)
hsla($hue, $saturation, $lightness, $alpha: 1) //=> color
模块包括以下,具体使用参考Built-In Modules
sass:math
操作数字sass:string
操作字符串sass:color
操作颜色sass:list
操作listsass:map
操作mapsass:selector
操作选择器sass:meta
sass内部使用的函数
结语
关于sass的介绍到此结束了,现在可以继续去阅读2020年的css周边:preprocessor、postcss及css-in-js(一周内发布,编写中) 其他部分。
参考
- sass
- ecma262
- v8
- dart-sass
- Node-Sass
- Announcing Dart Sass
- Node-Sass or Dart-Sass : The CSS Preprocessor Dilemma
- dart-sass-cli
- at-rules
- css自定义属性
- Support "selector&" GitHub issue
- BEM
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!