newmo 技術ブログ

技術で地域をカラフルに

Design Tokenが変化することを前提にした型安全なCSS — newmoでPanda CSSを採用した理由

© 2023-Present Segun Adebayo

newmoでは、1行目のCSSを書く前に1つ目のDesign Tokenを定義しました。Design Tokenを起点にした開発を実現するため、CSSスタイリングフレームワークにはPanda CSSを採用しています。

しかし、創業期のスタートアップでは、デザインシステムは完成品ではなく日々変化していくものです。Design Tokenの命名が変わる、階層構造が見直される、色の値が調整される——こうした変更が頻繁に発生します。数百箇所に散らばったtokenを手動で置換すると、必ず修正漏れが発生します。そもそもDesign Tokenの使用を強制できなければ、変更に追従する以前の問題です。

newmoでは、TypeScriptの型システムを活用して、Design Tokenの変更を安全に行える仕組みを構築しました。この記事では、なぜPanda CSSを選んだのか、そしてDesign Tokenが変化しても安全に追従できる仕組みをどう作ったのかを紹介します。

想定読者は、Design Tokenの命名変更で大量の修正が発生して困った経験がある方、またはこれからデザインシステムを構築しようとしている方です。Panda CSSDesign Token自体の解説は割愛し、運用面にフォーカスします。

Design Token運用の課題

newmoでは、2024年3月の創業当初から、デザイナーがFigmaでDesign Tokenを定義し、基本的なスタイリングはこのDesign Tokenを使う方針でした。

しかし、通常のCSSではDesign Tokenの使用を強制できません。結果として、レビュー時に次のようなやり取りが発生します。

  • 「この色は独自に定義したものですか?」
  • 「このスペーシングは定義されたものですか?」
  • 「なぜpxの直接指定が必要なのですか?」

こうしたレビューコメントを毎回書くのは非効率です。さらに、Design Tokenの使用が徹底されていなければ、tokenが変更されたときに全ての使用箇所を漏れなく修正できる保証もありません。

Design Tokenを使うことを強制し、変更時の修正漏れを型エラーで検出できる仕組みが必要でした。

なぜPanda CSSを選んだのか

検討した主な選択肢は次の3つでした。

Tailwind CSS Panda CSS vanilla-extract
系統 Utility First Utility First/styled-component CSS Modules
Runtime Cost 低い 低い 低い
知名度 高い 低い 中程度
Production-Ready 安定している 当時0.x系 安定している
Type Safe クラス名のみ CSS Token + CSSOM CSSOMのみ
Design Token 参考事例は多い Design Tokenの仕組みが組み込み 自前実装が必要

Panda CSSを選んだ決定的な理由は、Design Tokenの型チェックができる点でした。

// Panda CSS: Design Tokenを使わないとコンパイルエラー
css({ margin: "8px" }); // ❌ TypeScriptエラー

// Design Tokenを使う
css({ margin: "xl" }); // ✅ OK

// どうしても直接値が必要な場合は明示的にエスケープ
css({ margin: "[8px]" }); // ✅ 明示的なエスケープ

Tailwind CSSも実質的にDesign Tokenを扱えますが、CSSクラス名の文字列なので間違いを検出できません。IDEプラグインやLinterを追加すれば警告は出せますが、TypeScriptのコンパイルエラーほど強制力がありません。

当時Panda CSSは0.x系で知名度も低かったものの、ロードマップが明確に公開されていたことも評価ポイントでした。実際、2025年8月にはv1.0の安定版がリリースされています。

型安全なDesign Tokenの開発体験

IDEでの補完とエラー検出

日常的な開発では、IDEでの補完が便利です。

const styles = css({
  backgroundColor: "theme.", // ← Ctrl+Spaceで補完候補が表示
  // "theme.neutral.0"
  // "theme.neutral.5"
  // "theme.accent.brand.10"
  // ...
});

Design Tokenを使わずに直接#000000のような値を書くとコンパイルエラーになります。ビルドが通っていればDesign Tokenが使われていることが保証されるため、レビューで「なぜDesign Tokenではないのか」と毎回指摘する必要がなくなります。

tokenの変更を型エラーで検出

tokenの名前が変更されると、使用箇所が型エラーとして検出されます。

// 例: "green.500" を削除して "theme.accent.brand.50" に変更した場合

// Before: エラーなし
backgroundColor: "green.500";

// After: 型エラー
backgroundColor: "green.500";
// ❌ Type '"green.500"' is not assignable to type ...

// 新しいtokenに修正
backgroundColor: "theme.accent.brand.50"; // ✅

この型エラーにより、token変更時の修正漏れを防ぐことができます。

FigmaからコードへのToken同期

Panda CSSの型安全性を活かすには、Design Tokenをコードとして定義する必要があります。しかし、創業期のデザインシステムでは、tokenの定義が毎週のように変わることも珍しくありません。その都度、手作業でコードを書き直すのは現実的ではないため、自社開発のFigmaプラグインを作成しました。

デザイナーがFigmaでVariablesやStylesを定義し、プラグインを実行すると、Panda CSS形式のTypeScriptコードがファイルとして生成されます。たとえば色の場合は、対象のコレクションからCOLOR型のVariablesを取得してPanda CSS形式に変換します。テキストスタイルなど他のtokenも同様に変換されます。生成されたファイルをリポジトリのfigma-tokens.tsに上書きすることで、tokenの変更をコードに反映します。

生成されるコードは次のような形式です。

// figma-tokens.ts(Figmaプラグインで生成)
export const figmaTokens = {
  theme: {
    tokens: {
      colors: {
        theme: {
          neutral: {
            "0": { value: "#ffffff" },
            "5": { value: "#f8f9fa" },
            "10": { value: "#f2f4f6" },
            // ...
          },
        },
      },
    },
  },
};

