---
# System prepended metadata

title: Sass
tags: [Learn]

---

---
tags: Learn
---

# Sass

## 語法

Sass 語法有分兩種 分別為： `SCSS` 與 `Sass`

較為大宗的是 `SCSS` 語法 其寫法與原本的 css 較為相似

## 編譯

在網頁瀏覽器中 都只認識 css 檔案

所以寫好的 SCSS 都需要通過編譯器把它編譯成 css 檔後才能在網頁上使用

編譯的方式有三種：

1. 打包工具(如 webpack 或 gulp) 這裡不提供教學

2. 編輯器內建套件(如 VS Code 的 Live Sass Compiler)

3. prepros 軟體

### `VS Code` 的 `Live Sass Compiler`

首先開啟 `VS Code` 編輯器

然後點擊左側選單列最下面的`方塊 icon` (這是用來搜尋並安裝套件的)

點進去後在搜索框輸入 `Live Sass Compiler` 安裝它

接下來我們將項目資料夾拖曳到 `VS Code` 編輯器中

然後我們在項目資料夾中新增一個 `SCSS` 資料夾

在 `SCSS` 資料夾中新增一個 `all.scss` 檔案

開啟該 `all.scss` 檔 會發現下方狀態列出現了一個新的按鈕 `Watch Sass` 點下去即可實時編譯

(如果看不到下方狀態列 可在最上面的選單中點擊`檢視`選擇`外觀`裡面的`顯示/隱藏狀態列`)

這邊說明一下：

該套件是點擊後下方狀態列有出現 Watching.... 就表示啟動 
之後每次在 .scss 檔中點擊儲存檔案 就會自動執行編譯 不需要一直點它開關編譯
編譯成功會看到狀態列 Watching.... 那邊出現 綠色打勾 SUCCESS 
失敗的話檔案會變成紅色標題 並且下方狀態列會跳出一個區塊 顯示問題

另外附上 setting.json 設定：

```json=
{
  // 以下針對 liveSassCompile 套件設定 建議同學不要直接將此段貼到你的 json 中 因為有註解會出錯
  "liveSassCompile.settings.formats": [
    {
      "format": "expanded",
      "extensionName": ".css",
      "savePath": "/css" // 這裡是設定你編譯出來的 css 檔存放資料夾
    },
    {
      "format": "compressed", // 壓縮版
      "extensionName": ".min.css", // .min 表示壓縮檔 會將多餘空格與換行刪去 直接濃縮成一行 css 檔
      "savePath": "/css" // 這裡是設定你編譯出來的 css 檔存放資料夾
    }
  ],
  "liveSassCompile.settings.excludeList": ["**/node_modules/**", ".vscode/**"], 
  "liveSassCompile.settings.generateMap": false, // 建議 false 這裡是把 SASS 跟 CSS 做對應行數 設完會有很多支檔案
  "liveSassCompile.settings.autoprefix": ["> 1%", "last 2 versions"] // 這項套件會自動幫你加入前綴（Prefix）

}
```

### prepros 軟體

