SCSS 中这些技巧,你可能还不知道!

内置函数

sass官网提供了很多实用的内置函数,当然目前我也是在一步一步探索这些函数。目前我会将常用到的内置函数以及场景分享给大家使用。

darken/lighten

  • 定义

lighten()和darken()两个函数都是围绕颜色的亮度值做调整的,其中lighten()函数会让颜色变得更亮,与之相反的darken()函数会让颜色变得更暗。这个亮度值可以是0~1之间。

lighten(#fff,10%) //原色的亮度值 增加百分之10亮度
draken(#fff,10%) // 在原色的基础上 减少百分之10亮度
  • 应用场景

lighten和darken这两个内置函数经常被用到元素被hover/focus时,当我们hover,一个元素的时候。此时并不希望改变这个元素的色值,但是又想要用户感知到鼠标停留在这个元素上。此时这两个内置函数就发挥了他们的作用了。

@mixin button-type(
    $background,
    $border,
    $color,
    $hover-background: lighten($background, 7.5),
    $hover-border: lighten($border, 10%),
    $hover-color: $color
) {
    color: $color;
    background: $background;
    border-color: $border;
    &:hover {
        color: $hover-color;
        background: $hover-background;
        border-color: $hover-border;
    }
    &:focus,
    &.is-focus {
        color: $hover-color;
        background: $hover-background;
        border-color: $hover-border;
    }
    &[disabled],
    &.is-disabled {
        color: $color;
        background: $background;
        border-color: $border;
    }
}

desaturate

饱和度(Saturation)是指色彩的纯度,饱和度越高色彩越纯越浓,饱和度越低则色彩变灰变淡。

sass中的desaturate函数就是针对饱和度操作的内置方法。

desaturate($color, $amount) //=> color

使$color饱和度降低, 在$amount必须之间的数字0%和100%(包含)。

@warn

编写MixIn和函数时,您可能希望劝阻用户传递某些参数或某些值。他们可能正在传递现在已弃用的传统参数,或者他们可能会以不太最佳的方式调用您的API。

简单来说在mixin或者function内部,我们可以通过@warn操作符给用户提示一些警告内容输出在控制台。

@mixin prefix($property, $value, $prefixes) {
    @each $prefix in $prefixes {
        @if not index($known-prefixes, $prefix) {
            @warn "Unknown prefix #{$prefix}.";
        }

        -#{$prefix}-#{$property}: $value;
    }
    #{$property}: $value;
}

Inspect()断电函数

其实Inspect()函数用的比较少,主要是用来做校验类型的。

Inspect(...)表达式中的内容如果是正常会返回对应的内容,如果发生错误则会弹出一个错误提示。

Map相关内容

Map-has-key

$font-weights: ("regular": 400, "medium": 500, "bold": 700);

map.has-key($font-weights, "regular"); // true
map.has-key($font-weights, "bolder"); // false

map.has-key()在scss中的条件判断时应用场景特别多。

比如下方这段代码

$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;

$--breakpoints: (
    'xs' : (max-width: $--sm - 1),
    'sm' : (min-width: $--sm),
    'md' : (min-width: $--md),
    'lg' : (min-width: $--lg),
    'xl' : (min-width: $--xl)
);

@mixin res($key, $map: $--breakpoints) {
  // 循环断点Map,如果存在则返回
  @if map.has-key($map, $key) {
        @media only screen and #{inspect(map-get($map, $key))} {
            @content;
        }
    } @else {
        @warn "Undefeined points: `#{$map}`";
    }
}

map.get(map,k1,k2,...)

简单来说就是通过key在map中取到对应的value

$config: (a: (b: (c: d)));
map.get($config, a, b, c); // d

占位符选择器%作用

Sass 额外提供了一种特殊类型的选择器:占位符选择器 (placeholder selector)。与常用的 id 与 class 选择器写法相似,只是 # 或 . 替换成了 %。

%heading {
    margin-top: 0; // 1
    margin-bottom: $headings-margin-bottom;
    font-family: $headings-font-family;
    font-style: $headings-font-style;
    font-weight: $headings-font-weight;
    line-height: $headings-line-height;
    color: $headings-color;
}

其实使用%在大多数(~所有~)场景下,我的理解就是和@mixin是一样的效果。使用%占位符选择器的样式,只能被@extend进行调用。

需要注意的是,如果使用占位符选择器%定义的样式,单独使用的时候(未通过extend)进行调用,那么这段样式是不会编译到css的输出结果之后的。

Partials import

和css类似scss支持@import命令,但css的import命令每次调用都会创建一个额外的html请求,但scss的import命令是编译时将文件包含在css中,不需要额外发起请求。

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

简单来说,项目目录中的所有scss文件在编译阶段都会被编译成为一个个css文件。

但是对于一个公用样式文件,此时我们并不需要将它编译成为单独的css文件,而是希望将公用文件中的样式插入到对应引入样式文件中,我们只需要在引入它的文件中将它编译进入引入的css文件中就可以。

此时给文件名称以_开头就可以告诉scss在编译阶段并不会将它编译成为单独的css文件,而是仅仅会将它的样式编译进入引入它的样式文件中去。

比如一个文件夹两个 scss 文件,一个 root.scss,一个 _vars.scss。

// 第一个 scss 文件夹名 -o 是输出文件夹名称``npx node-sass scss -o output``// 只会有一个文件生成
rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/root.css //将 _vars 该名称为 vars.scss 再执行一遍
Rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/root.css
Rendering Complete, saving .css file...
Wrote CSS to /Users/liusha/Public/vikingship/output/vars.css
会有两个文件生成

这在组件库的开发中是非常有用的,定义单独组件的样式文件以Partials import进行定义,不单独打包成为css文件,在最终导入的样式文件中统一进行合并管理而不打包出单独的css文件。

变量声明global与default

!default 默认变量

提示

可以在变量的结尾添加 !default 给一个未通过 !default 声明赋值的变量赋值,此时,如果变量已经被赋值,不会再被重新赋值,但是如果变量还没有被赋值,则会被赋予新的值。

比如这样一段代码:

$color:red;
$color:blue !default;

.modules-a {
    color: $color; // red
}

我们可以看到即使是先声明的red,因为blue !default,所以红色覆盖了蓝色。!default声明变量的意思就是说如果项目中存在相同的声明则优先使用别的声明,如果不存在则使用!default的值,可以理解为默认值。

!global全局声明

提示

变量支持块级作用域,嵌套规则内定义的变量只能在嵌套规则内使用(局部变量),不在嵌套规则内定义的变量则可在任何地方使用(全局变量)。将局部变量转换为全局变量可以添加 !global 声明

在scss中我们都清楚局部变量的定义是无法影响同名的global变量的。但是我们可以通过!global在局部作用域中去定义一个全局都可以使用的变量。同样也可以通过!default在局部作用域中去覆盖一个全局变量的值。

#main {
    $width: 5em !global;
    width: $width;
}

