ultrahand: テーマ
プロジェクトのテーマを一度構築し、それ以降も成長させ続けます。テーマはデザイントークンファイルとその決定事項の短い記録であり、プロジェクト内に保持されるため、それ以降のすべてのデザインとレビューは毎回新しい外観から始めるのではなく、同じ基盤の上に構築されます。ファイル形式は shared/theme/README.md に、ルールは shared/theme/rules.md にあり、各ルールは shared/theme/sources.md を引用しています。
すべてのテーマは非交渉項目(NN-1 から NN-12: 階層、8ポイントグリッド、両方のカラースキーム、コントラスト、実際のタイプスケールなど)を維持し、すべての創造的軸(CR-1 から CR-10: 色相、ニュートラル、タイプの印象、角、奥行き、密度など)について新しい選択を行います。ルールは固定されているため、すべてのテーマはよく作られたものに感じられます。軸はオープンであるため、同じように見える製品は2つとありません。開始する前に rules.md をお読みください。
以下のスクリプトは ${CLAUDE_PLUGIN_ROOT}/shared/theme/ にあります。
1. 既存のものを探す
質問する前に確認してください。キャンバスのアーティファクトとしてのみ存在するデザインの場合は、アーティファクトのファイルをリストアップし、そこから design/ を読み込んでください。プロジェクト内で、design/THEME.md、*.tokens.json、tokens/、Style Dictionary の設定、およびプラットフォームのテーマコード(CSS カスタムプロパティ、tailwind.config.*、Flutter の ThemeData / ColorScheme、SwiftUI のカラーアセット、Compose の MaterialTheme)を検索してください。結果によってモードが決まります:
| 見つかったもの | モード |
|---|---|
| テーマファイルと THEME.md | 進化(ステップ4)。絶対に再生成しないでください。 |
| テーマファイルはないが、スタイルが適用されたコード | 抽出(ステップ2)、その後進化。 |
| 何もない(新製品) | 作成(ステップ3)。 |
2. コードがすでに持っているテーマを抽出する
目標は、現在の外観を整理してキャプチャすることであり、再デザインすることではありません。
node scan.mjs <src dir>を実行します(完全なリストを表示するには--jsonを追加)。これにより、すべての色、スペーシング値、角丸、フォントサイズがカウントと場所とともにリスト化され、ほぼ重複した色をグループ化し、グリッドから外れたスペーシングにフラグを立てます。プラットフォームのテーマコードも読んでください。テーマオブジェクトが名前を付けている値が意図された値であり、散在する生の値は逸脱です。- ロールに名前を付ける。 各色のクラスターについて、それが何に使用されているか(ページ背景、本文テキスト、ボタン、境界線、エラー)を特定し、それをセマンティックロール(README.md に必要なロールがリストされています)にマッピングします。1つのロールに1つの値: 2つのほぼ重複した値が1つのロールを提供している場合、より使用頻度の高い方を保持します。
- ルールにスナップする。 スペーシングを NN-2 スケールに、サイズをタイプスケール(NN-6)に、半径を短いスケール(NN-7)に折りたたみます。同じ色相のトーンステップに沿って移動することでコントラストの問題を修正します(NN-4)。各変更は製品への目に見える変更であるため、承認のために所有者にテーブル(
13px → 12px, 4箇所)としてリストし、所有者が変更を望まないものはそのままにしておきます。 - ギャップを埋める。 ほとんどのコードにはダークモード、フォーカスリング、
on-ロールがありません。汎用的なものを追加するのではなく、テーマ自身のパレット(NN-3、NN-10)でこれらをデザインします。 - コンポーネントを棚卸しする。 再利用可能なコンポーネント(
components/、ui/、widgets/、views/を確認)、そのバリアント、および各コンポーネントが定義されている場所をリストアップします。2つの異なるコンポーネントで同じ作業が行われている場合は注意してください。それはテーマにコピーするものではなく、統合候補(EV-4)です。 - それがすでに持っている個性を記録する。 既存の外観を各創造的軸に配置し、THEME.md に書き込みます。ブランドから与えられた選択(ロゴの色相、ライセンスされたフォント)は
locked、残りはopenです。
次にファイルを書き込みます(ステップ5)。
3. 新しいテーマを作成する
- 個性。 3つの形容詞とそれが誰のためのものかを尋ね、さらに固定されているもの(ブランドカラー、ロゴ、フォント、プラットフォーム(ネイティブ iOS または Android アプリはシステムフォントと色に依存します。テーマはそれらにマッピングされます [HIG-COLOR] [M3-COMPOSE]))があればそれも尋ねます。
- 3つの方向性。 少なくとも3つの創造的軸で異なる3つの方向性を提案します。各方向性には形容詞に関連付けられた1行の理由を付けます。rules.md が説明するデフォルトの外観は、所有者がそれを望まない限り避けてください。それらを並べて表示し(ステップ6)、所有者に1つを選んでもらうか、それらを混ぜ合わせてもらいます。
- 構築する。
- パレット: 各キーカラー(プライマリ、ニュートラル、エラー、および任意のアクセント)について、暗いものから明るいものへの約10~13ステップのトーンスケール [M3-HCT] [M3-COMPOSE]。これにより、すべてのロールが両方のカラースキームでコントラストを満たすステップを見つけられます。
- セマンティックカラー: すべての必須ロール、それぞれにダーク値、各
on-XはXに対してデザインされています。 - スペース: NN-2 スケール、次にジョブによるセマンティックスペーシング: パディング用の
insetステップ、およびgap-related、gap-group、gap-section。これにより、グループ間のギャップがグループ内のギャップよりも明確に大きいという構成ルール(shared/composition/principles.md の SEC-2)が組み込まれます。 - タイポグラフィ: 選択された比率(CR-5)からの1つのスケールで、ロールは display、headline、title、body、label。body はプラットフォームのデフォルト(ウェブでは16、iOS では17 pt、高密度デスクトップツールの場合は13~14のみ。NN-6 を参照)。
- シェイプ、奥行き、モーション、フォーカス: 選択されたコーナースタイルでの半径スケール、選択された奥行き方法での Elevation レベル、持続時間とイージングトークン、2 px 以上のフォーカスリング。
- コンポーネント: コンポーネントがそのロールと異なる必要がある場合のコンポーネントトークンのみ(EV-2)。ほとんどのコンポーネントは何も必要としません。
すべての値をバージョン 1.0.0 で開始します。
4. 既存のテーマを進化させる
「警告色を追加する」「より密度の高いテーブルが必要」「より暖かく感じさせる」などのリクエスト、またはデザインスキルや theme-review が追加を提案する場合:
- まず THEME.md とトークンを読んでください(EV-1)。
lockedされた決定を尊重します。リクエストがそれを変更する必要がある場合は、その旨を伝えて尋ねてください。 - 適合する既存のロールがあればそれを再利用します(EV-2)。実際のギャップがある場合のみトークンを追加し、既存のプリミティブステップにエイリアスします(EV-3)。
- 広範な変更(「より暖かく」「より遊び心のある」)は
openの創造的軸を動かします。プリミティブをシフトし、すべてのセマンティック名を維持し、コントラストチェックを再実行します。名前はリブランドを通じて真実であり続けます(NN-11)。 - 置き換えを指定した
$deprecatedで廃止します。後のバージョンで削除します(EV-6)。 - バージョンを上げ、何が変更され、どの画面で必要だったかを示すチェンジログ行を追加します(EV-5)。
5. 書き込みとチェック
- README.md に従って
design/theme.tokens.json(またはプロジェクトの既存のトークンロケーション)とdesign/THEME.mdを書き込みます。example.tokens.json が構造を示しています。 node check.mjs design/theme.tokens.jsonを実行し、すべてのエラーを修正します。警告は修正するか、THEME.md に理由を記載する必要があります。- コードに引き渡す。 ウェブの場合、
node css.mjs design/theme.tokens.json --out <path>/theme.cssにより、ダークモードが組み込まれた CSS カスタムプロパティが生成されます。他のプラットフォーム、または複数のプラットフォームを一度に扱う場合は、この形式を読み取って Swift、Android、Compose、Flutter を出力する Style Dictionary をお勧めします [STYLE-DICT]。スキャンで見つかった生の値をトークンに置き換えることを提案しますが、すべてのスタイルファイルに影響するため、確認なしに行わないでください。
6. テーマを表示する
プラグインキャンバス(shared/canvas/、その README に契約が記載されています)に見本を構築し、所有者がテーマの動作を確認できるようにします(16進数のリストではありません):
node "${CLAUDE_PLUGIN_ROOT}/shared/theme/css.mjs" design/theme.tokens.json --style --out theme.html
node "${CLAUDE_PLUGIN_ROOT}/shared/canvas/build.mjs" \
--presets "${CLAUDE_PLUGIN_ROOT}/skills/desktop/references/presets.md" \
--head theme.html --screen screen.html --title "<プロダクト名> テーマ" --out index.html
見本の screen.html はテーマのカスタムプロパティのみを使用し、以下を表示します: X 上の on-X テキストとしてのカラーロール(測定されたコントラスト比付き)、実際のコピーを使用したタイプスケール、スペーシングスケール、半径と Elevation レベル、各棚卸しされたコンポーネントのステート別表示、およびそれから構築された1つの小さな現実的な画面(リストとフォーム)。キャンバスの Light / Dark および 200% テキストトグルは、両方のカラースキームとテキストスケーリングをテストします。テーマにウェブフォントが必要な場合は、その <link> を同じ head ファイルに配置します。
方向性の比較(ステップ3): 各方向性を --style なしで、--selector '[data-state="a"]'(次に b、c)でスコープして出力し、それらを画面テンプレート自身の <style> の先頭に貼り付けます。キャンバスは画面内で data-state を設定しますが、ページの head からは到達できません。方向性名をラベルとして config.json で states a、b、c を宣言すると、ステートピッカーがそれらを切り替えます。
index.html をアーティファクトとして公開します。デザイン自体がアーティファクトである場合は、テーマを組み込んで(--head theme.html)再公開し、2つの design/ ファイルを含めます。
7. プレゼンテーション
アーティファクトリンクを共有します。数行で: モード(作成、抽出、または進化)、バージョン、個性が創造的軸のどこに位置するかとその理由、何がロックされていて何がオープンか、承認が必要なスナッピング変更、チェック結果、そして次のステップ: それを使ってデザインする(mobile、tablet、desktop スキルがそれを読み取ります)、または theme-review でコードを監査する。