Sass/Scss 学习总结(四) 之 @规则

Sass 支持所有 CSS3 的 @ 规则,以及一些已知的其他特定的 Sass “指令”。这些在 Sass 都有对应的效果。


@import

Sass 扩展了 CSS @import 规则,允许其导入 Scss 或 CSS 文件。被导入的 Scss 或 Sass 文件将一起合并到同一个 CSS 文件中。此外,被导入文件中所定义的任何变量或混入(mixins)都可以在主文件(主文件指的是导入其他文件的文件,即:A文件中导入了B文件,这里的主文件指的就是A文件)中使用。

Sass 会在当前目录和 Rack,Rails,Merb 目录下查找其他 Sass 文件。附加搜索目录可以使用:load_paths 选项或命令行中的 --load-path 选项指定。

import 默认情况下,它会寻找一个 Sass 文件直接导入(即将被导入的文件内部复制到主文件中),但在以下情况下,仅作为普通的 CSS @import 规则语句,不会导入任何 Sass 文件。

  • 如果文件的扩展名是 .css

  • 如果文件名以 http:// 开始

  • 如果文件名是 url()

  • 如果@import 中包含任何的媒体查询(media queries)

如果没有上述条件得到满足并且扩展名是 .scss 或 .sass ,那么 Sass 或 Scss 文件将被导入。

注:如果导入 Sass 或 Scss 文件,扩展名可以省略。

例如:

// a.scss 文件
// -------------------------------------------

$width: 200px;

div {
    width: $width;
}


// b.scss 文件
// -------------------------------------------

@import "a.scss"; // 也可以把扩展名省略 @import "a"
@import "foo.css"

p {
    width: $width; // 这个变量来自 a.scss
}


// 编译后的 b.css 文件
// -------------------------------------------

div {
    width: 200px; 
}

p {
    width: 200px; 
}


嵌套 @import 

虽然在大部分情况下,一般都是在文档的顶层使用 @import,但是也可以在 CSS 规则和 @media 规则中包含 @import 语句。就像一个基层的 @import,这里会包含 @import 导入文件的内容。但是,这样导入的规则只能嵌套在原先 @import 的地方。如:

// example 文件
// -------------------------------------------
.example {
    color: red;
}


// b.scss 文件
// -------------------------------------------

#main {
    @import "example"
}


// 编译后的 b.css 文件
// -------------------------------------------

#main .example {
    color: red;
}


@media

Sass 中 @media 指令的行为和纯 CSS 中一样,只是增加了一点额外的功能:它们可以嵌套在 CSS 规则。如果一个 @media 指令出现在 CSS 规则中,它将被冒泡到样式表的顶层,并且包含规则内所有的选择器。这使得很容易地添加特定 media 样式,而不需要重复使用选择器,或打乱样式表书写。例如:

// b.scss 文件
// -------------------------------------------

