MUIベースのデザインシステムVUI

MUIベースのデザインシステムVUI

はじめに

ここ数か月、社内デザインシステムであるVUI(Vizend UI)の実装に取り組んできました。VUIは、デザイナーと開発者が同じ方法で製品をつくるための共通言語であり、約束です。デザイントークン・コンポーネント・パターン・原則と、それらをコードまでつなぐ自動化を一つにまとめたデザインシステムです。その基盤エンジンとしてMUI(Material UI)を使用しています。ここでは、デザインシステムをどのようにつくっているのかを簡単に紹介します。

構成は次のとおりです。なぜデザインシステムが必要だったのか、MUIをベースとして使用した理由、デザインシステムで使用するデザイントークン、MUIテーマの動作原理、デザイントークンをMUIテーマがどのように利用し、実際のコンポーネントにトークンを適用した方法、モバイル/デスクトップやライト/ダークなど、モードごとにテーマを分離して構成した方法、最後にまだ解決できていない問題です。

1. なぜデザインシステムが必要だったのか

デザインシステムは一般に「再利用可能なUIコンポーネントの集合」と考えられています。しかし、私が実感したデザインシステムの本質は、製品をつくる方法に関する共通言語であり、約束でした。たとえるなら、UI Kitが「レゴブロックの入ったバスケット」だとすれば、デザインシステムにはブロックをつくる工場、組み立て説明書、そしてブロックそのもののすべてが含まれます。

出発点はAIでした。AIによるフロントエンドコードの自動生成を試みる中で、AIが理解できるように整理・実装されたシステムが必要になりました。また、システムを構築すれば、少人数でもデザインやパブリッシングを進められるというのが、二つ目の理由でした。人の記憶力や誠実さに依存する方法は、人数が少ないほど急速に破綻し、AIの活用性も低下します。VUIの原則として「システムによる強制と自動化」を掲げ、デザイン資産化を最大の目標に設定しました。

デザインシステムがない場合のコストは、具体的なものでした。

  • 断片化された体験:同じ「ボタン」でも、画面ごとに角の丸み・色・高さが微妙に異なります。統制しなければ、製品ごとに異なる顔になってしまいます。

  • コミュニケーションのオーバーヘッド:「そこの青色をもう少し濃くして」という曖昧な依頼が、デザインと開発の間をピンポンのように行き来します。

  • 反復作業(Toil):画面一つひとつに手作業でスタイルを適用する単純な反復作業が、ビジネスロジックに使う時間を奪います。

そこでVUIが目指したのは、単なる一貫性ではなく、工学的な一貫性(Engineered Consistency)です。Semantic Token(意味論的トークン)という共通言語で会話できるようにすることです。「背景を灰色でもっと濃く」ではなく、「Background-DefaultトークンをBackground-Darkに変更」と言えるようになれば、デザイン上の決定がそのままデータとなり、追跡やロールバックが可能になります。

2. MUIの選択

デザインシステムをつくる際、最初に直面する分かれ道はBuild vs Buyです。すべてのコンポーネントを自作するのか、検証済みのライブラリを導入して自分たちの衣装を着せるのか。私は後者を選び、そのベースとしてMUI(Material UI)を選びました。

2.1 自前で構築する場合

ボタン一つをつくることは、難しくありません。問題は、そのボタンがキーボードフォーカス、スクリーンリーダー(ARIA)、無効状態、RTL、クロスブラウザ対応まで、すべてを考慮して初めて「製品品質」になるという点にあります。さらにDataGrid、DatePicker、Autocompleteのような複雑度の高いコンポーネントまで自作して保守すると、小規模チームのリソースはビジネスロジックではなく「車輪の再発明」に費やされます。アクセシビリティや互換性の品質も、チームの能力によってばらついてしまいます。

2.2 候補の比較 — 私たちの基準でMUIが勝った理由

ライブラリを選ぶ際に設けた基準は三つでした。①ブランドを適用するためのテーマカスタマイズが、どれほど深く安全に行えるか、②エンタープライズ画面(表・日付・オートコンプリート)向けの複雑なコンポーネントエコシステムが十分に整っているか、③Figmaとの整合性(デザインと開発の同期)が可能か。

候補

強み

弱み

自前で構築(headlessの組み合わせ)

完全な自由度

構築・アクセシビリティ・保守のコストが最大 — 小規模チームには非現実的

Ant Design

完成度の高いエンタープライズコンポーネント

