--- 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); } } } ```