Sass - 巢狀嵌套

嵌套巢狀(Nesting)

Sass 嵌套可以解決 CSS 階層結構重複撰寫的問題。

#content article h1 {
color: #333;
}
#content article p {
margin-bottom: 1.4em;
}
#content aside {
background-color: #eee;
}

Sass 可以讓撰寫樣式更輕鬆。可以一次編寫一個樣式階層規則,而不必一次又一次地重複使用相同的選擇器,這種方式常使用於後代選擇器方式。Sass 會將自動將它們組合在一起,避免重複撰寫,讓樣式可讀性提高。這就好像俄羅斯娃娃一樣,每一層就像打開一個俄羅斯娃娃,可以很清楚每一層的關係。雖然,Nesting 嵌套功能非常方便,但有過度階層化的陷阱,會造成選擇器的層級過深,不超過 3 層即可。

#content {
article {
h1 {
color: #333;
}
p {
margin-bottom: 1.4em;
}
}
aside {
background-color: #eee;
}
}

嵌套屬性(Neseted Properties)

屬性嵌套被使用在屬性有相同的字元,例如:font-family、font-size、font-weight 都是以 font 作為屬性命名空間,為了避免重複輸入而使用屬性嵌套。

// scss
.font-setting {
font: {
family: '微軟正黑體';
size: 2rem;
weight: bold;
}
}

nav {
border: {
style: solid;
width: 1px;
color: #ccc;
}
}

// css
.font-setting {
font-family: '微軟正黑體';
font-size: 2rem;
font-weight: bold;
}

nav {
border-style: solid;
border-width: 1px;
border-color: #ccc;
}

使用 & 與父選擇器連結

在嵌套 CSS 規則時,若遇到偽類,使用後代選擇器的方式就不適用,此時需要與父層連結,例如元素設定 :hover 樣式時,以下例子編譯後 & 會直接替換成父選擇器來用。

// 無法與偽類連結
div a {
color: blue;
:hover {
color: red;
}
}
// 使用 & 連結
div a {
color: blue;
&:hover {
color: red;
}
}

如果沒有父選擇器,& 的值將是空。這意味著你可以在一個 mixin 中使用它來檢測父選擇是否存在。

@mixin does-parent-exist {
@if & {
&:hover {
color: red;
}
} @else {
a {
color: red;
}
}
}

雖然 body.ie 在巢狀裡面,但因為使用 &,編譯出來會獨立出自己的樣式,但是寫在別人巢狀裡面會有較難以閱讀的問題。

// scss
#content aside {
color: red;
body.ie & {
color: green;
}
}
// css
#content aside {
color: red;
}
body.ie #content aside {
color: green;
}
  • 範例
// scss
.alert {
&:hover {
font-weight {
font-weight: bold;
}
}
// & 被當作父選擇器 .alert,子代 & 前要空白
[dir='rtl'] & {
margin-left: 0;
margin-right: 10px;
}

// & 被當作父選擇器 .alert
body.box & {
color: Red;
}

:not(&) {
opacity: 0.8;
}
}
// css
.alert:hover font-weight {
font-weight: bold;
}

[dir='rtl'] .alert {
margin-left: 0;
margin-right: 10px;
}

body.box .alert {
color: Red;
}

:not(.alert) {
opacity: 0.8;
}

可以使用 & 增加後綴詞:

  • & 作為選擇器的第一個字符,其後可以跟隨後綴詞產生複合選擇器。
  • & 必須出現在的選擇器的開頭位置(愚人碼頭注:也就是作為選擇器的第一個字符),但可以跟隨後綴,將被添加到父選擇的後面。
// scss
.btn {
display: inline-block;
color: #212529;
text-align: center;
vertical-align: middle;
background-color: transparent;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
line-height: 1.5;
border-radius: 0.25rem;

&-primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
&:hover {
color: #fff;
background-color: #0069d9;
border-color: #0062cc;
}
}

&-lg {
padding: 0.5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: 0.3rem;
}
}

// css
.btn {
display: inline-block;
color: #212529;
text-align: center;
vertical-align: middle;
background-color: transparent;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
line-height: 1.5;
border-radius: 0.25rem;
}

.btn-primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}

.btn-primary:hover {
color: #fff;
background-color: #0069d9;
border-color: #0062cc;
}

.btn-lg {
padding: 0.5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: 0.3rem;
}

& 與判斷式 if

// 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(#c6539c);
}
// css
.app-background {
background-color: #036;
color: rgba(255, 255, 255, 0.75);
}

.sidebar.app-background {
background-color: #c6539c;
color: rgba(255, 255, 255, 0.75);
}

進階 & 使用

@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 {
/* ... */
}

群組選擇器(逗號分隔的選擇器)

// scss
.container {
h1,
h2,
h3 {
margin-bottom: 0.8em;
}
}

// CSS
.container h1,
.container h2,
.container h3 {
margin-bottom: 0.8em;
}

與各選擇器的結合

// scss
// 子代選擇器
ul {
> li {
list-style: 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;
}

@at-root

@at-root 可以寫在階層內,它會跳脫父層獨立出來。

// scss
.parent {
color: black;
@at-root {
.child1 {
color: black;
}
.child2 {
color: black;
}
}
}

// css
.parent {
color: black;
}

.child1 {
color: black;
}

.child2 {
color: black;
}

另一個例子

// scss
.tooltip {
font-size: 20px;
@at-root body.active .tooltip-backdrop {
position: fixed;
}
}
// css
.tooltip {
font-size: 20px;
}

body.active .tooltip-backdrop {
position: fixed;
}