Sass - 變數

變數(Variables)

變數使用 $ 來表示,變數名稱需以易懂,一眼就知道這個變數的用途為命名標準。變數寫在最上面,確保宣告可以讓 Sass 文件使用,賦值寫法與 CSS 屬性一樣。

宣告

// 宣告變數
$width: 300px;
$height: 300px;
$color: #ffa;

// 使用變數
.box {
width: $width;
height: $height;
background-color: $color;
border: 1px solid $color;
}

// 也可以以空格分開的多屬性值
$basic-border: 1px solid black;

// 也可以是以逗號分開的多屬性值
$main-font: '微軟正黑體', 'Helvetica', 'Arial';

//更複雜的變數
$highlight-color: #f90;
$highlight-border: 1px solid $highlight-color;
.selected {
border: $highlight-border;
}

作用域

變數定義在 CSS {} 塊級區塊內,變成只能在此區塊內使用,稱為區域變數;非區塊內最外層的為全域變數,整份文件皆可以使用,稱為全域變數。在值的後方添加 !global 可以將區域變數轉換為全域變數。

// scss
$nav-color: #f90;
nav {
$width: 100px;
width: $width;
color: $nav-color;
}

// css
nav {
width: 100px;
color: #f90;
}

連接符與底線相互兼容

變數可以使用連接符$hight-color、底線$hight_color,建議使用連接符,但 Sass 不會強迫任使用連字符或底線,也就是宣告連接符變數,接著使用底線變數,Sass 會相互兼容,在 Sass 的大多數地方,連接符命名的內容和底線命名的內容是互通的。除了變數可以這樣使用,其他也包括對混合(mixins)和函數(functions)的命名。注意的是,在 Sass 中純 CSS 部分不互通,比如 Class、ID 或屬性名。

// $link-color 和 $link_color 其實指向同一個變數
$link-color: blue;
a {
color: $link_color;
}

//編譯後

a {
color: blue;
}

默認屬性值 !default

!default 很像 css 屬性中 !important 的對立面,不同的是 !default 用於變數,含義是:如果這個變數被宣告賦值了,那就用它宣告的值,否則就用這個默認值。

// SCSS
$gray: #eaeaea;
$black: #333 !default; // $black 沒被賦值過,所以會套用預設值
$gray: #cacaca !default; // $gray 被賦值過,所以不會套用預設值

.block {
background-color: $black;
color: $gray;
}

/* CSS */
.block {
background-color: #333;
color: #eaeaea;
}

資料類型(Data Types)

Sass 有七種資料類型:

  • 數字(number):分為有單位或無單位,1,2,13,10px
  • 字串(string):分為有引號字串 “foo”,’bar’,與無引號字串 baz
  • 顏色(color):blue,#04a3f9,rgba(255,0,0,0.5)
  • 布林值(boolean):true,false
  • 空值(null):null
  • 陣列(list):用空格或逗號作為分隔,1.5em 1em 0 2em 或 Helvetica,Arial,sans-serif
  • maps(物件):Sass 物件,類似 JavaScript 的物件,使用鍵/值,(key1:value1, key2:value:2)

字串

分為有引號與無引號字串,在編譯 CSS 文件時不會改變其類型,只有一種情況例外,使用插值語法 #{}(interpolation) 時,有引號字符串將被編譯為無引號字符串,這樣便於在 mixin 中引用選擇器名。

// scss
@mixin firefox-message($selector) {
body.firefox #{$selector}:before {
content: 'Hi, Firefox users!';
}
}
// 傳字串
@include firefox-message('.header');

// css
body.firefox .header:before {
content: 'Hi, Firefox users!';
}

Lists(陣列)

Sass 陣列指的是處理 CSS 中像是 margin: 10px 15px 0 0 或 font-face: Helvetica, Arial, sans-seri,這種一串的值,不過,獨立的值也被視為陣列(只包含一個值得陣列)。陣列通常是用來存放沒有鍵名的變數,這意味著當要從陣列裡取得資料的時候只能透過索引值(1, 2, 3, 4),而不是鍵名(hover, active…等),在 Sass 中,List 從 1 開始算起,而不是其他類程式的 0。列表本身沒有太多的功能,但 Sass list functions 賦予了數組更多新功能:

  • nth():函式可以直接訪問陣列中的某一項。
  • join():函數可以將多個陣列連接在一起。
  • append():函數可以在陣列中添加新值。
  • @each():指令能夠遍歷陣列中的每一項。

陣列寫法:

// 一般
$var: 1px 2px 3px 4px;
// 二維使用逗號分開
$var1: 1px 2px, 3px 4px;

nth($list, $index)

在變數表示中,變數型態可以是「清單 (Lists)」的型式 (即:變數值可以為多個)。 nth($list, $index) 可以取出在 $list 中第 $index 的變數值,其中索引值是由 1 開始起算。

// list
$width: 100px, 200px, 300px;
$height: 100px, 200px, 300px;
$color: white, yellow, lightGreen;
$bgColor: red, green, blue;