デザイン言語が強く固定されている — ブランドの再定義や深いテーマカスタマイズが難しい

Chakra UI

軽量でトークンとの親和性が高い

表・日付などの高複雑度コンポーネントのエコシステムが比較的浅い

MUI(選択)

createThemeオーバーライドの深さ + x-data-grid・x-date-pickersエコシステム + Material Figma Kitとの整合性 + 巨大なコミュニティ

Materialデザインの色が残る → トークン・ラッピングで上書きする必要がある

決定的だったのはMUIの 公式テーマオーバーライドAPI(createTheme)でした。ブランドスタイルを「設定(Configuration)」層でのみ注入できるということは、すなわち次の原則を可能にしました。

2.3 Natural Upgrade — 「No Forking」原則

オープンソースを使うとき最大のリスクは、カスタマイズのために元のソースをフォーク(変更)した瞬間に発生します。フォークした瞬間、アップストリームの更新から永遠に遠ざかるためです。そこでVUIは、MUIのソースやnode_modulesを変更しないというルールを厳守します。すべてのカスタマイズは、MUIが公式に提供するTheme Override APIによってのみ行われます。その結果は「メンテナンスの自由」です — npm update 一度でセキュリティパッチと新機能が自然に取り込まれ、私たちのブランドオーバーライドはそのまま維持されます。

VUIのボタンはMUIのButtonをそのままラップするだけで、色・曲率・間隔などの外観はすべてトークン → テーマ 経路で適用されます。元のソースをフォークして作り変える代わりに、このように薄くラップしておけば、MUIのバージョンが上がってもラッパーは壊れません。

3. FigmaをSSOTに — デザイントークンのコード化

