Skip to content

ネイティブのHTML要素をYamada UIのコンポーネントに昇格させているstyled関数を知る

styled関数

Yamada UIでは、ネイティブのHTML要素にこの関数を適用することによってスタイル付きコンポーネントを生成している。

export const styled = <T extends As, P extends object = {}>(
element: T,
{ baseStyle, disableStyleProp, ...styledOptions }: StyledOptions = {}
) => {
if (!styledOptions.shouldForwardProp)
styledOptions.shouldForwardProp = shouldForwardProp(disableStyleProp)
const CSSObject = toCSSObject({ baseStyle, disableStyleProp })
const Component = emotionStyled(
element as ComponentType<any>,
styledOptions
)(CSSObject)
const UIComponent = forwardRef((props, ref) => {
const { colorMode, forced } = useColorMode()
return createElement(Component, {
ref,
'data-mode': forced ? colorMode : undefined,
...props
})
})
UIComponent.displayName = 'UIComponent'
return UIComponent as UIComponent<T, P>
}

この関数の中身はざっくりこんな感じ。

export const styled = <T extends As, P extends object = {}>(
element: T,
{ baseStyle, disableStyleProp, ...styledOptions }: StyledOptions = {}
) => {
// 1. shouldForwardPropの設定
if (!styledOptions.shouldForwardProp)
styledOptions.shouldForwardProp = shouldForwardProp(disableStyleProp)
// 2. スタイルオブジェクトの生成
const CSSObject = toCSSObject({ baseStyle, disableStyleProp })
// 3. Emotionのstyled関数を使用してスタイル付きコンポーネントを作成
const Component = emotionStyled(
element as ComponentType<any>,
styledOptions
)(CSSObject)
// 4. カラーモードを適用するためのラッパーコンポーネントを作成
const UIComponent = forwardRef((props, ref) => {
const { colorMode, forced } = useColorMode()
return createElement(Component, {
ref,
'data-mode': forced ? colorMode : undefined,
...props
})
})
UIComponent.displayName = 'UIComponent'
return UIComponent as UIComponent<T, P>
}

1. shouldForwardPropの設定

引数に渡された要素について、同時に引数として渡されたdisableStylePropをベースにして特定のpropsをその要素に設定するかどうかを判定する。

styledOptions.shouldForwardPropは、その判定結果を保持する。

2. スタイルオブジェクトの生成

toCSSObject関数を使用して、スタイルオブジェクトを生成している。この時、スタイルオブジェクトはbaseStyledisableStylePropを考慮して生成される。

3. Emotionのstyled関数を使用してスタイル付きコンポーネントを作成

ここは、Emotionのstyled関数を使用してスタイル付きコンポーネントを作ってるだけ。

4. カラーモードを適用するためのラッパーコンポーネントを作成

useColorModeフックを使用してカラーモードを取得し、data-mode属性としてコンポーネントに適用してる。

toCSSObject関数

スタイルオブジェクトを生成する関数。

type ToCSSObject = {
(
options: Pick<StyledOptions, 'baseStyle' | 'disableStyleProp'>
): FunctionInterpolation<StyledResolverProps>
}
export const toCSSObject: ToCSSObject =
({ baseStyle, disableStyleProp }) =>
(props: StyledResolverProps) => {
const { theme, css: customCSS, __css, sx, ...rest } = props
const propsCSS = filterObject<Dict, CSSUIProps>(
rest,
(prop) => prop in styleProps
)
const baseCSS = runIfFunc(baseStyle, props)
const computedCSS = css(
assignAfter({}, __css, baseCSS, filterUndefined(propsCSS), sx)
)(theme, disableStyleProp)
return customCSS ? [computedCSS, customCSS] : computedCSS
}

styled関数の中身の詳細

CursorのChatで聞いた。正直いまいちピンときてないので後で立ち戻りたい。

  1. プロパティの抽出: propsからtheme、css、__css、sxなどのスタイル関連プロパティを抽出します。
  2. スタイルプロパティのフィルタリング: filterObject関数を使用して、restからスタイルプロパティをフィルタリングし、propsCSSを生成し> ます。
  3. ベーススタイルの評価: runIfFunc関数を使用して、baseStyleを評価し、baseCSSを生成します。
  4. 最終的なCSSオブジェクトの生成: assignAfter関数を使用して、__css、baseCSS、propsCSS、sxを結合し、css関数を使用して最終的> なCSSオブジェクトを生成します。
  5. カスタムCSSの適用: customCSSが存在する場合、最終的なCSSオブジェクトとカスタムCSSを配列として返します。存在しない> 場合は、最終的なCSSオブジェクトのみを返します。 このようにして、toCSSObject関数は、与えられたプロパティとオプションに基づいて、最終的なスタイルオブジェクトを生成します。