.box {
width: nth($width, 2); // 200px
height: nth($height, 2); // 200px
color: nth($color, 2); // yellow
background-color: nth($bgColor, 2); // green
}

index($list, $value)

與 nth($list, $index) 很類似,是取出 $value 所對應的索引值。

$width: 100px, 200px, 300px;
$height: 100px, 200px, 300px;
$color: white, yellow, lightGreen;
$bgColor: red, green, blue;

.box {
width: index($width, 100px); // 1
height: index($height, 300px); // 3
color: index($color, lightGreen); // 3
background-color: index($bgColor, green); // 2
}

@each 與 list

// list
$list: ('red', 'blue', 'green');
@each $value in $list {
// 插值語法
.bg-color-#{$value} {
background-color: $value;
}
}
.bg-color-red {
background-color: 'red';
}

.bg-color-blue {
background-color: 'blue';
}

.bg-color-green {
background-color: 'green';
}

Map

Map 和 List 不ㄧ樣的地方在於你可以自訂每個資料的鍵名,也就是他們的名稱,Maps 必須始終使用括號括起來,並且必須用逗號分隔。它類似 Javascript 當中的 Object 的功能,提供了 key-value 的方式儲存變數,並且內建一些函式以供操作。使用函數以產生輸出字符串,這對於調試 maps 非常有用。inspect($value)

  • Map 的函式名稱
名稱 說明
map-get(map,key) 返回 map 裏面指定可以的 value
map-keys(map) 返回 map 裏面所有的 key(list)
map-values(map) 返回 map 裏面所有的 value(list)
map-has-key(map,key) 返回 map 裏面是否含有指定的 key
map-merge(map1,map2) 合併 兩個 map
map-remove(map,keys) 刪除指定 map 中的指定 key(map)
keywords(args) 返回一個函數參數組成的 map(map)
  • map-get($map,key):取出 $map 裡指定的 $key,將 value 取出來。
  • map-merge($map1,$map2):將兩個 $map 合併起來。
  • map-remove($map,key):從 Map 裡面刪除一個 $key。
  • map-keys($map):取出所有的 key。
  • map-values($map):取出所有的 value。
  • map-has-key($map,key):瀏覽裡面是否有 $key 值,有則回傳 true,沒有便回傳 false。
$map: (
key1: value1,
key2: value2,
key3: value3
);
  • 例子 1
// map
$map: (
'bg1': 'red',
'bg2': 'green',
'bg3': 'blue'
);
@each $key, $value in $map {
.#{$key} {
background-color: $value;
}
}
  • @each 與 map
// scss
$themes: (
'primary': blue,
'danger': red
);
@each $key, $value in $themes {
.btn-#{$key} {
background: $value;
}
}

// css
.btn-primary {
background: blue;
}

.btn-danger {
background: red;
}
$icon: (
'facebook': (
'pic': 'facebook.png',
'color': #3c5998
),
'google': (
'pic': 'google.png',
'color': #cb3726
),
'email': (
'pic': 'email.png',
'color': #666666
)
);

//$key 將會取出 facebook 字串, $value 會取出 facebook 屬定對應的值,pic 或 color
@each $key, $value in $icon {
$pic: map-get($value, 'pic'); //取出 facebook 的 pic
$color: map-get($value, 'color'); //取出 facebook 的 color

.#{$key} {
//將 facebook 轉型,變成 class name
background-image: url($pic);
background-color: $color;
}
}

@at-root

在開發時,如果想要 access 到外層的 class,又希望保持模組化所以將 class 寫在 nested 內時,@at-root 就相當好用。

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

@at-root 和 & 的結合

在@at-root 中也同樣可以配合&一起使用:

.foo {
@at-root .bar & {
color: gray;
}
}
.bar .foo {
color: gray;
}

以上範例和不加 @at-root 的 SCSS 代碼一樣,可以編譯出完全相同的代碼:

.foo {
.bar & {
color: gray;
}
}

BEM 規則

為了讓 CSS 保持命名空間,常常使用 BEM 來組織 CSS 代碼。不過卻常常出現 __-之類不太好看的符號,一不小心就可能打錯,我們可以使用 mixin 封裝這個功能。注意到在 mixin 當中使用了 @at-root,所以編譯的 CSS 會一律放到最頂層,這可以解決 BEM 層數太深的問題,也可以在開發時保持良好的模組性。你也可以在 mixin 加上一些參數決定是否要套用 @at-root 來保持開發上的彈性。

@mixin block($block_name) {
.#{$block_name} {
@content;
}
}

@mixin element($element_name) {
@at-root &__#{$element_name} {
@content;
}
}

@mixin modifier($modifier_name) {
@at-root &--#{$modifier_name} {
@content;
}
}

@include block(article-entry) {
padding: 20px;
@include element(content) {
font-size: 20px;
}

@include element(footer) {
background-color: #fff;
@include modifier(larger) {
height: 500px;
}
}
}
.article-entry {
padding: 20px;
}
.article-entry__content {
font-size: 20px;
}
.article-entry__footer {
background-size: #fff;
}
.article-entry__footer--larger {
height: 500px;
}