ksk-design-system 1.51.1 → 1.52.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/AGENTS.md CHANGED
@@ -40,6 +40,17 @@ UI を書く前に必ず確認すること:
40
40
  - [ ] `.tsx` 編集後に `bash scripts/lint-scratch.sh`、コンポーネント増減時は `npm run check` を実行したか
41
41
  - [ ] `FormField` を import する前にどちらか確認したか(react-hook-form の Controller と統合するなら `RhfFormField`=`ui/form` の `FormField` を index.ts で別名 export したもの。単純な label+error 表示は `patterns/form-field` の `FormField`。迷ったら後者)
42
42
 
43
+ ### 新規・変更するコンポーネントの API チェック(2026-08 の consumer 実機点検で3件連続した型)
44
+
45
+ 「DS 部品の既定値が consumer の想定とズレていた」不具合が同じ週に3件出た(#308 / #311 / exam-kit 側で発覚した ChipSelector の複数選択既定)。部品の props を設計・変更するときは次を確認する:
46
+
47
+ - [ ] **押せる要素は、押せると見て分かる面を既定で持つか**。素の Pressable + テキストだけの操作子を作らない。面・タップ領域(44pt)は `IconButton` 等の既存部品に委ねる(#308: Calendar の月送り矢印が面を持たず、consumer から差し替え口も無かった)
48
+ - [ ] **インタラクティブな部品は a11y 既定値を自分で持つか**。`accessibilityRole` と、状態(selected / disabled / checked)の `accessibilityState` 反映は**呼び出し側の義務にしない**。呼び出し側の明示値が優先される形で部品が既定を持つ(#311: Chip が role を持たず、スクリーンリーダーにただのテキストとして扱われた)
49
+ - [ ] **モードを持つ部品(単一/複数選択・開閉など)の既定値は、間違えて使うと壊れる側にしていないか**。既定で済ませた呼び出しが静かに誤動作する組み合わせ(例: ChipSelector の既定 `multiple=true` を単一選択のつもりで使うと、押しても values[0] が現在値のまま切り替わらない)は、既定を安全側へ倒すか、必須 prop にして省略をコンパイルエラーにする
50
+ - [ ] **名前付きスロット(meta / description 等)は「何を・どこに置くか」を JSDoc に書いたか**。書いていないスロットは consumer に流用され、想定外の位置崩れになる(#309: ActionTile の meta に選択チェックを入れられ、左下に浮いた)
51
+
52
+ いずれも lint では拾えない設計判断なので、レビューではこの4点を明示的に見る。
53
+
43
54
  ---
44
55
 
45
56
  ## このDSについて
package/CLAUDE.md CHANGED
@@ -40,6 +40,17 @@ UI を書く前に必ず確認すること:
40
40
  - [ ] `.tsx` 編集後に `bash scripts/lint-scratch.sh`、コンポーネント増減時は `npm run check` を実行したか
41
41
  - [ ] `FormField` を import する前にどちらか確認したか(react-hook-form の Controller と統合するなら `RhfFormField`=`ui/form` の `FormField` を index.ts で別名 export したもの。単純な label+error 表示は `patterns/form-field` の `FormField`。迷ったら後者)
42
42
 
43
+ ### 新規・変更するコンポーネントの API チェック(2026-08 の consumer 実機点検で3件連続した型)
44
+
45
+ 「DS 部品の既定値が consumer の想定とズレていた」不具合が同じ週に3件出た(#308 / #311 / exam-kit 側で発覚した ChipSelector の複数選択既定)。部品の props を設計・変更するときは次を確認する:
46
+
47
+ - [ ] **押せる要素は、押せると見て分かる面を既定で持つか**。素の Pressable + テキストだけの操作子を作らない。面・タップ領域(44pt)は `IconButton` 等の既存部品に委ねる(#308: Calendar の月送り矢印が面を持たず、consumer から差し替え口も無かった)
48
+ - [ ] **インタラクティブな部品は a11y 既定値を自分で持つか**。`accessibilityRole` と、状態(selected / disabled / checked)の `accessibilityState` 反映は**呼び出し側の義務にしない**。呼び出し側の明示値が優先される形で部品が既定を持つ(#311: Chip が role を持たず、スクリーンリーダーにただのテキストとして扱われた)
49
+ - [ ] **モードを持つ部品(単一/複数選択・開閉など)の既定値は、間違えて使うと壊れる側にしていないか**。既定で済ませた呼び出しが静かに誤動作する組み合わせ(例: ChipSelector の既定 `multiple=true` を単一選択のつもりで使うと、押しても values[0] が現在値のまま切り替わらない)は、既定を安全側へ倒すか、必須 prop にして省略をコンパイルエラーにする
50
+ - [ ] **名前付きスロット(meta / description 等)は「何を・どこに置くか」を JSDoc に書いたか**。書いていないスロットは consumer に流用され、想定外の位置崩れになる(#309: ActionTile の meta に選択チェックを入れられ、左下に浮いた)
51
+
52
+ いずれも lint では拾えない設計判断なので、レビューではこの4点を明示的に見る。
53
+
43
54
  ---
44
55
 
45
56
  ## 必須: セッション開始時に読み込むファイル
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "KSK Design System — Component Contracts",
4
- "version": "1.51.1",
4
+ "version": "1.52.0",
5
5
  "description": "全コンポーネントの構造化定義。バリアント・アクセシビリティ要件・使用ルールを機械可読形式で管理。",
6
6
  "counts": {
7
7
  "ui": 67,
@@ -2052,7 +2052,7 @@
2052
2052
  "name": "QuickActionGrid / ActionTile",
2053
2053
  "exported": false,
2054
2054
  "path": "src/components/patterns/quick-action-grid.tsx",
2055
- "description": "ショートカット/絵文字 action の button tile と grid。icon/emoji、label、description、meta、selected/disabled/loading state、2/3/4 column または auto-fit を持つ。",
2055
+ "description": "ショートカット/絵文字 action の button tile と grid。icon/emoji、label、description、meta、indicator、selected/disabled/loading state、2/3/4 column または auto-fit を持つ。",
2056
2056
  "variants": [
2057
2057
  "neutral",
2058
2058
  "selected",
@@ -2064,13 +2064,19 @@
2064
2064
  ],
2065
2065
  "accessibility": [
2066
2066
  "button semantics",
2067
- "aria-pressed when selected"
2067
+ "aria-pressed when selected (selected prop または variant=\"selected\" のどちらでも true)",
2068
+ "選択状態は色だけでなく既定チェック(indicator省略時)でも伝える(WCAG 1.4.1)",
2069
+ "既定チェックは読み上げ対象外(aria-hidden / accessibilityElementsHidden)。選択状態の正本は aria-pressed(web) / accessibilityState.selected(native)"
2068
2070
  ],
2069
2071
  "rules": [
2070
2072
  "card + button の hybrid を consumer 側で手組みしない",
2071
2073
  "shortcut row/grid の columns/gap を consumer 側で決め打ちしない",
2072
2074
  "children には ActionTile を使う",
2073
- "選択状態は selected prop を使う"
2075
+ "選択状態は selected prop を使う",
2076
+ "meta はカード下段右端の補足情報専用。選択インジケータには indicator prop を使う(issue #309)",
2077
+ "indicator はラベル行右端(loading 時 Spinner の位置)に表示。省略時は選択状態でDS標準チェックが既定表示される。null/undefined/falseはいずれも「指定なし」として扱われ、選択中なら既定チェックへフォールバックする",
2078
+ "loading は indicator / 既定チェックより優先して表示される",
2079
+ "native では indicator に文字列・数値を渡してもよい(DS 側が Text にラップする)。それ以外の ReactNode はそのまま描画されるため、consumer が Text 相当で包む"
2074
2080
  ],
2075
2081
  "subcomponents": [
2076
2082
  "QuickActionGrid",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "KSK Design System — Semantic Token Hex Cache",
4
- "version": "1.51.1",
4
+ "version": "1.52.0",
5
5
  "description": "semantic / semanticDark トークン(var(--Primitive-*) 参照)を実 hex に解決したサイドカー生成物。hex はデフォルト(Blue)テーマでの解決値であり、Brand 系(meta.themeDependentKeys に列挙)はテーマ差し替え(orange/green/violet 等)で実色が変わる。テーマ別の完全解決値は `ksk-design-system/native` エクスポート(バンドル済み native トークンモジュール)の themes を参照。tokens.json 本体のスキーマは変更せず、AI がこのファイルだけで実色を把握できるようにし、primitive 値の変更による semantic 実色のドリフトを --check で機械検出する。",
6
6
  "generatedBy": "scripts/generate-token-hex-cache.mjs",
7
7
  "theme": "default",
package/dist/index.js CHANGED
@@ -15362,16 +15362,16 @@ var vp = {
15362
15362
  4: "grid-cols-2 sm:grid-cols-4",
15363
15363
  auto: "grid-cols-[repeat(auto-fit,minmax(8rem,1fr))]"
15364
15364
  };
15365
- function bp({ className: e, icon: t, emoji: n, label: r, description: i, meta: a, selected: o = !1, loading: s = !1, variant: c = o ? "selected" : "neutral", disabled: l, type: u, ...d }) {
15366
- let f = l || s;
15365
+ function bp({ className: e, icon: t, emoji: n, label: r, description: i, meta: a, indicator: o, selected: s = !1, loading: c = !1, variant: l = s ? "selected" : "neutral", disabled: u, type: d, ...f }) {
15366
+ let p = u || c, m = s || l === "selected", h = o != null && o !== !1;
15367
15367
  return /* @__PURE__ */ H("button", {
15368
15368
  "data-slot": "action-tile",
15369
- "data-variant": c,
15370
- "aria-pressed": o || void 0,
15371
- type: u ?? "button",
15372
- disabled: f,
15373
- className: W("relative flex min-h-24 flex-col items-start justify-between gap-3 rounded-xl border border-[var(--Border-Low-Emphasis)] p-3 text-left transition-colors", "focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]/50 focus-visible:outline-none", "hover:bg-[var(--Surface-Secondary)]", vp[c], f && "cursor-not-allowed opacity-50", e),
15374
- ...d,
15369
+ "data-variant": l,
15370
+ "aria-pressed": m || void 0,
15371
+ type: d ?? "button",
15372
+ disabled: p,
15373
+ className: W("relative flex min-h-24 flex-col items-start justify-between gap-3 rounded-xl border border-[var(--Border-Low-Emphasis)] p-3 text-left transition-colors", "focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]/50 focus-visible:outline-none", "hover:bg-[var(--Surface-Secondary)]", vp[l], p && "cursor-not-allowed opacity-50", e),
15374
+ ...f,
15375
15375
  children: [/* @__PURE__ */ H("span", {
15376
15376
  className: "flex w-full items-start justify-between gap-3",
15377
15377
  children: [/* @__PURE__ */ H("span", {
@@ -15392,10 +15392,17 @@ function bp({ className: e, icon: t, emoji: n, label: r, description: i, meta: a
15392
15392
  children: r
15393
15393
  })
15394
15394
  ]
15395
- }), s && /* @__PURE__ */ V(pl, {
15395
+ }), c ? /* @__PURE__ */ V(pl, {
15396
15396
  size: "sm",
15397
15397
  label: "処理中"
15398
- })]
15398
+ }) : h ? /* @__PURE__ */ V("span", {
15399
+ className: W("shrink-0", (typeof o == "string" || typeof o == "number") && "typo-label-md text-[var(--Text-High-Emphasis)]"),
15400
+ children: o
15401
+ }) : m ? /* @__PURE__ */ V("span", {
15402
+ className: "typo-label-md shrink-0 text-[var(--Text-Accent-Primary)]",
15403
+ "aria-hidden": !0,
15404
+ children: "✓"
15405
+ }) : null]
15399
15406
  }), (i || a) && /* @__PURE__ */ H("span", {
15400
15407
  className: "flex w-full flex-wrap items-end justify-between gap-x-2 gap-y-1",
15401
15408
  children: [i && /* @__PURE__ */ V("span", {
@@ -17634,7 +17641,7 @@ function lh({ className: e, state: t = "idle", label: n, count: r, compact: i =
17634
17641
  className: W("ml-0.5 rounded-full bg-[var(--Surface-Primary)] px-1.5 py-0.5 typo-label-xs", "text-[var(--Text-High-Emphasis)]"),
17635
17642
  children: r > 99 ? "99+" : r
17636
17643
  })
17637
- ] }), m = W("inline-flex min-h-9 max-w-full items-center justify-center gap-1.5 rounded-full border border-[var(--Border-Low-Emphasis)] px-2.5 py-1", "typo-label-xs select-none", ah[t], f && "cursor-pointer transition-colors hover:bg-[var(--Surface-Tertiary)]", f && "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]/50", u && "opacity-50", i && "min-w-9 px-2", e);
17644
+ ] }), m = W("inline-flex max-w-full items-center justify-center gap-1.5 rounded-full border border-[var(--Border-Low-Emphasis)] px-2.5 py-1", "typo-label-xs select-none", ah[t], f && "min-h-9", f && "cursor-pointer transition-colors hover:bg-[var(--Surface-Tertiary)]", f && "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--Focus-High-Emphasis)]/50", u && "opacity-50", i && f && "min-w-9 px-2", i && !f && "p-1", e);
17638
17645
  if (!f) {
17639
17646
  let e = sh(d);
17640
17647
  return /* @__PURE__ */ V("span", {
package/dist/native/ui.js CHANGED
@@ -701,24 +701,24 @@ var Ve = {
701
701
  md: 12,
702
702
  lg: 16
703
703
  };
704
- function Ue({ variant: e = "filled", size: t = "md", shape: n = "pill", selected: r = !1, disabled: i = !1, count: a, removable: o = !1, onRemove: s, children: c, ...l }) {
705
- let { theme: u, scales: d } = K(), f = {
704
+ function Ue({ variant: e = "filled", size: t = "md", shape: n = "pill", selected: r = !1, disabled: i = !1, count: a, removable: o = !1, onRemove: s, children: c, accessibilityRole: l, accessibilityState: u, ...d }) {
705
+ let { theme: f, scales: p } = K(), m = {
706
706
  filled: {
707
- bg: u.surface.secondary,
708
- fg: u.text["high-emphasis"],
707
+ bg: f.surface.secondary,
708
+ fg: f.text["high-emphasis"],
709
709
  border: "transparent"
710
710
  },
711
711
  accent: {
712
- bg: u.surface["accent-primary-light"],
713
- fg: u.text["accent-primary"],
712
+ bg: f.surface["accent-primary-light"],
713
+ fg: f.text["accent-primary"],
714
714
  border: "transparent"
715
715
  },
716
716
  outline: {
717
717
  bg: "transparent",
718
- fg: u.text["high-emphasis"],
719
- border: u.border["medium-emphasis"]
718
+ fg: f.text["high-emphasis"],
719
+ border: f.border["medium-emphasis"]
720
720
  }
721
- }[e], p = r ? u.brand.primary : f.bg, m = r ? u.text["on-inverse"] : i ? u.text.disable : f.fg, h = r ? u.brand.primary : f.border;
721
+ }[e], h = typeof c == "string" || typeof c == "number" ? `削除: ${c}` : "削除", g = r ? f.brand.primary : m.bg, _ = r ? f.text["on-inverse"] : i ? f.text.disable : m.fg, v = r ? f.brand.primary : m.border;
722
722
  return /* @__PURE__ */ D(W, {
723
723
  style: {
724
724
  flexDirection: "row",
@@ -726,59 +726,68 @@ function Ue({ variant: e = "filled", size: t = "md", shape: n = "pill", selected
726
726
  },
727
727
  children: [/* @__PURE__ */ D(z, {
728
728
  disabled: i,
729
+ accessibilityRole: l ?? "button",
730
+ accessibilityState: {
731
+ selected: r,
732
+ disabled: i,
733
+ ...u
734
+ },
729
735
  style: ({ pressed: a }) => [{
730
736
  height: Ve[t],
731
737
  paddingHorizontal: He[t],
732
738
  flexDirection: "row",
733
739
  alignItems: "center",
734
740
  justifyContent: "center",
735
- gap: d.spacing.scale[1],
736
- borderRadius: n === "pill" ? d.borderRadius.full : d.borderRadius.sm,
741
+ gap: p.spacing.scale[1],
742
+ borderRadius: n === "pill" ? p.borderRadius.full : p.borderRadius.sm,
737
743
  borderWidth: e === "outline" || r ? 1 : 0,
738
- borderColor: h,
739
- backgroundColor: a && !i ? u.active["secondary-button"] : p,
744
+ borderColor: v,
745
+ backgroundColor: a && !i ? f.active["secondary-button"] : g,
740
746
  opacity: i ? .6 : 1
741
747
  }, o && {
742
748
  borderTopRightRadius: 0,
743
749
  borderBottomRightRadius: 0
744
750
  }],
745
- ...l,
751
+ ...d,
746
752
  children: [/* @__PURE__ */ E(H, {
747
- style: [q("label.sm"), { color: m }],
753
+ style: [q("label.sm"), { color: _ }],
748
754
  children: c
749
755
  }), a !== void 0 && /* @__PURE__ */ E(W, {
750
756
  style: {
751
757
  minWidth: 20,
752
758
  paddingHorizontal: 6,
753
- borderRadius: d.borderRadius.full,
754
- backgroundColor: r ? u.surface.primary : u.surface.tertiary,
759
+ borderRadius: p.borderRadius.full,
760
+ backgroundColor: r ? f.surface.primary : f.surface.tertiary,
755
761
  alignItems: "center",
756
762
  justifyContent: "center"
757
763
  },
758
764
  children: /* @__PURE__ */ E(H, {
759
- style: [q("label.xs"), { color: r ? u.text["accent-primary"] : u.text["medium-emphasis"] }],
765
+ style: [q("label.xs"), { color: r ? f.text["accent-primary"] : f.text["medium-emphasis"] }],
760
766
  children: a
761
767
  })
762
768
  })]
763
769
  }), o && /* @__PURE__ */ E(z, {
764
770
  onPress: s,
765
771
  disabled: i,
772
+ accessibilityRole: "button",
773
+ accessibilityLabel: h,
774
+ accessibilityState: { disabled: i },
766
775
  style: ({ pressed: a }) => ({
767
776
  height: Ve[t],
768
777
  paddingHorizontal: 10,
769
778
  alignItems: "center",
770
779
  justifyContent: "center",
771
- backgroundColor: a ? u.active["tertiary-button"] : p,
772
- borderTopRightRadius: n === "pill" ? d.borderRadius.full : d.borderRadius.sm,
773
- borderBottomRightRadius: n === "pill" ? d.borderRadius.full : d.borderRadius.sm,
780
+ backgroundColor: a ? f.active["tertiary-button"] : g,
781
+ borderTopRightRadius: n === "pill" ? p.borderRadius.full : p.borderRadius.sm,
782
+ borderBottomRightRadius: n === "pill" ? p.borderRadius.full : p.borderRadius.sm,
774
783
  borderWidth: e === "outline" || r ? 1 : 0,
775
784
  borderLeftWidth: 0,
776
- borderColor: h,
785
+ borderColor: v,
777
786
  opacity: i ? .6 : 1
778
787
  }),
779
788
  children: /* @__PURE__ */ E(H, {
780
789
  style: [q("label.md"), {
781
- color: m,
790
+ color: _,
782
791
  lineHeight: 14
783
792
  }],
784
793
  children: "×"
@@ -2247,7 +2256,7 @@ function Yt({ checked: e = !1, onChange: t, disabled: n = !1, label: r, descript
2247
2256
  style: {
2248
2257
  flexDirection: "row",
2249
2258
  gap: c.spacing.scale[2],
2250
- alignItems: "flex-start",
2259
+ alignItems: i ? "flex-start" : "center",
2251
2260
  minHeight: c.touchTargets.buttonCTA.min,
2252
2261
  opacity: n ? .6 : 1
2253
2262
  },
@@ -2255,7 +2264,7 @@ function Yt({ checked: e = !1, onChange: t, disabled: n = !1, label: r, descript
2255
2264
  pointerEvents: "none",
2256
2265
  accessibilityElementsHidden: !0,
2257
2266
  importantForAccessibility: "no-hide-descendants",
2258
- style: { paddingTop: 2 },
2267
+ style: { paddingTop: i ? 2 : 0 },
2259
2268
  children: /* @__PURE__ */ E(Jt, {
2260
2269
  checked: e,
2261
2270
  disabled: n
@@ -2322,7 +2331,7 @@ function Zt({ options: e, values: t = [], onChange: n, disabled: r = !1 }) {
2322
2331
  t.includes(e) ? n?.(t.filter((t) => t !== e)) : n?.([...t, e]);
2323
2332
  };
2324
2333
  return /* @__PURE__ */ E(W, {
2325
- style: { gap: i.spacing.scale[3] },
2334
+ style: { gap: i.spacing.scale[1] },
2326
2335
  children: e.map((e) => /* @__PURE__ */ E(Yt, {
2327
2336
  label: e.label,
2328
2337
  description: e.description,
@@ -2337,7 +2346,7 @@ function Zt({ options: e, values: t = [], onChange: n, disabled: r = !1 }) {
2337
2346
  function Qt({ options: e, value: t, onChange: n, disabled: r = !1 }) {
2338
2347
  let { theme: i, scales: a } = K();
2339
2348
  return /* @__PURE__ */ E(W, {
2340
- style: { gap: a.spacing.scale[3] },
2349
+ style: { gap: a.spacing.scale[1] },
2341
2350
  children: e.map((e) => {
2342
2351
  let o = t === e.value, s = r || e.disabled;
2343
2352
  return /* @__PURE__ */ D(z, {
@@ -2346,7 +2355,8 @@ function Qt({ options: e, value: t, onChange: n, disabled: r = !1 }) {
2346
2355
  style: {
2347
2356
  flexDirection: "row",
2348
2357
  gap: a.spacing.scale[2],
2349
- alignItems: "flex-start",
2358
+ alignItems: e.description ? "flex-start" : "center",
2359
+ minHeight: a.touchTargets.buttonCTA.min,
2350
2360
  opacity: s ? .5 : 1
2351
2361
  },
2352
2362
  accessibilityRole: "radio",
@@ -2360,7 +2370,7 @@ function Qt({ options: e, value: t, onChange: n, disabled: r = !1 }) {
2360
2370
  borderColor: o ? i.brand.primary : i.border["medium-emphasis"],
2361
2371
  alignItems: "center",
2362
2372
  justifyContent: "center",
2363
- marginTop: 2
2373
+ marginTop: e.description ? 2 : 0
2364
2374
  },
2365
2375
  children: o && /* @__PURE__ */ E(W, { style: {
2366
2376
  width: 10,
@@ -5541,10 +5551,10 @@ function ci({ state: e = "idle", label: t, count: n, compact: r = !1, loading: i
5541
5551
  style: [q("label.xs"), { color: u.text["high-emphasis"] }],
5542
5552
  children: n > 99 ? "99+" : n
5543
5553
  })
5544
- ] }), m = [{
5545
- minHeight: 36,
5546
- minWidth: r ? 36 : void 0,
5547
- paddingHorizontal: d.spacing.scale[2],
5554
+ ] }), m = !o && !!c, h = [{
5555
+ minHeight: m ? 36 : void 0,
5556
+ minWidth: r && m ? 36 : void 0,
5557
+ paddingHorizontal: r && !m ? d.spacing.scale[1] : d.spacing.scale[2],
5548
5558
  paddingVertical: d.spacing.scale[1],
5549
5559
  borderRadius: d.borderRadius.full,
5550
5560
  borderWidth: 1,
@@ -5556,19 +5566,19 @@ function ci({ state: e = "idle", label: t, count: n, compact: r = !1, loading: i
5556
5566
  gap: d.spacing.scale[1],
5557
5567
  maxWidth: "100%"
5558
5568
  }, s];
5559
- return o || !c ? /* @__PURE__ */ E(W, {
5560
- accessibilityRole: "text",
5561
- accessibilityLabel: t,
5562
- style: m,
5563
- children: p
5564
- }) : /* @__PURE__ */ E(z, {
5569
+ return m ? /* @__PURE__ */ E(z, {
5565
5570
  accessibilityRole: "button",
5566
5571
  accessibilityLabel: t,
5567
5572
  accessibilityState: { busy: i || e === "syncing" },
5568
5573
  onPress: c,
5569
- style: ({ pressed: e }) => [m, e && { opacity: .82 }],
5574
+ style: ({ pressed: e }) => [h, e && { opacity: .82 }],
5570
5575
  ...l,
5571
5576
  children: p
5577
+ }) : /* @__PURE__ */ E(W, {
5578
+ accessibilityRole: "text",
5579
+ accessibilityLabel: t,
5580
+ style: h,
5581
+ children: p
5572
5582
  });
5573
5583
  }
5574
5584
  var li = ci;
@@ -6735,36 +6745,36 @@ function Gi(e, t) {
6735
6745
  };
6736
6746
  }
6737
6747
  }
6738
- function Ki({ icon: e, emoji: t, label: n, description: r, meta: i, selected: a = !1, loading: o = !1, disabled: s = !1, variant: c = a ? "selected" : "neutral", onPress: l, style: u, accessibilityRole: d, accessibilityState: f, ...p }) {
6739
- let { theme: m, scales: h } = K(), g = s || o, _ = Gi(c, m);
6748
+ function Ki({ icon: e, emoji: t, label: n, description: r, meta: i, indicator: a, selected: o = !1, loading: s = !1, disabled: c = !1, variant: l = o ? "selected" : "neutral", onPress: u, style: d, accessibilityRole: f, accessibilityState: p, ...m }) {
6749
+ let { theme: h, scales: g } = K(), _ = c || s, v = o || l === "selected", y = a != null && a !== !1, b = Gi(l, h);
6740
6750
  return /* @__PURE__ */ D(z, {
6741
- onPress: l,
6742
- disabled: g,
6743
- accessibilityRole: d ?? "button",
6751
+ onPress: u,
6752
+ disabled: _,
6753
+ accessibilityRole: f ?? "button",
6744
6754
  accessibilityState: {
6745
- disabled: g,
6746
- selected: a,
6747
- busy: o,
6748
- ...f
6755
+ disabled: _,
6756
+ selected: v,
6757
+ busy: s,
6758
+ ...p
6749
6759
  },
6750
- ...p,
6760
+ ...m,
6751
6761
  style: ({ pressed: e }) => [{
6752
6762
  minHeight: 96,
6753
6763
  borderWidth: 1,
6754
- borderRadius: h.borderRadius.xl,
6755
- padding: h.spacing.scale[3],
6756
- gap: h.spacing.scale[3],
6764
+ borderRadius: g.borderRadius.xl,
6765
+ padding: g.spacing.scale[3],
6766
+ gap: g.spacing.scale[3],
6757
6767
  justifyContent: "space-between",
6758
- opacity: g ? .5 : 1,
6759
- ..._,
6760
- backgroundColor: e ? m.surface.secondary : _.backgroundColor
6761
- }, u],
6768
+ opacity: _ ? .5 : 1,
6769
+ ...b,
6770
+ backgroundColor: e ? h.surface.secondary : b.backgroundColor
6771
+ }, d],
6762
6772
  children: [/* @__PURE__ */ D(W, {
6763
6773
  style: {
6764
6774
  flexDirection: "row",
6765
6775
  alignItems: "flex-start",
6766
6776
  justifyContent: "space-between",
6767
- gap: h.spacing.scale[2]
6777
+ gap: g.spacing.scale[2]
6768
6778
  },
6769
6779
  children: [/* @__PURE__ */ D(W, {
6770
6780
  style: {
@@ -6772,7 +6782,7 @@ function Ki({ icon: e, emoji: t, label: n, description: r, meta: i, selected: a
6772
6782
  minWidth: 0,
6773
6783
  flexDirection: "row",
6774
6784
  alignItems: "center",
6775
- gap: h.spacing.scale[2]
6785
+ gap: g.spacing.scale[2]
6776
6786
  },
6777
6787
  children: [
6778
6788
  t && /* @__PURE__ */ E(H, {
@@ -6783,30 +6793,47 @@ function Ki({ icon: e, emoji: t, label: n, description: r, meta: i, selected: a
6783
6793
  typeof n == "string" ? /* @__PURE__ */ E(H, {
6784
6794
  numberOfLines: 1,
6785
6795
  style: [q("label.md"), {
6786
- color: m.text["high-emphasis"],
6796
+ color: h.text["high-emphasis"],
6787
6797
  flex: 1
6788
6798
  }],
6789
6799
  children: n
6790
6800
  }) : n
6791
6801
  ]
6792
- }), o && /* @__PURE__ */ E(Ge, { size: "sm" })]
6802
+ }), s ? /* @__PURE__ */ E(Ge, { size: "sm" }) : y ? typeof a == "string" || typeof a == "number" ? /* @__PURE__ */ E(H, {
6803
+ style: [q("label.md"), { color: h.text["high-emphasis"] }],
6804
+ children: a
6805
+ }) : a : v ? /* @__PURE__ */ E(W, {
6806
+ pointerEvents: "none",
6807
+ accessibilityElementsHidden: !0,
6808
+ importantForAccessibility: "no-hide-descendants",
6809
+ children: /* @__PURE__ */ E(H, {
6810
+ style: [q("label.md"), { color: h.text["accent-primary"] }],
6811
+ children: "✓"
6812
+ })
6813
+ }) : null]
6793
6814
  }), (r || i) && /* @__PURE__ */ D(W, {
6794
6815
  style: {
6795
6816
  flexDirection: "row",
6796
6817
  alignItems: "flex-end",
6797
6818
  justifyContent: "space-between",
6798
- gap: h.spacing.scale[2]
6819
+ gap: g.spacing.scale[2]
6799
6820
  },
6800
6821
  children: [typeof r == "string" ? /* @__PURE__ */ E(H, {
6801
6822
  style: [q("body.sm"), {
6802
- color: m.text["medium-emphasis"],
6823
+ color: h.text["medium-emphasis"],
6803
6824
  flex: 1
6804
6825
  }],
6805
6826
  children: r
6806
6827
  }) : r, typeof i == "string" ? /* @__PURE__ */ E(H, {
6807
- style: [q("label.sm"), { color: m.text["low-emphasis"] }],
6828
+ style: [q("label.sm"), {
6829
+ color: h.text["low-emphasis"],
6830
+ marginLeft: "auto"
6831
+ }],
6832
+ children: i
6833
+ }) : i && /* @__PURE__ */ E(W, {
6834
+ style: { marginLeft: "auto" },
6808
6835
  children: i
6809
- }) : i]
6836
+ })]
6810
6837
  })]
6811
6838
  });
6812
6839
  }
@@ -7,7 +7,14 @@ interface ActionTileProps extends Omit<React.ComponentProps<"button">, "children
7
7
  emoji?: React.ReactNode;
8
8
  label: React.ReactNode;
9
9
  description?: React.ReactNode;
10
+ /** カード下段の右端に置く補足情報(例:「所要5分」「要解放」)。選択インジケータには使わない — `indicator` を使う。 */
10
11
  meta?: React.ReactNode;
12
+ /**
13
+ * 選択状態などを示す小さな表示。**ラベル行の右端**(loading 時に Spinner が入る位置)に置かれる。
14
+ * 省略時、選択状態なら DS 標準のチェックを表示する。表示自体を消したい場合の口は用意していない
15
+ * (選択を色だけで伝えると WCAG 1.4.1 に反するため)。
16
+ */
17
+ indicator?: React.ReactNode;
11
18
  selected?: boolean;
12
19
  loading?: boolean;
13
20
  variant?: ActionTileVariant;
@@ -16,7 +23,7 @@ interface QuickActionGridProps extends React.ComponentProps<"div"> {
16
23
  columns?: QuickActionGridColumns;
17
24
  gap?: QuickActionGridGap;
18
25
  }
19
- declare function ActionTile({ className, icon, emoji, label, description, meta, selected, loading, variant, disabled, type, ...props }: ActionTileProps): React.JSX.Element;
26
+ declare function ActionTile({ className, icon, emoji, label, description, meta, indicator, selected, loading, variant, disabled, type, ...props }: ActionTileProps): React.JSX.Element;
20
27
  declare function QuickActionGrid({ className, columns, gap, ...props }: QuickActionGridProps): React.JSX.Element;
21
28
  export { ActionTile, QuickActionGrid };
22
29
  export type { ActionTileProps, ActionTileVariant, QuickActionGridColumns, QuickActionGridGap, QuickActionGridProps };
@@ -14,4 +14,4 @@ export interface ChipProps extends Omit<PressableProps, "children" | "style"> {
14
14
  onRemove?: () => void;
15
15
  children?: React.ReactNode;
16
16
  }
17
- export declare function Chip({ variant, size, shape, selected, disabled, count, removable, onRemove, children, ...rest }: ChipProps): React.JSX.Element;
17
+ export declare function Chip({ variant, size, shape, selected, disabled, count, removable, onRemove, children, accessibilityRole, accessibilityState, ...rest }: ChipProps): React.JSX.Element;
@@ -11,7 +11,14 @@ export interface ActionTileProps extends AccessibilityProps {
11
11
  emoji?: React.ReactNode;
12
12
  label: React.ReactNode;
13
13
  description?: React.ReactNode;
14
+ /** カード下段の右端に置く補足情報(例:「所要5分」「要解放」)。選択インジケータには使わない — `indicator` を使う。 */
14
15
  meta?: React.ReactNode;
16
+ /**
17
+ * 選択状態などを示す小さな表示。**ラベル行の右端**(loading 時に Spinner が入る位置)に置かれる。
18
+ * 省略時、選択状態なら DS 標準のチェックを表示する。表示自体を消したい場合の口は用意していない
19
+ * (選択を色だけで伝えると WCAG 1.4.1 に反するため)。
20
+ */
21
+ indicator?: React.ReactNode;
15
22
  selected?: boolean;
16
23
  loading?: boolean;
17
24
  disabled?: boolean;
@@ -25,5 +32,5 @@ export interface QuickActionGridProps {
25
32
  children: React.ReactNode;
26
33
  style?: StyleProp<ViewStyle>;
27
34
  }
28
- export declare function ActionTile({ icon, emoji, label, description, meta, selected, loading, disabled, variant, onPress, style, accessibilityRole, accessibilityState, ...accessibilityProps }: ActionTileProps): React.JSX.Element;
35
+ export declare function ActionTile({ icon, emoji, label, description, meta, indicator, selected, loading, disabled, variant, onPress, style, accessibilityRole, accessibilityState, ...accessibilityProps }: ActionTileProps): React.JSX.Element;
29
36
  export declare function QuickActionGrid({ columns, gap, children, style, }: QuickActionGridProps): React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ksk-design-system",
3
- "version": "1.51.1",
3
+ "version": "1.52.0",
4
4
  "type": "module",
5
5
  "description": "KSK Design System — フリーランス向けマルチテーマ対応デザインシステム",
6
6
  "license": "MIT",
@@ -144,7 +144,7 @@
144
144
  | ProgressSteps | `@/components/patterns/progress-steps` | — | Step2Active, FirstStep, ThirdStep, AllComplete, ThreeSteps |
145
145
  | AUTO_PROMPT_SUPPRESSION_EVENT, AutoPrompt, PromptCoordinatorProvider | `@/components/patterns/prompt-coordinator` | — | Default |
146
146
  | Prose | `@/components/patterns/prose` | — | PrivacyPolicy, SingleSection |
147
- | ActionTile, QuickActionGrid | `@/components/patterns/quick-action-grid` | — | EmojiOnly, IconAndLabel, WithMeta, NarrowWithLongMeta |
147
+ | ActionTile, QuickActionGrid | `@/components/patterns/quick-action-grid` | — | EmojiOnly, IconAndLabel, WithMeta, IndicatorOverride, SelectedWithoutDescription, NarrowWithLongMeta |
148
148
  | ReviewOverlay | `@/components/patterns/review-overlay` | — | Default, WithPins |
149
149
  | Screen | `@/components/patterns/screen` | — | ScrollableWithFooter, WithHeader, FixedPhotoHero |
150
150
  | SearchBar | `@/components/patterns/search-bar` | — | Default, WithValue, Disabled, WithOnSearch |