Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Your design tokens stop at the web boundary

2026年10月06日に「Dev.to」が公開したITニュース「Your design tokens stop at the web boundary」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

複数プラットフォームでデザイン値を共有する際、手動入力による不整合が頻発する。この解決策は、デザイン値を唯一の源とし、各プラットフォームへ自動生成する仕組みだ。用途を示すセマンティックトークン活用、既存UIとの連携で導入障壁を下げ、生成ファイルの手動編集を検出する仕組みで、デザインの一貫性を保つ。

出典: Your design tokens stop at the web boundary | Dev.to公開日:

ITニュース解説

システム開発では、Webサイトやモバイルアプリ(iOS、Android、Flutterなど)のように、複数の異なる環境で動くアプリケーションを同時に開発する機会が多く存在する。このようなマルチプラットフォーム開発において、各アプリケーションのデザインが統一されていることは非常に重要である。この統一されたデザインを実現するために、「デザインシステム」というアプローチが採用される。デザインシステムでは、色、フォント、余白といったデザインに関する基本的な要素を「デザイン・トークン」として定義し、それらを一元的に管理することを目指す。例えば、Webアプリケーションで使う青色の定義をJSONファイルに記述し、そこからCSS(スタイルシート)を生成してWebコンポーネントがその色を参照するようにする。これにより、すべてのWebコンポーネントが同じ青色を参照し、デザインの一貫性が保たれる。

しかし、このデザイン・トークンの管理はWebの境界で止まってしまうことが多い。Webアプリケーションのデザイン・トークンはうまく機能するものの、同じデザインをiOSやAndroidのアプリにも適用しようとすると問題が発生する。多くのデザインシステムでは、Webで定義した色や余白の値を、iOSアプリのSwiftコードやAndroidアプリのKotlinコードに手作業で入力し直してしまう。これは一見、仕方がない選択に見えるが、これが後の大きな問題の根源となる。なぜなら、Webのデザイン・トークンの値が変更されたとしても、手作業で入力されたiOSやAndroidのコードにはその変更が自動的に反映されないからである。この二つの値は、かつて人間が一度だけ同じにしたという事実によってのみ関連づけられているに過ぎない。そのため、ある週は一致していた値が、次の四半期には食い違ってしまうという事態が頻繁に起こる。この食い違いの原因となった変更は、各プラットフォームで個別に、それぞれが正しいと判断された変更が複数回行われた結果であり、特定のコミット(変更履歴)で全体としてズレが生じたことを特定することは困難である。この問題は、「もっと注意深く作業すべきだ」といった精神論で解決できるものではなく、仕組みとして対応する必要がある構造的な問題である。人間が常にすべてのプラットフォームの値をチェックし続けることは、現実的ではないため、必ずどこかで不整合が生じてしまう。

本当に複数のプラットフォームで「単一の情報源」としてデザイン・トークンを機能させるためには、いくつかの条件を満たす必要がある。第一に、すべてのプラットフォームで使われる値は、その単一の情報源から「生成」されるべきであり、決して「コピー」されてはならない。具体的には、デザイン・トークンの定義ファイルから、各プラットフォームが利用できる形式のファイルが自動的なビルドプロセスによって生成される必要がある。そして、これらの生成されたファイルは、開発者が手作業で編集しないようにするべきである。第二に、生成されたファイルは可視化されるべきである。これは、Gitなどのバージョン管理システムにコミットされ、プルリクエスト(変更提案)のレビュープロセスで、デザイン・トークンの変更が複数のプラットフォームのファイルに一貫して反映されていることを確認できるようにすることを意味する。一つのトークンを変更した際に、それに対応するWeb、iOS、Android、Flutterの各ファイルが同時に変更される様子が、レビューで明確に確認できる状態が理想である。第三に、生成されたファイルを誤って手動で編集してしまった場合に、そのエラーが明確に検出される仕組みが必要である。緊急のバグ修正などの際に、手動で値を直接書き換えてしまうと、せっかく導入した自動生成の仕組みが台無しになる可能性があるため、そのような手動編集を防ぐ、あるいは検出する仕組みが不可欠となる。特にこの第三の条件は、デザインシステムが納期という現実的なプレッシャーの中で生き残れるかどうかを決定する重要な要素である。

デザイン・トークンの形式自体は、実はそれほど重要ではないことが多いが、標準的な形式を持つことは非常に有効である。「Design Tokens Community Group (DTCG)」が提唱する形式は、トークンが$typeや$valueを持つJSON構造で定義され、グループ化も可能な標準的な方法を提供する。この標準形式の利点は、型付けされた機械可読な構造であること、そして「Style Dictionary」のようなツールがこの形式を理解し、さまざまなプラットフォーム向けの出力(CSS、Swift、Kotlinなど)に変換するエコシステムが既に存在することである。つまり、どのような形式であるかよりも、ツールが処理できる一貫した形式で定義されていることが重要だ。

DTCG形式を単に利用して、blue-500のような基本的な色を各プラットフォームに生成するだけでは、真のデザインシステムとは言えない。これは単なる「ビルドツール付きのカラーパレット」に過ぎない。本当に重要なのは、その上に「セマンティック(意味のある)な層」を構築することである。例えば、プリミティブなトークンとしてcolor.azure.700 = #1d4ed8のような具体的な色を定義する一方で、セマンティックなトークンとしてcolor.primary = {color.azure.700}やcolor.ring = {color.azure.700}のように、その色が持つ意味で名前を付ける。これにより、例えばcolor.primaryがcolor.azure.700と同じ値を持つ場合でも、将来的にcolor.primaryだけ別の色に変更する必要が生じた際に、そのセマンティックな定義だけを変更すればよく、コード全体でその色を使っている箇所を探して一つずつ修正するような大変な作業が不要になる。コンポーネントは常にセマンティックなトークンを参照し、プリミティブなトークンはセマンティックな層の実装詳細として扱われる。この区別ができているかの実用的なテストは、「この色は何のためにあるのか?」という問いに、トークン名だけで答えられるかどうかである。もし「それは青だ」としか答えられないなら、それはまだカラーパレットに過ぎない。