このファイルはPanda CSSの設定ファイル(panda.config.ts)から読み込まれ、既存のtoken定義とマージされます。

// panda.config.ts
import { figmaTokens } from "./figma-tokens.ts";

export default defineConfig({
  theme: {
    tokens: {
      colors: {
        ...figmaTokens.theme.tokens.colors,
      },
    },
  },
});

Panda CSSのビルド時にTypeScriptの型として生成されるため、Figmaでtokenを追加・変更したら、プラグインを実行してファイルを更新するだけで、IDEの補完候補や型チェックに反映されます。

Token変更時の安全なマイグレーション

Figmaプラグインでtokenの追加や変更には対応できますが、既存のtokenを別の名前に置き換える場合は、アプリケーションコード内のtoken参照箇所も修正が必要です。newmoでは、こうしたtoken変更に対応するため、3段階のアプローチを取っています。

1. deprecated flagで警告

まず、Panda CSSの設定ファイルに定義された古いtokenにdeprecated: trueを設定します。IDEで警告が表示されますが、コンパイルエラーにはなりません。これにより、新規コードでは新しいtokenが使われるようになります。

Cursor上でdeprecatedの警告が表示されたときの様子

// panda.config.ts
export default defineConfig({
  theme: {
    tokens: {
      colors: {
        green: {
          500: {
            value: "#00A724",
            deprecated: true, // IDEで警告表示
          },
        },
      },
    },
  },
});

@pandacss/no-deprecated-tokensルールを有効にすれば、ESLintでエラーにすることも可能です。

2. マイグレーションツールで一括置換

次に、マッピングファイルを作成し、自社開発のマイグレーションツールで一括置換します。

// マッピングファイル(旧token名 → 新token名)
{
  "mappings": {
    "green.500": "theme.accent.brand.50",
    "neutral.100": "theme.neutral.10",
    "white": "theme.neutral.0"
  }
}

このツールのポイントは、単純な文字列置換ではなく「Panda CSSのコード範囲内のみ」を置換する点です。

ツールの内部では、Panda CSSが提供するpanda debugコマンドを自動実行しています。このコマンドは、プロジェクト内の全ファイルを解析し、Panda CSSの使用状況をJSON形式で出力します。

$ pnpm exec panda debug
🐼 info [cli] Writing styled-system/debug/src__app__components__Modal.ast.json
🐼 info [cli] Writing styled-system/debug/src__app__components__Button.ast.json
...
🐼 info [cli] Found 192/664 files using Panda

出力されるJSONには、各ファイルのどの行・列でどのtokenが使われているかという詳細な位置情報が含まれています。

// panda debugの出力例
{
  "css": [
    {
      "box": {
        "node": "CallExpression",
        "line": 100,
        "column": 20,
        "endLine": 103,
        "endColumn": 1
      },
      "data": [
        {
          "backgroundColor": "green.500" // 置換対象
        }
      ]
    }
  ]
}

マイグレーションツールは、このJSON情報とマッピングファイルを組み合わせて置換を実行します。css()cva()の呼び出し範囲内にあるtokenだけを置換するため、コメントや変数名に偶然含まれる同じ文字列は置換されません。

// マイグレーションツールの置換処理
for (const mapping of mappings) {
  // mappingからcss() などの位置情報を取り出す
  // line/column から endLine/endColumn にあるtoken文字列を置換していく
}

実行すると、次のように置換結果が表示されます。

$ node panda-token-migrator --mapping new-color-map.json

Modified 2 files:

  src/app/components/Modal.tsx
    - green.500 → theme.accent.brand.50 (3 replacements)

  src/app/components/Button.tsx
    - neutral.100 → theme.neutral.10 (1 replacement)

このアプローチの利点は、静的解析によって正確なファイルパスと行番号を把握できるため、通常の文字列置換よりも精度が高い点です。実際のPanda CSSのコードのみが対象となり、偶然Tokenの値と同じ文字列をコード上で使っているケースは除外されます。--dry-runオプションで事前に影響範囲を確認できるため、数百箇所の変更でも安心して実行できます。

3. 型エラーで修正漏れを検出

マイグレーションツールで対応できない箇所があったとしても、deprecated tokenを削除した時点で型エラーとして検出されます。

Cursor上で型エラーが検出されている状態

deprecated flag、マイグレーションツール、型チェックの三段構えのアプローチにより、数百箇所の変更を伴う大規模なtoken変更でも、修正漏れなく安全に実施できています。TypeScriptの型チェックがセーフティネットとして機能することが、この仕組みの肝です。

まとめ

newmoでPanda CSSを採用した理由は、Design Tokenの型チェックができる唯一の選択肢だったからです。TypeScriptの型システムと組み合わせることで、Design Tokenの使用を強制し、レビューで毎回確認する必要がなくなりました。Figmaからの自動変換やマイグレーションツールなどの仕組みを整えることで、運用面でもスムーズに使えています。

特に重要なのは、Design Tokenが変化していくことを前提にした運用ができる点です。創業期のスタートアップでは、Design Tokenは完成品ではなく日々変化していくものです。そうした環境でも安全にtokenの変更に追従できる仕組みを構築できました。この仕組みがなければ、Design Tokenを継続的に改善していくことは困難だったと思います。

一方で、Panda CSSはTailwind CSSほど知名度が高くないため、参考事例やAIの学習データが限られる点には注意が必要です。公式ドキュメントは充実しているものの、エディタ連携が不安定になることもあります。

Design Tokenを活用したデザインシステムの構築を考えている場合、特にtokenが変化していくことを前提にしたプロジェクトでは、Panda CSSは選択肢の一つとして検討する価値があります。

newmo Engineering Advent Calendar 2025 written by @tacamy