chakra-uiのsxや__cssはどうやって解決されているか
chakra-uiではsxや__cssにスタイル定義が書ける、通常のstyle prop との違いは
themeの値を使った書き方ができる("xl", "red.500", etc...)
m:2やmt:3といったStyled System のプロパティの省略も使える("m", "mt", "p", "pt", etc...)
ドキュメントによると主に Styled System のjsxにpropsを直接書くスタイルを好まない人向けとなっている
実際書くとこんな感じ
code:test.tsx
const Hello = () => {
return (
<Box
__css={{ fontSize: "xl", color: "red.500", m: 8 }}
sx={{ fontWeight: "bold" }}
hello
</Box>
)
}
これはどうやって実現しているか?
Boxの実装は
code:packages/layout/src/box.tsx
import {
chakra,
....
} from "@chakra-ui/system"
export const Box = chakra("div")
これは下記の通り定義されている
code:packages/system/src/system.ts
export const chakra = (styled as unknown) as ChakraFactory &
HTMLChakraComponents
domElements.forEach((tag) => {
// @ts-expect-error
})
実態はstyledというコンポーネントを返すメソッドになっている
domElements.forEach(...ではタグごとにショートカットのメソッドのプロパティを生やしている。これによりChakra Factory を直接jsxに書ける
code: ts
<chakra.div p={2} >hogehoge</chakra.div>
引き続き実体であるstyledの定義を見てみる
code:packages/system/src/system.ts
export function styled<T extends As, P = {}>(
component: T,
options?: StyledOptions,
) {
const { baseStyle, ...styledOptions } = options ?? {}
const opts = { ...styledOptions, shouldForwardProp }
const styledFn = emotionStyled(component as React.ComponentType<any>, opts)
const args = styleResolver({ baseStyle })
const StyledComponent: any = styledFn(args)
return StyledComponent as ChakraComponent<T, P>
}
emotionStyledは@emotion/styledのことなので、chakraメソッドはemotionのラッパーである
ここでstyleResolverメソッドに注目する
code:packages/syste/src/system.ts
export const styleResolver: StyleResolver = ({ baseStyle }) => (props) => {
const {
theme,
layerStyle,
textStyle,
apply,
noOfLines,
isTruncated,
css: cssProp,
__css,
sx,
...rest
} = props
.......
......
/**
* The computed, theme-aware style object. The other of the properties
* within objectAssign determines how styles are overriden.
*/
const finalStyles = objectAssign(
{},
__css,
baseStyle,
{ apply },
_layerStyle,
_textStyle,
truncateStyle,
styleProps,
sx,
)
// Converts theme-aware style object to real css object
const computedCSS = css(finalStyles)(props.theme)
// Merge the computed css object with styles in css prop
const cssObject: Interpolation<StyleResolverProps> = objectAssign(
computedCSS,
isFunction(cssProp) ? cssProp(theme) : cssProp,
)
return cssObject
}
sx, __css などThemeの値や Styled System のプロパティが使えるものは props から分けられた後、objectAssign でマージされている
その後cssメソッドで素のCSSObjectに変換される、下記みたいな感じ
css({m: 8, fontSize: "xl"})(props.theme) -> {margin: "1.5rem", fontSize: "1.25rem"}
ここでstyleResolverは このstyleオブジェクトをそのまま返しているのではなく propsを引数にとってスタイルを返す関数 を返している
emotionのstyledでpropsの内容から動的にスタイルを指定したいときに propsを引数にとってスタイルを関数 を引数に指定できる機能を使用するためである
styled("div")(props => ({ backgroundColor: props.hoge ? "black" : "red"})
先ほどのstyledメソッドの抜粋を確認しておくと、styleResolverの戻り値argsをemotionのstyled(styledFn)に渡しているのがわかる、これでスタイルが反映されるのである
const StyledComponent: any = styledFn(args)
以上
次はcssメソッドを深掘りしたい