iT邦幫忙

5

scss 超入門實作

WM 2019-06-20 23:15:45550 瀏覽

拜讀Alex前輩的scss教學,做個筆記。

建置專案

css資料夾是空的,scss資料夾裡面有個style.scss,也是空的。

https://ithelp.ithome.com.tw/upload/images/20190620/20112573AfNgt7nb5v.png

index.html,body內的程式碼。

<div class="tabList">
    <header>
      <ul>
        <li><a href="javascript:void(0)">A</a></li>
        <li><a href="javascript:void(0)">B</a></li>
        <li><a href="javascript:void(0)">C</a></li>
        <li><a href="javascript:void(0)">D</a></li>
        <li><a href="javascript:void(0)">E</a></li>
      </ul>
    </header>
    <div>
      <ul>
        <li>
          <h3>Microsoft 微軟Surface Pro</h3>
          <p>原價$41888★狂降一萬二!</p>
          <p>29888</p>
        </li>
        <li>
          <h3>Microsoft 微軟Surface Pro</h3>
          <p>原價$41888★狂降一萬二!</p>
          <p>29888</p>
        </li>
        <li>
          <h3>Microsoft 微軟Surface Pro</h3>
          <p>原價$41888★狂降一萬二!</p>
          <p>29888</p>
        </li>
      </ul>
    </div>
  </div>

預備工具

Prepros 這套工具可以幫助我們將SCSS轉成CSS。

建立編輯SCSS所需的環境

Prepros下載安裝完成後,打開軟體。
https://ithelp.ithome.com.tw/upload/images/20190620/20112573vVUynkPuol.png

將專案拖曳至Prepros內。
https://ithelp.ithome.com.tw/upload/images/20190620/20112573A89C5l9yb7.png
此時,Prepros會持續監控專案的scss狀態。

在開發過程中,Prepros不能關閉,不然會無法及時將scss轉譯成css。

回到專案,開始撰寫scss。

巢狀(Nesting)

巢狀(Nesting)可說是新手接觸scss的第一個功能,也是最容易理解的。

style.scss

.tabList {
  > header {
    color: red;
  }
  > div {
    color: green;
  }
}

.tabList代表選取tabList class,在.tabList大括號裡面的>(大於)代表子代選取器。

所以

> header {
    color: red;
  }

表示選取.tabList子代中的header元素,並將其color屬性設定為紅色。

以此類推,上一段的程式碼,表示選取.tabList子代中的header和div元素,並將其color屬性設定為紅色和綠色。

完成之後,存檔。

這時在視窗的右下角會出現這個圖示。
https://ithelp.ithome.com.tw/upload/images/20190620/20112573TUfggxs6pH.png
表示,成功將scss轉譯成css。

這時會發現css資料夾多了一個style.css,檔名同原本的style.scss。

在開發過程,基本上我們可以專注於style.scss就好,至於style.css,Prepros會自動幫我們產生。

之後要做任何修改,請在style.scss,不要在style.css。

Why? 你都用scss了,幹嘛還回頭用css。

雖然說瀏覽器只認css,在css修改沒有問題,但既然都用scss,就不用再回css了吧。

注意,請記得在index.html引入style.css。

再往下一層修改其他樣式。

.tabList {
  > header {
    > ul {
      list-style: none;
      font-size: 0;
    }
    li {
      display: inline-block;
      width: calc(100% / 6);
      font-size: 16px;
    }
  }
  div {
    color: red;
  }
}

使用>選取header子代中的ul,以及後代中的li。

至於這段程式碼的功能請看參考影片,這篇會著重於scss的開發技巧。

註解

scss的註解有兩種方式:/ /跟/* */。

那這兩種的差別是,/ /(雙斜線)不會被編譯成css,換言之,使用/ /(雙斜線)的註解,只會scss看到,css看不到。

相反地,/* */會被編譯成css,所以css會看到。

//你看不到我!你看不到我!

/*多行註解會顯示*/

變數(Variable)

上一段程式碼中,li的width是使用calc方式算出來的,這是css的語法,所以會直接顯示於css,不會轉譯。

那有沒有辦法直接在scss算出來結果,再轉css。

有,使用變數(Variable)可以做到這點。

既然講到變數,就表示已經開始涉略到寫程式思維了。

簡單講,變數就像一個容器,可以為這個容器取名,裡面放的,是我們要運算的值,通常會是數值。

scss宣告變數的方式