VUIの6層アーキテクチャで最下層(Layer 1)にあるのがデザイントークンです。トークンとは、色(#Hex)、間隔(px)、タイポグラフィ、曲率など、あらゆる視覚的な決定をプラットフォームに依存しないデータとして抽象化したものです。ハードコーディングとトークンの違いは「意味」にあります。

// Bad — 이 색이 무슨 의미인지 코드만 봐선 모른다
background-color: #2196F3;
// Good — '브랜드의 메인 컬러'임이 이름에 드러난다
background-color: tokens.color.brand.primary.main;

核心原則はFigmaが単一の真実の供給源(SSOT)であるということです。色を変えたい場合はコードではなくFigmaで変更し、コードはその決定を「書き写すだけ」にします。そのため、トークンを手作業で転記するプロセスをなくし、パイプラインで自動化しました。

3.1 トークンコード化パイプライン(Figma → JSON → TS)

パイプラインは3段階です。まずデザイナーがFigma Variablesに定義した色・間隔・タイポグラフィの値を、Figmaカスタムプラグインを使ってW3C DTCG標準JSON 形式で抽出します。次にStyle Dictionaryというライブラリを使って、トークン間の参照チェーン(component → semantic → primitive)を解決し、実際の値として確定します。最後にその結果がTypeScriptファイルとして保存され、MUIがこのデザイントークンを利用します。Figmaの視覚的な決定が人の手を介さず、コードトークンまで流れ込む仕組みです。参照切れがあってもビルドを失敗させずログに記録するようにしました。デザインが変更されている間もCIを停止させないためです。標準フォーマット(W3C DTCG)で一度抽出しておけば、その後はどのツールでも読み取れるため、自動化が可能になります。

3.2 トークン階層と参照関係(Semantic Naming — primitive → semantic → component)

トークンは名前だけで用途が分かるように、3層構造にします。primitive(絵の具そのもの: blue-500)、semantic(意味・役割: primary-main)、component(特定部品での用途: button-contained-bg)。最下層の絵の具だけを変えても上位層がすべて追従するように、一方向の依存(primitive ← semantic ← component)のみを許可します。

// primitive — 물감 자체 (실제 색 값)
color/blue/500          = #2196F3
// semantic — 의미·역할 (primitive 를 가리킴)
color/primary/main      = {color.blue.500}
// component — 특정 부품의 쓰임 (semantic 을 가리킴)
button/contained/bg     = {color.primary.main}
// → blue-500 한 곳만 바꾸면 primary, button 까지 연쇄 반영된다

4. MUI Themeはトークンをどのように利用するのか

トークンがデータ(Layer 1)だとすれば、そのデータを実際のコンポーネントの「衣服」に変えるエンジンがMUI Theme(Layer 2)です。ここでMUIテーマの動作原理を理解することが重要です。

4.1 MUI Theme Configurationの動作原理 — 3チャネル

MUIコンポーネントはThemeProvider コンテキストで受け取った themeオブジェクトを読み込み、スタイルを計算します。注入経路は大きく3つのチャネルに分かれます。

  • グローバルデザイン値:palette / typography / spacing / shapeなど、すべてのコンポーネントが共有するグローバル値

  • デフォルト props:components.MuiX.defaultProps — コンポーネントのデフォルトの動作・形状

  • スタイルオーバーライド:components.MuiX.styleOverrides — variant・color・stateの組み合わせごとのスタイル

image1.png

VUIはこの3つのチャネルをすべてトークンで埋めます。そのため、1か所(ThemeOptions)だけを変更すれば、すべてのコンポーネントが同時に変わります。テーマの生成は、ブランドとモードを引数に取るヘルパーでラップしています。

4.2 token-adapter — 「値は自動、構造だけ手動」

自動生成されたトークンのパス(例:component.button['md-radius'])をコンポーネントのスタイルに直接記述すると、デザイナーがトークン構造を一度変更するたびにコードが壊れます。そこで、token-adapter.tsを用意し、自動生成トークンのパスを安定したスロット名に1回だけマッピングしました。その後、トークンのが変わってもパスは変わらないため自動的に反映され、トークンの構造(パス)が変わったときだけアダプターの1行を修正します。components.tsは、このアダプターが公開するbuttonTokens / chipTokens / alertTokensなどを受け取り、MUIのstyleOverridesを記述します。

// components.ts — token-adapter 의 슬롯을 MUI styleOverrides 로 연결
import { buttonTokens, chipTokens, alertTokens } from './token-adapter';

// MUI Theme 객체에 선언된 MUI Button 스타일 선언코드
MuiButton: {
  styleOverrides: {
    contained: ({ theme, ownerState }) => ({
      backgroundColor: buttonTokens.variant.contained.bg[ownerState.color],
      borderRadius: theme.shape.radiusControlSm,
    }),
  },
}

MUI標準のpaletteにないVUI固有のスロット(例:surface / field / border / icon / overlay)は、module augmentationによって型を拡張し、theme.vuiまたは拡張されたpaletteスロットからアクセスできるようにしました(mui-augmentation.ts)。IDEの自動補完だけで、数多くのトークンを迷わず取り出して使えます — 型システムそのものが参照可能なドキュメントになるというわけです。

4.3 Figma MCPでデザイン トークンの整合性を検証する

デザインシステムを構築するうえで重要な作業の一つは、「Figmaで定義されたコンポーネント仕様を実際のトークン・テーマに接続」し、その2つにずれがないかをデザインの整合性を検証することでした。最近では、この照合作業にAI Agentを使用します。

核心は2つの段階に分かれます。まず Figma MCPを介して、AI AgentがFigmaフレーム内の特定のコンポーネントに実際に適用されたデザイントークン情報(色・タイポグラフィ・間隔・radiusなど、どの変数がどこにバインドされているか)を直接読み取り、分析します。その後、このFigma側のトークン情報を、コンポーネントが 実際に実装されたStorybookと比較します。

<Storybook>

image2.png

<Figma>

image3.png

2つの結果を並べて、AI Agentが比較することで、実装コード内の デザイントークンが欠落している箇所や、Figmaと値が一致しない箇所を検査できます。人が毎回目視で照合していた作業を、AI Agentが一次チェックしてくれるというわけです。この検証が可能な理由は、結局のところ トークンという共通言語のおかげです — Figmaとコードが同じ基準(トークン)で表現されるため、初めて自動比較が成り立ちます。

5. モード別のテーマ構成 — コンポーネントはそのまま、テーマだけを切り替える

実際の製品には、1種類の画面しかありません。大きなモニターでも小さなスマートフォンでも、明るい画面でも暗い画面でも、また異なるブランドの製品でも、同じようにきれいに表示される必要があります。VUIはこうしたバリエーションを一度に扱いますが、核心は コンポーネントコードはそのままにして「テーマ」だけを差し替えるという点です。現在、この切り替えの軸は3つあります。

  • 画面サイズ(デスクトップ / モバイル) — 同じコンポーネントでも、画面が小さくなると文字サイズと間隔が自動的により詰まったものになります。画面幅を見て、テーマがデスクトップ用・モバイル用の値に自動で切り替えます。

  • ライト / ダークモード — 背景と文字の色がモードに合わせて一括で切り替わります。ユーザーがダークモードをオンにすると、コンポーネントはそのままですが、色のセットだけが暗いセットに置き換わります。

  • ブランド(例:vizend / devlime) — 製品ごとにブランドカラーや雰囲気を変えて適用できます。新しいブランドが必要になったら、デザイン上で色だけを新たに定義して追加すれば済みます。

重要なのは、この3つの軸をどのように組み合わせても、開発者はコンポーネントコードを1行も変更しないということです。画面の最上部で「どのブランドで、どのモードか」だけを一度決めれば、その下にあるすべてのコンポーネントが自動的にそれに合ったテーマを適用します。コンポーネントごとに複雑な分岐を追加する代わりに、その複雑さをテーマという1つの層に引き上げ、下層をシンプルにしたというわけです。

// 맨 위에서 '브랜드 / 모드'만 정하면 끝 — 컴포넌트 코드는 그대로
<VuiThemeProvider brand="vizend" mode="dark">
  <App />
</VuiThemeProvider>

6. まだ解決すべき課題

設計と実装を進める中で明らかになった、まだ解決できていない課題です。

6.1 デザイントークンのマッピング問題 — MUI Themeですべてのスタイルを管理することはできない。

VUIの意味ベースのカラーは、MUI標準の theme.paletteスロット構造に完全にはマッピングできません。MUI paletteはprimary/secondary/text/background程度を想定していますが、私たちのセマンティックトークンにはそれ以上に多くのニュアンスがあります。その結果、vuiColors.bg.surfaceのようなヘルパーやCSS変数による 迂回アクセスを利用する経路が増えました。「Figmaでtext.mutedという意味を定義したのに、MUI paletteのtextスロットにはその意味をそのまま収める場所がない」という問題は、module augmentationによって一部緩和されましたが、標準と拡張が共存する不自然さは残っています。

6.2 構造変更検知の非対称性、そしてコンポーネント配線の未完成

token-adapterのおかげで、トークンのの変更は完全に自動化されていますが、トークンの構造(パス)の変更については、人がbuild.logを直接読み、アダプターを修正しなければなりません — 自動と手動の非対称性が不便です。また、トークンアダプターにはスロットがありますが、components.tsのstyleOverridesがまだ適用されていないコンポーネントが多数残っているため、それらのコンポーネントではユーザーがsxでトークンを直接利用する必要があります。データは揃っているものの、配線がまだ不十分な状態であり、一度対応すれば将来的な保守コストはほとんどかかりませんが、現時点では未完成です。

6.3 デザイン原則の策定と文書化の必要性

これまでは「トークンのコード化」に集中してきましたが、その上で人が従うべきデザイン原則コンポーネント使用ガイドは、まだ十分に整理されていません。システムは整いましたが、「これをどのように、いつ使うべきか」を示すドキュメントが不足しています。

2種類のドキュメントが必要です。1つはデザイン原則ドキュメントです — なぜsemanticレイヤーとcomponentレイヤーを分けたのか、どのような要望をトークンとして受け入れ、どのような要望を断るのかといった意思決定の基準を残しておけば、6か月後に加わった人でも同じ判断を下せます。もう1つはコンポーネント使用ガイドです — 各VUIコンポーネントをいつ、どのようなpropの組み合わせで使うのが標準なのか、よくある誤った使い方は何かを整理することで、一貫性を利用段階にまで維持できます。

トークンをコード化するところまで来たので、次は原則と使用方法をドキュメントとしてコード化する番です。これが新しいチームメンバーのオンボーディング速度とシステムの持続可能性を左右します。

おわりに

VUIを作る中で得た一文は、これです — 「一貫性の責任を人の誠実さではなく、システムに持たせる。」 MUIをベースに選んだこと、FigmaをSSOTとしたこと、トークンをコード化してテーマとして利用するようにしたこと、バリエーションを設定として吸収したことは、すべてこの一つの方向を指しています。

この取り組みの意義は、二重にあると考えています。近いところでは、小規模なチームが少ない人数でも標準品質のデザインとパブリッシングを両立できるようにするレバレッジです。遠いところでは、ビジェンドプラットフォームが目指すフロントエンドコード自動生成の礎です — 整理され、トークンによって標準化されたデザインシステムがあってこそ、AIがその上で一貫した画面コードを生成できるからです。

Brown

Site footer