🎨

Vanilla Extract を使った多次元スタイルバリアントの活用

2024/12/21に公開

Vanilla Extract を使った多次元スタイルバリアントの活用

はじめに

モダンなフロントエンド開発では、一貫性と拡張性を維持するためにデザインシステムを効果的に管理することが重要です。Vanilla Extractは、TypeScriptで型安全なCSSを扱えるライブラリで、こうした課題を解決する強力なツールを提供します。この記事では、Vanilla ExtractのstylestyleVariantを使って、多次元スタイルバリアントを効率的に作成する方法を解説します。

まず、典型的なデザインシステムを表現するサンプルデータを作成し、属性の組み合わせに基づいてスタイルバリアントを生成する方法を示します。最後に、複雑なケースを処理するためのユーティリティメソッドを使用してプロセスを簡略化します。

デザインシステムのサンプルデータ

以下は、デザインシステム用のマスターデータの例です:

{
  "palettes": {
    "marine": {
      "fontFamily": "Arial",
      "primary": {
        "light": {
          "backgroundColor": "#007bff",
          "color": "#ffffff"
        },
        "dark": {
          "backgroundColor": "#0056b3",
          "color": "#ffffff"
        }
      },
      "error": {
        "light": {
          "backgroundColor": "#dc3545",
          "color": "#ffffff"
        },
        "dark": {
          "backgroundColor": "#c82333",
          "color": "#ffffff"
        }
      }
    },
    "outerspace": {
      "fontFamily": "Courier New",
      "primary": {
        "light": {
          "backgroundColor": "#28a745",
          "color": "#ffffff"
        },
        "dark": {
          "backgroundColor": "#218838",
          "color": "#ffffff"
        }
      },
      "error": {
        "light": {
          "backgroundColor": "#dc3545",
          "color": "#ffffff"
        },
        "dark": {
          "backgroundColor": "#c82333",
          "color": "#ffffff"
        }
      }
    }
  },
  "sizes": {
    "small": {
      "fontSize": "12px",
      "padding": "4px 8px",
      "borderRadius": "4px"
    },
    "medium": {
      "fontSize": "14px",
      "padding": "8px 12px",
      "borderRadius": "6px"
    },
    "large": {
      "fontSize": "16px",
      "padding": "12px 16px",
      "borderRadius": "8px"
    }
  }
}

多次元デザインシステムのクラス生成

1次元のスタイルバリアント

まずはシンプルな1次元スタイルバリアントを生成します。サイズに基づいたparagraphVariantsを定義する例です:

import { styleVariants } from '@vanilla-extract/css';
import masterData from './masterData.json';

const paragraphVariants = styleVariants({
  small: {
    fontSize: masterData.sizes.small.fontSize,
    padding: masterData.sizes.small.padding,
    borderRadius: masterData.sizes.small.borderRadius,
  },
  medium: {
    fontSize: masterData.sizes.medium.fontSize,
    padding: masterData.sizes.medium.padding,
    borderRadius: masterData.sizes.medium.borderRadius,
  },
  large: {
    fontSize: masterData.sizes.large.fontSize,
    padding: masterData.sizes.large.padding,
    borderRadius: masterData.sizes.large.borderRadius,
  }
});

Reactコンポーネントでの使用例

import { paragraphVariants } from './styles.css.ts';

const Paragraph = ({ size, children }) => {
  const paragraphClass = paragraphVariants[size];
  return <p className={paragraphClass}>{children}</p>;
};

2次元のスタイルバリアント

次に、テーマとパレットを組み合わせたスタイルを定義する例です。手動で定義すると次のようになります:

const marineTagVariants = styleVariants({
  "primary-light": {
    backgroundColor: masterData.palettes.marine.primary.light.backgroundColor,
    color: masterData.palettes.marine.primary.light.color,
  },
  "primary-dark": {
    backgroundColor: masterData.palettes.marine.primary.dark.backgroundColor,
    color: masterData.palettes.marine.primary.dark.color,
  },
  "error-light": {
    backgroundColor: masterData.palettes.marine.error.light.backgroundColor,
    color: masterData.palettes.marine.error.light.color,
  },
  "error-dark": {
    backgroundColor: masterData.palettes.marine.error.dark.backgroundColor,
    color: masterData.palettes.marine.error.color,
  }
});

Reactコンポーネントでの使用例

import { marineTagVariants } from './styles.css.ts';

const MarineTag = ({ theme, palette, semantic, children }) => {
  const tagClass = marineTagVariants[`${palette}-${semantic}`];
  return <span className={tagClass}>{children}</span>;
};

3次元のスタイルバリアント

テーマ、パレット、セマンティクスなどの複数の属性を扱う場合、手動ではなくループを使用する方が効率的です:

const tagVariants = styleVariants(
  masterData.themes.reduce((acc, theme) => {
    masterData.palettes.forEach((palette) => {
      masterData.semantics.forEach((semantic) => {
        acc[`${theme}-${palette}-${semantic}`] = {
          backgroundColor: palette[semantic][theme].backgroundColor,
          color: palette[semantic][theme].color,
        };
      });
    });
    return acc;
  }, {})
);

ユーティリティメソッドで簡略化

手動での組み合わせ処理は冗長になりがちです。ユーティリティメソッドを使用すると、このプロセスが大幅に簡略化されます。

ユーティリティメソッドの例

以下のパッケージをインストールします:

npm install @shiwasawa/style-utils

buildExtractを使って多次元バリアントを生成します:

import { buildExtract } from '@shibasawa/style-utils';

export const tagVariants = styleVariants(
  buildExtract(
    (palette, semantic, theme) => ({
      backgroundColor: palette[semantic][theme].backgroundColor,
      color: palette[semantic][theme].color,
    }),
    Object.values(masterData.palettes),
    Object.keys(masterData.semantics),
    Object.keys(masterData.themes)
  )
);

Reactコンポーネントでの使用例

import { tagVariants } from './styles.css.ts';

const Tag = ({ theme, palette, semantic, children }) => {
  const tagClass = tagVariants[`${palette}-${semantic}-${theme}`];
  return <span className={tagClass}>{children}</span>;
};

おわりに

この記事では、Vanilla Extractを使って多次元スタイルバリアントを扱う方法を解説しました。1次元の組み合わせから始め、2次元、3次元のシナリオを取り上げ、最終的にユーティリティメソッドでプロセスを簡略化しました。このアプローチは、モダンなデザインシステムにおける拡張性とメンテナンス性を保証します。

ぜひ、これらのテクニックをプロジェクトに取り入れて、その効果を実感してください。さらに高度な例については、リンク先のユーティリティリポジトリを参照し、試してみてください!

Discussion