$width: 100%;
$buttonNumber: 6;

以$為開頭,再取變數名稱,之後,將值設定給這個變數。

變數$width,它的值為100%;變數$buttonNumber,它的值為6。

接下來就可以利用變數直接在scss中做計算,css所顯示的會是計算結果。

scss

width: $width / $buttonNumber;

css

width: 16.66667%;

繼承(Extend)

對於寫後端程式的開發者而言,繼承(extend)是必備的基本觀念,但前端人員,尤其是設計師,這個就非常陌生了。

先實作,再解釋,會比較容易理解。

將a做成按鈕區塊。

li {
      display: inline-block;
      width: $width / $buttonNumber;
      font-size: 16px;
      > a {
        display: block;
        width: 100%;
        height: 20px;
        line-height: 20px;
        text-decoration: none;
      }
    }

a就會變成按鈕區塊。

那之後要是需要再次將其他的a變成按鈕區塊呢?

按照css的做法,肯定是要將所有的樣式再寫一次,有程式觀念的人一定知道,這是非常沒有效率的做法,尤其專案愈來愈複雜的情況,會很難維護。

css做不到的,scss可以做到,將重複使用的程式碼包起來,之後若有需要,再叫出來即可。

修改上面的程式碼。

我們將

display: block;
text-decoration: none;

抽出來,這是之後將a做成按鈕區塊必備的屬性設定。

設定一個class(aButton),將屬性包起來。

scss

$width: 100%;
$buttonNumber: 6;

.aButton {
  display: block;
  text-decoration: none;
}

.tabList {
  > header {
    > ul {
      list-style: none;
      font-size: 0;
    }
    li {
      display: inline-block;
      width: $width / $buttonNumber;
      font-size: 16px;
      > a {
        width: 100%;
        height: 20px;
        line-height: 20px;
      }
    }
  }
  div {
    color: red;
  }
}

好了,接下來,使用繼承,使a吃到.aButton的設定。

> a {
        @extend .aButton;
        width: 100%;
        height: 20px;
        line-height: 20px;
      }

使用@extend,繼承的關鍵字,後面接上要繼承的class,如此一來,a就會繼承.aButton,並吃到.aButton所有樣式設定。

css

.aButton, .tabList > header li > a {
  display: block;
  text-decoration: none; }

.tabList > header > ul {
  list-style: none;
  font-size: 0; }

.tabList > header li {
  display: inline-block;
  width: 16.66667%;
  font-size: 16px; }
.tabList > header li > a {
  width: 100%;
  height: 20px;
  line-height: 20px; }

.tabList div {
  color: red; }

請看第一段,不只.aButton有吃到設定,a確實也吃到設定。

往後,遇到相同的需求,只需將繼承.aButton即可。

但這種命名方式有個缺陷,.aButton的功能是用來被繼承的,它並不是html裡面的任何元素,卻被轉譯成css,是有點奇怪。

因此,我們可以將.aButton改成%aButton。

%aButton {
  display: block;
  text-decoration: none;
}
> a {
        @extend %aButton;
        width: 100%;
        height: 20px;
        line-height: 20px;
      }

%aButton就不會是class了,只是一個集合樣式設定的區塊,它的存在就是為了繼承。

a一樣可以繼承%aButton,%aButton也不會被轉譯成css。

轉譯後,會發現css已經沒有%aButton的蹤影了。

.tabList > header li > a {
  display: block;
  text-decoration: none; }

所以,只要有共用的樣式設定,就可以使用繼承來解決。

函式(Function)

有時,遇到屬性的數值設定,像是font-size、padding、margin、line-height。

有經驗的設計師都會使用級數,或有規則的運算邏輯去設定數值大小,而不會隨便設一個數值。

專案小,每個數值可以手調,那萬一專案很大呢?動輒數十頁,不就改到瘋掉?

既然數值是可以用規則算出來,那我們可以設定一個函式(Function),計算出結果。

假設,我們希望某些特定的元素padding,是使用10px、20px、30px這種級距去設定的。

宣告Function

scss

$basePadding: 10px;

@function paddingLevel($count: 1) {
  @return $basePadding * $count;
}

首先在最上層,先設定基準padding:10px。

使用@function關鍵字,宣告一個名為paddingLevel的function。

名稱後面的小括號($count: 1),會放入級距等級,變數$count預設是1。

意思是,假使我們都沒放任何數值,$count就會以1計算,若放入2,就會以2去計算。

