# SCSS/CSS 學習筆記
###### tags: `OKR` `SCSS`
##### 2020/05/24(Update:2020/06/23)
## 網站資源
[SCSS官方網站](https://sass-lang.com/)
[SCSS官方網站-中文](https://sass.bootcss.com/)
## Sass/Scss運作
早期Sass底層是以Ruby語言來撰寫,因此要使用Sass需要有Ruby環境,但官方已經於2019/03/26表示不再維護Ruby Sass!現階段比較主流的是DartSass及 LibSass,DartSass可讓Sass被編譯到JavaScript庫中,而LibSass最常見的就是因應Node.js而推出的Node-sass,目前我們正在使用的則是LibSass。
## Scss與Sass差異
### CSS寫法
```
.menu{..}
.menu ul{..}
.menu li{..}
.menu a{..}
```
### SCSS寫法
目前公司也是採用SCSS,優點如下:
- 相關教學可參考SCSS官方網站
- 因為撰寫風格跟CSS很像,巢狀式架構可把區塊群組化,但要記得階層性建議不要超過3~4層內會比較好,以避免影響網頁渲染效能。還有層次結構變多的,反而會讓CSS變的更不彈性,反而不好維護。
- 可以兼容CSS,應用到比CSS更強大的變數功能。
```
.menu{..}
.menu ul{..}
.menu li{..}
.menu a{..}
```
### SASS寫法
可少寫括號與分號,但若資料量越大後,自己閱讀性不太容易判別錯誤,目前也不太習慣使用這種寫法
```
.menu
..
ul
..
li
..
a
..
```
## SCSS引用管理
- SCSS的好處是可以讓專案各頁功能所需要的樣式群組化,不用通通擠在同一支CSS底下去撰寫,可以寫入很多支的SCSS,藉由引用(import)來彙整編譯成一支CSS。
- 參考學習[sass-7+1結構](https://gist.github.com/rveitch/84cea9650092119527bc)
### 模組化管理
依專案所需規劃可建立一支專門引用SCSS的管理頁,這樣可以很清楚知道專案有引用那些SCSS
規劃檔案的結構
```sass
(有底線代表不會被編譯成一支檔,合併使用)
_variable.scss //專門放相關變數
_mixin.scss //專門放相關mixin函式
_mixinRwd.scss //rwd media
_base.scss //全域設定SCSS
_index.scss //專案頁面SCSS
```
例如建立一支main.scss
依據上方的結構,這頁專門import上述的scss==>最後編譯成main.css
引用格式如下:
```sass
@import "variable";
@import "mixin";
@import "mixinRwd";
@import "base";
//你也可以建立分類的資料夾
@import "/page/index";
@import "abstracts/variables", "abstracts/functions", "abstracts/mixins";
```
也可以導入CSS文件
Imports where the URL ends with .css.
Imports where the URL begins http:// or https://.
Imports where the URL is written as a url().
```sass
@import "theme.css";
@import "http://fonts.googleapis.com/css?family=Droid+Sans";
@import url(theme);
```
## SCSS使用
### 支援類別(Value Types)
- Numbers:可能有也可能沒有單位,例如12或100px。
- Strings:可以帶或不帶引號,例如"Helvetica Neue"或bold。
- Colors:可以通過其十六進制表示形式或名稱(如#c6538c或)來引用,也可以blue從函數(如rgb(107, 113, 127)或)返回hsl(210, 100%, 20%)。
- Lists of values:可以由空格或逗號並且其可以被包含在方括號或沒有括號可言,等進行分離1.5em 1em 0 2em,Helvetica, Arial, sans-serif或[col1-start]。
- boolean:true和false。
- null:預設先給空值。
- Maps:將值與鍵相關聯的地圖,例如("background": red, "foreground": pink)
### 註解功能
有兩種註解方式:
- 使用「//」兩個斜線來備註內容,而這些內容不會被SCSS編譯出來,只有自己才會看得到。
- 若是想要被顯示出來,則使用「 /* 標註文字內容 */ 」
### 文檔註解功能
在@mixin中我們可以增加各種功能詳細描述,可參考`project_template`公司公版,裡面正在慢慢擴充一些常用的@mixin,以便共享於各專案使用。
使用「///」符號來註解,更多的註解用途可參考[SassDoc](http://sassdoc.com/annotations/)
```sass
@example 描述功能名稱,實際應用在什麼地方
@param 是用來記錄@mixin或function的參數
/// @example tab分頁樣式-4種樣式
/// @param {String} $tab-type 參數請填tab名稱,預設為'underLineTab'
/// @param {Size} $line_Height 參數請填tab底線高度, 預設為'3px'
/// @param {Color} $line_Color 參數請填底線顏色
```
### 變數功能 Variable
- 客製變數設定,變數名稱前面記得都要加`$`符號
- 相同的變數名稱,擺在後面的會蓋掉前面的。
- 變數名稱,可以使用「 - 」「 _ 」,官方提到$font-size和&font_size都會引用相同的變數,請二選一。
- 將變數放到相同需要對應的SCSS裡面,CSS編譯結果會自動顯示出來
#### 基本變數寫法
```sass
//變數設定
$text-color:#3F51B5;
$font-xl:20px;
//將變數放置對應地方
.abc{
font-size: $font-xl;
background: $text-color;
border: 1px solid $text-color;
color: red;
}
```
編譯結果如下
```sass
.abc{
font-size:20px;
background:#3F51B5;
border:1px solid #3F51B5;
color:red
}
```
### 進行數值運算calc()
- 可以進行加減乘除的運算,也可能定位。
- 必須注意在使用+和-的時候,在前後一定要加上空白,不然會被誤為是給了個負值。*和/可以不用加,但為了格式統一方便和閱讀建議還是加一下。
- 要注意若是加上其它變數一起計算,要記得變數因為是字串,所以要記得要上#{變數名稱}
```sass
/* 加減乘除運算 */
width: calc(100% - 50px);
/* 定位 */
top:calc(50% - 1px);
/* 變數計算,記得加上字串辨識 */
line-height: calc(#{$select-height} - 15px);
```
### 選擇器selector應用組合
符號可以放在第一層,也可以放第二層,或是在兩者的之間
```sass
ul > {
li {
list-style-type: none;
}
}
h2 {
+ p {
border-top: 1px solid gray;
}
}
p {
~ {
span {
opacity: 0.8;
}
}
}
```
- 星號*可以同時被用於子對象選擇器中
```sass
#container * {
border: 1px solid black;
}
```
- X+Y 相鄰兄弟選擇器
選擇後面同層的第一個元素class賦予狀態
```sass
/* h2相鄰的兄弟p,增加border-top灰線 */
h2 {
+ p {
border-top: 1px solid gray;
}
}
```
- X~Y 相鄰兄弟選擇器
選擇後面同層的所有相同class賦予狀態
```sass
/* h2相鄰的所有兄弟p,增加border-top灰線 */
p {
~ {
span {
opacity: 0.8;
}
}
}
```
- &替代符號
```sass
p {
&:hover{}
.active &{}
:not(&){}
}
```
```sass
/* 編譯結果 */
p:hover{}
.active p{}
:not(p){} //選擇所有不是p的元素,[補充]:not它的作用是防止特定的元素被選中
```
&符號搭配BEM結構撰寫時非常好用,而無須多次撰寫較長的class,看以下範例就可以得知,目前公司專案也是儘量採BEM結構模式撰寫。
```sass
.accordion{
/* ... */
&__copy{
/* ... */
&--open{
/* ... */
}
}
}
```
```sass
/* 編譯結果 */
.accordion{/* ... */}
.accordion__copy{/* ... */}
.accordion__copy--open{/* ... */}
```
- #{}插入值
通常會應用在放入className,或是css名稱,css名稱可拿來應用在有多種屬性值時,例如left/right/top/bottom,範例如下
```sass
@mixin xxx($className,$top-or-bottom,$left-or-right){
.icon-#{$className}{
#{$top-or-bottom}:0;
#{$left-or-right}:0;
}
}
@include xxx('mail',top,right)
```
```sass
/* 編譯結果 */
.icon-mail{
top:0;
right:0;
}
```
- CSS-nth-child選擇器
```sass
/* first-child代表第一個 */
li:first-child {
border: 0;
border-top-left-radius: $border-radius;
border-top-right-radius: $border-radius;
}
/* last-child代表最後一個 */
li:last-child {
border: 0;
border-bottom-left-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
/* nth-child(n)代表指定選擇第幾個,從(1)開始 */
li:nth-child(1) {
border: 0;
border-bottom-left-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
/* nth-child(odd)奇數 */
p:nth-child(odd) {
background: red;
}
/* nth-child(even)偶數 */
p:nth-child(even) {
background: blue;
}
/* :nth-child(an+b)若是使用計算方式,n則可從(0)開始 */
tr:nth-child(3n+1){
background-color:#69C;
}
```
- unique-id()自動產出名稱
官方有這個功能,會自動產生9個字串的字母數字,例如"uat4r38ew",雖然目前未曾實際應用於專案上,但有篇文章提到可以使用在CSS Cache上感覺滿適合的,可參考下方連結:<br>[SCSS Unique Id for Cache Busting your CSS](https://medium.com/@defrian.yarfi/sass-unique-id-for-cache-busting-your-css-fbb58d126d2d)
所謂CSS Cache緩衝是以前有曾經遇過的狀態,就是SCSS內裡面可能會引用CSS/圖片等,但有可能發生有檔案名稱一樣,但有新版本資料,卻發生明明已更新資料,但網頁上看到的是舊資料問題,這時候就可以透過添加版本參數來更新。
```sass
/* 上方文章分享-圖片緩衝 */
$ver:unique_id();
@mixin imageCacheBust($url) {
background-image: #{'url("'}#{$url}#{'?v='}#{$ver}#{'")'};
}
.sprite {
@include imageCacheBust('asset/images/logo.png');
}
```
```sass
/* 編譯結果 */
.sprite {
background-image: url('asset/images/logo.png?v=u95ab40e0');
}
```
### Sass Map變數組合應用
Sass map是類似json的一種變數,可以使用Sass Map來定義基本的元件變數,與json最大的差異是使用`()`取代`{}`。
三種function
```sass
//列出指定map的key
map-keys($map)
//列出指定map的value
map-values($map)
//判別布林值true or false
map-has-key($map, $key)
```
```sass
$color:(
primary:#cccccc;
secondary:#000000;
)
/* 編譯結果 */
//以下只是展示範例回傳的結果,僅供參考
map-keys($color,primary);
.test{
color:primary
}
map-values($color,#cccccc);
.test{
color:#cccccc
}
map-has-key($color,primary);
.test{
color:true
}
```
##### 範例一顏色組合
```sass
/* variables組合 */
//"$key":$value
//可以依據不同的元件用一個群組,方便辨識,例如border.button.text.link..等等
$primary-color: #098081;
$primary2-color: #c4dbd5;
$primary3-color: #6e6d6d;
//可以在依網站的顏色去自訂名稱,方便記憶
$border-set:(
"border-primary1": $primary-color,
"border-primary2": $primary2-color,
"border-primary3": $primary3-color
);
```
```sass
/* variables使用 */
//使用map-get()取值
.button {
color:map-get($border-set,border-primary3)
}
```
##### 範例二按鈕組合
上面是只有單純顏色的組合,若像是按鈕這種會包含多樣式種類的,可以寫成以下組合
```sass
/* variables組合 */
// 定義按鈕的不同狀態設定
$btn-config:(
default:(
class: 'default',
color: #333,
bg: #fff,
border-color: #ccc
),
primary:(
class: 'primary',
color: #fff,
bg: #009AFF,
border-color: #009AFF
),
danger:(
class: 'danger',
color: #fff,
bg: #D84315,
border-color: #009AFF
)
);
```
然後賦予基本button的結構+搭配mixin()來完成多組按鈕組合,詳細可參考分享網站[透過SassMap製作多個樣式](https://wcc723.github.io/css/2016/12/25/sass-map/)
```sass
/* 首先刻出一個按鈕基本結構 */
.btn{
/*......*/
&:hover,&:focus{
color: #333;
background-color: #e6e6e6;
}
}
/* @mixin */
//通常按鈕結構最常變更的是背景顏色、外框顏色、還有文字顏色
@mixin button-set($color,$background,$border-color){
color:$color;
background-color:$background;
border-color:$border-color;
&:hover,&:focus,&:active{
color:$color;
background-color:darken($background,5%);
border-color:darken($border-color,5%);
}
}
```
```sass
/* @each+map-get()+@include */
//$key,$value
@each $keyname, $value in $btn-config{
$class:map-get($value,class);
$color:map-get($value,color);
$bg:map-get($value,bg);
$border-color:map-get($value,border-color);
.btn-#{$class}{
@include button-set($color,$background,$border-color)
}
}
```
```htmlmixed
/* html結構 */
//這樣就可以寫出類似像bootstrap btn不同class的btn
<button class="btn">這是一個按鈕</button>
<button class="btn btn-primary">這是一個按鈕</button>
<button class="btn btn-danger">這是一個按鈕</button>
```
### 好用的SCSS組合應用
#### mixin相類似參數寫法
若CSS有相類似的語法,範例如下
```sass
/* 原本這樣寫 */
@mixin banner($image,$bg-size: cover, $bg-height: 500px, $bg-position: 50% 50%) {
backgorund-image:$image;
background-repeat: no-repeat;
background-size: $bg-size;
background-position: $bg-position;
max-width: 100%;
height: $bg-height;
}
```
```sass
/* 後來這樣寫 */
@mixin banner($image,$bg-size: cover, $bg-height: 500px, $bg-position: 50% 50%) {
max-width: 100%;
height: $bg-height;
background:{
image:$image;
repeat:no-repeat;
size:$bg-size;
position:50% 50%;
}
}
```
#### @mixin+@if布林值判斷
- 返回true或false
- @else if or @else
```sass
/* mixin建立組合 */
@mixin avatar($size, $circle: false) {
width: $size;
height: $size;
@if $circle {
border-radius: $size / 2; //返回true
}
}
.square-av { @include avatar(100px, $circle: false); }
.circle-av { @include avatar(100px, $circle: true); }
```
```sass
/* 編譯結果 */
.square-av {
width: 100px;
height: 100px; //返回false,所以沒有border-radius
}
.circle-av {
width: 100px;
height: 100px;
border-radius: 50px; //返回true
}
```
另一種寫法
例如判斷是否需要border-radius(沒有外框為0/有外框就給數值),寫法如下:
```sass
/* mixin建立組合 */
@mixin square($size, $radius: 0) {
width: $size;
height: $size;
@if $radius != 0 {
border-radius: $radius; //假如不等於0
}
}
.text{
@include square(100px,$radius:4px);
}
```
```sass
/* 編譯結果 */
.text {
width: 100px;
height: 100px;
border-radius: 4px;
}
```
#### 區塊共用
有時網頁會有很多區塊會共用到不同的背景或圖片樣式,這時候都還要特別幫那個區塊給一個class或是直接將css寫在那個區塊裡,使用mixin()來建立組合,有需要就直接引用到區塊裡
```sass
/* mixin建立組合 */
@mixin section-background($color){
#{if (&, '&.section-background', '.section-background')}{
background-color:$color;
color:#000000;
}
}
```
```sass
/* include使用 */
.sidebar{
@include section-background(#cccccc);
}
```
```sass
/* 編譯結果 */
.sidebar.section-background{
background-color:#cccccc;
color:#000000;
}
```
#### 多類型@mixin合併+@error報錯提示
舉例來說,若有多種類型的tab分頁style,原本一種tab就寫一支@minxin,那要如何使用@mixin來合併,引用時只要判斷類型輸入即可呢?答案就是使用`@if`,範例說明如下:
```sass
/* mixin多類型建立組合 */
//可以在@mixin裡放置預置的變數
//日後若有新的tab種類,即可依序新增下去..
//@error為編譯時若有錯誤會編譯失敗並停止,顯示錯誤訊息提醒,@warn也是一種錯誤提醒,但sass雖會報錯,但還是會繼續編譯,所以反而使用@error會更好
@mixin navTab(
$tab-type: underLineTab //預設的tab種類
$selector: "*", //若父層底下有子層的內容,可以使用*來代表
){
@if $tab-type == underLineTab {
/*這裡放第一種tab的css*/
}@else if $tab-type == borderTab{
/*這裡放第二種tab的css*/
}@else if $tab-type == squareTab{
/*這裡放第三種tab的css*/
}@else if $tab-type == nogutterTab{
/*這裡放第四種tab的css*/
}@else {
@error "請填寫$tab-type正確參數:underLineTab/borderTab/squareTab/nogutterTab";
}
}
```
#### @mixin+@content
網路上提到常見斷點參考
iPad - 768px
iPad以下 - 767px
iPhone 6 Plus - 414px (視專案族群)
iPhone 6 - 375px (視專案族群)
iPhone 5、SE - 320px
使用在rwd範例,以下參考bootstrap斷點設計
```sass
//rwd breakpoint 設定各種斷點,也可以在陸續新增自訂的尺寸
$col-xl: 1200px;
$col-lg: 992px;
$col-md: 768px;
$col-sm: 576px;
@mixin rwd($col-xl) {
@media (max-width: $col-xl) {
@content;
}
}
@mixin rwd($col-lg) {
@media (max-width: $col-lg) {
@content;
}
}
@mixin rwd($col-md) {
@media (max-width: $col-md) {
@content;
}
}
@mixin rwd($col-sm) {
@media (max-width: $col-sm) {
@content;
}
}
```
```sass
.circle-image2 {
@include circle(blue, $white, $size);
//一樣使用@include引用
@include rwd($col-xl) {
width: $size/2;
height: $size/2;
line-height: $size/2;
font-size: 15px;
}
@include rwd($col-sm) {
width: $size/2;
height: $size/2;
line-height: $size/2;
font-size: 13px;
}
}
```
```sass
//編譯結果
.circle-image2 {
background: blue;
border-radius: 99%;
width: 500px;
height: 500px;
line-height: 500px;
font-size: 100px;
color: #fff;
text-align: center;
}
@media (max-width: 1200px) {
.circle-image2 {
width: 250px;
height: 250px;
line-height: 250px;
font-size: 15px;
}
}
@media (max-width: 576px) {
.circle-image2 {
width: 250px;
height: 250px;
line-height: 250px;
font-size: 13px;
}
}
```
### @each迴圈
@each <variable> in <expression> { ... }
each迴圈可以設定一個變數群組然後分別套用
```sass
$sizes: 40px, 50px, 80px;
@each $size in $sizes {
.icon-#{$size} {
font-size: $size;
height: $size;
width: $size;
}
}
```
```sass
/* 編譯結果 */
.icon-40px {
font-size: 40px;
height: 40px;
width: 40px;
}
.icon-50px {
font-size: 50px;
height: 50px;
width: 50px;
}
.icon-80px {
font-size: 80px;
height: 80px;
width: 80px;
}
```
替換背景顏色方法範例
```htmlmixed
<div class="container">
<div class="box box1 box-primary"></div>
<div class="box box2 box-primary"></div>
<div class="box box3 box-danger"></div>
<div class="box box4 box-secondary"></div>
<div class="box box5 box-danger"></div>
</div>
```
```sass
//設定顏色群組
//"$key":$value,
$colorset: (
"primary": #ccc,
"danger": #dd4444,
"secondary": #3e5bbb
);
//scss變數寫法:#{$key}
@each $key, $value in $colorset {
.box-#{$key} {
background: $value;
}
}
```
顯示效果如下
![](https://i.imgur.com/VDlBcXp.png)
### @for迴圈
透過scss可以搭配@for迴圈來使用連續變數的功能,例如以下顏色漸變範例
```htmlmixed
<div class="container">
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
</div>
```
```sass
//box基本屬性
.box {
height: 150px;
width: 150px;
float: left;
background: #ccc;
}
//box1~box5給予連續漸變顏色效果,設定$i(1~5)數值為變數
//@for迴圏 from-through(跑1到5數值);from-to(跑1到4數值),兩者須注意有不同
//scss變數寫法:#{$i}
@for $i from 1 through 5 {
.box#{$i} {
background: darken(white, $i * 10%);
}
}
```
顯示效果如下
![](https://i.imgur.com/jCP3W0A.png)
### @debug 偵測錯誤
跟console.log一樣,幫你查看打印出來的值是否正常。以下為一個簡單的例子,實際專案下執行webpack編譯時就會看到訊息。但另外有發現Vscode Live Sass使用@debug不會出現,看了一下它的說明與更新內容好像本來就沒有進一步支援。
```sass
$font-sizes: 10px + 20px;
$style: (
color: #bdc3c7
);
.container{
@debug $style;
@debug $font-sizes;
}
```
```sass
/* 打印結果,會出現類似畫面 */
Line 7 CSS: (color: #bdc3c7)
Line 8 CSS: 30px
```
### @at-root 跳脫子層
可以將一個或多個樣式規式跳出子層,而不是嵌在父層selector裡面,範例如下:
```sass
.parent{
...
@at-root .child{....}
}
```
```sass
/* 編譯結果 */
.parent{...}
.child{...}
```
```sass
.parent{
...
.step-child { ... }
@at-root{
.child1{...}
.child2{...}
}
}
```
```sass
/* 編譯結果 */
.parent{...}
.parent .step-child{...}
.child1{...}
.child2{...}
```
### @use與@forward
分享一篇看到寫的很清楚的文章,文章提到[詳解Sass新特性- 模塊](https://blog.csdn.net/qq_36380426/article/details/103502336)的內容。
內容提到sass團隊正在推行sass模塊化機制,建議大家使用@use.@forward,但目前僅Dart Sass 1.23.0完全支援這兩種特性,加上[官網@import說明](https://sass.bootcss.com/documentation/at-rules/import)上也有提到Sass團隊不鼓勵繼續使用@import規則,預計在未來幾年會逐步淘汰它,雖然還不是現在,但或許在淘汰前可能要慢慢去研究@use+@forward。
文件中提到@use與@import最主要的差異(待須實際試用):
@import:假如不同的頁面引用了相同的scss,樣式可能會被重覆加載,導致重複代碼
@use:不管使用了多少次樣式表,都只會引用和執行一次