Web以外のプラットフォームへのデザイン・トークンの生成は、見た目よりも手間がかからない。Style Dictionaryのようなツールは、デザイン・トークンを解析し、各プラットフォームの要件に合わせた「変換(transform)」を適用し、最終的に「フォーマット(format)」に従って出力ファイルを生成する。例えば、Webでは16pxと書かれた値が、iOSでは16.0(CGFloat型)、Androidでは16.dp(ComposeのDp型)、Flutterでは16.0(Dartのdouble型)のように自動的に変換される。出力ファイルの形式も、WebではCSSカスタムプロパティ、iOSではSwiftのenumやstruct、AndroidではKotlinのobject、FlutterではDartのクラスといった具合に、プラットフォームの慣習に合わせた形になる。

特にダークモードのようなテーマ対応は注意が必要である。Webでは、ライトモードとダークモードで異なる値のセットを用意し、コンポーネントは一つの「契約」(例:--color-surface)を参照することで、現在のテーマに応じて適切な色を自動的に適用できる。この考え方をネイティブアプリにも持ち込む必要がある。つまり、KinetixColors.blueLightやKinetixColors.blueDarkのように直接テーマを指定するのではなく、現在のカラーテーマに基づいて解決されるようなセマンティックなアクセサー(値を取得するための仕組み)を用意する。これにより、ビューのコードはテーマによらず同じ記述で済み、コンポーネントがテーマを意識して分岐する必要がなくなる。もし生成されたネイティブの出力が、すべてのビューにテーマごとの分岐を強制するようなものであれば、技術的には境界を越えたとしても、実用上はまだ十分とは言えない。

デザイン・トークンを導入する上で、ほとんどの人が見落としがちな、しかし非常に有用な点は、トークンを「単独で採用可能(adoptable)」にすることである。これは、既存のアプリケーションにデザインシステムを導入する際、新しいUIウィジェット(コンポーネント)を丸ごと置き換えることなく、デザインの基本値(色、余白、タイポグラフィなど)だけを導入できるようにするという考え方である。例えば、Flutterアプリケーションにおいて、既存のウィジェットを維持したまま、デザインシステムの色やフォントを適用したいという要望があったとする。このような場合、生成されたデザイン・トークンをFlutterの標準的なテーマ機能(ThemeData)に変換するアダプターを提供することで、既存のElevatedButtonのような標準ウィジェットが、デザインシステムで定義された適切な色や角丸、文字サイズを自動的に継承するようになる。これにより、アプリケーションのコードにカスタムウィジェットを一つも追加することなく、デザインシステムを部分的に導入することが可能になる。このアプローチは、AndroidのCompose MaterialThemeラッパーやiOSの環境注入型テーマなど、他のプラットフォームにも同様に適用できる。この戦略的な利点は、デザインシステムの導入における最初の一歩のハードルを劇的に下げることである。全面的な移行ではなく、既存のプロジェクトに数日で導入できる規模の作業となり、実際にズレが生じやすい「値」の統一をまず解決できる。

デザインシステムを健全に保つためには、開始時から導入すべき二つのチェックがある。一つは「契約テスト」である。これは、定義したすべてのセマンティック・トークンが、各プラットフォームの生成出力に存在するかを確認するテストである。値が完全に一致するかどうかではなく(プラットフォーム固有の変換によって値は変化し得るため)、トークン名がすべてのプラットフォームで一貫して存在するかをチェックする。これにより、誰かがWeb向けに新しいセマンティック・トークンを追加したが、ネイティブプラットフォームのビルドプロセスにその変更が組み込まれていなかった、といった見落としを防ぐことができる。もう一つは「手動編集禁止チェック」である。これは、CI/CDパイプライン(継続的インテグレーション/継続的デリバリー)において、生成されたファイルを手動で変更した場合にビルドが失敗するようにする、あるいは生成されたファイルを新たに生成し直したものと比較して差分がないかを確認するものである。これにより、緊急のホットフィックスなどで誤って生成ファイルを直接編集してしまう事態からシステムを守る。これらのチェックは、バージョン管理システムへのプルリクエストごとに実行され、デザインシステムの一貫性と堅牢性を保証するために非常に有効である。

しかし、デザイン・トークンにも限界がある。デザイン・トークンはあくまでデザインの基本的な値を定義するものであり、UIコンポーネントそのものではない。例えば、WebのボタンとiOSのボタンが同じ間隔尺度(spacing scale)を持つようになったとしても、それぞれのプラットフォームのUI要素の振る舞いや操作感が完全に一致するわけではない。また、デザイン・トークンは、誰がセマンティック・トークンを追加できるのか、特定のプラットフォームが他のプラットフォームには不要な固有の値を必要とする場合にどう対処するか、古いトークンをどのように廃止するかといった、より複雑なガバナンス(統治)に関する問題は解決できない。これらは人間関係や組織のプロセスに関わる問題であり、技術的なパイプラインだけでは解決できない。デザイン・トークンのパイプラインが解決するのは、どのプラットフォームのファイルに書かれた数値がWebのそれと同じであるか、といった、本来人間が判断する必要のない、単純な整合性の問題を取り除くことである。これにより、開発者は人間的な判断が必要な、より本質的な問題に集中できるようになる。

関連コンテンツ

関連IT用語