MUKI AI Summary
決定使用 MUI 作為 UI 工具後,首先定義主要配色,選擇復古紅、綠、黑。MUI 官方文件詳細說明如何客製化顏色,可參考 Palette 和 Color 文檔。MUI 預設顏色狀態包括 primary、secondary、error 等,這些狀態可以替換或客製化。
可以使用 MUI 的 color 替換預設顏色,或自行定義新顏色,如設定 retro 顏色。可用 ThemeProvider 指定設定檔,並透過 sx prop 覆蓋預設 CSS。若客製化設定多,可將 theme 獨立出來,並移除不需要的樣式設定。...
確定要使用 Material UI (以下簡稱 MUI) 當作我這次的 UI Tools 後,我第一個要做的事情,就是先把幾個主要配色定義好。
因為要做的是股市網站,所以想用紅色跟綠色做主要搭配,還是選擇我愛的復古紅,然後配上復古綠跟常用的復古黑 (自以為取名一堆復古 XXD)
MUI 的官方文件也寫得很詳細,如果我需要客製化自己的顏色風格,可以參考 Palette 以及 Color 兩份文件,主要看 Palette 即可,Color 是列出 MUI 所有的顏色設定,以及提供線上工具可以直接試色。
預設的顏色
MUI 預設的幾個狀態跟 Bootstrap 一樣:
primary
- 網站主要的介面 / 元素secondary
- 網站次要的介面 / 元素error
- 告訴使用者應該注意的元素warning
- 潛在的危險動作,或是重要的訊息info
- 高亮一般(中性)的資訊success
- 告訴(指示)使用者觸發的操作已成功完成
我們可以選擇直接替換這幾個狀態,也可以客製化全新的顏色。
用 MUI 的 color 替換預設狀態的顏色
▼ 首先可以從 MUI color 文件看到他提供的 Color palette
有用過 TailwindCSS 的朋友,對這種顏色設計方式應該不陌生。
▼ 如果要使用,就是先 import
想要的顏色再使用它,舉例如下
import { teal } from '@mui/material/colors' function App() { return ( <> <Button variant="contained" style={{ backgroundColor: teal[300] }}>Button</Button> </> ) } export default App
這樣的寫法就跟使用 TailwindCSS 的 <Button variant="contained" className="!bg-teal-300">Primary</Button>
,對我來說,應該就是看誰的顏色更符合網站需求,就用誰家的 XXD
▼ 假設你喜歡 MUI 的 teal
,想要將他替換成 primary
的顏色 (預設為藍色),我們可以用 ThemeProvider
做設定
import { createTheme, ThemeProvider } from '@mui/material/styles' import Button from '@mui/material/Button' import { teal } from '@mui/material/colors' // 這個寫法是直接將 primary 改成 teal 的顏色,預設為 teal[500] const theme = createTheme({ palette: { primary: teal } }) function App() { return ( <ThemeProvider theme={theme}> <Button variant="contained">Primary</Button> </ThemeProvider> ) } export default App
▼ 想要使用特定編號的顏色,例如 teal-300
,就要改寫在 primary
物件裡
// 以上省略... // 改寫在 primary.main 裡面,以設定特定編號的顏色 const theme = createTheme({ palette: { primary: { main: teal[300] } } }) // 以下省略...
用自定的顏色替換預設的顏色
這次我們想要將 primary
的顏色替換成 #ABCDEF
,不過 MUI 裡面沒有提供這個色碼,所以我們要自己設定。
每個顏色都有四個參數能設定,分別是:
main
light
dark
contrastText
除了 main
是必填外,其他三個參數可以由系統自動計算,或是指定色碼
// 以上省略... const theme = createTheme({ palette: { primary: { main: '#ABCDEF', light: '#FF0000', // dark: 沒有填寫就會利用 palette.primary.main 自動計算顏色數值 // contrastText: 同上 } } })
客製化全新的顏色
如果不想要動到 primary
... 等預設的顏色,我們也可以自己定義新的顏色。
▼ 我想新增一個名為 retro
的顏色,色碼是 #FF0000
。我用的是 TypeScript,所以記得要建立 PaletteColor
以及 SimplePaletteColorOptions
的 interface
,使用時才不會報錯。
import { createTheme } from '@mui/material/styles' declare module '@mui/material/styles' { interface PaletteColor { retro?: string } interface SimplePaletteColorOptions { retro?: string } } let theme = createTheme() theme = createTheme(theme, { palette: { retro: theme.palette.augmentColor({ color: { main: '#FF0000' }, name: 'retro' }) } })
▼ 用 ThemeProvider
指定設定檔,以 <Button />
為例子,使用 TypeScript 的話,記得要再指定 ButtonPropsColorOverrides
的 interface
,並把 <Button color>
設為 retro
declare module '@mui/material/Button' { interface ButtonPropsColorOverrides { retro: true } } function App() { return ( <ThemeProvider theme={theme}> <Button variant="contained" color="retro">Retro Color</Button> </ThemeProvider> ) }
▼ 以此類推,如果想要在 <Alert />
元件使用這個 retro color,就要加入 <Alert />
的 interface
,以下是全部的程式碼,歡迎參考
import { createTheme, ThemeProvider } from '@mui/material/styles' import Button from '@mui/material/Button' import Alert from '@mui/material/Alert' import { Box, Stack } from '@mui/system' import { Typography } from '@mui/material' declare module '@mui/material/styles' { interface PaletteColor { retro?: string } interface SimplePaletteColorOptions { retro?: string } } declare module '@mui/material/Button' { interface ButtonPropsColorOverrides { retro: true } } declare module '@mui/material/Alert' { interface AlertPropsColorOverrides { retro: true } } let theme = createTheme() theme = createTheme(theme, { palette: { retro: theme.palette.augmentColor({ color: { main: '#FF0000' }, name: 'retro' }) } }) function App() { return ( <ThemeProvider theme={theme}> <Stack> <Alert severity="error" color="retro">Error 的 Alert 元件</Alert> </Stack> <Stack direction="row"> <Button variant="contained" color="retro">紅色按鈕</Button> </Stack> </ThemeProvider> ) } export default App
▼ 最後呈現的樣子
如何使用 light, dark 的顏色
如果沒有特別指定 light
, dark
的顏色,MUI 也會根據 main
color 去計算這兩種顏色,那麼該怎麼使用這兩個顏色呢?
▼ 我們沒辦法直接用 color 這個參數,這樣寫是有問題的
// ⭕️ 正確寫法 <Button variant="contained" color="retro">紅色按鈕</Button> // ❌ 錯誤寫法,console 會報錯 <Button variant="contained" color="retro.dark">紅色的深色按鈕</Button>
▼ 比較好的寫法,是使用 sx
prop
<Alert severity="error" sx={{ bgcolor: 'retro.dark' }}>Error 的 Alert 元件</Alert>
可以把 sx
看成是 innerStyle,可以覆蓋 MUI 預設的 CSS,有較高的權重。而 MUI 的所有元件都可以使用 sx
prop,是官方推薦的做法。
拆分檔案,將 theme 設定獨立出來
如果客製化的設定較多,我們可以把 theme 設定獨立出來。
假設我有三個字定義的顏色參數:
retroDark
retroRed
retroGreen
▼ 先寫好 style 的 interface
declare module '@mui/material/styles' { interface PaletteColor { retroRed?: string retroDark?: string retroGreen?: string } interface SimplePaletteColorOptions { retroRed?: string retroDark?: string retroGreen?: string } }
▼ 設定這些 style 要用在哪些元件上?只要有用到的元件,都需要定義 interface
,我這邊用了 Button
和 Alert
declare module '@mui/material/Button' { interface ButtonPropsColorOverrides { retroRed: true retroDark: true retroGreen: true } } declare module '@mui/material/Alert' { interface AlertPropsColorOverrides { retroRed: true retroDark: true retroGreen: true } }
▼ 最後可以用迴圈產 createTheme
的物件,這樣就不用重複寫很多次啦 XD
type ColorKeys = 'retroDark' | 'retroRed' | 'retroGreen' const colors: Record<ColorKeys, string> = { retroDark: '#000000', retroRed: '#FF0000', retroGreen: '#FFFF00' } let theme = createTheme() for (const color in colors) { const key = color as ColorKeys theme = createTheme(theme, { palette: { [color]: theme.palette.augmentColor({ color: { main: colors[key] }, name: color }) } }) }
客製化其他的元件
我發現使用 MUI Button 時,他有將 Button
的文字樣式設定成全大寫,也就是用了這個 CSS 屬性:text-transform: uppercase;
▼ 我在按鈕寫了一個 hi
<Button variant="contained" color="retro">hi</Button>
▼ 渲染後,文字會強制大寫
這點真的很困擾我,好想知道 MUI 的設計師為什麼要這麼做,畢竟正常的設計邏輯,應該是有需要再設定,而不是不需要時去設定。
▼ 如果想要移除大寫的 CSS,一樣在 createTheme
處理,跑完上面的顏色迴圈後,再補上各個元件的客製化設定
theme = createTheme(theme, { components: { MuiButton: { styleOverrides: { root: { // 移除預設全大寫的設定 textTransform: 'none' } } } } })
▼ 最後可以單獨把 createTheme
的程式碼拉出來,用 import
載入,頁面才不會落落長
import { createTheme } from '@mui/material/styles' declare module '@mui/material/styles' { interface PaletteColor { retroRed?: string retroDark?: string retroGreen?: string } interface SimplePaletteColorOptions { retroRed?: string retroDark?: string retroGreen?: string } } declare module '@mui/material/Button' { interface ButtonPropsColorOverrides { retroRed: true retroDark: true retroGreen: true } } type ColorKeys = 'retroDark' | 'retroRed' | 'retroGreen' const colors: Record<ColorKeys, string> = { retroDark: '#000000', retroRed: '#FF0000', retroGreen: '#FFFF00' } let theme = createTheme() for (const color in colors) { const key = color as ColorKeys theme = createTheme(theme, { palette: { [color]: theme.palette.augmentColor({ color: { main: colors[key] }, name: color }) } }) } theme = createTheme(theme, { components: { MuiButton: { styleOverrides: { root: { textTransform: 'none' } } } } }) export default theme
import { ThemeProvider } from '@mui/material/styles' import theme from './global.theme.config' function App() { return ( <ThemeProvider theme={theme}> <Button variant="contained" color="retroDark">Retro Dark</Button> </ThemeProvider> ) } export default App
小結
第一次用歐美風的 CSS Library,希望能跟他相處愉快(合掌)。大家有任何問題,或是有哪裡寫錯了,都歡迎留言指正唷,感謝。