vanilla-extract の recipe を活用する
こんにちは、上條(mk-0A0)です。
最近、「サイズは同じだけどページによって色が異なる」というよくあるボタンのデザインに遭遇しました。色の指定のためだけに新しくクラスを定義するのはちょっとめんどくさいな〜と思っていたのですが、vanilla-extract の recipe を使うことでより良い実装にできたので共有したいと思います。
vanilla-extract とは
vanilla-extract とは、TypeScript で記述できる CSS ライブラリです。
型安全なので開発体験の向上につながるのと、ゼロランタイムを謳っているためパフォーマンスの向上にも期待できます。
過去に弊社ブログでも記事になっているので、ぜひこちらもご覧ください。
型安全な CSS Modules?「vanilla-extract」
recipe とは
vanilla-extract で使えるの機能の一つで、複数のスタイルをまとめることができます。スタイルの出し分けが必要なときに便利です。
Recipes – vanilla-extract
vanilla-extract だけをインストールした状態では使用できないので、別途インストールが必要になります。
npm install @vanilla-extract/recipes
使い方
サンプルとしてボタンのスタイルを作成してみます。Style.css.ts
を作成して Button
を定義します。vanilla-extract から recipe
関数を import し、関数内でオブジェクトを定義します。recipe
で主に使うオプションは以下2つです。
base
:すべての Button
に共通するスタイルを定義するvariants
:複数パターンがあるスタイルを定義する
このサンプルではボタンの背景色とテキストカラーが black か white 2つのパターンがあります。
import { recipe } from '@vanilla-extract/recipes'
const Button = recipe({
base: {
width: '200px',
height: '80px'
},
variants: {
backgroundColor: {
black: { backgroundColor: '#000000' },
white: { backgroundColor: '#EEEEEE' },
},
textColor: {
black: { color: '#000000' },
white: { color: '#FFFFFF' },
},
},
})
実際にこの Button を使用すると以下のようになります。backgroundColor には black、textColor にはwhite を指定しました。
import { Button } from "./Style.css"
const Home = () => {
return (
<button className={Button({ backgroundColor: 'black', textColor: 'white' })}>
Button
</button>
)
}
export default Home
ちなみに backgroundColor に white、textColor に black を指定するとこのようになります。
import { Button } from "./Style.css"
const Home = () => {
return (
<button className={Button({ backgroundColor: 'white', textColor: 'black' })}>
Button
</button>
)
}
export default Home
また、variants は boolean での判定も可能です。
角丸の有無を出し分けるスタイル rounded
を boolean で作成すると以下のようになります。
import { recipe } from '@vanilla-extract/recipes'
const Button = recipe({
base: {...},
variants: {
backgroundColor: {...},
textColor: {...},
rounded: {
true: {
borderRadius: 999,
},
},
},
})
import { Button } from "./Style.css"
const Home = () => {
return (
<button className={Button({ backgroundColor: 'black', textColor: 'white', rounded: true })}>
Button
</button>
)
}
export default Home
なんとなく recipe というものが掴めてきたでしょうか?
他のオプション
base
variants
以外にもオプションがあるので、それらを紹介します。
defaultVariants
設定した variants の中からデフォルトで当てたいスタイルを指定できます。defaultVariants を設定しておくと、そのスタイルを使用する場合は tsx 側でのオプション設定が不要になります。
import { recipe } from '@vanilla-extract/recipes'
const Button = recipe({
base: {...},
variants: {...},
defaultVariants: {
backgroundColor: 'black',
textColor: 'white',
},
})
import { Button } from "./Style.css"
const Home = () => {
return (
// backgroundColor: 'black', textColor: 'white'
<button className={Button()}>Button</button>
)
}
export default Home
compoundVariants
特定の variants が指定されたときのスタイルを定義します。以下サンプルの場合、backgroundColor が white、 textColor が black、 rounded が true のときに border が設定されます。
import { recipe } from '@vanilla-extract/recipes'
const Button = recipe({
base: {...},
variants: {...},
defaultVariants: {...},
compoundVariants: [
{
variants: {
backgroundColor: 'white',
textColor: 'black',
rounded: true,
},
style: {
border: '1px solid #000',
},
},
],
})
import { Button } from "./Style.css"
const Home = () => {
return (
<button className={Button({ backgroundColor: 'white', textColor: 'black', rounded: true })}>
Button
</button>
)
}
export default Home
メリット
一つのクラスでここまで柔軟にスタイルを当てられるのは大きなメリットです。無理にすべてのスタイルを集約しようとすると煩雑になって逆に使いづらくなってしまいそうですが、適度な粒度であれば開発体験の向上につながるのではないでしょうか。
まとめ
recipe はスタイルだけの集合体が作れます。そのスタイルのパターンが豊富な場合はスタイルを当てるだけの tsx コンポーネントを作ることもあると思いますが、そうなったとしてもきれいに記述できるので良いと思いました。
今までよく分かってなかった compoundVariants の使い方をようやく理解できたので、実際に使用できるところがないかコードを見直したいです。
Gaji-Laboでは、 Next.js 経験が豊富なフロントエンドエンジニアを募集しています
弊社では Next.js の知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違う Gaji-Labo を味わいに来ませんか?
Next.js の設計・実装を得意とするフロントエンドエンジニア募集要項
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!