首先到 [Prepros 官網](https://prepros.io/) 去安裝該軟體

安裝完成後打開該軟體 將項目資料夾拖曳至軟體中

然後我們在項目資料夾中新增一個 `SCSS` 資料夾

在 `SCSS` 資料夾中新增一個 `all.scss` 檔案 即可

接下來當你的 `all.scss` 編寫後 按下儲存

`prepros` 就會幫你做編譯 並在項目資料夾中生成 `css` 資料夾

裡面的 `all.css` 就是最後我們在網頁中要引入的樣式檔案了

先說 這個套件很煩會每隔一段時間跳視窗問你要不要買正式版

但我推薦這個軟體是因為他可以開手機版畫面：

1. 點擊右上角三個 icon 的左邊第一個 icon (兩個小長方形那個 Server)

2. 點擊 Network Preview 用手機掃 QR Code 就可以看到手機版畫面了



## SCSS 語法

### & 連結符

在完全重複的名稱中可以通過連結符來減少撰寫的代碼

舉例如下：

```scss
a {
  color: red;
  &:hover {
    color: pink;
  }
}

// 編譯結果如下

a {
  color: red;
}

a:hover {
  color: pink;
}
```

### $ 變數

我們可以在檔案的最上方設定變數 並在下方代碼中使用

舉例如下：

```scss
$primary: #ff0000;
$lineH: 40px;

a {
  color: $primary;
  line-height: $lineH;
}

// 編譯結果如下

a {
  color: #ff0000;
  line-height: 40px;
}
```

### 顏色

`darken(color,%)` 是顏色加深

`lighten(color,%)` 是顏色加亮

括號中的第一個參數是顏色 可以傳入變數也可以直接傳入顏色

括號中的第二個參數的 % 則是要加深或加亮的百分比

舉例如下：

```scss
$primary: #ff0000;

.bg-dark {
  background-color: darken($primary, 20%);
}

.bg-light {
  background-color: lighten($primary, 20%);
}

.bg-base {
  background-color: $primary;
}

// 編譯結果如下

.bg-dark {
  background-color: #990000;
}

.bg-light {
  background-color: #ff6666;
}

.bg-base {
  background-color: #ff0000;
}
```

### 匯入

Sass 可以將多個小檔案匯出為一個 css 檔

這也讓我們能更好的管理代碼 將代碼分門別類

比如我們可以把變數單獨做成一支叫 `_var.scss` 的小檔案

(在匯入時 通常變數都放在最上面)

然後通過 `@import` 來將所有小檔案匯入到 `all.scss` 中

- 小檔案的名稱最前面需加上下底線以做區分 不然編譯器會自動將該檔案編譯成一支 css 檔

- 加上下底線的目的是讓編譯器知道這是要匯入用的 不須被編譯

- 要注意的是 在匯入到 `all.scss` 時 是不需要加上下底線的

舉例如下：

```scss
// 在 all.scss 匯入 _variable.scss
@import "./variable";

a {
  color: $red;
}
```

### css reset

[Reset](https://meyerweb.com/eric/tools/css/reset/) 是清空所有樣式

[Normailze](https://necolas.github.io/normalize.css/) 則是保有一些預設樣式(如 li 的 list-style 等)

通常匯入時放在變數與自己撰寫的樣式中間 不要放在自己撰寫的樣式下面

### @mixin

當撰寫的樣式中有很多重複的內容時 可以使用 `@mixin`

首先我們創建一支 `_mixin.scss`

然後在裡面撰寫重複的內容( `@mixin 自定義名稱 { 樣式代碼 }` )

比如今天有很多地方使用到了圖片取代文字

當我們建立好圖片取代文字的 `mixin` 後 在要使用的地方加上 `@include 自定義名稱;` 這行 即可

舉例如下：

```scss
@mixin hideText {
  display: block;
  text-indent: 110%;
  overflow: hidden;
  white-space: nowrap;
}

.header {
  width: 100vw;
  height: 50px;
  background-color: #fff;
  .logo {
    a {
      height: 50px;
      width: 140px;
      background: url("./img/logo.png") no-repeat;
      background-size: cover;
      @include hideText;
    }
  }
}

// 編譯結果為

.header {
  width: 100vw;
  height: 50px;
  background-color: #fff;
}

.header .logo a {
  height: 50px;
  width: 140px;
  background: url("./img/logo.png") no-repeat;
  background-size: cover;
  display: block;
  text-indent: 110%;
  overflow: hidden;
  white-space: nowrap;
}
```

### @mixin + 變數

在設置 @mixin 時也可以往裡面添加變數

比如某個樣式中同個顏色或尺寸重複使用多次 我們可以寫成這樣

```scss
@mixin box($color, $size) {
  width: $size;
  height: $size;
  margin: $size/10;
  background-color: $color;
  border: 2px solid $color;
}

.box {
  @include box(gold, 100px);
}

.box2 {
  @include box(red, 200px);
}

// 編譯結果
.box1 {
  width: 100px;
  height: 100px;
  margin: 10px;
  background-color: gold;
  border: 2px solid gold;
}

.box2 {
  width: 200px;
  height: 200px;
  margin: 20px;
  background-color: red;
  border: 2px solid red;
}
```

### @mixin + @content 撰寫 RWD

以下使用範例說明：

```scss
@mixin pad {
  @media (max-width: 768px) {
    @content;
  }
}

@mixin mobile {
  @media (max-width: 568px) {
    @content;
  }
}

.header {
  width: 960px;
  height: 60px;
  @include pad {
    width: 100%;
    height: 50px;
  }
  @include mobile {
    width: 100%;
    height: 40px;
  }
}

//編譯結果

.header {
  width: 960px;
  height: 60px;
}

@media (max-width: 768px) {
  .header {
    width: 100%;
    height: 50px;
  }
}

@media (max-width: 568px) {
  .header {
    width: 100%;
    height: 40px;
  }
}
```

### @for

類似 js 中的 for 迴圈 適用於數字的 常見於隔線系統中

使用方式有兩種:

```scss
// 第一種 from...to... 只輸出 1~3
@for $i from 1 to 4 {
  .box {
    background-color: $i;
  }
}

// 輸出後如下
.box {
  background-color: 1;
  background-color: 2;
  background-color: 3;
}

// 第二種 from...through... 會輸出 1~3
@for $i from 1 through 3 {
  .box {
    background-color: $i;
  }
}

// 輸出後如下
.box {
  background-color: 1;
  background-color: 2;
  background-color: 3;
}

// scss 無法直接將變數套到樣式名稱上
// 但可以通過 #{變數} 實現
@for $i from 1 through 5 {
  .box-#{$i} {
    opacity: $i * 20%;
  }
}

// 輸出後如下
.box-1 {
  opacity: 20%;
}

.box-2 {
  opacity: 40%;
}

.box-3 {
  opacity: 60%;
}

.box-4 {
  opacity: 80%;
}

.box-5 {
  opacity: 100%;
}
```

### @each

類似 @for 但用於字串 使用時須先定義一個集合變數

- 該集合變數又稱 `Sass map` 專門用來搭配 @each 的

- 若要抓取集合中的其中一個 需通過 `map-get(集合變數名, 'key值')`

如下：

```scss
// 定義的集合變數
$themes: (
  // key: value
  "primary": blue,
  "danger": red,
  "dark": #000
);

// 抓取 danger
.bg-danger {
  background-color: map-get($themes, "danger");
}
```

@each 使用方式如下：

```scss
$themes: (
  // key: value
  "primary": blue,
  "danger": red,
  "dark": #000
);

@each $key, $val in $themes {
  .box-#{$key} {
    background-color: $val;
  }
}

// 輸出結果如下
.box-primary {
  background-color: blue;
}

.box-danger {
  background-color: red;
}

.box-dark {
  background-color: #000;
}
```

### @extend

這個技巧適用於多個類名中存在相同樣式時

比如格線系統中的 `.col-1 ~ .col-12`

在撰寫 @for 時 就可以用到 如下：

```scss
%col {
  max-width: 100%;
  padding: 0 ($gutter-w / 2);
  flex-basis: 100%;
}

@for $i from 1 to 13 {
  .col-#{$i} {
    @extend %col;
  }
}

// 輸出後
.col-12,
.col-11,
.col-10,
.col-9,
.col-8,
.col-7,
.col-6,
.col-5,
.col-4,
.col-3,
.col-2,
.col-1 {
  max-width: 100%;
  padding: 0 ($gutter-w / 2);
  flex-basis: 100%;
}
```

## CSS/Sass 設計模式

### SMACSS

base 通常用來存放基本設定(全站設定) 比如一些值些針對標籤的設定

- `base` 內容可能就是 `body` `html` `a` 等標籤的樣式設定

`layout` 通常用來存放共用設定(網站設定) 比如頭部底部等在各個頁面都會出現的樣式

- `layout` 內容可能就是 `header` `footer` 等樣式

- `layout` 也可以處理一些共用的東西 在樣式名加上前綴詞用以辨認 `.l-xxx` or `.layout-xxx`

`module` 模組化的意思 比如把按鈕或表單等 做成一個模組化的樣式

- 先設置他的基本樣式 再以基本樣式為基底修改顏色主題、大小等

- 以 `bootstrap` 來說 `btn` 是按鈕基本樣式 `btn-primary` 是修改按鈕顏色樣式 `btn-lg` 是修改按鈕大小樣式

- module 會建議不要直接在樣式中指定標籤名稱(EX: `.header a` 會建議改為 `.header-link`)

### OOCSS

OOCSS 的原則是 盡量避免使用繼承選擇符(如 `.list > a` 這種) 它追求的是可以重複使用的樣式 在任何元素上都能套用 不拘泥於要套用在指定容器下的某個元素標籤

1. 結構與樣式分離

   結構像是`元素的大小、定位`等

   樣式則是`顏色、背景或邊框`等

   以 BS4 來說 `.btn` 就是`結構` `.btn-primary` 則是`樣式`

2. 容器與內容分離:

   容器型元件：如 `.grid` `.card` `form` 等

   內容型元件：如 `button` `input` `progress-bar` 等

## CSS 命名規範

### 大小駝峰

大駝峰指的是所有英文字的字首都大寫 EX: LastName

小駝峰則是指第一個英文字字首小寫其餘字首大寫 EX: lastName

- 當在設計 class 名稱時 建議將一個名稱使用大駝峰或小駝峰的方式撰寫 而不要用下底線或減號把字拆開 原因是會讓人誤以為它有兩層 舉例來說 `.bookList` 如果寫成 `.book-list` 會讓人以為在 `.book` 裡面有一個列表 `.book-list`

### BEM

BEM 指的是 block, element, modifire (即區塊, 元素, 修飾符)

區塊與元素之間用兩個下底線分隔(xxx\_\_xx) 區塊與修飾符 or 元素與修飾符之間則用兩個中線分隔(xxx--xx)

假設 我們有一個 `header` 它身上有陰影 它裡面有 LOGO

那 `.header` 就是 `header` 的區塊

`.header--shadow` 就是 `header` 的修飾符

`.header__logo` 就是 `header` 的元素

再舉例來說 我們有一個 `menu` 它裡面有三個按鈕 選中的時候有不同的樣式

那 `.menu` 就是 `menu` 的區塊

`.menu__item` 就是 `menu` 的元素

`.menu__item--active` 就是 `menu` 的元素(即 `.menu__item`)的修飾符

## 自制 grid 格線系統

首先要定義:

- 每個 `col` 之間間距為多少 ex: `30px`

- 整個 `container` 最大寬度為多少 ex: `1200px`, `960px`

- 要將 `col` 劃分為多少格 ex: `12`, `16`

將間距設為變數 `$gutter`, 格線系統共多少格設為變數 `$gridNum`

設計步驟如下:

```scss
$gutter: 30px;
$gridNum: 12;
$maxW: 1200px;
// 先將所有元素設置 box-sizing 為 border-box
* {
  box-sizing: border-box; // 設定後元素的內距和邊框就不會增加元素本身的寬度
}

// 接下來設置一個最外層 container 裡面放置 row
.container {
  max-width: $maxW;
  margin: 0 auto;
  padding: 0 ($gutter-w / 2); // 補上 row 的 margin
}

// 然後設置 row 裡面放置 col
.row {
  display: flex;
  margin: 0 (-($gutter-w / 2)); // 補上 col 的 padding
  flex-wrap: wrap; // 內容超過 100% 後會換行
}

// 再設置每個 col 的樣式
.col {
  max-width: 100%;
  padding: 0 ($gutter-w / 2); // col 之間的距離
  flex-basis: 100%; // col 佔據的寬度
}

// 最後設置當寬度大於中斷點 767px 時 各自的 col 寬度多少
@media (min-width: 767px) {
  @for $i from 1 to $gridNum + 1 {
    .col-#{$i} {
      max-width: 100% * ($i / $gridNum);
      flex-basis: 100% * ($i / $gridNum);
    }
  }
}
```
