# 如何修改 Material UI Theme 在 [Material UI][material-ui] 這套 Material Design 的 React 組件實作中, theme 是怎麼運作的呢? ## TL;DR 在設計新的組件,選擇配色時,可以跟我說要用色盤中的哪些顏色,或者是要長得像哪個 Material UI 提供的官方 component ,這樣我可以在 `getStyles` 函式中直接寫明白,也方便未來修改。 如果指定要長得像一個以上的 components ,那我會人工反查出那些 components 用到色盤中的哪些顏色。 ## 技術細節 在其文件中,提到客製化 theme 的[方法][material-ui-theme-doc]是: ```javascript import React from 'react'; import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import getMuiTheme from 'material-ui/styles/getMuiTheme'; import AppBar from 'material-ui/AppBar'; const Main = () => ( <MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}> <AppBar title="My AppBar" /> </MuiThemeProvider> ); export default Main; ``` [material-ui]: http://www.material-ui.com [material-ui-theme-doc]: http://www.material-ui.com/#/customization/themes 可以注意到是把自己的 theme ,例如 `darkBaseTheme` ,交給 `getMuiTheme` ,才會得到完整的 theme 。 `darkBaseTheme(v0.17.2)` 長這樣: ```javascript import { cyan700, grey600, pinkA100, pinkA200, pinkA400, fullWhite, } from '../colors'; import {fade} from '../../utils/colorManipulator'; import spacing from '../spacing'; export default { spacing: spacing, fontFamily: 'Roboto, sans-serif', borderRadius: 2, palette: { primary1Color: cyan700, primary2Color: cyan700, primary3Color: grey600, accent1Color: pinkA200, accent2Color: pinkA400, accent3Color: pinkA100, textColor: fullWhite, secondaryTextColor: fade(fullWhite, 0.7), alternateTextColor: '#303030', canvasColor: '#303030', borderColor: fade(fullWhite, 0.3), disabledColor: fade(fullWhite, 0.3), pickerHeaderColor: fade(fullWhite, 0.12), clockCircleColor: fade(fullWhite, 0.12), }, }; ``` 大意是使用 `material-ui` 給的顏色與工具,完成一組設定。 而 `getMuiTheme` 是幫你把這組設定用在所有的元件上: ```javascript // ... muiTheme = merge({ appBar: { color: palette.primary1Color, textColor: palette.alternateTextColor, height: spacing.desktopKeylineIncrement, titleFontWeight: typography.fontWeightNormal, padding: spacing.desktopGutter, }, // ..., tooltip: { color: white, rippleBackgroundColor: grey700, }, } ``` --- 在製作一個新的 component 時,會準備一組處理 style 的函式 `getStyles` ,例如: ```javascript function getStyles(props, context) { const { palette } = context.muiTheme const backgroundColor = props.backgroundColor || palette.primary2Color const gridColor = lighten(backgroundColor, 0.3) return { root: { width: '100%', color: props.color || palette.alternateTextColor, gridColor, backgroundColor, }, } } ``` 從 React component 的 `context` 中(要設定好 `contextTypes` , React 才准你用)拿到共用的 `muiTheme` ,也就是我們傳給 `getMuiTheme` ,他建立起來的那組。 然後再參考 `props` 裡面的 `style` 與其他邏輯,完成這個 component 的 style 。 所以我的建議是,在設計新的組件,選擇配色時,可以跟我說要用色盤中的哪些顏色,或者是要長得像哪個 Material UI 提供的官方 component ,這樣我可以在 `getStyles` 函式中直接寫明白,也方便未來修改。 如果指定要長得像一個以上的 components ,那我會人工反查出那些 components 用到色盤中的哪些顏色。 --- 一些額外的 style 會從新 component 的 `index.css` 中提供,但和 Material UI 本來設定 style 的方式不是很相容(它的優先權比較高),故多半會加上 `!important` 來覆蓋。 `flexbox` 相關的修正也是這樣加上去的。 ###### tags: note