Yamada UIのカスタマイズ
はじめに
Yamada UIのメンテナーをしていますが、まだまだ知らない機能がたくさんあります。
その機能yamadaさんしか知らないのでは?みたいな機能もたくさんあります。
公式ガイドのほうに情報は網羅されていますが複雑なので、初めて使っていただく方に、最低限これだけ押さえておけばというのをお伝えできればと思います。
内容は結構端折っているので、もっと詳しく知りたい方はドキュメントを読んでください。
推奨する使い方
各コンポーネントはスタイルのpropsを設定することでスタイリングすることができます。
<Container bg="green.100" p="md" fontSize="md" borderWidth="1px">
This is Container
</Container>
ただ、この方法でのスタイリングは最小限にとどめるべきです。
Yamada UIでは、コンポーネントのスタイルをまとめて管理することができます。
全てのContainer
に以下のスタイルが適用できるようになります。
import { ComponentStyle } from "@yamada-ui/react"
export const Container: ComponentStyle = {
// すべての場合に設定されるスタイル
baseStyle: {
bg: "green.100",
},
//size="sm"とかsize="md"のようにしたときに設定されるスタイル
sizes: {
sm: { p: "sm", fontSize: "sm" },
md: { p: "md", fontSize: "md" },
lg: { p: "lg", fontSize: "lg" },
},
// variant="with-border-dotted"のようにしたときに設定されるスタイル
variants: {
"wtih-border-solid": {
borderWidth: "1px",
},
"wtih-border-dotted": {
borderWidth: "1px",
borderStyle: "dotted",
},
},
// sizeやvariantを何も指定しなかった場合、どれを適用させるか
defaultProps: {
size: "md",
variant: "wtih-border-solid",
},
}
<Container size="sm" variant="with-border-dotted">
This is Container
</Container>
こうすることでスタイルを一元管理でき、例えば、サイト全体のスタイルを修正するとなった場合にも、theme/
以下のファイルを編集するだけで達成できます。
例外として個別にスタイリングする必要がある場合だけ、コンポーネントに直接スタイルのpropsを渡しましょう。
Yamada UIには、ほかにも一元的にスタイルを管理するための機能が備わっています。それを紹介していきます。
カスタマイズ
theme
フォルダを作成し、管理します。
./theme
├ components # 各コンポーネントのスタイルを定義するフォルダ
├ tokens # 各トークンを定義するフォルダ
├ styles # グローバルスタイルやリセットスタイルを定義するフォルダ
├ semantics.ts # セマンティックトークンを設定するファイル
├ config.ts # コンフィグを設定するファイル
└ index.ts # テーマをデフォルトでエクスポートするファイル
index.ts
でまとめてexportします。
いらないものは適宜削除してください。
extendTheme
を使うと、デフォルトのテーマに上書きすることができます。
import { extendTheme, extendConfig, UsageTheme } from "@yamada-ui/react"
import styles from "./styles"
import components from "./components"
import tokens from "./tokens"
import { semantics } from "./semantics"
import { customConfig } from "./config"
const customTheme: UsageTheme = {
styles,
components,
semantics,
...tokens,
}
export const theme = extendTheme(customTheme)()
export const config = extendConfig(customConfig)
作成したtheme
はUIProvider
に渡せば反映されます。
import { UIProvider } from "@yamada-ui/react"
import { theme, config } from "theme"
...
<UIProvider theme={theme} config={config}>
<App />
</UIProvider>
それぞれ詳しく説明していきます。
components
各コンポーネントごとのスタイルをまとめて管理できます。
これにより、スタイルを柔軟に維持していくことが可能です。
カスタマイズしたいコンポーネントごとにファイルを作成します。
./theme/comonents
├ container.ts
├ button.ts
└ index.ts
index.ts
でまとめてexportします。
import { Container } from "@yamada-ui/react"
import { Button } from "@yamada-ui/react"
const components = { Container, Button }
export default components
基本的な設定
冒頭で載せちゃいましたが、以下のようにすることで、container
はすべてこのスタイルが適用されます。
import { ComponentStyle } from "@yamada-ui/react"
export const Container: ComponentStyle = {
// すべての場合に設定されるスタイル
baseStyle: {
bg: "green.100",
},
//size="sm"とかsize="md"のようにしたときに設定されるスタイル
sizes: {
sm: { p: "sm", fontSize: "sm" },
md: { p: "md", fontSize: "md" },
lg: { p: "lg", fontSize: "lg" },
},
// variant="with-border-dotted"のようにしたときに設定されるスタイル
variants: {
"wtih-border-solid": {
borderWidth: "1px",
},
"wtih-border-dotted": {
borderWidth: "1px",
borderStyle: "dotted",
},
},
// sizeやvariantを何も指定しなかった場合、どれを適用させるか
defaultProps: {
size: "md",
variant: "wtih-border-solid",
},
}
デフォルトの設定がどうなっているかは、各コンポーネントのページのテーマタブで見れます。
複数コンポーネントの設定がある場合
複数コンポーネントがあるような場合は、型がComponentMultiStyle
になり、さらに一段階ネストしてそれぞれのコンポーネントのスタイルを記述します。
補足
Accordion
には、AccordionItem
等の複数コンポーネントがあります。
このように複数コンポーネントがある場合もありますが、AreaChart
のように、内部で複数コンポーネントを使用している場合もComponentMultiStyle
になります。
単一コンポーネントか複数コンポーネントかはドキュメントのテーマタブを参照してください。
Accordion
のテーマのデフォルト設定は以下のようになっています。container
,item
,button
,panel
,icon
にそれぞれのコンポーネントのスタイルを設定します。
export const Accordion: ComponentMultiStyle = {
baseStyle: {
container: {},
item: {},
button: {
transitionProperty: "common",
transitionDuration: "normal",
_focusVisible: {
boxShadow: "outline",
},
_disabled: {
opacity: 0.4,
cursor: "not-allowed",
},
py: "3",
px: "4",
},
panel: {
px: "4",
pb: "3",
},
icon: {
ml: "auto",
fontSize: "1.25em",
color: ["blackAlpha.600", "whiteAlpha.700"],
},
},
variants: {
basic: {
item: {
borderTopWidth: "1px",
borderColor: "inherit",
_last: {
borderBottomWidth: "1px",
},
},
button: {
_hover: {
bg: ["blackAlpha.50", "whiteAlpha.50"],
_disabled: {
bg: "none",
},
},
},
},
card: {
item: {
borderWidth: "1px",
rounded: "md",
bg: ["blackAlpha.50", "whiteAlpha.50"],
_expanded: {
bg: ["white", "black"],
},
_notFirst: {
mt: "md",
},
},
button: {
_hover: {
bg: ["blackAlpha.100", "whiteAlpha.100"],
_expanded: {
bg: "none",
},
_disabled: {
bg: "none",
},
},
},
},
unstyled: {},
},
defaultProps: {
variant: "basic",
},
}
スタイルを動的に変更する
以下のように関数を使用して動的にスタイルを設定することができます。
カラースキーム等に応じてスタイルを変更する場合に利用します。テーマやカラーモード以外にも、そのコンポーネントに渡されたPropsであれば、受け取ることが可能です。
補足
この後も出てきますが、以下のような使い分けです。
- テーマ(theme): Tipsの複数のテーマを切り替えるのように、複数のテーマがある場合に、現在のテーマを取得できます。
- カラーモード(colorMode): ダークモード/ライトモードを取得できます。
- カラースキーム(colorScheme):
<Button colorScheme="primary" />
のように渡された値が取得できます。トークンを追加するでカラースキームをつかした場合は、それも渡すことができます。
import {
ComponentStyle,
isGray,
isAccessible,
} from "@yamada-ui/react"
export const Container: ComponentStyle = {
...
variants: {
"wtih-border-solid": ({theme: t, colorMode: m, colorScheme: c}) => ({
bg: isGray(c)
? [`${c}.50`, `${c}.700`]
: [isAccessible(c) ? `${c}.400` : `${c}.500`, `${c}.600`],
borderWidth: "1px",
}),
"wtih-border-dotted": {
borderWidth: "1px",
borderStyle: "dotted",
},
},
...
}
Button
はデフォルトで結構カスタマイズされているので参考にしてみてください。
Buttonのデフォルトのテーマ設定
tokens
新しくトークンを追加したり、既存のトークンを変更することができます。
トークンの種別ごとにファイルを作成します。./theme/tokens
├ colors.ts
├ fontSize.ts
└ index.ts
index.ts
でまとめてexportします。
import { colors } from "./colors"
import { fontSize } from "./fontSize"
const tokens = { colors, fontSize }
export default tokens
トークンとは
fontsizeなどを指定するときに使うsm
, md
から、z-indexのyamcha
, kurillin
などがデフォルトで設定されています。
colorの指定に利用できるred.500
などもトークンになります。
これらはコンポーネントのスタイルを設定するときに利用できます。
僕はドラゴンボール見てないので、クリリンがどれくらいの強さなのかわかりません。
// <Button fontSize="0.875rem" zIndex={9} color="#ea4334" />
<Button fontSize="sm" zIndex="kurillin" color="red.500" />
デフォルトで、どういったトークンがあるかは以下のドキュメントに記載されています。
トークンを追加する
ドキュメントそのままですが、以下の例ではbanner
というトークンを新たに追加し、black
というデフォルトで定義されているトークンを上書きしています。
import { ThemeTokens } from "@yamada-ui/react"
export const colors: ThemeTokens = {
banner: "#9d38a0",
black: ["#1F2123", "#101112"],
}
Yamada Colors
Yamada UIにはデフォルトでも豊富なカラースキームが提供されていますが、これ以外の色をカラースキームとして追加したい場合があるかもしれません。
こんな時に使えるのが、Yamada Colorsです!
(ドキュメントの左メニューの下の方にある「カラージェネレーター」)
カラースキームとして追加したい色を選択して、My PalettersからJSONでエクスポートします。それをそのままコピペでトークンとして追加できます。
スクショ
import { ThemeTokens } from "@yamada-ui/react"
export const colors: ThemeTokens = {
banner: "#9d38a0",
black: ["#1F2123", "#101112"],
himmel: {
"50": "#f7f9fc",
"100": "#f0f4fa",
"200": "#e0e9f5",
"300": "#d1def0",
"400": "#c2d3eb",
"500": "#b3c8e6",
"600": "#81a4d5",
"700": "#4f80c4",
"800": "#34609d",
"900": "#24416b",
"950": "#1b3252",
}
}
styles
グローバルに適用するスタイルや、繰り返し使用するテキストのスタイル、レイヤーのスタイルをまとめて管理できます。
カスタマイズしたいstyleごとにファイルを作成します。
./theme/styles
├ global-style.ts
├ reset-style.ts
├ layer-styles.ts
├ text-styles.ts
└ index.ts
index.ts
でまとめてexportします。
import { globalStyle } from "./global-style"
import { resetStyle } from "./reset-style"
import { layerStyles } from "./layer-styles"
import { textStyles } from "./text-styles"
const styles = { globalStyle, resetStyle, layerStyles, textStyles }
export default styles
globalStyle
bodyのスタイルの変更など、グローバルのスタイルの変更ができます。
以下のようにすると、背景色が変更できます。import { UIStyle } from "@yamada-ui/react"
export const globalStyle: UIStyle = {
body: {
bg: ["sky.50", "sky.950"],
},
}
関数にすることも可能です。関数の場合は、theme
とcolorMode
などを取得できます。
import { UIStyle } from "@yamada-ui/react"
export const globalStyle: UIStyle = ({ theme, colorMode }) => ({
body: {
bg: "red.600",
color: "white",
},
})
resetStyle
各ブラウザで設定されている、リセットスタイルの変更ができます。
globalStyle
と同様に、スタイルオブジェクトまたはスタイルオブジェクトを返す関数を定義できます。
layerStyles
繰り返し使用するレイヤーのプロパティを定義しておくことが可能です。
以下のようにテーマにトークンとスタイルを定義する必要があります。import { LayerStyles } from "@yamada-ui/react"
export const layerStyles: LayerStyles = {
masterRoshi: {
position: "relative",
m: "md",
boxSize: "4xs",
border: "6px solid #000",
rounded: "full",
bg: "#FFF",
fontFamily: "serif",
color: "#000",
fontSize: "8xl",
fontWeight: "bold",
_after: {
content: '""',
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: -1,
rounded: "md",
bg: "#FF7F0B",
boxSize: "3xs",
},
},
}
追加したlayerStyleは以下のように利用できます。
<Center layerStyle="masterRoshi">亀</Center>
textStyles
繰り返し使用するテキストのプロパティを定義しておくことが可能です。
使い方はlayerStyleと同様です。export const textStyles: TextStyles = {
gradient: {
fontSize: "5xl",
bgGradient: "linear(to-r, orange.400, red.500)",
bgClip: "text",
},
}
<Heading textStyle="gradient" isTruncated>
クリリンのことか……クリリンのことかーーーっ!!!!!
</Heading>
その他のスタイル
layerStyle
やtextStyle
以外にも独自のスタイルを定義できます。
import { CSSUIObject } from "@yamada-ui/react"
export const gangnamStyle: Record<string, CSSUIObject> = {
title: {
fontSize: "2xl",
bg: "black",
color: "blue.700",
},
text: {
bg: "yellow.700",
},
}
index.ts
で一緒にexportします。
import { globalStyle } from "./global-style"
import { resetStyle } from "./reset-style"
import { layerStyles } from "./layer-styles"
import { textStyles } from "./text-styles"
+import { gangnamStyle } from "./gangnam-styles"
-const styles = { globalStyle, resetStyle, layerStyles, textStyles }
+const styles = { globalStyle, resetStyle, layerStyles, textStyles, gangnamStyle }
export default styles
独自に定義したスタイルを使用する場合はapply
に渡します。
<Text as="h1" apply="gangnamStyle.title">
フリーレン様。カンナムスタイルは最近の曲ではありません。
</Text>
semantics
primary
やsecondary
等の色を変更できます。
以下のようにすることで変更が可能です。
colorSchemes
に設定できるのは、red.50
~red.950
のように、セットになっているものです。もちろんYamada Colorsで追加したhimmel
も利用できます。
デフォルトの設定
import { ThemeSemantics } from "@yamada-ui/react"
export const semantics: ThemeSemantics = {
colors: {
primary: "himmel.500",
},
colorSchemes: {
primary: "himmel",
},
}
config
デフォルトのカラーモードやテーマ、CSSカスタムプロパティのプレフィックスを変更することができます。
デフォルトのconfig
標準でシステム設定にあわせたい場合は、以下のようにします。
export const config: ThemeConfig = {
initialColorMode: "system",
}
Tips
デフォルトのテーマ設定は以下のフォルダに入ってます。
ライトモード/ダークモードの切り替え
Yamada UIは標準でダークモードとライトモードの切り替えをサポートしています。useColorMode()
でカラーモードの切り替えができます。
const { colorMode, changeColorMode, toggleColorMode } = useColorMode();
return (
<Wrap gap="md">
<Button onClick={() => changeColorMode("light")}>ライトモード</Button>
<Button onClick={() => changeColorMode("dark")}>ダークモード</Button>
<Button onClick={() => changeColorMode("system")}>システム</Button>
<Button onClick={toggleColorMode}>
{colorMode === "light" ? "ダーク" : "ライト"}モードに切り替える
</Button>
</Wrap>
);
複数のテーマを切り替える
Yamada UIは複数のテーマを用意しておき、それを切り替えできる機能を提供しています。
複数のsemantics
を切り替えできるイメージです。
semantics.ts
を以下のように書き換えて、複数のsemantics
を定義しておきます。
import { ThemeSchemes } from "@yamada-ui/react"
export const themeSchemes: ThemeSchemes = {
frieren: {
semantics: {
colors: {
primary: "frieren.500",
secondary: "himmel.500",
},
colorSchemes: {
primary: "frieren",
secondary: "himmel",
},
},
},
fern: {
semantics: {
colors: {
primary: "fern.500",
secondary: "stark.500",
},
colorSchemes: {
primary: "fern",
secondary: "stark",
},
},
},
}
index.ts
も合わせて変更します。
-import { semantics } from "./semantics"
+import { themeSchemes } from "./semantics"
export const customTheme: UsageTheme = {
styles,
components,
- semantics,
+ themeSchemes,
...tokens,
}
テーマの切り替えはuseTheme
を使って以下のようにできます。
changeThemeScheme
に切り替え先のテーマ名を渡しましょう。
const { theme, changeThemeScheme } = useTheme()
return (
<Button onClick={() => {changeThemeScheme("fern")}>
fern
</Button>
)
ThemeSchemeScript
複数のテーマを切り替えるでテーマを正常に切り替えるために、head
もしくはbody
内にThemeSchemeScript
を追加する必要があります。
ThemeSchemeScript
を追加しない場合、テーマ切り替えのちらつきが発生します。
initialThemeScheme
にはconfigで設定した値を渡します。
config
のカスタマイズをしていない場合は、@yamada-ui/react
からdefaultConfig
をimportしてdefaultConfig.initialThemeScheme
を渡しましょう。
import { UIProvider } from "@yamada-ui/react"
import { theme, config } from "theme"
...
+<ThemeSchemeScript initialThemeScheme={config.initialThemeScheme} />
<UIProvider theme={theme} config={config}>
<App />
</UIProvider>
ColorModeScript
ライトモード/ダークモードの切り替えでカラーモードを正常に切り替えるために、head
もしくはbody
内にColorModeScript
を追加する必要があります。
ColorModeScript
を追加しない場合、カラーモード切り替えのちらつきが発生します。
initialColorMode
にはconfigで設定した値を渡します。
config
のカスタマイズをしていない場合は、@yamada-ui/react
からdefaultConfig
をimportしてdefaultConfig.initialColorMode
を渡しましょう。
import { UIProvider } from "@yamada-ui/react"
import { theme, config } from "theme"
...
+<ColorModeScript initialColorMode={config.initialColorMode} />
<UIProvider theme={theme} config={config}>
<App />
</UIProvider>
CLI
カスタマイズしたテーマのトークンの型を生成できます。
別パッケージなので、@yamada-ui/cli
をインストールして利用しましょう。
pnpm
の部分とpathは自身の環境にあわせて変更してください。
pnpm yamada-cli tokens ./src/theme/index.ts
最後に
初めて使っていただく方に、と言っておきながらだいぶ長くなってしまいました。
少しでもYamada UIの良さを伝えられていたらうれしいです。
今回はスタイリングの部分でしたが、ほかにもたくさんあります。
- ドキュメントが日本語
- ダークモードを考慮した設計になっている
- アニメーションに強い
- コンポーネント個別でインストール可能 など
現在は、メンテナーの中でこんなコンポーネントあったらよくない?くらいでいろいろと作っていっているので、追加の要望やバグがあれば報告ください!
検証用に作成したリポジトリ。(NextJS AppRouter)
Discussion