#sidebar {
    // 同样可以使用$width全局变量
    width: $width;
}
#main {
    width: 5em;
}

#sidebar {
    width: 5em;
}

mixin

参数变量...

提示

有时,不能确定混合指令需要使用多少个参数,比如一个关于 box-shadow 的混合指令不能确定有多少个 'shadow' 会被用到。这时,可以使用参数变量 … 声明(写在参数的最后方)告诉 Sass 将这些参数视为值列表处理.

其实就类似于js中的...rest运算符。

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

使用,分隔为.shadowed元素添加多个阴影

@content-- 向混合样式中导入内容

提示

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

比如这样的代码,我们在include中填充了对应的样式,在mixin中可以通过@content使用。

@mixin apply-to-ie6-only {
    * html {
        @content;
    }
}
@include apply-to-ie6-only {
    #logo {
        background-image: url(/logo.gif);
    }
}
// mixin中接受了include 可以理解为插槽
* html #logo {
    background-image: url(/logo.gif);
}

为便于书写,@mixin 可以用 = 表示,而 @include 可以用 + 表示,所以上面的例子可以写成:

// = 简写mixin
=apply-to-ie6-only
    * html
        @content

// + 简写include
+apply-to-ie6-only
    #logo
        background-image: url(/logo.gif)

注意: 当 @content 在指令中出现过多次或者出现在循环中时,额外的代码将被导入到每一个地方。

@at-root

常规用法

