@cakejp/design-system 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ---
13
13
 
14
+ ## [0.9.0] - 2026-07-08
15
+
16
+ ### ✨ Added
17
+
18
+ - **EcPageShell**(primitives・新規): EC 標準ページの骨格。`contracts/composition.json` の pageSkeletons(min-h-dvh / flex-1 / SP の pb-20)を構造的に保証し、bottomTab を渡すと下余白をシェルが自動管理するため付け忘れ・二重がけを防ぐ。ロゴ・タブ項目は焼き込まず全て props で受ける
19
+ - **FunnelShell**(primitives・新規): EC 購入導線(カート→お届け先→支払い→確認)向けの集中モード。issue #37 MTG フィードバック「EC側にも Full Page(集中モード)があり、ヘッダーが変わる」の実体化。戻る+進捗インジケータの簡素ヘッダーを持ち、グロナビ・タブバー・パンくずを構造的に持たない離脱抑制の枠。ステップ定義は props で受ける
20
+ - **AdminSideNav / AdminHeader**(primitives・新規): AdminLayout 内で `MissingComponent` ラップのままだったサイドナビとヘッダーを正式コンポーネント化。項目データは全て props で受け、Cake.jp 固有のメニュー定義は ec の AdminLayout が既定値として持つ。公開 API は完全互換
21
+
22
+ ### 🛡 Internal
23
+
24
+ - **shell の層分け原則を確立**: primitives=汎用枠(データ全部 props)/ ec=Cake.jp 既定値 / pages=ストーリーのみ非配布、という3層の責務分担を EcPageShell・FunnelShell・AdminSideNav・AdminHeader の実装で徹底。`components.json` の `kind:"shell"` は5件(AdminLayout / WorkspaceLayout / MypageShell / EcPageShell / FunnelShell)に
25
+ - **composition.json**: pageSkeletons(mobile/desktop)に `implementation` フィールドを追加し EcPageShell がこの骨格の実装体であることを明記、FunnelShell の funnel エントリを新設
26
+ - **screen-patterns.json**: 各パターンの `dsComponentsNote` に AdminLayout / EcPageShell / MypageShell / WorkspaceLayout / FunnelShell のシェル使い分け基準を追記
27
+
28
+ ---
29
+
30
+ ## [0.8.0] - 2026-07-08
31
+
32
+ ### ✨ Added
33
+
34
+ - **contracts/screen-patterns.json**(新規): CRUD 機能でどの画面パターンを使うか AI・エンジニアが機械的に判断できるようにする契約ファイル。Standard Page(View / Edit)・Focused Workspace・ResponsiveDialog・Dialog の5パターンを定義し、CRUD 対応表と AI が辿れる6ステップ決定木を収録。npm 配布されるため消費側の codex/エンジニアにも届く。人間向け正本は `guidelines/foundations/screen-patterns.md`(issue #37 の型化)
35
+ - **contracts/composition.json**(新規): ページ骨格・余白リズム4レベル・Card/Text/CTA の階層関係を契約化。CTA「default は1画面1つ」ルールを正式ルールへ昇格
36
+ - **WorkspaceLayout**(primitives・新規): 全画面集中編集シェル。編集ヘッダーは左に戻る・右上に上位アクションのみを持ち、グロナビ/パンくずを構造的に持たない設計(既存 Header の `variant="back"` + `rightSlot` を再利用し、上位アクションは任意 prop で「ある場合のみ」を型で表現)。`components.json` では `kind:"shell"` に分類
37
+ - **SectionNav**(primitives・新規): ページ内セクション移動専用の目次型ナビ。サイドバー型の SideNav とはアフォーダンスが異なるため新設し、両者は相互排他ルールで使い分けを明示
38
+ - **components.json の `kind:"shell"`**: AdminLayout / WorkspaceLayout / MypageShell をシェル系コンポーネントとして分類
39
+ - **scripts/check-docs-drift.sh**(新規): guidelines md 内に書かれたコンポーネント名の実在性を検査。`npm run check` に組込み
40
+
41
+ ### 🐛 Fixed
42
+
43
+ - **ResponsiveDialog / ChartControls の export 漏れ**: `index.ts` からのエクスポートが欠落しており、消費側で import できなかった問題を修正
44
+ - **Card の `py-0` 残骸による余白ゼロ**: Storybook ストーリー2件で余白が意図せずゼロになっていた問題を修正
45
+ - **guidelines の架空コンポーネント名21箇所**: 実装に存在しないコンポーネント名(例: `TopAppBar` → 実際は `Header`)をドキュメント全体で実名に修正
46
+ - **responsive 違反2件**: `anniversary.stories.tsx` の SP専用実装(レスポンシブクラス0件)に `@md:` コンテナクエリで PC 対応を追加し、`favorites.stories.tsx` の `@container` 宣言漏れ(`@md:` が効いていなかった)を修正
47
+
48
+ ### 🛡 Internal
49
+
50
+ - **MypageShell を `pages/` から `ec/` へ移動**: `pages/` はストーリー専用・非配布ディレクトリとして純化
51
+ - **Header の重複登録と counts 不整合を修正**: `components.json` で primitives / ec 双方に登録されていた重複を解消
52
+ - **check-drift.sh に重複登録検出を追加**: `.foundations` / `.primitives` / `.ec` 全エントリの `name` / `path` の重複を検出し、重複時は該当ティアを明示して exit 1(過去に Header が primitives と ec に重複登録された事故の再発防止)(#39)
53
+ - **lint 負債211件を解消**: `npm run lint` の既存負債(170 errors / 41 warnings、issue #37 実装前から存在)を eslint.config.js のルール緩和なしで全解消。`no-unused-vars` / Storybook framework 移行 / 冗長 story name 削除 / Fast Refresh 例外の理由コメント化ほか (#40)
54
+ - **全 guidelines md に「正本は contracts」注記を追加**: ドキュメントと契約ファイルの二重管理による齟齬を防止
55
+
56
+ ---
57
+
14
58
  ## [0.7.0] - 2026-06-19
15
59
 
16
60
  ### ✨ Added
package/CLAUDE.md CHANGED
@@ -19,6 +19,8 @@ PdM や社長が AI(Claude 等)で HTML モックを作る際、このパッ
19
19
  |---------|------|----------|
20
20
  | **contracts/rules.json** | 禁止パターン・アクセシビリティ要件・ライティングルール | **コード生成前に必ず** |
21
21
  | **contracts/components.json** | 全コンポーネントの定義(variants・rules・accessibility) | コンポーネント利用・画面作成時 |
22
+ | **contracts/screen-patterns.json** | 画面パターン判断基準(CRUD・編集/作成/削除UIの選択) | 管理画面・編集/作成/削除UIを作る時 |
23
+ | **contracts/composition.json** | ページ骨格・余白リズム・視覚階層の組み方 | レイアウト・画面構成を組む時 |
22
24
  | **tokens.json** | カラー・タイポグラフィ・スペーシング・影・角丸 | トークン値を参照する時 |
23
25
  | **CLAUDE_PRODUCTION.md** | 本番実装参照ルール・Figmaチェックリスト・本番サイト対応表 | 本番再現・新規画面作成時 |
24
26
 
@@ -3,11 +3,12 @@
3
3
  "name": "Cake.jp Design System — Component Contracts",
4
4
  "version": "0.1.0",
5
5
  "description": "全コンポーネントの構造化定義。バリアント・アクセシビリティ要件・使用ルールを機械可読形式で管理。",
6
+ "kind": "kind:\"shell\" は複数ページで再利用されるレイアウト枠(AdminLayout / WorkspaceLayout / MypageShell 等)。無印は部品コンポーネント。固有ページを組むときは該当 shell から始める。src/components/pages/ 配下のストーリーは特定ページ専用・非配布のため contracts の対象外。",
6
7
  "counts": {
7
8
  "foundations": 47,
8
- "primitives": 36,
9
- "ec": 23,
10
- "total": 106
9
+ "primitives": 42,
10
+ "ec": 24,
11
+ "total": 113
11
12
  }
12
13
  },
13
14
  "foundations": [
@@ -440,7 +441,10 @@
440
441
  "name": "FormField",
441
442
  "path": "src/components/primitives/form-field.tsx",
442
443
  "variants": {
443
- "descriptionPosition": ["above", "below"]
444
+ "descriptionPosition": [
445
+ "above",
446
+ "below"
447
+ ]
444
448
  },
445
449
  "rules": [
446
450
  "Label+Input+エラーメッセージをセットで管理",
@@ -603,15 +607,117 @@
603
607
  {
604
608
  "name": "SideNav",
605
609
  "path": "src/components/primitives/side-nav.tsx",
606
- "category": "navigation"
610
+ "category": "navigation",
611
+ "rules": [
612
+ "ページ間遷移(別 URL へのナビゲーション)に使う。チェブロン付きで「別ページへ移動する」アフォーダンスを持つ",
613
+ "ページ内アンカー移動(href=\"#...\")に流用しない → SectionNav を使う(見た目と挙動の不一致を防ぐ)"
614
+ ]
615
+ },
616
+ {
617
+ "name": "SectionNav",
618
+ "path": "src/components/primitives/section-nav.tsx",
619
+ "category": "navigation",
620
+ "rules": [
621
+ "ページ内セクション移動(アンカー/目次)専用。href=\"#section-id\" のページ内スクロールに使う",
622
+ "ページ間遷移に使わない → SideNav を使う",
623
+ "チェブロン・矢印を足さない(遷移アフォーダンスを持たせない。同一画面内の位置移動に見せる)",
624
+ "WorkspaceLayout の sectionNav にはこれを使う(SideNav を流用しない)",
625
+ "固有幅・背景・境界を持たない(w-full)。パネル化・幅決定はシェル/利用側の責務"
626
+ ]
627
+ },
628
+ {
629
+ "name": "EcPageShell",
630
+ "path": "src/components/primitives/ec-page-shell.tsx",
631
+ "kind": "shell",
632
+ "variants": [],
633
+ "rules": [
634
+ "EC 標準ページ(Standard Page(View / Edit))の骨格。composition.json pageSkeletons(mobile / desktop)の実装体。min-h-dvh flex flex-col の全高コンテナに header / main / bottomTab / footer を積む",
635
+ "あくまで枠。Cake.jp のロゴ・タブ項目・ナビ項目・文言を焼き込まない。header / bottomTab は利用側(ec 層)が組み立てて渡す",
636
+ "bottomTab を渡すと main の下余白(pb-20 相当)はシェルが管理する。ページ側で pb-20 を書かない(付け忘れ/二重がけの構造的防止)",
637
+ "左右 padding は持たない(Container の責務)。中身は Section(全幅背景帯)+ Container(中央寄せ・左右余白)で組む",
638
+ "管理画面は AdminLayout、複数情報をまたぐ集中編集は WorkspaceLayout、マイページ配下は MypageShell を使う"
639
+ ]
640
+ },
641
+ {
642
+ "name": "WorkspaceLayout",
643
+ "path": "src/components/primitives/workspace-layout.tsx",
644
+ "kind": "shell",
645
+ "variants": [],
646
+ "rules": [
647
+ "Focused Workspace(全画面集中編集)のシェル。編集ヘッダー(Header variant=back)+ 任意のセクションナビ + 編集本体で構成",
648
+ "グローバルナビ・パンくずを持たない(構造的に置けない)。集中を妨げる導線は出さない",
649
+ "primaryActionはワークスペース全体の上位アクション(公開申請・承認依頼等)がある場合のみ。セクション/行単位の操作はコンテンツ内に置く",
650
+ "標準の編集はAdminLayoutのStandard Page(Edit)(旧Form Page)。複雑・複数情報をまたぐ集中編集・没入執筆のみWorkspaceLayoutを使う",
651
+ "sectionNavは編集内ナビ(アンカー/ステップ)のみ。PCでサイド表示・SPで非表示"
652
+ ]
653
+ },
654
+ {
655
+ "name": "FunnelShell",
656
+ "path": "src/components/primitives/funnel-shell.tsx",
657
+ "kind": "shell",
658
+ "variants": [],
659
+ "rules": [
660
+ "EC 購入導線等のファネル用集中モード(Full Page)のシェル。集中ヘッダー(Header variant=back)+進捗インジケータ(steps/activeStep)+ステップ本体+任意の bottomAction で構成。min-h-dvh flex flex-col の全高コンテナ",
661
+ "あくまで枠。ステップ定義(名前・数)・文言・商品データを焼き込まない。steps / activeStep は利用側(ドメイン)が渡す",
662
+ "グローバルナビ・BottomTabBar・パンくずを持たない(構造的に置けない)。離脱を誘う導線を出さず購入に集中させる",
663
+ "bottomAction(StickyActionBar 等)を渡すと本文の下余白はシェルが管理する。ページ側で pb を書かない(付け忘れ/二重がけの構造的防止)",
664
+ "左右 padding は持たない(Container の責務)。管理画面の複雑な集中編集は WorkspaceLayout を使う(EC 購入導線=FunnelShell)"
665
+ ]
666
+ },
667
+ {
668
+ "name": "Header",
669
+ "path": "src/components/primitives/header.tsx"
670
+ },
671
+ {
672
+ "name": "AdminSideNav",
673
+ "path": "src/components/primitives/admin-side-nav.tsx",
674
+ "category": "navigation",
675
+ "rules": [
676
+ "管理画面向けサイドナビの汎用枠。PC は左端固定アイコンレール(ホバーでサブメニュー)、SP は制御ドロワー(サブメニューはアコーディオン)",
677
+ "項目データ(メニュー定義・文言)は全て items prop で受ける。特定事業のメニューを部品側に焼き込まない(Cake.jp 既定は AdminLayout が持つ)",
678
+ "SP ドロワー開閉は open / onOpenChange で制御する(内部 state を持たない)",
679
+ "icon は ReactNode か描画コンテキスト(active / placement)で出し分ける関数を渡す"
680
+ ]
681
+ },
682
+ {
683
+ "name": "AdminHeader",
684
+ "path": "src/components/primitives/admin-header.tsx",
685
+ "rules": [
686
+ "管理画面向けの上端固定ヘッダーの汎用枠。左に SP ハンバーガー+brand、右に actions slot",
687
+ "文言合成(『{shopName}管理』等)はしない。brand / actions を利用側が組み立てて渡す",
688
+ "onMenuClick 省略時はハンバーガー非表示"
689
+ ]
607
690
  }
608
691
  ],
609
692
  "ec": [
610
693
  {
611
694
  "name": "AdminLayout",
612
695
  "path": "src/components/ec/admin-layout.tsx",
696
+ "kind": "shell",
613
697
  "rules": [
614
- "管理画面のレイアウトシェル"
698
+ "管理画面のレイアウトシェル。枠は primitives の AdminSideNav / AdminHeader を組み込む",
699
+ "Cake.jp 管理メニュー8項目+サブメニュー・通知ベル・プロフィール・『{shopName}管理』を既定値として内蔵する(ドメイン特化層)",
700
+ "navItems で既定メニューを差し替え可、headerActions でヘッダー右アクションを差し替え可"
701
+ ]
702
+ },
703
+ {
704
+ "name": "MypageShell",
705
+ "path": "src/components/ec/mypage-shell.tsx",
706
+ "kind": "shell",
707
+ "variants": [],
708
+ "features": [
709
+ "sp-header-back",
710
+ "pc-logo-header",
711
+ "pc-breadcrumb",
712
+ "pc-2column-sidenav",
713
+ "sp-bottom-tab-bar"
714
+ ],
715
+ "rules": [
716
+ "マイページ配下の固有ページ(注文履歴 / ポイント / クーポン / 会員情報 / お届け先 / ブックマーク / 口コミ / 記念日 等)はこの shell で組む",
717
+ "本番の mypage_layout.ctp + menu.ctp 準拠。SP は Header(variant=back)+h1+コンテンツ+BottomTabBar、PC は Header(variant=logo)+Breadcrumb+2カラム(SideNav 256px + main)",
718
+ "サイドナビは ec/_mypage-menu-data の mypageMenuGroups を使用。activeMenu で現在ページを強調する",
719
+ "breadcrumbs 省略時は「ホーム > {ユーザー}さまのマイページ > {title}」を自動生成する",
720
+ "マイページトップ(Dashboard)はメインカラムを白背景で持たずセクションごとにカード化する別パターンなので、この shell を使わない"
615
721
  ]
616
722
  },
617
723
  {
@@ -766,10 +872,6 @@
766
872
  "name": "StatCard",
767
873
  "path": "src/components/ec/stat-card.tsx"
768
874
  },
769
- {
770
- "name": "Header",
771
- "path": "src/components/primitives/header.tsx"
772
- },
773
875
  {
774
876
  "name": "ProductListItem",
775
877
  "path": "src/components/ec/product-list-item.tsx",
@@ -0,0 +1,135 @@
1
+ {
2
+ "meta": {
3
+ "name": "Cake.jp Design System — Composition Contracts",
4
+ "version": "0.1.0",
5
+ "description": "ページ骨格・視覚階層・余白リズムの機械可読な正本。画面パターンを選んだ後の「実際の組み方」を規定する。ページの外枠(モバイル/デスクトップ/管理)、商品グリッドの列数、余白の4段リズム、Surface のネスト階層、テキスト階層、CTA の優先順位を構造化する。人間向けの解説は guidelines/composition/layout.md・guidelines/composition/hierarchy.md・guidelines/overview.md。",
6
+ "source": "guidelines/composition/layout.md, guidelines/composition/hierarchy.md, guidelines/overview.md, guidelines/components/button.md(CTA 密度ルール)を正本化",
7
+ "roleSeparation": "screen-patterns.json はページ種別(Standard Page / Focused Workspace / ResponsiveDialog / Dialog)を選ぶための契約。composition.json は選んだ後、その骨格をどう組むか(外枠構造・余白リズム・階層)の契約。前者=何を使うか、後者=どう並べるか。",
8
+ "componentNameNote": "本契約のコンポーネント名は src/index.ts のエクスポート実名。guidelines の一部散文にある解説用の別名(TopAppBar / BottomNavigation / BottomAction 等)は実装上それぞれ Header / BottomTabBar / StickyActionBar に対応する。CategoryScroll・CategoryShortcuts は横スクロール/ショートカット群の実装コンポーネント。"
9
+ },
10
+ "pageSkeletons": {
11
+ "description": "ページの外枠。上から順の構成要素と、外枠に課される構造ルールを規定する。手組みの独自シェルを作らず、実名コンポーネントで組む。",
12
+ "mobile": {
13
+ "requiredComponents": ["Header", "BottomTabBar"],
14
+ "optionalComponents": ["StickyActionBar", "CategoryScroll", "CategoryShortcuts"],
15
+ "implementation": "EcPageShell(primitives, kind:\"shell\")がこの骨格の実装体。header に Header・bottomTab に BottomTabBar を渡す。bottomTab を渡すと main の下余白(pb-20 相当)をシェルが管理するので、ページ側で pb-20 を書かない。手組みの独自シェルを作らない。",
16
+ "structure": [
17
+ "ルート: div.flex.min-h-dvh.flex-col(縦フレックスの全高コンテナ。背景は bg-[var(--Surface-Primary)] を明示)",
18
+ "上部固定: Header(title / onBack。旧称 TopAppBar)",
19
+ "スクロール領域: main.flex-1.overflow-y-auto(本文セクション群。高さは flex-1 に委ね、ハードコードしない)",
20
+ "下部固定: BottomTabBar(グローバルナビ。旧称 BottomNavigation)または StickyActionBar(下部 CTA。旧称 BottomAction)"
21
+ ],
22
+ "rules": [
23
+ "全高レイアウトは min-h-dvh を使う(min-h-screen は不可。モバイルのアドレスバー分ずれる)",
24
+ "スクロール本文は flex-1 で伸縮させる。高さをハードコードしない",
25
+ "下部に StickyActionBar(固定 CTA)がある場合、main に pb-20 を付けて本文が隠れないようにする",
26
+ "モバイルの全ページは原則 Header を上端・BottomTabBar を下端に持つ(下部 CTA ページでは BottomTabBar の代わりに StickyActionBar)",
27
+ "横スクロールリストは flex.overflow-x-auto.gap-3.[-webkit-overflow-scrolling:touch](カテゴリ横スクロールは CategoryScroll を使う)"
28
+ ]
29
+ },
30
+ "desktop": {
31
+ "requiredComponents": ["Container"],
32
+ "optionalComponents": ["Section", "SectionHeader"],
33
+ "implementation": "EcPageShell(primitives, kind:\"shell\")が mobile と共通の実装体。PC では bottomTab(BottomTabBar)が消え下余白も外れる。左右 padding は Container・全幅背景帯は Section が持ち、シェルは持たない。手組みの独自シェルを作らない。",
34
+ "structure": [
35
+ "ルート: div.flex.min-h-screen(横フレックス。サイドバー+本文)",
36
+ "ナビ: aside(サイドバーまたは上部ナビ。border-r border-[var(--Border-Medium-Emphasis)] で仕切る)",
37
+ "本文: main.flex-1(中央寄せ幅は Container、全幅背景帯は Section が持つ)"
38
+ ],
39
+ "rules": [
40
+ "デスクトップの全ページはサイドバーまたは上部ナビを必ず持つ",
41
+ "本文領域は flex-1 で伸縮させる",
42
+ "仕切り罫線は色を明示する(border-[var(--Border-Medium-Emphasis)]。幅だけの border は currentColor に化ける)",
43
+ "左右 padding は Container が所有し、背景は Section が所有する(余白の二重がけを防ぐ)"
44
+ ]
45
+ },
46
+ "admin": {
47
+ "requiredComponents": ["AdminLayout", "AdminSideNav", "AdminHeader"],
48
+ "optionalComponents": ["SectionHeader", "DataTable", "Section", "Container"],
49
+ "structure": [
50
+ "ルート: AdminLayout(管理画面の専用シェル。サイドナビ AdminSideNav・ヘッダー AdminHeader を内包)",
51
+ "サイドナビ・ヘッダーは AdminLayout 経由で組み込まれる(AdminSideNav / AdminHeader を直接手組みしない)",
52
+ "本文: AdminLayout の children に space-y-6 のコンテナを置き、ページタイトル(typo-heading-2xl)+管理コンテンツを並べる"
53
+ ],
54
+ "rules": [
55
+ "管理画面のシェルは AdminLayout を使い、手組みしない",
56
+ "管理画面の表は DataTable を使う(生の table 禁止)",
57
+ "ページ直下のセクション間は space-y-6 相当(管理画面の縦リズム)"
58
+ ]
59
+ },
60
+ "funnel": {
61
+ "requiredComponents": ["FunnelShell", "Header"],
62
+ "optionalComponents": ["Container", "SectionHeader", "StickyActionBar", "FormField"],
63
+ "implementation": "FunnelShell(primitives, kind:\"shell\")がこの骨格の実装体。EC 購入導線(カート→お届け先→支払い→確認 の一直線ファネル)に使う集中モード(Full Page)。集中ヘッダー(Header variant=back)は FunnelShell 経由で組み込む。steps / activeStep で進捗インジケータを出し、bottomAction に StickyActionBar 等を渡す。ステップ定義・文言・商品データはシェルに焼き込まず利用側が渡す。手組みの独自シェルを作らない。",
64
+ "structure": [
65
+ "ルート: FunnelShell(min-h-dvh flex flex-col の全高コンテナ。集中ヘッダー+進捗インジケータ+本体+下部 CTA を内包)",
66
+ "上部固定: FunnelShell 内の Header(variant=back。左上=戻る/中央=タイトル)",
67
+ "進捗: FunnelShell 内の steps インジケータ(完了=Brand 色・現在=強調・未来=Low-Emphasis の点+ラベル。SP はラベル省略)",
68
+ "本文: FunnelShell の children(左右 padding は Container・全幅背景帯は Section が持ち、シェルは持たない)",
69
+ "下部固定: FunnelShell の bottomAction(StickyActionBar 等の CTA)"
70
+ ],
71
+ "rules": [
72
+ "購入導線のシェルは FunnelShell を使い、手組みしない",
73
+ "グローバルナビ・BottomTabBar・パンくずを置かない(離脱を誘う導線を出さず購入に集中させる)",
74
+ "進捗を表示する(steps / activeStep でユーザーの現在地を示す)",
75
+ "下部 CTA(StickyActionBar)は bottomAction に渡す。本文の下余白はシェルが管理するのでページ側で pb を書かない",
76
+ "管理画面の複雑な集中編集は WorkspaceLayout を使う(EC 購入導線=FunnelShell)"
77
+ ]
78
+ }
79
+ },
80
+ "productGrid": {
81
+ "description": "商品グリッドのブレークポイント別列数と gap。ProductCard を並べる際の標準。",
82
+ "breakpoints": [
83
+ { "viewport": "mobile", "width": "< 640px", "columns": 2, "gap": "gap-3", "tailwind": "grid-cols-2 gap-3" },
84
+ { "viewport": "tablet", "width": "640–1024px", "columns": 3, "gap": "gap-4", "tailwind": "sm:grid-cols-3 sm:gap-4" },
85
+ { "viewport": "desktop", "width": "> 1024px", "columns": 4, "gap": "gap-4", "tailwind": "lg:grid-cols-4" }
86
+ ],
87
+ "example": "grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-4 lg:grid-cols-4",
88
+ "component": "ProductCard"
89
+ },
90
+ "spacingRhythm": {
91
+ "description": "一貫した余白が視覚階層を生む。4段のリズムを守る。space-y-* / gap-* いずれでも同値。",
92
+ "levels": [
93
+ { "level": "page-sections", "spacing": "space-y-8 / gap-8", "usage": "主要セクション間(ページ内の大きな塊の区切り)" },
94
+ { "level": "section-content", "spacing": "space-y-4 / gap-4", "usage": "セクション内の要素間" },
95
+ { "level": "related-items", "spacing": "space-y-2 / gap-2", "usage": "密接に関連する要素間" },
96
+ { "level": "inline", "spacing": "gap-1 / gap-1.5", "usage": "アイコン+テキスト、ラベル+値のインライン" }
97
+ ]
98
+ },
99
+ "cardHierarchy": {
100
+ "description": "Surface のネストは3層まで。入れ子で背景を一段ずつ落として階層を表現する。",
101
+ "layers": [
102
+ { "depth": 1, "token": "--Surface-Primary", "hex": "#FFFFFF", "usage": "メインのコンテンツカード" },
103
+ { "depth": 2, "token": "--Surface-Secondary", "hex": "#FBF9F8", "usage": "ネストしたセクション・交互行" },
104
+ { "depth": 3, "token": "--Surface-Tertiary", "hex": "#F4F2F1", "usage": "抑えたコンテナ・無効領域" }
105
+ ],
106
+ "rule": "ネストは3層まで。4層目以降は情報設計を見直す。背景は Surface トークンで明示(未指定で親を透かさない)。"
107
+ },
108
+ "textHierarchy": {
109
+ "description": "typo-* とテキスト強調トークンの組み合わせで階層を作るツリー。上ほど強い階層。トークン名は tokens.json の typography.* / semantic.text.* に対応。",
110
+ "tree": [
111
+ { "role": "page-title", "typo": "typo-heading-2xl", "color": "--Text-High-Emphasis" },
112
+ { "role": "section-heading", "typo": "typo-heading-lg", "color": "--Text-High-Emphasis" },
113
+ { "role": "body", "typo": "typo-body-md", "color": "--Text-High-Emphasis" },
114
+ { "role": "secondary-text", "typo": "typo-body-md", "color": "--Text-Medium-Emphasis" },
115
+ { "role": "meta", "typo": "typo-body-sm", "color": "--Text-Low-Emphasis", "usage": "メタ情報・タイムスタンプ" },
116
+ { "role": "smallest-label", "typo": "typo-label-xs", "color": "--Text-Low-Emphasis" }
117
+ ],
118
+ "accentNote": "価格などの最強調は typo-label-lg + --Text-Accent-Primary(オレンジ)。強調は色ではなく typo 系(heading/label)で表現し、font-bold を足さない。"
119
+ },
120
+ "ctaHierarchy": {
121
+ "description": "Button の variant 優先順位。1ビューポート内での CTA 密度を制御する。",
122
+ "priority": [
123
+ { "variant": "default", "role": "Primary CTA(オレンジ)", "note": "1ビューポートに1つまで(唯一の主 CTA)" },
124
+ { "variant": "secondary", "role": "Secondary action" },
125
+ { "variant": "tertiary", "role": "Tertiary action" },
126
+ { "variant": "ghost", "role": "Minimal action(メニュー・ツールバー内)" },
127
+ { "variant": "link", "role": "Inline link" }
128
+ ],
129
+ "rules": [
130
+ "default(オレンジ)の CTA は1ビューポートに1つまで。主アクションを1つに絞る",
131
+ "複数アクションを並べる時は default を tertiary / ghost と組み合わせる",
132
+ "モバイルの全幅下部 CTA は size=\"xl\" + w-full(StickyActionBar 内)"
133
+ ]
134
+ }
135
+ }
@@ -0,0 +1,224 @@
1
+ {
2
+ "meta": {
3
+ "name": "Cake.jp Design System — Screen Patterns Contracts",
4
+ "version": "0.1.0",
5
+ "description": "CRUD 機能でどの画面パターンを使うかの機械可読な判断基準。5パターンの定義・CRUD 対応・決定木・編集UIの観点比較を構造化する。人間向けの解説は guidelines/foundations/screen-patterns.md。",
6
+ "source": "GitHub issue #37 CRUDの画面設計の型化と基本パターンの整備",
7
+ "relatedContract": "本契約はページ種別の選択(何を使うか)を担う。選んだ後の骨格の組み方(外枠構造・余白リズム・視覚階層)は contracts/composition.json を参照する。",
8
+ "shellComponents": "各パターンの dsComponents に挙がるレイアウトシェル(AdminLayout / WorkspaceLayout / MypageShell 等)の実装一覧は contracts/components.json の kind:\"shell\" を参照する。",
9
+ "disclaimer": "本契約は正解を1つに固定するものではない。最適なパターンは案件ごとに変わり、集中させたい度合いのグラデーション・IA設計・API制約(1回保存か段階保存か・楽観ロック・URL直リンク要件等)に依存する。decisionTree は候補を1つに絞るための共通言語であり、絞った後にIA/API制約で調整してよい(理由を残すこと)。",
10
+ "model": {
11
+ "description": "画面パターンは2軸で整理する。第1軸=シェル(通常シェル Standard を保つか / 集中モード Focused か / オーバーレイか)、第2軸=態様(見る / 変更して保存・確定する)。",
12
+ "axes": {
13
+ "shell": ["standard(グロナビ/サイドナビを保つ通常シェル)", "focused(グロナビを外す集中モード)", "overlay(元画面の上に重ねる)"],
14
+ "mode": ["view(見る・探す・一覧する)", "edit(内容を変更して保存・確定する)"]
15
+ },
16
+ "standardPageNote": "Standard Page は通常シェルの総称(上位分類)で、View(閲覧)と Edit(変更して保存・確定)の2態様を持つ。Edit はフォームに限らず、通常シェルを保ったまま内容を変更し保存/確定する画面全般(管理フォーム編集・カートの数量変更+購入手続きへ・記事の軽い編集)を含む。"
17
+ }
18
+ },
19
+ "patterns": [
20
+ {
21
+ "id": "standard-page-view",
22
+ "name": "Standard Page(View)",
23
+ "group": "standard-page",
24
+ "aliases": ["通常ページ", "Standard Page"],
25
+ "purpose": "閲覧・一覧・詳細の表示(Read)。見る・探す・一覧するのが主目的。グロナビ/サイドナビを保つ通常シェル。",
26
+ "dsComponents": ["AdminLayout", "EcPageShell", "DataTable", "Section", "Container", "SectionHeader"],
27
+ "dsComponentsNote": "シェルは文脈で選ぶ: 管理画面=AdminLayout(サイドナビ+ヘッダー内包)/ EC 消費者側=EcPageShell(Header+BottomTabBar を SP 下部固定で受ける最頻出の骨格)/ マイページ配下=MypageShell。中身は Section(全幅背景帯)+ Container(中央寄せ・左右余白)で組む。",
28
+ "navigation": {
29
+ "globalNav": "あり",
30
+ "breadcrumb": "あり",
31
+ "header": "通常ヘッダー"
32
+ },
33
+ "crud": ["Read"],
34
+ "whenToUse": [
35
+ "一覧・詳細・ダッシュボードなど閲覧が主目的",
36
+ "編集の保存が主目的ではない"
37
+ ],
38
+ "whenNotToUse": [
39
+ "通常シェルを保ったまま内容を変更し保存/確定するのが主目的(→ Standard Page(Edit))",
40
+ "複数情報をまたぐ集中編集(→ Focused Workspace)"
41
+ ]
42
+ },
43
+ {
44
+ "id": "standard-page-edit",
45
+ "name": "Standard Page(Edit)",
46
+ "group": "standard-page",
47
+ "aliases": ["通常ページ+保存", "Form Page", "form-page"],
48
+ "purpose": "通常シェル(グロナビ/サイドナビ)を保ったまま内容を変更し保存/確定する画面全般。フォームに限らない(管理フォーム編集・カートの数量変更+購入手続きへ・記事の軽い編集)。既存管理画面ではこれがスタンダード。",
49
+ "dsComponents": ["AdminLayout", "EcPageShell", "Form", "Section", "Container", "SectionHeader"],
50
+ "dsComponentsNote": "シェルは文脈で選ぶ: 管理画面のフォーム編集=AdminLayout / EC 消費者側の編集(カートの数量変更+確定 等)=EcPageShell(下部 CTA 主体なら bottomTab の代わりに StickyActionBar を渡す)/ マイページ配下=MypageShell。",
51
+ "navigation": {
52
+ "globalNav": "あり",
53
+ "breadcrumb": "あり",
54
+ "header": "通常ヘッダー"
55
+ },
56
+ "crud": ["Update", "Create"],
57
+ "whenToUse": [
58
+ "単一エンティティのまとまった項目を1画面で編集・保存する(管理フォーム編集)",
59
+ "サイドナビ/グロナビのコンテキストを保ったまま内容を変更したい",
60
+ "その場で編集し確定アクションへ進む(例: カートの数量変更→購入手続きへ)",
61
+ "通常シェルを保った軽い編集(例: 記事の見出し・本文の軽微な修正)",
62
+ "1回保存が基本で未保存の途中状態が発生しにくい"
63
+ ],
64
+ "whenNotToUse": [
65
+ "一部の軽い項目だけ・一覧を見たまま素早く終わる(→ ResponsiveDialog)",
66
+ "複数情報を横断し段階的で時間がかかる・没入して執筆したい(→ Focused Workspace)"
67
+ ],
68
+ "examples": [
69
+ "管理画面の商品編集フォーム(サイドナビを保ったまま保存)",
70
+ "EC のカート画面(シェル維持・その場で数量変更・購入手続きへ確定)",
71
+ "記事エディタの軽い編集(通常シェルのまま見出し・本文を直す。没入執筆なら Focused Workspace)"
72
+ ]
73
+ },
74
+ {
75
+ "id": "focused-workspace",
76
+ "name": "Focused Workspace",
77
+ "aliases": ["Full Page", "編集ワークスペース", "集中モード"],
78
+ "purpose": "複数情報をまたぐ複雑な編集に集中する。グロナビを外し編集に専念させる。",
79
+ "dsComponents": ["WorkspaceLayout", "FunnelShell", "SectionNav", "ResponsiveDialog"],
80
+ "dsComponentsNote": "実装は用途で 2 つのシェルを使い分ける。管理の複雑編集(複数情報を横断・編集に集中)=WorkspaceLayout(グロナビ非表示・編集ヘッダーを内包)。EC 購入導線(カート→お届け先→支払い→確認 の一直線ファネル)=FunnelShell(グロナビ・BottomTabBar・パンくずなし・進捗インジケータ+下部 CTA を内包)。どちらも手組みの独自レイアウトを作らない。編集内のセクションナビには SectionNav(ページ内アンカー移動専用。SideNav を流用しない)を渡す。編集内の作成・編集ダイアログには ResponsiveDialog / AlertDialog を併用する。",
81
+ "navigation": {
82
+ "globalNav": "なし(編集内のセクションナビ=アンカー/ステップは可)",
83
+ "breadcrumb": "原則なし(戻る導線で代替)",
84
+ "header": "編集ヘッダー(左上=戻る、右上=ワークスペース全体の上位アクションのみ・ある場合)"
85
+ },
86
+ "crud": ["Create", "Update"],
87
+ "whenToUse": [
88
+ "複数エンティティ・関連情報をまたぐ編集",
89
+ "作業時間が長い・段階的(下書き・途中保存・複数ステップ)",
90
+ "未保存状態が発生しやすくブラウザ更新・戻るの破棄/復元を考慮したい",
91
+ "URL 共有・直接アクセスが必要",
92
+ "バリデーションやAPIエラー(409・404・権限・競合)が複雑",
93
+ "将来的に項目・ステップが増える見込みが大きい"
94
+ ],
95
+ "whenNotToUse": [
96
+ "一部の軽い項目だけの編集(→ ResponsiveDialog)",
97
+ "通常シェルを保ったまま保存/確定する標準編集(→ Standard Page(Edit))"
98
+ ],
99
+ "detailRules": [
100
+ "グロナビは出さない",
101
+ "編集内のセクションナビ(アンカー/ステップ)は可",
102
+ "編集ヘッダー左上=戻る導線",
103
+ "編集ヘッダー右上=ワークスペース全体の上位アクションのみ(例: 登録申請・公開申請・承認依頼・全体を確定する)。ある場合のみ置く",
104
+ "セクション/行単位の操作(商品を追加・行を削除・セクション単位の編集・個別項目の保存)はヘッダーでなく対象コンテンツの近くに置く",
105
+ "パンくずは原則置かず戻る導線で代替する",
106
+ "離脱・更新・戻るで作業が失われないよう未保存の破棄/復元を設計する",
107
+ "管理画面限定ではなくEC側にも集中モードは存在し、その場合ヘッダーの上位アクションや戻り先が変わる"
108
+ ]
109
+ },
110
+ {
111
+ "id": "responsive-dialog",
112
+ "name": "ResponsiveDialog",
113
+ "aliases": ["Semi Modal"],
114
+ "purpose": "軽〜中程度の作成・編集。一覧のコンテキストを維持したまま素早く終える。",
115
+ "dsComponents": ["ResponsiveDialog"],
116
+ "dsComponentsNote": "PC=Dialog / SP=Sheet を自動切替。素の Dialog / Sheet を手組みで出し分けない。",
117
+ "navigation": {
118
+ "globalNav": "元画面のまま(オーバーレイ表示)",
119
+ "breadcrumb": "元画面のまま",
120
+ "header": "ダイアログ/シートのヘッダー"
121
+ },
122
+ "crud": ["Create", "Update"],
123
+ "whenToUse": [
124
+ "入力項目が少ない一部の軽い項目の作成・編集",
125
+ "一覧のコンテキストを見たまま作業したい",
126
+ "その場で短時間に完結する(未保存状態が発生しにくい)",
127
+ "バリデーション・APIエラーが単純"
128
+ ],
129
+ "whenNotToUse": [
130
+ "複数情報を横断し段階的で時間がかかる(→ Focused Workspace)",
131
+ "URL 共有・直接アクセス・ブラウザ更新の考慮が必要(→ Focused Workspace)",
132
+ "破壊的操作の確認(→ AlertDialog)"
133
+ ]
134
+ },
135
+ {
136
+ "id": "dialog",
137
+ "name": "Dialog / AlertDialog",
138
+ "aliases": ["Dialog"],
139
+ "purpose": "確認・警告・実行確認。操作を一度止めて明示的に確認する。破壊的操作は AlertDialog。",
140
+ "dsComponents": ["Dialog", "AlertDialog"],
141
+ "navigation": {
142
+ "globalNav": "元画面のまま(オーバーレイ表示)",
143
+ "breadcrumb": "元画面のまま",
144
+ "header": "ダイアログのヘッダー"
145
+ },
146
+ "crud": ["Delete"],
147
+ "whenToUse": [
148
+ "削除確認・実行確認・警告",
149
+ "操作を一度止めて明示的な確認を取りたい",
150
+ "選択肢を提示して分岐させたい",
151
+ "破壊的操作(削除・取り消し)の確認は AlertDialog"
152
+ ],
153
+ "whenNotToUse": [
154
+ "フォーム入力(→ ResponsiveDialog)",
155
+ "破壊的確認なのに素の Dialog で組む(→ AlertDialog)"
156
+ ]
157
+ }
158
+ ],
159
+ "crudMatrix": [
160
+ { "operation": "Read", "situation": "閲覧・一覧・詳細", "pattern": "standard-page-view" },
161
+ { "operation": "Create", "situation": "軽い作成(項目少・一覧文脈維持)", "pattern": "responsive-dialog" },
162
+ { "operation": "Create", "situation": "複雑な作成(複数情報をまたぐ・段階的)", "pattern": "focused-workspace" },
163
+ { "operation": "Update", "situation": "標準的な編集(通常シェルを保ったまま保存/確定。管理フォーム編集・カートの数量変更+確定・記事の軽い編集)", "pattern": "standard-page-edit", "note": "既存仕様のスタンダード" },
164
+ { "operation": "Update", "situation": "軽い編集(一部の軽い項目・一覧文脈維持)", "pattern": "responsive-dialog" },
165
+ { "operation": "Update", "situation": "複雑な編集(複数情報を横断・編集に集中)", "pattern": "focused-workspace" },
166
+ { "operation": "Delete", "situation": "削除確認・警告・実行確認", "pattern": "dialog", "note": "破壊的操作は AlertDialog" }
167
+ ],
168
+ "decisionTree": [
169
+ {
170
+ "step": 1,
171
+ "question": "破壊的操作の確認か?(削除・取り消しなど、一度止めて明示的に確認したい)",
172
+ "yes": "dialog",
173
+ "yesNote": "AlertDialog を使う",
174
+ "no": 2
175
+ },
176
+ {
177
+ "step": 2,
178
+ "question": "確認・警告・実行確認のダイアログか?(破壊的でないが操作を一度止めたい・選択肢を提示したい)",
179
+ "yes": "dialog",
180
+ "yesNote": "Dialog を使う",
181
+ "no": 3
182
+ },
183
+ {
184
+ "step": 3,
185
+ "question": "閲覧が主目的か?(見る・探す・一覧する。編集の保存が主目的ではない)",
186
+ "yes": "standard-page-view",
187
+ "no": 4
188
+ },
189
+ {
190
+ "step": 4,
191
+ "question": "編集が複数の関連情報・エンティティをまたぐか?(例: 商品+価格+配送+公開設定を横断)",
192
+ "yes": "focused-workspace",
193
+ "no": 5
194
+ },
195
+ {
196
+ "step": 5,
197
+ "question": "下書き・複数ステップの段階的作業、または URL共有・直接アクセス・ブラウザ更新への考慮が必要か?",
198
+ "yes": "focused-workspace",
199
+ "no": 6
200
+ },
201
+ {
202
+ "step": 6,
203
+ "question": "一部の軽い項目だけで、一覧のコンテキストを見たまま素早く終わるか?",
204
+ "yes": "responsive-dialog",
205
+ "no": "standard-page-edit",
206
+ "noNote": "通常シェルを保ったまま保存/確定する既存管理画面の標準形(Standard Page(Edit)。旧称 Form Page。カートの数量変更+確定・記事の軽い編集もここ)"
207
+ }
208
+ ],
209
+ "editUiSelection": {
210
+ "description": "ResponsiveDialog vs Standard Page(Edit) vs Focused Workspace の観点別比較。standardPageEdit は旧称 Form Page。多数決ではなく、強い制約(未保存状態・APIエラー・URL共有)が1つでも右に振れたら Focused Workspace を検討する。",
211
+ "criteria": [
212
+ { "criterion": "入力項目数", "responsiveDialog": "少ない", "standardPageEdit": "中程度", "focusedWorkspace": "多い" },
213
+ { "criterion": "編集対象", "responsiveDialog": "一部の軽い項目(参照不要)", "standardPageEdit": "単一エンティティのまとまった項目", "focusedWorkspace": "複数エンティティ・関連情報をまたぐ" },
214
+ { "criterion": "作業時間", "responsiveDialog": "短い(その場で完結)", "standardPageEdit": "中程度(1画面で完結)", "focusedWorkspace": "長い・段階的" },
215
+ { "criterion": "一覧コンテキスト", "responsiveDialog": "見たまま作業したい", "standardPageEdit": "通常シェル(サイドナビ/グロナビ)を保って作業", "focusedWorkspace": "不要・むしろ集中したい" },
216
+ { "criterion": "未保存状態", "responsiveDialog": "発生しにくい(1回保存)", "standardPageEdit": "1回保存が基本", "focusedWorkspace": "発生しやすい(下書き・途中保存・複数ステップ)" },
217
+ { "criterion": "バリデーション", "responsiveDialog": "単純", "standardPageEdit": "標準", "focusedWorkspace": "複雑(相互依存・条件付き)" },
218
+ { "criterion": "APIエラー", "responsiveDialog": "単純表示で足りる", "standardPageEdit": "標準表示", "focusedWorkspace": "409・404・権限・競合を丁寧に扱いたい" },
219
+ { "criterion": "URL共有・直接アクセス", "responsiveDialog": "不要", "standardPageEdit": "ページURLはあるが編集は付随", "focusedWorkspace": "必要(編集画面自体を共有・直リンク)" },
220
+ { "criterion": "ブラウザ更新・戻る・進む", "responsiveDialog": "考慮不要", "standardPageEdit": "通常想定", "focusedWorkspace": "考慮したい(未保存の破棄/復元)" },
221
+ { "criterion": "将来拡張", "responsiveDialog": "小さい", "standardPageEdit": "中程度", "focusedWorkspace": "大きい(項目・ステップが増える見込み)" }
222
+ ]
223
+ }
224
+ }