~/hayashigoto/logs/design-harness-as-code

>cat/logs/design-harness-as-code.md

title: デザインをコードで渡す。「Design Harness」という考え方
published:
topics: Design HarnessAI AgentFrontendShimon

私は、AIエージェントにデザインを渡す一次資料として、動く実装とそのコードを使うことにした。 この作業環境をDesign Harness呼んでいる。

design.mdデザイントークンには、色、余白、書体、避ける表現を書ける。 しかし、要素同士の緊張感、動き出す前の間、ひとつだけ大きく置く判断、整然とした構造を崩す位置までは固定しにくい。 私がデザインを見て好きだと思う部分は、個々の値よりも要素同士の関係に宿っていることが多い。

そこで、好きな判断を含むデザインを先にコードで表現し、その実装をAIエージェントが観察して別の画面へ移せる状態にした。 cookie-demo は、この考え方を試す最初のDesign Harnessです。

文章と実装の役割

文章でデザインを説明すると、書き手が観察できた特徴だけが残る。 「黒い背景」「Y2K」「粒子感」「大きなタイポグラフィ」と書けても、その記述から生成した画面は特徴を表面的に足し合わせたものになりやすい。

原因のひとつは、モデルの理解力ではなく、入力がすでに圧縮されていることにある。 文章を書いた時点で、私がまだ説明できていない判断は落ちている。

Design Harnessでは、参照する証拠に次の優先順位を付けた。

  1. ブラウザで動く完成状態
  2. 完成状態を作っているソースコード
  3. 別の画面へ移す判断軸を記した design.md
  4. AIエージェントの作業手順を記した SKILL.md
  5. 選んだ不変条件を記録する shimon.config.mjs

文章は、実装のどこを見るか、何をそのままコピーしないかを示す案内図になる。 最も情報量の多い参照は、動く実装とコードのまま残す。

テンプレートとDesign Harnessの違い

cookie-demo は、NewJeans「Cookie」の約7秒間をCanvas 2DとCSSで描き直したスクロール駆動のリールです。 巨大なcookie、散らばるcrumb、CDケース、放送画面のような文字、手描きの線が、スクロールに合わせて場面を受け渡していく。

これを部品集として扱えば、次に作るものにもcookie、CD、青い背景、Y2K風の文字が現れる。 それはモチーフの複製であって、Design Harnessが移したい判断ではない。

別の画面へ持ち出す対象は、次のような関係です。

  • 動きに接近、衝突、解放、提示という明確なbeatがある
  • 各状態で支配的な物体をひと決め、他の情報を競合させない
  • 放送画面の精密さと手描きの乱れを、役割の違う要素として衝突させる
  • 素材の見え方と運動を分離せず、蝶番なら回転し、crumbなら散るという因果を持たせる
  • scrollを効果の発火装置ではなく、構成の中を移動するtransportとして扱う

別の画面では、媒体、配色、書体、密度、空間の作り方を変えてよい。 見た目が似ていなくても、階層、リズム、素材の論理が残っていれば、同じ参照から生まれた提案として評価できる。

AIエージェントの解釈を残す条件

AIエージェントに解釈を任せると、元の実装では考えていなかった読み替えが出る。 通常の再現作業なら差分として戻すところだが、デザインではそのずれが次の資産になる可能性がある。

ただし、「予想外なら何でもよい」という基準にしない。 焦点が分散している、動きが因果を持たない、装飾が素材と無関係なら、構成を見直す。 元とは違う見た目でも、画面全体の階層とリズムが一貫しているなら、すぐに元へ戻さず候補として残す。

Design Harnessは正解を一つに固定する規格ではない。 解釈できる範囲と、その評価に使える証拠を増やすための作業環境です。

視覚的不変条件を観測するShimon

解釈を許すことと、意図しない回帰を放置することは別です。 cookie-demoCanvas、3D transform、blend modeを含むため、この環境ではスクリーンショットのpixel差分がGPU由来の揺れも拾ってしまった。

そこで、画面から選んだ観測値をJSONとして保存するCLIを作り、Shimon名付けた。 プロジェクト側は、再現する状態と観測値をJavaScriptで定義する。

export default {
  target: {
    url: "http://127.0.0.1:4322/",
    viewport: { width: 1600, height: 1000 },
  },
  cases: [
    { name: "start" },
    {
      name: "handoff",
      prepare: (page) =>
        page.evaluate(() => scrollTo(0, document.body.scrollHeight * 0.3)),
    },
  ],
  probe(page) {
    return page.evaluate(() => ({
      timecode: document.querySelector("#tc")?.textContent,
      caseTransform: document.querySelector("#case")?.style.transform,
    }));
  },
};

現在のハーネスは、静止状態と14のスクロール地点を再現する。 各状態でCanvasの内容、47個のcrumb、主要要素の矩形とtransform、描画用CSS変数、背景を観測している。

npx shimon selftest --json
npx shimon capture before --json
# 実装を変更する
npx shimon capture after --json
npx shimon diff before after --json

selftest同じ条件で2回取得し、ハーネス自身が安定しているかを先に確認する。 diff示すのは、選んだ観測値が変わったという事実だけです。 変化の良し悪しは判定しない。

リファクタリングなら、観測値が変わらないことを期待する。 意図した再設計なら差分を読み、どれを新しい基準として受け入れるかを人が判断する。 Shimonに美しさを判定させないことで、デザインの評価と回帰検証を分けている。

実際の作業手順

Design Harnessを使う作業は、次の順序にしている。

  1. 完成状態を最初から最後まで往復して見る
  2. 移したい関係に関わるソースを読む
  3. 何を移し、何を変えるかを短く宣言する
  4. 一貫した提案を作り、想定外の解釈も候補として残す
  5. 人が構成と質を判断する
  6. Shimonで既知の挙動差分を確認する

fingerprintを通すこと自体は目的にしない。 検査対象を削ってgreenにしても、デザインの判断は改善しない。 差分が意図したものなら理由を記録し、基準側を更新する。

現時点の限界

Design Harnessは、説明できない判断を完全に保存する仕組みではない。 参照する実装自体が弱ければ、AIエージェントが読める情報も弱い。 どの不変条件を観測するかにも人の判断が入る。

ひとつの実装を強い参照にすると、その癖へ引っ張られる危険もある。 今後は、異なる媒体で生まれた複数の解釈を残し、参照同士の距離を広げたい。

それでも、最初からデザインを文章だけに圧縮するより、動くコード、判断軸、作業手順、観測可能な不変条件を一緒に渡すほうが、モデルの解釈力を使える余地は大きい。 Design Harnessは完成した規格ではなく、自分の好みとAIエージェントの解釈を次の制作へ持ち越す作業環境として育てています。