Angular Material內建了4種不同主題的theme,未來應該還會持續增加,但這些theme未必是我們喜歡的,而在Angular Material中,要設計自己的theme非常簡單,我們就來看看該如何做吧!
Angular Material使用SCSS來設計,並提供了許多的@mixin
可以使用,让我們轻易就能够定制化顏色的功能,首先第一步,我們先在项目下建立一个custom-theme.scss
文档,如下图:
加入包含Angular Material的theming,並汇入基本的样式:
@import '~@angular/material/theming';
@include mat-core();
在执行@include mat-core()
后,会將所有元件都共用的基本样式都加入,因此这个动作只需要做一次就好。
接着我们就可以來设定theme的顏色了,Material Design已經訂了許多顏色的調色盤(palette),同時在Angular Material中都有設計好对应主色的变量,举例來说:light blue的顏色就可以使用$mat-light-blue
,而pink則可以使用$mat-pink;我們可以透過Angular Material另外提供的輔助工具mat-palette()
,來設定這些顏色的亮度。例如:
$custom-primary: mat-palette($mat-light-blue);
另外我們也能提供3個參數,分別為,顏色主要的亮度(预设为500),前色的色调 以及 深色的色调
$custom-accent: mat-palette($mat-orange, 500, A100, A700);//最后两个参数在theme中一般用不到可略
在這裡我們先把Material Design中的三种主要色彩都定义好,如下:
$custom-primary: mat-palette($mat-light-blue, 500); /* 500是預設值,也可以忽略 */
$custom-accent: mat-palette($mat-green);
$custom-warn: mat-palette($mat-brown);
接下來我們可以使用mat-light-theme
建立淺色主題,或mat-dark-theme
建立深色主題,之後只需要使用@include angular-material-theme()
就可以取得所有的顏色結果啦!
/* 建立深色主題 */
$custom-theme: mat-dark-theme($custom-primary, $custom-accent, $custom-warn);
@include angular-material-theme($custom-theme);
最后我们要在style.css中加入样式,不过要记得我們目前是用SCSS,
因此要将style.css改为style.scss,
同時修改.angular-cli.json的app.styles
,
最後在style.scss中加入我們自訂的樣式,就完成啦!
結果如下:
看起來是不是別有一番風味啊!
如果不知道顏色怎麼搭比較好,可以到 Material Design Color Palette Generator這個網站,隨意選擇兩種顏色,就可以看到效果參考囉!
要建立多個theme也很簡單,把@include angular-material-theme();
的部分放到一個css class下,再切換不同的class就可以了,如下:
@import '~@angular/material/theming';
@include mat-core();
$custom-primary: mat-palette($mat-light-blue);
$custom-accent: mat-palette($mat-green);
$custom-warn: mat-palette($mat-brown);
$custom-theme: mat-dark-theme($custom-primary, $custom-accent, $custom-warn);
.custom-theme-1 {
@include angular-material-theme($custom-theme);
}
$custom-primary-2: mat-palette($mat-yellow, 800);
$custom-accent-2: mat-palette($mat-deep-orange);
$custom-warn-2: mat-palette($mat-pink);
$custom-theme-2: mat-dark-theme($custom-primary-2, $custom-accent-2, $custom-warn-2);
.custom-theme-2 {
@include angular-material-theme($custom-theme-2);
}
上面的程式中,我們使用兩組变量,並分別放到.custom-theme-1
及.custom-theme-2
之中,只需要切換不同的class,就可以改变整個画面喽!
當有多個theme時,由於overlay通常會在theme的範圍之外,因次在需要dialog這類的程式顯示為異常,如下:
這時候我們必須做額外的設定,這時候我們需要注入OverlayContainer
,並透過它取得overlay的container,然後把樣式加上去:
import { OverlayContainer } from '@angular/cdk/overlay';
@Component({ })
export class DashboardComponent implements OnInit {
theme = 'custom-theme-1';
constructor(private overlayContainer: OverlayContainer) { }
ngOnInit() {
this.overlayContainer.getContainerElement().classList.add(this.theme);
}
toggleTheme() {
const originalTheme = this.theme;
this.theme = this.theme === 'custom-theme-1' ? 'custom-theme-2' : 'custom-theme-1';
this.overlayContainer.getContainerElement().classList.remove(originalTheme);
this.overlayContainer.getContainerElement().classList.add(this.theme);
}
}
這時候再切換就會一切正常啦!
注:以下网址提供了更多的免费及收费UI,可在里面找到适合自己项目的UI,具体细节再进一步调,缺点是下载时,需要登录用户,需要Google用户登录,靠!
https://www.uplabs.com/
今天我們學到使用Angular Material所提供的SCSS,並了解到Material Design的調色盤中,在Angular Material都有對應的顏色,只需要使用$mat-xxxx
變數即可,而透過mat-palette()
可以得到實際顏色的配置,包含亮色調及暗色調。
最後我們使用mat-dark-theme()
來取得深色的主題顏色,當然也能夠使用mat-light-theme
來得到亮色的主題,最後再使用angular-material-theme()
得到完整Angular Material裡面相關元件的class。
由於SCSS的特性,我們也能輕易把這些主題樣式包裝到另外一個class之中,來達到切換樣式的效果,可以說是非常的有彈性啊!
本日的程式碼GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-32-theme
分支:day-32-theme
今天來分享一些筆者近期撰寫Angular Material文章,以及開始在實際專案中使用Angular Material所整理出來的一些CSS小技巧,有些在文件上可以輕鬆找到,有些則是遇到後才整理出來的,希望能對各位讀者大大們在使用Angular Material時有所幫助!
昨天的文章在介紹自訂theme時,我們提到了內建顏色的$mat-XXXX
變數,這些變數的實際原始碼看起來如下:
要直接取得裡面的顏色,可以搭配使用SASS的map-get()
方法,例如要取得紅色色票中亮度為500的顏色代碼,可以使用如下方式:
.custom-style {
color: map-get($mat-red, 500);
}
如此一來就能取得#f44336
的色碼啦!
另外在Angular Material中還有定義一個mat-color()
方法,方便我們設定顏色,以及他的透明度,甚至可以不用擔心亮度,直接使用darker
或lighter
參數,來決定定義好的亮度,除此之外也可以直接設定透明度,被用在許多Angular Material中的背景顏色的設定,如以下樣式我們選用暗色調的紅色,以透明度50%,作為樣式的背景:
.custom-color {
background: mat-color(mat-palette($mat-red), darker, 0.5);
}
關於顏色的定義,除了上Material Design網站上觀看調色盤外,也可以直接查Angular Material的_palette.scss,可以看到完整的顏色變數。
其實這已經脫離Angular Material的範圍了,但很值得簡單介紹一下,因為Angular Material的顏色一定要搭配SASS並寫在scss檔中,有時候比較懶惰,只想要單純使用顏色時,若有人直接把相關顏色都訂成對應的class,那實在是太方便了!而material-colors套件就是一個幫我們把Material Design顏色都訂成CSS class的好東西!
加入這個style樣式後,我們可以直接使用class="color-red-500"
,來取得紅色色票且亮度為500的顏色,另外被景色也可以使用class="bg-red-500"
來達到預期的效果。
除此之外material-colors套件也有定義好的SASS檔,相當值得參考。
在Material Design中,為了讓畫面具有立體感,對於陰影(shadows)的使用可以說是非常講究,這種陰影設計也被稱為elevation,作為元件與元件之間高低差的概念,也就是陰影越深,高度看起來就越高。
下圖是從Material Design設計指南中擷取的圖片,對於各種高度效果的建議:
數值越高代表在畫面呈現上應該在越上層,例如Dialog類型的應該要在最高的位置24,選單Menu在8,而子選單會比選單在高一點,因此在高度9的位置。
當我們自己在設計元件時,為了讓元件更有立體感,可以參考這個高度表,評估一下自己設計的元件屬於哪種元件分類,進而選擇適當的高度,來達到畫面呈現的一致感!
至於具體的CSS樣式到底該如何設計呢?其實Angular Material已經都幫我們設計好啦!只要套用mat-elevation-zX
的樣式就可以了!X
的部分,請直接換成對應的高度,例如我們在使用overlay並顯示在整個畫面上時,通常是類似dialog的角色,這時候就可以要顯示元件上加一個class="mat-elevation-z24"
,就能夠具有一致的顯示啦!
另外我們也能夠使用mat-elevation()
這個mixin,只需要以適當的高度當作參數帶入,就會產生對應高度的陰影樣式:
@import '~@angular/material/theming';
.my-custom-button {
@include mat-elevation(2);
}
等於得到如下的陰影樣式:
.my-custom-button {
box-shadow:
0px 3px 1px -2px rgba(0, 0, 0, 0.2),
0px 2px 2px 0px rgba(0, 0, 0, 0.14),
0px 1px 5px 0px rgba(0, 0, 0, 0.12);
}
真是省時又省力啊!
這個技巧在之前其實已經提過幾次,但真的很實用也很重要,因此在這邊再次提一下。在Angular Material中,幾乎所有的元件與directive,都會加上一個與它自己同名的CSS class,例如mat-button
這個directive會在它的元件上加入同名的CSS class。因此我們可以直接在撰寫CSS時,透過這個class來調整樣式,例如:
.mat-button {
font-size: 24px;
}
透過這樣的方式,要針對某個元件為調整符合需求的呈現就變得非常簡單哩。
Angualr Material中,有許多popup的呈現,都能透過設定panelClass來改變整個popup的呈現,以筆者實際遇到的一個狀況為例:專案需要一個dialog,已經由美工設計好,這個dialog是沒有padding的,但使用Angular Material打開的dialog預設會套用一個mat-dialog-container
樣式,團隊討論後不希望直接像上一段描述的調整全域mat-dialog-container
樣式,這時候我們就替要打開的dialog設定了一個panelClass:custom-dialog
,並調整樣式把裡面mat-dialog-container
的padding設為0,如下:
.custom-dialog {
.mat-dialog-container {
padding: 0px;
}
}
之後打開dialog時,程式調整成:
this.dialog.open(SomeDialogComponent, {
panelClass: 'custom-dialog'
});
就能夠改變顯示dialog底層panel的樣式啦!
其他可能的狀況如背景顏色、調整陰影等等,都可以透過panelClass來做設定!
這個技巧在所有popup類型的元件都可以使用,除了dialog、snackbar是使用程式控制以外,其他元件如
或
等,都有一個panelClass
的input可以直接使用。
另外使用Angular CDK的overlay時,也能夠透過panelClass來設定樣式。
要單獨使用某個元件其實不難,只要import對應的Module就好,就算把所有Module都import近來好了,Angular Material也會在build時透過tree shaking機制來甩掉用不到的程式,因此原則上是不用擔心會入過多程式造成檔案龐大的問題,但這是程式面的部分,而CSS就不是這麼一回事了!
當我們使用@include angular-material-theme()
的同時,也就代表所有Angular Material元件的樣式都被加了進來,但或許我們真的只需要用到整個Angular Material的1~2個功能,又對檔案大小有所要求時,其實我們還是能單獨匯入特定元件的樣式。
簡單的步驟說明如下:
要單獨使用某個樣式,當然就必須拋棄現有theme的CSS檔(或看看未來Angular Material會不會提供單純theme的SCSS定義檔),因此我們必須自己定義theme的顏色,這部分在昨天的文章已經介紹過了,如下:
@import '~@angular/material/theming';
@include mat-core();
$custom-primary: mat-palette($mat-light-blue);
$custom-accent: mat-palette($mat-green);
$custom-warn: mat-palette($mat-brown);
$custom-theme: mat-dark-theme($custom-primary, $custom-accent, $custom-warn);
接下來我們要加入核心的樣式,這些樣式是大部分元件都通用的,因此必須先加進來,除了mat-core()
已經有了最基本的共用樣式外,還需要加入mat-core-theme()
.custom-theme-1 {
@include mat-core-theme($custom-theme);
}
最後,我們只加入想要的元件樣式就好,Angular Material中將所有的元件都依模組切成了不同的mixin,只需要針對對應模組加入mat-xxxxx-theme
,例如button對應的就是mat-button-theme
.custom-theme-1 {
@include mat-core-theme($custom-theme);
@include mat-button-theme($custom-theme);
}
這時候就只有按鈕元件會套用相關的樣式啦!
上列步驟是一個簡單且保險的方法,但還是可能會加入許多不必要的樣式;舉例來說,mat-core()
包含了overlay相關的樣式,在只使用button時根本用不到;而mat-core-theme()
裡面其實也沒有與button相關的共用樣式,加入這個設定只會產生用不到的樣式而已,這時候我們可以來看看_core.scss中mat-core()
和mat-core-theme()
到底加入了些什麼共用的樣式:
// Mixin that renders all of the core styles that are not theme-dependent.
@mixin mat-core($typography-config: null) {
// Provides external CSS classes for each elevation value. Each CSS class is formatted as
// `mat-elevation-z$zValue` where `$zValue` corresponds to the z-space to which the element is
// elevated.
@for $zValue from 0 through 24 {
.#{$_mat-elevation-prefix}#{$zValue} {
@include mat-elevation($zValue);
}
}
@include angular-material-typography($typography-config);
@include mat-ripple();
@include mat-option();
@include mat-optgroup();
@include cdk-a11y();
@include cdk-overlay();
}
// Mixin that renders all of the core styles that depend on the theme.
@mixin mat-core-theme($theme) {
@include mat-ripple-theme($theme);
@include mat-option-theme($theme);
@include mat-optgroup-theme($theme);
@include mat-pseudo-checkbox-theme($theme);
// Wrapper element that provides the theme background when the
// user's content isn't inside of a `mat-sidenav-container`.
.mat-app-background {
$background: map-get($theme, background);
background-color: mat-color($background, background);
}
// Marker that is used to determine whether the user has added a theme to their page.
.mat-theme-loaded-marker {
display: none;
}
}
可以看到有些其實是用不到的,但當使用
時,mat-option-theme()
就可能是必要的!這時候我們就能夠排除使用mat-core()
和mat-core-theme()
,自己選擇要加入的mixin囉,再進一步的瘦身啦!
以上方法其實已經對於檔案大小有吹毛求疵的要求了,一般狀況很少用到,純粹炫耀筆者對Angular Material的理解程度之深(誤) 但當真的有所要求或追求極致時,就是一個可以考慮調教的地方囉!
順帶一提,以筆者目前的範例專案,使用Angular CLI 1.6.0的ng serve指令產出未壓縮的
style.bundle.js
檔,使用上述方法瘦身後,減少約150k的大小(當然,許多樣式都消失了)。
Angular CDK是Angular Material共用的部分抽取出來的工具,在之前的文章我們也提過,不是每個專案都需要使用Material Design,但每個專案基本上都需要打造自己的元件!因此我們可以不裝Angular Material,但強烈建議把Angular CDK加入專案中,但Angular CDK其實還是有基本的樣式,像是overaly的灰底等等,那麼只加入Angular CDK的專案,該怎麼把這些樣式加進來呢?
其實從上一章套用部分樣式中我們就能略知一二,在mat-core()
中我們加入了cdk-a11y()
和cdk-overlay()
兩個mixin,給Angular CDK使用,所以我們只需要把Angular CDK的SCSS路徑找出來,再加入style.scss之中就好囉:
@import "~@angular/cdk/_a11y.scss";
@import "~@angular/cdk/_overlay.scss";
@include cdk-a11y();
@include cdk-overlay();
當然啦!如果沒用到Angular CDK的a11y和overlay功能,一樣可以不加入這兩組樣式。
在筆者一段不算長的時間學習以及正式專案中部分使用Angular Material及Angular CDK經驗中,所遇到的一些需求,如下:
在Angular Material中已經幫我們把這些都設想好了,我們只要依照前人走好的路,就能更輕鬆達成這些目標,真的是太幸福啦!