ksk-design-system 2.1.1 → 2.3.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
@@ -56,6 +56,16 @@ UI を書く前に必ず確認すること:
56
56
 
57
57
  ---
58
58
 
59
+ ## DS公開通知
60
+
61
+ DSへ依頼するときは、DSの通常issueとappの取り込み待ち通常issueを作成する。DS正本の作業ブランチで `node bin/init.js register-consumer-request --ds-issue N --consumer-issue URL` を実行し、台帳変更をPRに含める。未公開CLIで古いnpm版を起動しないようsourceのコマンドを使う。公開済みCLIでは `npx ksk-ds register-consumer-request` も同じ引数で使える。
62
+
63
+ 登録CLIは既存のgh認証で両issueを確認し、app issueへ `ds:waiting` を自動付与する。closedまたは `ds:released` のissueは降格させない。修正PR作成後は同じコマンドに `--fix-pr N` を付けて紐付ける。省略時は既存fixPrを維持し、累積台帳を削除しない。`--dry-run` は通信・書き込みなしのローカルプレビューで、issue実在や権限は確認しない。失敗は非zero。台帳保存後のラベル失敗は同じコマンドで再試行する。
64
+
65
+ 公開通知はDSの一括bump(`scripts/update-consumers.sh VERSION`)時だけ実行する。公開版に修正が含まれることを確認し、bump PR付きの通知と `ds:released` を付ける。6時間pollingとapp側workflow/scriptは廃止し、`init-release-notices` は移行案内を出して停止する。app側の取り込み・動作確認が完了するまで追従issueは閉じない。詳しくは [公開通知手順](https://github.com/ekusiek716/ksk-design-system/blob/main/docs/consumer-release-notices.md) を参照。
66
+
67
+ 同じapp issueに複数のDS依頼がある場合、公開台帳内の該当依頼がすべて公開・通知済みになるまで `ds:waiting` を残す(`ds:released` と併存する場合あり)。通知はrepo owner本人 `ekusiek716` のPAT認証が前提。通知失敗時は既存bump PRを保持して同じversion・対象repoの一括bumpを再実行し、通知runtime単独実行・並列実行はしない。
68
+
59
69
  ## このDSについて
60
70
 
61
71
  **KSK Design System** は、フリーランスデザイナー / エンジニア / PdM が **複数クライアント案件を1つのDSで高速に回す** ために設計された統合デザインシステムです。
package/CLAUDE.md CHANGED
@@ -163,6 +163,16 @@ afterEach フックが全ストーリー(tags フィルタなし)に対し
163
163
 
164
164
  ---
165
165
 
166
+ ## DS公開通知
167
+
168
+ DSへ依頼するときは、DSの通常issueとappの取り込み待ち通常issueを作成する。DS正本の作業ブランチで `node bin/init.js register-consumer-request --ds-issue N --consumer-issue URL` を実行し、台帳変更をPRに含める。未公開CLIで古いnpm版を起動しないようsourceのコマンドを使う。公開済みCLIでは `npx ksk-ds register-consumer-request` も同じ引数で使える。
169
+
170
+ 登録CLIは既存のgh認証で両issueを確認し、app issueへ `ds:waiting` を自動付与する。closedまたは `ds:released` のissueは降格させない。修正PR作成後は同じコマンドに `--fix-pr N` を付けて紐付ける。省略時は既存fixPrを維持し、累積台帳を削除しない。`--dry-run` は通信・書き込みなしのローカルプレビューで、issue実在や権限は確認しない。失敗は非zero。台帳保存後のラベル失敗は同じコマンドで再試行する。
171
+
172
+ 公開通知はDSの一括bump(`scripts/update-consumers.sh VERSION`)時だけ実行する。公開版に修正が含まれることを確認し、bump PR付きの通知と `ds:released` を付ける。6時間pollingとapp側workflow/scriptは廃止し、`init-release-notices` は移行案内を出して停止する。app側の取り込み・動作確認が完了するまで追従issueは閉じない。詳しくは [公開通知手順](https://github.com/ekusiek716/ksk-design-system/blob/main/docs/consumer-release-notices.md) を参照。
173
+
174
+ 同じapp issueに複数のDS依頼がある場合、公開台帳内の該当依頼がすべて公開・通知済みになるまで `ds:waiting` を残す(`ds:released` と併存する場合あり)。通知はrepo owner本人 `ekusiek716` のPAT認証が前提。通知失敗時は既存bump PRを保持して同じversion・対象repoの一括bumpを再実行し、通知runtime単独実行・並列実行はしない。
175
+
166
176
  ## このDSについて
167
177
 
168
178
  **KSK Design System** は、フリーランスデザイナー / エンジニア / PdM が **複数クライアント案件を1つのDSで高速に回す** ために設計された統合デザインシステムです。
package/DESIGN.md CHANGED
@@ -12,18 +12,18 @@ colors:
12
12
  onPrimary: "#FFFFFF"
13
13
  # --- Neutral scale ---
14
14
  neutral0: "#FFFFFF"
15
- neutral50: "#F9FAFB"
16
- neutral100: "#F3F4F6"
17
- neutral200: "#E5E7EB"
18
- neutral500: "#6B7280"
19
- neutral900: "#111827"
15
+ neutral50: "#FAFAFA"
16
+ neutral100: "#F4F4F5"
17
+ neutral200: "#E4E4E7"
18
+ neutral500: "#71717A"
19
+ neutral900: "#18181B"
20
20
  # --- Surface / Text / Border(semantic) ---
21
21
  surface: "#FFFFFF" # var(--Surface-Primary)
22
- surfaceMuted: "#F9FAFB" # var(--Surface-Secondary)
23
- textHigh: "#111827" # var(--Text-High-Emphasis)
24
- textMedium: "#374151" # var(--Text-Medium-Emphasis)
25
- textLow: "#4B5563" # var(--Text-Low-Emphasis)
26
- border: "#E5E7EB" # var(--Border-Low-Emphasis)
22
+ surfaceMuted: "#FAFAFA" # var(--Surface-Secondary)
23
+ textHigh: "#18181B" # var(--Text-High-Emphasis)
24
+ textMedium: "#3F3F46" # var(--Text-Medium-Emphasis)
25
+ textLow: "#52525B" # var(--Text-Low-Emphasis)
26
+ border: "#E4E4E7" # var(--Border-Low-Emphasis)
27
27
  focus: "#60A5FA" # var(--Focus-High-Emphasis)
28
28
  # --- Semantic(status) ---
29
29
  success: "#15803D"
@@ -39,7 +39,7 @@ typography:
39
39
  caption: { fontSize: "11px", lineHeight: "1.5", fontWeight: 400 } # typo-caption(注釈/法的表記専用。本文の下限は body=12px。10px の typo-body-xs は補助ラベル専用で本文禁止)
40
40
  rounded:
41
41
  sm: "4px"
42
- surface: "8px" # Card / Input / Popover → var(--Radius-Surface)
42
+ surface: "14px" # Card / Input / Popover → var(--Radius-Surface)
43
43
  modal: "24px" # 中央 Dialog / AlertDialog → var(--Radius-Modal)
44
44
  sheet: "32px" # bottom / float Sheet → var(--Radius-Sheet)
45
45
  full: "9999px" # Button / Chip
@@ -50,8 +50,8 @@ spacing:
50
50
  elevation: # 影色は neutral(Gray-900 ベース rgba(17,24,39,…))でテーマ非依存
51
51
  sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
52
52
  md: "0 0 8px rgba(20, 20, 20, 0.08)"
53
- lg: "0px 12px 32px -4px rgba(17, 24, 39, 0.12), 0px 8px 16px -6px rgba(17, 24, 39, 0.12)"
54
- dialog: "0px 12px 32px -4px rgba(17, 24, 39, 0.12), 0px 8px 16px -6px rgba(17, 24, 39, 0.12), 0px 1px 4px 1px rgba(0, 0, 0, 0.2)"
53
+ lg: "0px 12px 32px -4px rgba(24, 24, 27, 0.12), 0px 8px 16px -6px rgba(24, 24, 27, 0.12)"
54
+ dialog: "0px 12px 32px -4px rgba(24, 24, 27, 0.12), 0px 8px 16px -6px rgba(24, 24, 27, 0.12), 0px 1px 4px 1px rgba(0, 0, 0, 0.2)"
55
55
  motion: # 実体は var(--Motion-*)(src/styles/motion.css)
56
56
  microIn: "150ms ease-out" # --Motion-Duration-Fast / fade-in / scale-in
57
57
  enter: "200ms ease-out" # --Motion-Duration-Base / fade-in-up / slide-in
@@ -135,9 +135,9 @@ KSK の必須正本・publish 依存にせず、KSK 固有の multi-theme / nati
135
135
  | 用途 | トークン | 既定値(Blue) |
136
136
  |---|---|---|
137
137
  | ブランド | `var(--Brand-Primary)` | `#2563EB` |
138
- | 背景(白/薄灰) | `var(--Surface-Primary)` / `-Secondary` | `#FFFFFF` / `#F9FAFB` |
139
- | 文字(強/中/弱) | `var(--Text-High/Medium/Low-Emphasis)` <!-- docs-drift-ignore: --Text-High/Medium/Low-Emphasis --> | `#111827` / `#374151` / `#4B5563` |
140
- | 罫線 | `var(--Border-Low-Emphasis)` | `#E5E7EB` |
138
+ | 背景(白/薄灰) | `var(--Surface-Primary)` / `-Secondary` | `#FFFFFF` / `#FAFAFA` |
139
+ | 文字(強/中/弱) | `var(--Text-High/Medium/Low-Emphasis)` <!-- docs-drift-ignore: --Text-High/Medium/Low-Emphasis --> | `#18181B` / `#3F3F46` / `#52525B` |
140
+ | 罫線 | `var(--Border-Low-Emphasis)` | `#E4E4E7` |
141
141
  | 状態 | `--Success/Warning/Caution/Info-Base` <!-- docs-drift-ignore: --Success/Warning/Caution/Info-Base --> | `#15803D` / `#C2410C` / `#DC2626` / `#2563EB` |
142
142
 
143
143
  - **状態色の正本**: 上記は `*-Base`。Caution/Info は Primitive **600**、Success/Warning は白文字 AA(4.5:1) 確保のため Primitive **700**(600 だと 3.3〜3.6:1)。`tokens.json` を正本とし、本表はその要約。
package/NATIVE_RECIPES.md CHANGED
@@ -182,6 +182,8 @@ import { Button, PhotoHero, Screen } from "ksk-design-system/native/ui"
182
182
 
183
183
  Web では `CompactFilePicker` / `ImageAttachmentPicker` が hidden file input、trigger、preview、remove affordance を持ちます。Native では DocumentPicker / ImagePicker の起動だけ consumer が渡し、trigger と preview は DS が持ちます。
184
184
 
185
+ `loadingLabel`(loading 中にボタンへ表示する文言)は web の既定が `"処理中"` なのに対し、native には既定値が無く、未指定時は native Button の spinner のみが表示されます(issue #539。web/native で意図的に非対称)。
186
+
185
187
  ```tsx
186
188
  <ImageAttachmentPicker
187
189
  multiple
package/PUBLISHING.md CHANGED
@@ -244,6 +244,16 @@ publish.yml が失敗・スタックした場合の実行可能な手順は次
244
244
  それでも解消しない場合は、npm 側の障害か Trusted Publishing 設定(npmjs.com の
245
245
  Trusted Publisher 登録)を疑い、対応後に再度 workflow_dispatch で実行する。
246
246
 
247
+ ## 依頼元appへの公開通知
248
+
249
+ DSへ依頼するときは、DSの通常issueとappの取り込み待ち通常issueを作成する。DS正本の作業ブランチで `node bin/init.js register-consumer-request --ds-issue N --consumer-issue URL` を実行し、台帳変更をPRに含める。未公開CLIで古いnpm版を起動しないようsourceのコマンドを使う。公開済みCLIでは `npx ksk-ds register-consumer-request` も同じ引数で使える。
250
+
251
+ 登録CLIは既存のgh認証で両issueを確認し、app issueへ `ds:waiting` を自動付与する。closedまたは `ds:released` のissueは降格させない。修正PR作成後は同じコマンドに `--fix-pr N` を付けて紐付ける。省略時は既存fixPrを維持し、累積台帳を削除しない。`--dry-run` は通信・書き込みなしのローカルプレビューで、issue実在や権限は確認しない。失敗は非zero。台帳保存後のラベル失敗は同じコマンドで再試行する。
252
+
253
+ 公開通知はDSの一括bump(`scripts/update-consumers.sh VERSION`)時だけ実行する。公開版に修正が含まれることを確認し、bump PR付きの通知と `ds:released` を付ける。6時間pollingとapp側workflow/scriptは廃止し、`init-release-notices` は移行案内を出して停止する。app側の取り込み・動作確認が完了するまで追従issueは閉じない。詳しくは [公開通知手順](https://github.com/ekusiek716/ksk-design-system/blob/main/docs/consumer-release-notices.md) を参照。
254
+
255
+ 同じapp issueに複数のDS依頼がある場合、公開台帳内の該当依頼がすべて公開・通知済みになるまで `ds:waiting` を残す(`ds:released` と併存する場合あり)。通知はrepo owner本人 `ekusiek716` のPAT認証が前提。通知失敗時は既存bump PRを保持して同じversion・対象repoの一括bumpを再実行し、通知runtime単独実行・並列実行はしない。
256
+
247
257
  ## 関連
248
258
 
249
259
  - [UPDATING.md](./UPDATING.md) — 消費側(DS を npm 依存に持つプロジェクト)向けのアップデート手順
package/README.md CHANGED
@@ -76,14 +76,17 @@ DS のルールが適用されません。既存ファイルはスキップさ
76
76
  > として Critical 判定されるためです(LPM Firewall が 1.49.2 / 1.51.1 をこの理由でブロック判定)。
77
77
  > v1.59.0 以前から更新する場合、既に設置済みのファイルはそのまま使えます。
78
78
 
79
+ <!-- consumer-example:css:start -->
79
80
  ```css
80
- /* globals.css / app.css(CSS の場所に応じて ../../ の数を調整) */
81
+ /* src/styles/app.css(アプリのルートで一度だけ読み込む) */
81
82
  @import "tailwindcss";
82
83
  @import "ksk-design-system/preset";
83
84
  @import "ksk-design-system/themes/default";
84
- @import "./themes/my-client.css"; /* Brand 色を差し替えたテーマ */
85
85
  @source "../../node_modules/ksk-design-system/dist";
86
86
  ```
87
+ <!-- consumer-example:css:end -->
88
+
89
+ 独自ブランドのテーマを使う場合は、この後にテーマCSSをimportしてください。
87
90
 
88
91
  Tailwind CSS v4 は `node_modules` を既定では走査しません。`@source` がないと、
89
92
  DS 内部だけで使うレイアウト・サイズ・状態クラスが生成されず、コンポーネントの表示や操作が崩れます。
@@ -94,9 +97,23 @@ consumer 側の Tailwind と DS を同じビルドで処理するため、上記
94
97
  DS のクラス CSS は生成されます(issue #258)。ただし consumer 自身のコードは
95
98
  consumer 側の走査対象なので、`@source` は引き続き推奨構成です。
96
99
 
100
+ <!-- consumer-example:tsx:start -->
97
101
  ```tsx
98
- import { Button, Card, Input, FormField } from "ksk-design-system"
102
+ import { Button, Input, Label } from "ksk-design-system"
103
+
104
+ export function Example() {
105
+ return (
106
+ <form>
107
+ <Label htmlFor="company">会社名</Label>
108
+ <Input id="company" name="company" defaultValue="サンプル株式会社" />
109
+ <Button type="submit">保存</Button>
110
+ </form>
111
+ )
112
+ }
99
113
  ```
114
+ <!-- consumer-example:tsx:end -->
115
+
116
+ DSリポジトリの開発者向け検査として、`npm run test:consumer:web` は、配布READMEの上記例を空consumerで型検査・CSSビルドし、Node SSRからChromiumへのhydrationと操作を検査します。初回は `npx playwright install chromium` が必要です。
100
117
 
101
118
  新規クライアント案件では、テーマファイルで `--Primitive-Brand-500` などブランドカラーの 10 行を定義するだけで、全コンポーネントがそのブランドカラーで動作します。
102
119
 
@@ -474,6 +491,16 @@ npm run metrics -- --range last-year
474
491
 
475
492
  > npm は匿名集計のため、CI やミラーの重複を含む **install 回数の目安** です(ユニークユーザー数ではありません)。
476
493
 
494
+ ## DS公開通知
495
+
496
+ DSへ依頼するときは、DSの通常issueとappの取り込み待ち通常issueを作成する。DS正本の作業ブランチで `node bin/init.js register-consumer-request --ds-issue N --consumer-issue URL` を実行し、台帳変更をPRに含める。未公開CLIで古いnpm版を起動しないようsourceのコマンドを使う。公開済みCLIでは `npx ksk-ds register-consumer-request` も同じ引数で使える。
497
+
498
+ 登録CLIは既存のgh認証で両issueを確認し、app issueへ `ds:waiting` を自動付与する。closedまたは `ds:released` のissueは降格させない。修正PR作成後は同じコマンドに `--fix-pr N` を付けて紐付ける。省略時は既存fixPrを維持し、累積台帳を削除しない。`--dry-run` は通信・書き込みなしのローカルプレビューで、issue実在や権限は確認しない。失敗は非zero。台帳保存後のラベル失敗は同じコマンドで再試行する。
499
+
500
+ 公開通知はDSの一括bump(`scripts/update-consumers.sh VERSION`)時だけ実行する。公開版に修正が含まれることを確認し、bump PR付きの通知と `ds:released` を付ける。6時間pollingとapp側workflow/scriptは廃止し、`init-release-notices` は移行案内を出して停止する。app側の取り込み・動作確認が完了するまで追従issueは閉じない。詳しくは [公開通知手順](https://github.com/ekusiek716/ksk-design-system/blob/main/docs/consumer-release-notices.md) を参照。
501
+
502
+ 同じapp issueに複数のDS依頼がある場合、公開台帳内の該当依頼がすべて公開・通知済みになるまで `ds:waiting` を残す(`ds:released` と併存する場合あり)。通知はrepo owner本人 `ekusiek716` のPAT認証が前提。通知失敗時は既存bump PRを保持して同じversion・対象repoの一括bumpを再実行し、通知runtime単独実行・並列実行はしない。
503
+
477
504
  ## 📄 ライセンス
478
505
 
479
506
  [MIT](./LICENSE) © 奥野圭祐 (Keisuke Okuno)
package/RELEASE.md CHANGED
@@ -74,6 +74,10 @@ GitHub Release の作成をスキップしてしまう。そのため:
74
74
  ローカルから `npm publish` を直接叩く経路は廃止した(publish.yml への一本化。
75
75
  下記参照)。
76
76
 
77
+ ## 未公開の修正
78
+
79
+ - [#529](https://github.com/ekusiek716/ksk-design-system/issues/529): native Dialog のダークモードで、中央・上寄せの確認面が背景と同化する問題を修正。パネルに `surface.secondary` を使い、余白と角丸を見分けられるようにする。light・fullscreen の背景、寸法、safe-area は維持。公開後に利用アプリへ取り込み、実機で確認する。
80
+
77
81
  ## ホットフィックス履歴
78
82
 
79
83
  | 日付 | バージョン | 内容 | 影響 |
@@ -0,0 +1,138 @@
1
+ import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { join, relative, resolve } from "node:path"
3
+ import { spawnSync } from "node:child_process"
4
+ import { parseConsumerIssue, validateContract } from "../templates/consumer-release-notices/check-ds-release.mjs"
5
+
6
+ function positiveInt(value, name) {
7
+ if (!/^[1-9]\d*$/.test(String(value)) || !Number.isSafeInteger(Number(value))) {
8
+ throw new Error(`${name} must be a positive safe integer`)
9
+ }
10
+ return Number(value)
11
+ }
12
+
13
+ function issueUrl(value) {
14
+ if (typeof value !== "string" || /\s/.test(value)) throw new Error("consumer-issue must be a GitHub issue URL without whitespace")
15
+ const canonical = value.toLowerCase()
16
+ parseConsumerIssue(canonical)
17
+ return canonical
18
+ }
19
+
20
+ function statOrNull(path) {
21
+ try { return lstatSync(path) } catch (error) {
22
+ if (error.code === "ENOENT") return null
23
+ throw error
24
+ }
25
+ }
26
+
27
+ function safePath(root, path) {
28
+ let current = root
29
+ const parts = relative(root, path).split(/[\\/]/)
30
+ for (const [index, part] of parts.entries()) {
31
+ current = join(current, part)
32
+ const stat = statOrNull(current)
33
+ if (stat?.isSymbolicLink()) throw new Error(`Symlink is not allowed: ${current}`)
34
+ if (stat && index < parts.length - 1 && !stat.isDirectory()) throw new Error(`Not a directory: ${current}`)
35
+ }
36
+ }
37
+
38
+ export function validateConsumerRequests(registry) {
39
+ validateContract(registry)
40
+ for (const request of registry.requests) issueUrl(request.consumerIssue)
41
+ return registry
42
+ }
43
+
44
+ function github(runGh) {
45
+ return (endpoint, method = "GET", fields = {}, paginate = false) => {
46
+ const args = ["api", "--hostname", "github.com", endpoint, "--method", method]
47
+ if (paginate) args.push("--paginate", "--slurp")
48
+ for (const [key, value] of Object.entries(fields)) args.push("-f", `${key}=${value}`)
49
+ const result = runGh("gh", args, { encoding: "utf8", timeout: 30000, maxBuffer: 8 * 1024 * 1024 })
50
+ // Do not echo gh stderr: it may include credentials or private response details.
51
+ if (result.error || result.status !== 0) throw new Error(`GitHub ${method} failed: ${endpoint}. Check gh authentication/permissions and retry the same command.`)
52
+ try { return JSON.parse(result.stdout) } catch { throw new Error(`Invalid GitHub response: ${endpoint}`) }
53
+ }
54
+ }
55
+
56
+ function readIssue(api, repo, number) {
57
+ const issue = api(`repos/${repo}/issues/${number}`)
58
+ if (!issue || issue.pull_request || issue.number !== number ||
59
+ typeof issue.html_url !== "string" || issueUrl(issue.html_url) !== `https://github.com/${repo}/issues/${number}` ||
60
+ !["open", "closed"].includes(issue.state)) throw new Error(`Expected a regular issue: ${repo}#${number}`)
61
+ return issue
62
+ }
63
+
64
+ function ensureWaiting(api, repo, number) {
65
+ // Re-read after writing the registry so a retry cannot downgrade released/closed work.
66
+ const issue = readIssue(api, repo, number)
67
+ if (issue.state === "closed") return
68
+ const pages = api(`repos/${repo}/issues/${number}/labels?per_page=100`, "GET", {}, true)
69
+ if (!Array.isArray(pages) || pages.some((page) => !Array.isArray(page))) throw new Error("Invalid issue label response")
70
+ const labels = pages.flat()
71
+ if (labels.some((label) => typeof label?.name !== "string")) throw new Error("Invalid issue label response")
72
+ if (labels.some((label) => ["ds:released", "ds:waiting"].includes(label.name.toLowerCase()))) return
73
+ const endpoint = `repos/${repo}/labels/ds%3Awaiting`
74
+ try { api(endpoint) } catch {
75
+ try { api(`repos/${repo}/labels`, "POST", { name: "ds:waiting", color: "fbca04", description: "DS修正版の公開待ち" }) }
76
+ catch { api(endpoint) } // Another registration may have created the label concurrently.
77
+ }
78
+ api(`repos/${repo}/issues/${number}/labels`, "POST", { "labels[]": "ds:waiting" })
79
+ console.log(`Ensured ds:waiting on ${repo}#${number}`)
80
+ }
81
+
82
+ function register(args, cwd, runGh) {
83
+ const options = new Map()
84
+ for (let i = 0; i < args.length; i++) {
85
+ const key = args[i]
86
+ if (!["--ds-issue", "--consumer-issue", "--fix-pr", "--dry-run"].includes(key) || options.has(key)) throw new Error(`Unknown or duplicate option: ${key}`)
87
+ if (key === "--dry-run") options.set(key, true)
88
+ else {
89
+ const value = args[++i]
90
+ if (!value || value.startsWith("--")) throw new Error(`Missing value: ${key}`)
91
+ options.set(key, value)
92
+ }
93
+ }
94
+ const dsIssue = positiveInt(options.get("--ds-issue"), "ds-issue")
95
+ const consumerIssue = issueUrl(options.get("--consumer-issue"))
96
+ const fixPr = options.has("--fix-pr") ? positiveInt(options.get("--fix-pr"), "fix-pr") : undefined
97
+ const packagePath = join(cwd, "package.json")
98
+ const contracts = join(cwd, "contracts")
99
+ safePath(cwd, packagePath)
100
+ safePath(cwd, contracts)
101
+ if (!existsSync(join(cwd, ".git")) || !statOrNull(contracts)?.isDirectory() ||
102
+ !existsSync(packagePath) || JSON.parse(readFileSync(packagePath, "utf8")).name !== "ksk-design-system") {
103
+ throw new Error("Run registration at the DS source repo root (ksk-design-system + .git + contracts/), never in a consumer")
104
+ }
105
+ const path = join(contracts, "consumer-requests.json")
106
+ safePath(cwd, path)
107
+ const before = existsSync(path) ? readFileSync(path, "utf8") : null
108
+ const registry = before === null ? { schemaVersion: 1, requests: [] } : JSON.parse(before)
109
+ validateConsumerRequests(registry)
110
+ const existing = registry.requests.find((request) => request.dsIssue === dsIssue && issueUrl(request.consumerIssue) === consumerIssue)
111
+ if (existing) {
112
+ existing.consumerIssue = consumerIssue
113
+ if (fixPr !== undefined) existing.fixPr = fixPr
114
+ } else registry.requests.push({ dsIssue, consumerIssue, fixPr: fixPr ?? null })
115
+ const after = JSON.stringify(registry, null, 2) + "\n"
116
+ if (options.has("--dry-run")) { console.log(after.trimEnd()); return }
117
+ const api = github(runGh)
118
+ const target = parseConsumerIssue(consumerIssue)
119
+ readIssue(api, "ekusiek716/ksk-design-system", dsIssue)
120
+ readIssue(api, target.repo, target.number)
121
+ if (before === null || JSON.stringify(JSON.parse(before)) !== JSON.stringify(registry)) {
122
+ writeFileSync(path, after)
123
+ console.log(`Updated ${path}`)
124
+ } else console.log("Consumer request already registered (no-op)")
125
+ ensureWaiting(api, target.repo, target.number)
126
+ }
127
+
128
+ export function runConsumerReleaseNoticesCli(command, args, { cwd, runGh = spawnSync }) {
129
+ try {
130
+ if (command === "register-consumer-request") register(args, resolve(cwd), runGh)
131
+ else if (command === "init-release-notices") throw new Error("init-release-notices is retired: remove the consumer polling workflow/script. Release notices run only during the DS bulk bump (scripts/update-consumers.sh).")
132
+ else throw new Error(`Unknown command: ${command}`)
133
+ return 0
134
+ } catch (error) {
135
+ console.error(`[ksk-ds ${command}] ${error.message}`)
136
+ return 1
137
+ }
138
+ }
package/bin/init.js CHANGED
@@ -59,6 +59,9 @@ if (cmd === "help" || cmd === "--help" || cmd === "-h") {
59
59
  使い方:
60
60
  npx ksk-design-system init AI ルールファイルを設置
61
61
  npx ksk-design-system init --force 既存ファイルを上書き
62
+ npx ksk-ds register-consumer-request --ds-issue N --consumer-issue URL [--fix-pr N] [--dry-run]
63
+ DS依頼を登録しapp issueへds:waitingを付与(gh認証使用)
64
+ npx ksk-ds init-release-notices 廃止済み。一括bump時の公開通知へ移行
62
65
  npx ksk-design-system demo [dir] DS リポを clone + npm install(お試し)
63
66
  dir 省略時は ./ksk-ds-demo
64
67
  npx ksk-ds lint src DS-first ルール違反を検査
@@ -75,6 +78,11 @@ if (cmd === "help" || cmd === "--help" || cmd === "-h") {
75
78
  await exitWith(0)
76
79
  }
77
80
 
81
+ if (cmd === "register-consumer-request" || cmd === "init-release-notices") {
82
+ const { runConsumerReleaseNoticesCli } = await import("./consumer-release-notices.js")
83
+ await exitWith(runConsumerReleaseNoticesCli(cmd, args.slice(1), { cwd: process.cwd(), pkgRoot }))
84
+ }
85
+
78
86
  if (cmd === "lint") {
79
87
  const { runLintCli } = await import("./lint.js")
80
88
  const status = await runLintCli(args.slice(1), { cwd: process.cwd(), pkgRoot })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "KSK Design System — Component Contracts",
4
- "version": "2.1.1",
4
+ "version": "2.3.0",
5
5
  "description": "全コンポーネントの構造化定義。バリアント・アクセシビリティ要件・使用ルールを機械可読形式で管理。",
6
6
  "counts": {
7
7
  "ui": 68,
@@ -1657,10 +1657,14 @@
1657
1657
  "compactStatus",
1658
1658
  "actions"
1659
1659
  ],
1660
+ "props": {
1661
+ "verticalAlignment": "\"center\" | \"top\" (既定 \"center\") — ヘッダー行の縦位置。複数行 brand で status/actions が中央へ下がるのを防ぐ。top でも status/actions 内部は中央揃えのまま(native と同仕様。issue #536)"
1662
+ },
1660
1663
  "rules": [
1661
1664
  "ブランド/ロゴは brand slot に置く",
1662
1665
  "長い同期状態は status と compactStatus の両方を渡す",
1663
- "右側 actions のために brand に flex-shrink をかけない"
1666
+ "右側 actions のために brand に flex-shrink をかけない",
1667
+ "複数行になり得る brand を使うときは verticalAlignment=\"top\" を検討する(既定は上下中央)"
1664
1668
  ]
1665
1669
  },
1666
1670
  {
@@ -2380,9 +2384,13 @@
2380
2384
  "grid",
2381
2385
  "list"
2382
2386
  ],
2387
+ "props": {
2388
+ "loadingLabel": "ReactNode (既定 \"処理中\") — loading 時にボタンへ表示する文言。i18n対応(issue #539)。native は既定値を持たず、未指定時は native Button の spinner のみを表示する(web/native で意図的に非対称。scripts/check-native-parity.mjs の INTENTIONAL_DEFAULT_GAPS 参照)"
2389
+ },
2383
2390
  "accessibility": [
2384
2391
  "img alt",
2385
- "remove button aria-label"
2392
+ "remove button aria-label",
2393
+ "aria-busy on trigger button while loading(テスト/自動化向けの状態フック。loading 中はボタン自体が disabled になるため、スクリーンリーダーへの読み上げ効果は限定的)"
2386
2394
  ],
2387
2395
  "rules": [
2388
2396
  "consumer 側で hidden input + Button trigger を重複実装しない",
@@ -0,0 +1,10 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "requests": [
4
+ {
5
+ "dsIssue": 531,
6
+ "consumerIssue": "https://github.com/ekusiek716/trip-todo/issues/168",
7
+ "fixPr": 532
8
+ }
9
+ ]
10
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "meta": {
3
3
  "name": "KSK Design System — Semantic Token Hex Cache",
4
- "version": "2.1.1",
4
+ "version": "2.3.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",
@@ -288,10 +288,10 @@
288
288
  },
289
289
  "semantic": {
290
290
  "surface.primary": "#FFFFFF",
291
- "surface.secondary": "#F9FAFB",
292
- "surface.tertiary": "#F3F4F6",
293
- "surface.quaternary": "#E5E7EB",
294
- "surface.inverse": "#111827",
291
+ "surface.secondary": "#FAFAFA",
292
+ "surface.tertiary": "#F4F4F5",
293
+ "surface.quaternary": "#E4E4E7",
294
+ "surface.inverse": "#18181B",
295
295
  "surface.accent-primary": "#3B82F6",
296
296
  "surface.accent-primary-light": "#EFF6FF",
297
297
  "surface.caution": "#FEF2F2",
@@ -299,11 +299,11 @@
299
299
  "surface.warning": "#FFFBEB",
300
300
  "surface.info": "#EFF6FF",
301
301
  "surface.caution-strong": "#EF4444",
302
- "surface.disable": "#F3F4F6",
303
- "text.high-emphasis": "#111827",
304
- "text.medium-emphasis": "#374151",
305
- "text.low-emphasis": "#4B5563",
306
- "text.disable": "#9CA3AF",
302
+ "surface.disable": "#F4F4F5",
303
+ "text.high-emphasis": "#18181B",
304
+ "text.medium-emphasis": "#3F3F46",
305
+ "text.low-emphasis": "#52525B",
306
+ "text.disable": "#A1A1AA",
307
307
  "text.on-inverse": "#FFFFFF",
308
308
  "text.on-media": "#FFFFFF",
309
309
  "text.accent-primary": "#2563EB",
@@ -312,10 +312,10 @@
312
312
  "text.warning": "#C2410C",
313
313
  "text.info": "#2563EB",
314
314
  "text.caution-on-inverse": "#F87171",
315
- "object.high-emphasis": "#111827",
316
- "object.medium-emphasis": "#4B5563",
317
- "object.low-emphasis": "#9CA3AF",
318
- "object.disable": "#D1D5DB",
315
+ "object.high-emphasis": "#18181B",
316
+ "object.medium-emphasis": "#52525B",
317
+ "object.low-emphasis": "#A1A1AA",
318
+ "object.disable": "#D4D4D8",
319
319
  "object.on-inverse": "#FFFFFF",
320
320
  "object.accent-primary": "#3B82F6",
321
321
  "object.caution": "#EF4444",
@@ -323,9 +323,9 @@
323
323
  "object.warning": "#F97316",
324
324
  "object.info": "#3B82F6",
325
325
  "object.rating": "#F59E0B",
326
- "border.high-emphasis": "#374151",
327
- "border.medium-emphasis": "#D1D5DB",
328
- "border.low-emphasis": "#E5E7EB",
326
+ "border.high-emphasis": "#3F3F46",
327
+ "border.medium-emphasis": "#D4D4D8",
328
+ "border.low-emphasis": "#E4E4E7",
329
329
  "border.accent-primary": "#3B82F6",
330
330
  "border.caution": "#EF4444",
331
331
  "border.success": "#22C55E",
@@ -341,13 +341,13 @@
341
341
  "brand.amazon": "#232F3E",
342
342
  "hover.primary-button": "#1D4ED8",
343
343
  "hover.secondary-button": "#EFF6FF",
344
- "hover.tertiary-button": "#F3F4F6",
345
- "hover.ghost-button": "#F3F4F6",
344
+ "hover.tertiary-button": "#F4F4F5",
345
+ "hover.ghost-button": "#F4F4F5",
346
346
  "hover.destructive-button": "#B91C1C",
347
347
  "active.primary-button": "#1E40AF",
348
348
  "active.destructive-button": "#991B1B",
349
349
  "active.secondary-button": "#DBEAFE",
350
- "active.tertiary-button": "#E5E7EB",
350
+ "active.tertiary-button": "#E4E4E7",
351
351
  "focus.high-emphasis": "#60A5FA",
352
352
  "caution.base": "#DC2626",
353
353
  "caution.action": "#B91C1C",
@@ -408,10 +408,10 @@
408
408
  "categorical.16.bold": "#6D28D9"
409
409
  },
410
410
  "semanticDark": {
411
- "surface.primary": "#111827",
412
- "surface.secondary": "#1F2937",
413
- "surface.tertiary": "#374151",
414
- "surface.quaternary": "#4B5563",
411
+ "surface.primary": "#18181B",
412
+ "surface.secondary": "#27272A",
413
+ "surface.tertiary": "#3F3F46",
414
+ "surface.quaternary": "#52525B",
415
415
  "surface.inverse": "#FFFFFF",
416
416
  "surface.accent-primary": "#3B82F6",
417
417
  "surface.accent-primary-light": "#1E3A8A",
@@ -420,8 +420,8 @@
420
420
  "surface.warning": "#78350F",
421
421
  "surface.info": "#1E3A8A",
422
422
  "surface.caution-strong": "#EF4444",
423
- "surface.disable": "#1F2937",
424
- "text.on-inverse": "#111827",
423
+ "surface.disable": "#27272A",
424
+ "text.on-inverse": "#18181B",
425
425
  "text.on-media": "#FFFFFF",
426
426
  "text.accent-primary": "#60A5FA",
427
427
  "text.success": "#4ADE80",
@@ -429,8 +429,8 @@
429
429
  "text.warning": "#FB923C",
430
430
  "text.info": "#60A5FA",
431
431
  "text.caution-on-inverse": "#B91C1C",
432
- "text.on-inverse-secondary": "#374151",
433
- "object.on-inverse": "#111827",
432
+ "text.on-inverse-secondary": "#3F3F46",
433
+ "object.on-inverse": "#18181B",
434
434
  "object.accent-primary": "#60A5FA",
435
435
  "object.caution": "#F87171",
436
436
  "object.success": "#4ADE80",