.sidebar {
    width: 300px;
    @media screen and (orientation: landscape) {
        width: 500px;
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

.sidebar {
    width: 300px;
}

@media screen and (orientation: landscape) {
    .sidebar {
    	width: 500px; 
    } 
}

@media 的查询(queries)也可以相互嵌套。这些查询(queries)在编译时,将会使用 and 操作符号结合。例如:

// b.scss 文件
// -------------------------------------------

@media screen {
    .sidebar {
        @media (orientation: landscape) {
            width: 500px;
        }
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

@media screen and (orientation: landscape) {
    .sidebar {
        width: 500px;
    }
}

@media 甚至可以使用 SassScript(比如变量、函数、以及运算符)代替条件的名称或者值:

// b.scss 文件
// -------------------------------------------

$media: screen;
$feature: -webkit-min-device-pixel-ratio;
$value: 1.5;

@media #{$media} and ($feature: $value) {
    .sidebar {
	width: 500px;
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

@media screen and (-webkit-min-device-pixel-ratio: 1.5) {
    .sidebar {
        width: 500px;
    }
}


@extend

设计一个页面时常常遇到这种情况:当一个样式类(class)含有另一个类的所有样式,以及它自己的特定样式。处理这种最常见的方法是在 HTML 同时使用一个通用样式类和特殊样式类。例如:假设我们设计需要一个普通错误的样式和一个严重错误的样式。我们可以类似这样写:

// html 文件
// -------------------------------------------

<div class="error seriousError">
    Oh no! You've been hacked!
</div>


// CSS 样式
// -------------------------------------------

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

.seriousError {
    border-width: 3px;
}

@extend 指令避免这些问题,告诉 Sass 一个选择器的样式应该继承另一选择器。例如:

// b.scss 文件
// -------------------------------------------

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

.seriousError {
    @extend .error;
    border-width: 3px;
}


// 编译后的 b.css 文件
// -------------------------------------------

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

.seriousError {
    border-width: 3px;
}

这意味着 .error 说定义的所有样式也适用于 .seriousError,除了 .seriousError 的特定样式。相当于,每个带有 .seriousError 类的元素也带有 .error 类。

其他使用了  .error 规则也会同样继承给 .seriousError,例如:

// b.scss 文件
// -------------------------------------------

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

.error.intrusion {
    background-image: url("/image/hacked.png");
}

.seriousError {
    @extend .error;
    border-width: 3px;
}


// 编译后的 b.css 文件
// -------------------------------------------

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

.error.intrusion, .intrusion.seriousError {
    background-image: url("/image/hacked.png");
}

.seriousError {
    border-width: 3px;
}


多重扩展

同一个选择器可以扩展多个选择器。这意味着,它继承了被扩展选择器的所有样式。例如:

// b.scss 文件
// -------------------------------------------

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

.attention {
    font-size: 3em;
    background-color: #ff0;
}

.seriousError {
    @extend .error;
    @extend .attention;
    border-width: 3px;
}


// 编译后的 b.css 文件
// -------------------------------------------

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

.attention, .seriousError {
    font-size: 3em;
    background-color: #ff0;
}

.seriousError {
    border-width: 3px;
}

每个带 .seriousError 类的元素也有 .error 类和 .attention 类,因此,定义在文档后面的样式优先级高于定义在文档前面的样式:.seriousError 的背景颜色是 #ff0,而非 #fdd,因为 .attention 是在 .error 后面定义。

多重扩展也可以用逗号分隔的选择器列表(list)写入。例如,@extend .error,  .attention等同于 @extend .error;  @extend .attention。


链式扩展

一个选择器可以扩展另一个选择器,另一个选择器又扩展的第三选择器选择。例如:

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

.seriousError {
    @extend .error;
    border-width: 3px;
}

.criticalError {
    @extend .seriousError;
    position: fixed;
    top: 10%;
    bottom: 10%;
    left: 10%;
    right: 10%;
}

现在,带 .seriousError 类的每个元素将包含 .error 类,而带 .criticalError 类的每个元素不仅包含 .criticalError 类也会同时包含 .error 类,上面的代码编译为:

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

.seriousError, .criticalError {
    border-width: 3px;
}

.criticalError {
    position: fixed;
    top: 10%;
    bottom: 10%;
    left: 10%;
    right: 10%;
}


选择器序列

选择器序列,比如:.foo  .bar 或 .foo + .bar,目前还不能作为扩展。但是,选择器序列本身可以使用 @extend。例如:

// b.scss 文件
// -------------------------------------------

#fake-links .link {
    @extend a;
}

a {
    color: blue;
    &:hover {
        text-decoration: underline;
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

a, #fake-links .link {
    color: blue;
}

a:hover, #fake-links .link:hover {
    text-decoration: underline;
}


合并选择器序列

有时,选择器序列扩展另一个选择器,这个选择器出现在另一选择器序列中。在这种情况下,这两个选择器序列需要合并。例如:

#admin .tabbar a {
    font-weight: bold;
}

#demo .overview .fakelink {
    @extend a;
}

技术上讲能够生成所有匹配条件的结果,但是这样生成的样式表太复杂了,上面这个简单的例子就可能有 10 种结果。所以,Sass 只会编译输出有用的选择器。

当两个列(sequence)合并时,如果没有包含相同的选择器,将生成两个新选择器;第一列出现在第二列之前,或者第二列出现在第一列之前,例如:

// b.scss 文件
// -------------------------------------------

#admin .tabbar a {
    font-weight: bold;
}

#demo .overview .fakelink {
    @extend a;
}


// 编译后的 b.css 文件
// -------------------------------------------

#admin .tabbar a, 
#admin .tabbar #demo .overview .fakelink, 
#demo .overview #admin .tabbar .fakelink {
	font-weight: bold; 
}

如果两个列(sequence)包含了相同的选择器,相同部分将会合并在一起,其他部分交替输出。在下面的例子里,两个列都包含 #admin,输出结果中它们合并在了一起:

// b.scss 文件
// -------------------------------------------

#admin .tabbar a {
    font-weight: bold;
}

#admin .overview .fakelink {
    @extend a;
}


// 编译后的 b.css 文件
// -------------------------------------------

#admin .tabbar a, 
#admin .tabbar .overview .fakelink, 
#admin .overview .tabbar .fakelink {
	font-weight: bold; 
}


@extend-only 选择器

