# 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:不管使用了多少次樣式表,都只會引用和執行一次