← Learn

コンポーネント契約とは

プロップの型は「何を受け取るか」を言う。契約は「何を絶対にしてはいけないか」を——AIエージェントが読める場所に——書き記す。

約6分

コンポーネント契約とは、ひとつのUIコンポーネントについての合意された仕様——入力(プロップ)、状態、使ってよいデザイントークン、アクセシビリティ要件、そして do / don't——を、人間にもAIコーディングエージェントにも読める形で書いたものである。

見た目だけでなく、どの実装も「越えてはいけない境界」を定義する。契約があれば、人でも別チームでもAIエージェントでも、デザインシステムを黙って破ることなくコンポーネントを再実装・拡張できる。ルールが「記憶」ではなく「明文」になっているからだ。

プロップの型でもなく、Storybookのstoryでもない

あなたは既に ButtonProps のTypeScript型や、全バリアントを描くstoryを持っているかもしれない。どちらも有用だが、どちらも契約ではない。プロップの型は「何を受け取るか」を言い、storyは「どう見えうるか」を見せる。契約は、そのどちらも運ばないものを足す——越えてはいけない境界だ。「primaryの背景はaccentトークン以外にしない」「このコンポーネントの状態はこの4つだけで、それ以外は無い」「フォーカスは常に見える」。

この発想はAPI契約から借りている。そこでは実装ではなく合意こそが、全員がそれに向けてコードを書く対象だ。コンポーネント契約は、その規律をUIの表層に持ち込む。

なぜ暗黙の契約はAIの下で壊れるか

再利用されるコンポーネントには、すでに契約がある。たいていそれは、デザイナーの頭・レビュアーの判断・ドキュメントの一段落に宿っている——記憶が運ぶ暗黙の契約だ。それは、コンポーネントを読むのが「その場にいた人」である限りは機能する。

AIエージェントは、その場にいなかった。Claude Code や Cursor がコンポーネントを再生成するとき、どのトークンが許可され、どの状態が正当で、どのアクセシビリティ規則が譲れないのかを知らない。プロップの型は読める——だが「color.signal をボタン背景に使うな」は、型に現れない設計上の禁止だ。だからエージェントは、無かったはずの5つ目の状態を発明し、近く見える色に手を伸ばす。その違反がコンポーネントを跨いで積み重なった状態が、近くで見たデザイントークン・ドリフトの姿だ。

契約を実在させる

契約は、コンポーネントを作る全員が同じものを読んでこそ機能する——レビューの人間も、深夜2時のエージェントも。実在させるのは3つだ。

  • 各コンポーネントの 許可トークン・状態・a11y規則・do/don't を明文化する——プロップだけでなく。
  • 人間とエージェントが同じ場所から読む 機械可読な単一ソース に置く。
  • 実装を契約に照らして検証する——不正なプロップだけでなく 境界を越えたトークンや状態 を指摘する。
一つの契約、複数の読み手。PRをレビューする人間も、コンポーネントを書くエージェントも、同じ明示の境界を参照する。

Harnd では、各コンポーネント仕様が安定ID(cmp_)を持つ契約になっている。だから人間とエージェントは同じ合意に向けて実装し、境界を越えた瞬間は運ではなく品質ゲートが捉える。

デザインシステムの単位は「契約」だ

デザインシステムの単位はコンポーネントだ、と思いがちだ。違う——単位は合意である。再利用されるのはコードではない。「そのコンポーネントが何であってよいか」という共有された理解だ。その合意が書き留められていなければ、コンポーネントライブラリは共有された誤解のコレクションにすぎず、コピーのたびに少しずつ間違いが増えていく。

型システムは、プログラムの契約を機械可読にした。コンポーネント契約は、デザイン上の決定に同じことをする——AIエージェントがあなたのUIの一級の実装者になった今、それが効いてくる。意図は、記憶ではなく契約として運ばれなければならない。トークン・契約・品質ゲートを一つのソースに保つ層全体については、デザインハーネスとは何かを読んでほしい。

コンポーネントに、エージェントが実際に読める契約を持たせたいなら——早期アクセスに登録

エージェントがUIを書いても、デザインの一貫性を保つ。