有时候你只会想写一个 @extend 扩展样式类,不想直接在你的 HTML 中使用,在写一个 Sass 样式库时,这是特别有用,如果他们需要,在这里你可以提供 @extend 扩展样式给用户,如果他们不需要,直接被忽视。

对于这种情况,如果使用普通的样式类,在你最终生成的样式表中,会有很多额外的CSS,并且在 HTML 被使用时,和其他样式类结合的时候容易造成冲突。这就是 Sass 为什么支持“占位选择器”的原因(例如,%foo)。

占位选择器看起来很像普通的 class 和 id 选择器,只是 # 或 . 被替换成了 %。他可以像 class 或者 id 选择器那样使用,而它本身的规则,不会被编译到 CSS 文件中,例如:

#context a%extreme {
    color: blue;
    font-weight: bold;
    font-size: 2em;
}

占位符选择器,就像 class 和 id 选择器那样可以用于扩展。扩展选择器,将会编译成 CSS ,占位符选择器本身不会被编译。例如:

// b.scss 文件
// -------------------------------------------

#context a%extreme {
    color: blue;
    font-weight: bold;
    font-size: 2em;
}

.notice {
	@extend %extreme;
}


// 编译后的 b.css 文件
// -------------------------------------------

#context a.notice {
    color: blue;
    font-weight: bold;
    font-size: 2em;
}

注:通常,当你扩展一个选择器的时候,如果说 @extend 不起作用了,你会收到一个错误提示。例如,如果没有 .notice 选择器,你这么写 a.important { @extend .notice },将会报错。

这时候,可以在扩展器后面添加个 !optional ,可以取消这种错误。如:

a.important {
    @extend .notice !optional;
}


指令中的 @extend

在指令中使用 @extend 时(比如在@media 中)存在一些限制:Sass 不可以将 @media 层外的 CSS 规则扩展给指令层内的 CSS,这样会生成大量的无用代码。意思是说,如果在 @media (或者其他 CSS 指令)中使用@extend,必须扩展给相同指令层中的选择器。

下面的例子是可行的:

@media print {
    .error {
        border: 1px #f00;
        background-color: #fdd;
    }
    .seriousError {
        @extend .error;
        border-width: 3px;
    }
}

但下面这个例子会报错:

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

@media print {
    .seriousError {
	// You may not @extend an outer selector from within @media
        @extend .error;
        border-width: 3px;
    }
}


@at-root

在第二章,我们学习 Sass 语法时,知道 Sass 在嵌套时,内层选择器会继承外层选择器。如:

// b.scss 文件
// -------------------------------------------

div {
    a {
	color: red;
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

div a {
    color: red; 
}

但是有时候,我们需要在内层里跳出这种继承,这时候,我们可以使用 @at-root。如:

// b.scss 文件
// -------------------------------------------

div {
    @at-root a {
	color: red;
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

a {
    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,如:

// b.scss 文件
// -------------------------------------------

@media print {
    .page {
	width: 800px;

	a {
    	    color: red;

	    @at-root(without: rule)
	    {
	        span { color: #00f }
	    }
	}
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

@media print {
    .page {
        width: 800px;
    }
    .page a {
        color: red;
    }
    span {
        color: #00f;
    }
}

由上面编译后的 CSS 文件,可以看出 @at-root(without: rule) 可以跳出父级 a 以及 父父级 .page ,但是,跳不出 @media。


@at-root(without: media)

我们把上面的代码 @at-root(without: rule) 换成 @at-root(without: media),如:

// b.scss 文件
// -------------------------------------------

@media print {
    .page {
	width: 800px;

	a {
	    color: red;

	    @at-root(without: media)
	    {
		span { color: #00f }
	    }
	}
    }
}


// 编译后的 b.css 文件
// -------------------------------------------

@media print {
    .page {
        width: 800px;
    }
    .page a {
        color: red;
    }
}

.page a span {
    color: #00f;
}

由上可以看出 @at-root(without: media) 可以跳出 @media ,但是没有跳出父级选择器,如果我们想跳出 @media 和 父级嵌套,可以一次添加两个指令,两个指令用空格分隔,如:

// b.scss 文件
// -------------------------------------------

@media print {
    .page {
	width: 800px;

	a {
            color: red;

	    @at-root(without: media rule)
	    {
		span { color: #00f }
	    }
	}
    }
}

// 编译后的 b.css 文件
// -------------------------------------------

@media print {
    .page {
        width: 800px;
    }
    .page a {
        color: red;
    }
}

span {
    color: #00f;
}


@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),效果是一样的。


未经允许不得转载:易读小屋  »  Sass/Scss 学习总结(四) 之 @规则