样式规则(Style Rules)
总览
和css一样,样式规则是Sass的基础,原理也差不多:用选择器选择你所要改变样式的元素,然后声明一些属性去决定它的样式。
Sass:
.button { padding: 3px 10px; font-size: 12px; border-radius: 3px; border: 1px solid #e1e4e8; }
css:
.button { padding: 3px 10px; font-size: 12px; border-radius: 3px; border: 1px solid #e1e4e8; }
1)嵌套(Nesting)
当然Sass致力于让码生幸福,不会让我们一遍又一遍地去写重复的选择器,所以你可以写嵌套样式,Sass会自动把外层选择器和内层样式规则结合起来。
Sass:
nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } }
css:
nav ul { margin: 0; padding: 0; list-style: none; } nav li { display: inline-block; } nav a { display: block; padding: 6px 12px; text-decoration: none; }
注意:嵌套规则很好用,但是同时你也无法从视觉上去感受你到底产生了多少css,嵌套得越深加载所需带宽越高,浏览器渲染工作量也越大,所以尽量嵌套浅一些。
1.选择器列表(Selecor Lists)
嵌套规则在处理选择器列表(就是用逗号分隔的选择器)上还是比较智能的,每个复合选择器(逗号之间的)分别嵌套,再被结合为一个选择器列表。
Sass:
.alert, .warning { ul, p { margin-right: 0; margin-left: 0; padding-bottom: 0; } }
css:
.alert ul, .alert p, .warning ul, .warning p { margin-right: 0; margin-left: 0; padding-bottom: 0; }
2.选择器连接符(Selector Combinators)
你也可以嵌套使用了连接符的选择器,连接符可以放在外部选择器的尾部,内部选择器的开头,或者是自身要连接的选择器之间。
Sass:
ul > { li { list-style-type: none; } } h2 { + p { border-top: 1px solid gray; } } p { ~ { span { opacity: 0.8; } } }
css:
ul > li { list-style-type: none; } h2 + p { border-top: 1px solid gray; } p ~ span { opacity: 0.8; }
3.高级嵌套(Advanced Nesting)
万一你不想让你的嵌套仅仅按顺序以派生连接符(就那空格)结合起来,Sass也是支持你的。详情见下节父选择器。
2)插值(Interpolation)
你可以使用插值来从表达式(比如变量、函数调用)中注入动态值到你的选择器中,这在你书写混合变量的时候格外有用,因为这样的话你就可以根据用户传入的变量来决定使用什么选择器。
Sass:
@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: "??"; }
PS:Sass只有在求得插值之后才会去解析选择器,所以你可以放心地在选择器的任何地方使用插值,不用担心它是不是还没有解析。
你可以在需要动态决定选择器的时候把插值和父选择器&、at-rules和选择器函数结合使用来达到效果。详情见下节父选择器。
属性声明(Property Declarations)
和css一样,Sass里的属性声明定义了和选择器匹配的元素的样式。但是Sass定义了额外的特性来增强易书写性和智能性。重要的是,声明的值可以是Sass表达式,它会被求值然后包含在结果中。
Sass:
.circle { $size: 100px; width: $size; height: $size; border-radius: $size / 2; }
css:
.circle { width: 100px; height: 100px; border-radius: 50px; }
1)插值(Interpolation)
属性名可以包括插值,这样的话就可以按需决定使用哪个属性名了。甚至整个属性名都可以由插值决定。
Sass:
@mixin prefix($property, $value, $prefixes) { @each $prefix in $prefixes { -#{$prefix}-#{$property}: $value; } #{$property}: $value; } .gray { @include prefix(filter, grayscale(50%), moz webkit); }
css:
.gray { -moz-filter: grayscale(50%); -webkit-filter: grayscale(50%); filter: grayscale(50%); }
2)嵌套(Nesting)
很多css属性以相同的前缀开头,这些前缀扮演着类似于命名空间的作用。举个例子,font-family、font-size、font-weight就都以font-开头。Sass通过允许属性声明嵌套来使写法更加简易。外层属性会加到内层属性上,通过连字符来连接。
Sass:
.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; }
PS:有些css属性使用命名空间名作为属性名的简略写法,这种情况你可以同时使用简略写法和复杂的嵌套写法。
Sass:
.info-page { margin: auto { bottom: 10px; top: 2px; } }
css:
.info-page { margin: auto; margin-bottom: 10px; margin-top: 2px; }
3)隐式声明(Hidden Declarations)
有时候你想要一个属性声明只在特定时候出现,如果一个属性的值是null或者空的未加引号的字符串,Sass就不会把它编译到css中。
$rounded-corners: false; .button { border: 1px solid black; border-radius: if($rounded-corners, 5px, null); }
css:
.button { border: 1px solid black; }
4)自定义属性(Custom Properties)
css自定义属性,也被称为css变量,它们的声明语法比较特殊,允许变量名包含几乎任何文本,而且这些变量JavaScript也可以访问,所以任何变量对于书写者来说都可能有意义,包括被正常转为SassScript的变量。因此Sass解析自定义属性声明的时候跟正常属性声明不一样。所有类似于SassScript的字符会原样输出为css。唯一注入动态变量到自定义属性的方法是插值。
Sass:
$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; }
注意:不幸的是插值会移除字符串的引号,所以将来自Sass变量的被引号包裹的字符串注入自定义属性就比较困难,但是你可以使用inspect()方法来保留引号。
Sass:
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto; $font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas; :root { --font-family-sans-serif: #{inspect($font-family-sans-serif)}; --font-family-monospace: #{inspect($font-family-monospace)}; }
css:
:root { --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto; --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas; }
父选择器(Parent Selector)
父选择器&是Sass独创的一个选择器,用于在嵌套选择器中指向其外层选择器,利用父选择器可以实现外层选择器的多重复杂重用,比如增加一个伪类或者其他选择器。
和正常的嵌套机制不同,当父选择器在一个选择器内部使用时它指代的就是这个选择器。
Sass:
.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; }
注意:由于父选择器可以代表类选择器比如h1,它只能出现在类选择器能够出现的地方即复合选择器开头,例如span&就是不合法的写法。我们也在尝试放松这些限制,你想的话可以加入我们。
1)添加后缀(Adding Suffix)
你也可以使用父选择器来为外层选择器添加后缀,这在使用某些例如BEM这种使用高度结构化类名的命名原则时会格外有用,只要外层选择器以含有字母和数字的名称(比如class,id,元素选择器)结尾,你就可以使用父选择器添加额外文本。
Sass:
.accordion { max-width: 600px; margin: 4rem auto; width: 90%; font-family: "Raleway", sans-serif; background: #f4f4f4; &__copy { display: none; padding: 1rem 1.5rem 2rem 1.5rem; color: gray; line-height: 1.6; font-size: 14px; font-weight: 500; &--open { display: block; } } }
css:
.accordion { max-width: 600px; margin: 4rem auto; width: 90%; font-family: "Raleway", sans-serif; background: #f4f4f4; } .accordion__copy { display: none; padding: 1rem 1.5rem 2rem 1.5rem; color: gray; line-height: 1.6; font-size: 14px; font-weight: 500; } .accordion__copy--open { display: block; }
2)SassScript用法(In SassScript)
父选择器可以在SassScript中使用,属于特殊表达式,返回当前父选择器,和选择器函数使用的格式一样,包含以空格分隔的列表即复杂选择器(the complex selectors )的以逗号分隔的选择器列表(the selector list),复杂选择器又可能包含不带引号的字符串即复合选择器(the compound selectors)。
Sass:
SCSS SYNTAX .main aside:hover, .sidebar p { parent-selector: &; // => ((unquote(".main") unquote("aside:hover")), // (unquote(".sidebar") unquote("p"))) }
css:
.main aside:hover, .sidebar p { parent-selector: .main aside:hover, .sidebar p; }
如果父选择器在样式规则外层使用,它会返回null,而null属于falsey,因此你可以借此来决定在一个样式规则中要不要引用一个混合类型。
Sass:
@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); }
高级嵌套(Advanced Nesting)
你可以把父选择器作为正常表达式使用,也就是说你可以把它传入函数或者插值,即使是在别的选择器里。结合选择器函数和@at-root规则使用可以达到非常强大的嵌套效果。
举个例子,假设你要写一个选择器来匹配外层选择器和一个元素选择器,你可以像这样使用selector-unify()函数来结合父选择器和另一个自定义选择器:
Sass:
@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 { /* ... */ }
注意:Sass在处理嵌套的时候,它也不知道这些选择器是由什么样的插值产生的,所以它会自动把外层选择器加到内层选择器前面,即使你使用了父选择器作为SassScript表达式。所以你要使用@at-root规则来明确告诉Sass不要去包含外层选择器。
占位符选择器(Placeholder Selectors)
Sass还有一种特殊的选择器‘占位符’。它的长相和行为都很像一个类选择器,不同的是它以%开头并且不输出到css中。事实上,任何含有了占位符选择器的复杂选择器(被逗号隔离的那些)及其对应样式规则都不会被输出到css中。
Sass:
.alert:hover, %strong-alert { font-weight: bold; } %strong-alert:hover { color: red; }
css:
.alert:hover { font-weight: bold; }
一个选择器它都被全部忽略了那要它干嘛?诶,它还可以被扩展。不像类选择器,如果不进行扩展,并且不要求库的用户为其HTML使用特定的类名,则占位符不会出现使得css一团乱。
Sass:
%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库时有些样式规则只需要某些情况下起作用,这时候占位符就显得格外有用。但根据经验,如果你只是为自己的应用编写样式表的话,如果有可用的类选择器,扩展类选择器通常更好。