大括號裡面,@return關鍵字,表示會將運算結果回傳,講白話,會將結果給丟出來。

$basePadding * $count,是運算部分,我們放入2,結果就會是10*2,@return就會將20px,回傳。

使用function

scss

.a {
  padding: paddingLevel();
}
.b {
  padding: paddingLevel(2);
}
.c {
  padding: paddingLevel(3);
}

假設我們有不同的class,要套用不同級數的padding,可以輸入Function名稱(等級)。

css就會像這樣。

.a {
  padding: 10px; }

.b {
  padding: 20px; }

.c {
  padding: 30px; }

傳入2個以上數值

剛剛的Function,只能傳入一個數值,那如果想要把所有要算的值全部包含在Function裡,也是可以的。

@function paddingLevel($count: 1, $basePadding: 10px) {
  @return $basePadding * $count;
}

小括號( ),多了一個參數$basePadding,預設值為10px,並用逗號( , )跟$count做區隔。

scss

.a {
  padding: paddingLevel();
}
.b {
  padding: paddingLevel(2,20px);
}
.c {
  padding: paddingLevel(3,30px);
}

css

.a {
  padding: 10px; }

.b {
  padding: 40px; }

.c {
  padding: 90px; }

結果應該很容易理解吧。

以上講到的,巢狀(Nesting)、變數(Variable)、繼承(Extend)、函式(Function),算是最基礎的功能了。

接下來要講的mixin,算是整合這些的應用,也比較進階。

mixin

先看mixin跟extend的差異。

scss

%aButton {
  display: block;
  text-decoration: none;
}

@mixin aButton2 {
  display: block;
  text-decoration: none;
}

.a {
  @extend %aButton;
}
.b {
  @extend %aButton;
}
.c {
  @extend %aButton;
}

.d {
  @include aButton2();
}
.e {
  @include aButton2();
}
.f {
  @include aButton2();
}

使用@mixin關鍵字,將共用樣式給包起來。

看起來似乎跟extend有點像。

如若要套用mixin的話,要使用@include關鍵字。

css

.a, .b, .c {
  display: block;
  text-decoration: none; }

.d {
  display: block;
  text-decoration: none; }

.e {
  display: block;
  text-decoration: none; }

.f {
  display: block;
  text-decoration: none; }

很明顯的區別,雖然達到的目的都一樣。

extend是共用一份樣式。

但mixin會產生好幾份同樣的樣式。

由此可知,這顯然不是mixin該做的事。

那mixin的功能究竟是什麼?

可以把mixin想像成,由變數(Variable)、繼承(Extend)、函式(Function),所集合而成的大補帖。

恩,聽起來籠統,直接實作。

我們想要設定字體大小從12px開始,每個級距是4px。

意思就是字體從12px、16px、20px這樣加大的。

scss

$baseSize: 12px;
$sizeLevel: 4px;

@function font($level: 0) {
  @if $level < 0 {
    $level: 0;
  }
  @return $baseSize + $sizeLevel * round($level);
}

@if是判斷式關鍵字,意思是當$level小於0的時候,就把$level設為0,也就是預防輸入負數的情況發生。

回傳的是,基本字體大小(12px)+字體級距(4px)*四捨五入(輸入的等級)。

round()是四捨五入的函式,回傳整數。

scss

.a {
  font-size: font(5);//12+5*4
}
.b {
  font-size: font(6);//12+6*4
}
.c {
  font-size: font(7);//12+7*4
}

css

.a {
  font-size: 32px; }

.b {
  font-size: 36px; }

.c {
  font-size: 40px; }

字體大小的級距規則訂好了,那行高呢?

不同的字體大小也有相對應的行高,再次設定行高的計算規則。

scss

$baseLineSize: 10px;
$paddingLevel: 1.2;

@function rhythm($size) {
  @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;
}

$size是字體大小,ceil()會回傳大於或等於所給定數值的最小整數。

處理邏輯是這樣的:

ceil(字體大小 *1.2 / 10px)* 10px。

scss

.a {
  font-size: font(5);//12+5*4
  line-height: rhythm(font(5));//ceil(32*1.2/10)*10
}
.b {
  font-size: font(6);//12+6*4
  line-height: rhythm(font(6));//ceil(36*1.2/10)*10
}
.c {
  font-size: font(7);//12+7*4
  line-height: rhythm(font(7));//ceil(40*1.2/10)*10
}

