# 如何修改 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