提示

@at-root指令可以使一个或多个规则被限定输出在文档的根层级上,而不是被嵌套在其父选择器下。

.parent {
    ...
    @at-root .child { ... }
}
.parent { ... }
.child { ... }

@at-root同样也可以当作一个作用域给多个选择器去使用:

.parent {
    ...
    @at-root {
        .child1 { ... }
        .child2 { ... }
    }
    .step-child { ... }
}
.parent { ... }
.child1 { ... }
.child2 { ... }
.parent .step-child { ... }

支持参数

@at-root (without: ...) and @at-root (with: ...)

默认使用@at-root不传递任何时,他的作用为跳出选择器的作用域嵌套,当然可以传递参数去使用。

比如下面的@at-root意为跳出@media的嵌套:

@media print {
    .page {
        width: 8in;
        @at-root (without: media) {
        color: red;
        }
    }
}
@media print {
    .page {
        width: 8in;
    }
}
.page {
    color: red;
}

默认 @at-root 只会跳出选择器嵌套,而不能跳出@media或 @support,如果要跳出这两种,则需使用 @at-root(without: media),@at-root(without: support)。这个语法的关键词有

  • 四个:
    • all(表示所有)
    • rule(表示常规,默认行为)
    • media(表示 media)
    • support(表示 support )

我们默认的 @at-root 其实就是 @at-root( without: rule ):跳出作用域嵌套规则。

  • @at-root(without: rule)

    • rule 关键词只能跳出选择器嵌套,不能跳出 @media 和 @support
  • @at-root(without: media)

    • 可以跳出 @media ,但是没有跳出父级选择器
  • @at-root(without: support)

    • @at-root(without: support) 和 @at-root(without: media) 相似,只是跳出的是 @support。
  • @at-root(without: all)

    • @at-root(without: all) 是跳出所的指令和规则,如上面的代码里 @at-root(without: media rule) 我们可以换成 @at-root(without: all),效果是一样的。

@each in

maps数据格式

首先我们来说说在scss中定义类似js中的对象。

$map: (key1: value1, key2: value2, key3: value3);

我们通过()就可以定义了。比如这样的Maps结构定义。

$blue: #0d6efd !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #d63384 !default;
$red: #dc3545 !default;
$orange: #fd7e14 !default;
$yellow: #fadb14 !default;
$green: #52c41a !default;
$teal: #20c997 !default;
$cyan: #17a2b8 !default;

$primary: $blue !default;
$secondary: $gray-600 !default;
$success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$dark: $gray-800 !default;

$theme-colors: (
    'primary': $primary,
    'secondary': $secondary,
    'success': $success,
    'info': $info,
    'warning': $warning,
    'danger': $danger,
    'light': $light,
    'dark': $dark,
);

我们定义了一个maps的主题,分别存在对应的名称和对应的颜色值。

提示

@each 指令的格式是 $var in <list>, $var 可以是任何变量名,比如 $length 或者 $name,而 <list> 是一连串的值,也就是值列表。

"数组"迭代

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

@each $animal in puma, sea-slug, egret, salamander {
    .#{$animal}-icon {
        background-image: url('/images/#{$animal}.png');
    }
}
.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');
}

此时类似于js中的数组迭代。

对象迭代

当然@each $val,$key in maps,也支持"迭代"一个对象(maps)。比如:

@each $key, $value in $theme-colors {
    .#{$prefix}-icon--#{$key} {
        color: $value;
    }
}

多个值迭代

@each 指令也可以使用多个变量,如@each $var1, $var2, ... in 。如果是列表列表,则子列表的每个元素都分配给相应的变量。例如

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

css :scope选择器

提示

在JavaScript中,当用于Element.querySelectorElement.querySelectorAllElement.closest时,:scope是指调用这些方法的元素。例如,document.body.querySelector(":scope")返回body元素。尽管CSS对 :scope的支持已被移除,但 :scope 的这种用法仍然被支持

使用:scope选择器可以匹配对应的方法比如element.querySelector(':scope')则会返回element元素本身。

注意

需要主要的是:scope伪类在css中已经不被大多数浏览器支持,甚至已经废弃。但是在js这些方法中仍然被主流浏览器支持。

贡献者: mankueng