css

.a {
  font-size: 32px;
  line-height: 40px; }

.b {
  font-size: 36px;
  line-height: 50px; }

.c {
  font-size: 40px;
  line-height: 50px; }

只要使用function,輸入數值,就可以得到相對應的結果。

不過,要得到字體大小與行高,得需要2個function,那有沒有更簡便的方式,只要輸入級距,就可以自動產字體大小與行高呢?

有,就是mixin。

@mixin font($level: 1, $line-height: auto) {
  $size: font($level);
  $line: rhythm($size);

  font-size: $size;

  @if $line-height == auto or $line-height < $line {
    line-height: $line;
  } @else {
    line-height: $line-height;
  }
}

一樣使用@mixin關鍵字,宣告mixin,取名font,有兩個參數。

$level預設為1,$line-height預設為auto。

$size: font($level),表示呼叫font() function,把計算結果設定給$size變數。

$line: rhythm($size),同理。

font-size: $size,設定字體大小的屬性值為$size變數的值。

@if這段表示,如果$line-height等於auto或者我們自行輸入的$line-height小於$line的值的話,就把$line的值設定給line-height屬性,不然line-height屬性值就使用我們自己輸入的值。

@if判斷式,對於沒有學過程式邏輯的人來說,確實是個門檻,不過scss的強大與效率,是值得投資的。

完整的scss程式碼

$baseSize: 12px;
$baseLineSize: 10px;
$sizeLevel: 4px;
$paddingLevel: 1.2;

@mixin aButton {
  display: block;
  text-decoration: none;
}

@function font($level: 0) {
  @if $level < 0 {
    $level: 0;
  }
  @return $baseSize + $sizeLevel * round($level);
}

@function rhythm($size) {
  @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;
}

@mixin font($level: 1, $line-height: auto) {
  $size: font($level);
  $line: rhythm($size);

  font-size: $size;

  @if $line-height == auto or $line-height < $line {
    line-height: $line;
  } @else {
    line-height: $line-height;
  }
}

.a {
  @include aButton();
  @include font(5);
}
.b {
  @include aButton();
  @include font(6);
}
.c {
  @include aButton();
  @include font(7);
}

所產生的css

.a {
  display: block;
  text-decoration: none;
  font-size: 32px;
  line-height: 40px; }

.b {
  display: block;
  text-decoration: none;
  font-size: 36px;
  line-height: 50px; }

.c {
  display: block;
  text-decoration: none;
  font-size: 40px;
  line-height: 50px; }

看到了嗎?只要一行mixin,就可以產生font-size與line-height。

這就是mixin真正的價值所在。

接下來,我們要將這些function與mixin獨立出來,讓專案的其他scss或其他專案也可以使用。

首先,在scss資料夾新增_function.scss。

加底線的原因是,這個檔案就不會被轉譯成css,_function.scss的功能只是做運算,不需要轉成css。

_function.scss

$baseSize: 12px;
$baseLineSize: 10px;
$sizeLevel: 4px;
$paddingLevel: 1.2;

@mixin aButton {
  display: block;
  text-decoration: none;
}

@function font($level: 0) {
  @if $level < 0 {
    $level: 0;
  }
  @return $baseSize + $sizeLevel * round($level);
}

@function rhythm($size) {
  @return ceil($size * $paddingLevel / $baseLineSize) * $baseLineSize;
}

@mixin font($level: 1, $line-height: auto) {
  $size: font($level);
  $line: rhythm($size);

  font-size: $size;

  @if $line-height == auto or $line-height < $line {
    line-height: $line;
  } @else {
    line-height: $line-height;
  }
}

裡面只有處理邏輯與設定屬性值,沒有指定任何元素。

只需要在需要計算出字體大小與行高的scss引入_function.scss即可。

test.scss

@import "function";

.a {
  @include aButton();
  @include font(5);
}
.b {
  @include aButton();
  @include font(6);
}
.c {
  @include aButton();
  @include font(7);
}

給初學者的建議,若覺得mixin太複雜,可以先使用變數(Variable)、繼承(Extend)、函式(Function)就好。

等到熟悉了,再將這些功能整合進mixin即可。

SCSS還有很多地方可以講,之後有機會再慢慢補充。

參考來源
[ Alex 宅幹嘛 ] ?‍?從 CSS 到 SASS (SCSS) 超入門觀念引導

Demo
MyGitHub


尚未有邦友留言

立即登入留言