@godxjp/ui 28.6.0 → 28.7.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.
@@ -1,10 +1,15 @@
1
1
  import {
2
- BookPlus,
2
+ BookOpen,
3
+ Boxes,
4
+ Calendar,
3
5
  Clock3,
4
6
  Database,
7
+ FileText,
5
8
  LayoutGrid,
9
+ Mail,
6
10
  PlugZap,
7
11
  ReceiptText,
12
+ Server,
8
13
  ShieldCheck,
9
14
  } from "lucide-react";
10
15
 
@@ -13,132 +18,287 @@ import {
13
18
  ServiceLauncherCard,
14
19
  ServiceLauncherCardSkeleton,
15
20
  } from "@godxjp/ui/data-display";
16
- import { Button } from "@godxjp/ui/general";
21
+ import { Button, Text } from "@godxjp/ui/general";
17
22
  import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
18
23
 
19
24
  /**
20
- * ServiceLauncherCard — organization-scoped launcher states. The consumer supplies every status,
21
- * entitlement reason and action; the component owns only the shared visual contract.
25
+ * ServiceLauncherCard — one tile for one application in an organization's launcher.
26
+ *
27
+ * WHY THIS PAGE IS GENERIC. It used to render this product's own service catalogue —
28
+ * `attendance.godx.jp`, plans `Standard/Enterprise/Free`, a 「カタログを見る」 link — which made a
29
+ * reader ask whether the component belonged in a UI framework at all. A fair question: a page that
30
+ * demonstrates a product cannot demonstrate a component. The data here is therefore ANY
31
+ * organization's internal tools, on RFC 2606 reserved domains, with no plan names of ours.
32
+ *
33
+ * WHAT IS NOT NEGOTIABLE is the state set — available / restricted / suspended / archived /
34
+ * loading / catalog entry. Those ARE the component's API surface, because the component infers
35
+ * nothing: `statusLabel`, `statusTone`, `disabledReason` and `action` all come from the consumer's
36
+ * own entitlement contract. There is no `href`, no `available`, no `entitlement` prop to find.
37
+ *
38
+ * ResponsiveGrid owns the 3 → 2 → 1 ladder. The page writes no grid tracks and no page-local CSS.
22
39
  */
40
+
41
+ /** A section heading. Headings are headings, not Cards — a Card here would nest borders. */
42
+ function Section({
43
+ title,
44
+ why,
45
+ children,
46
+ }: {
47
+ title: string;
48
+ why: string;
49
+ children: React.ReactNode;
50
+ }) {
51
+ return (
52
+ <Flex direction="col" gap="sm">
53
+ <Text as="div" weight="medium">
54
+ {title}
55
+ </Text>
56
+ <Text as="div" size="sm" tone="muted">
57
+ {why}
58
+ </Text>
59
+ {children}
60
+ </Flex>
61
+ );
62
+ }
63
+
23
64
  export default function Demo() {
24
65
  return (
25
66
  <PageContainer
26
67
  title="ServiceLauncherCard"
27
- subtitle="利用可能・制限中・読み込み中・カタログ導線"
68
+ subtitle="社内アプリケーション一覧のタイル — 利用可能・制限・停止・アーカイブ・読み込み中・カタログ導線"
28
69
  >
29
70
  <Flex direction="col" gap="lg">
30
- <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
31
- <ServiceLauncherCard
32
- icon={Clock3}
33
- title="勤怠管理"
34
- statusLabel="LIVE"
35
- statusTone="success"
36
- description="打刻、シフト、休暇申請を一つのワークスペースで管理します。"
37
- metadata="attendance.godx.jp · Standard"
38
- action={<Button>サービスを開く</Button>}
39
- />
40
- <ServiceLauncherCard
41
- icon={ReceiptText}
42
- title="請求管理"
43
- statusLabel="要契約"
44
- statusTone="warning"
45
- description="請求書、支払い方法、利用明細を管理します。"
46
- metadata="billing.godx.jp"
47
- disabledReason="この組織には有効な請求管理プランがありません。"
48
- action={<Button disabled>サービスを開く</Button>}
49
- />
50
- <ServiceLauncherCard
51
- icon={ShieldCheck}
52
- title="高度なセキュリティ管理と監査ログ"
53
- statusLabel="管理者限定"
54
- statusTone="info"
55
- description="権限、二要素認証ポリシー、監査イベントを確認します。"
56
- metadata="security.godx.jp · Enterprise"
57
- action={<Button variant="outline">権限を確認</Button>}
58
- />
59
- <ServiceLauncherCardSkeleton label="サービスを読み込み中" />
60
- <ServiceCatalogCta
61
- title="サービスカタログから追加"
62
- action={<Button variant="outline">カタログを見る</Button>}
63
- />
64
- </ResponsiveGrid>
71
+ <Section
72
+ title="1 · 基本 · 利用できるサービス"
73
+ why="アイコン・名称・ステータス・説明・識別子・操作。ステータスの文言は常に利用側のコピーで、コンポーネントが決めるのは tone に対応する Badge の意味づけだけです。"
74
+ >
75
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
76
+ <ServiceLauncherCard
77
+ icon={Clock3}
78
+ title="勤怠管理"
79
+ statusLabel="利用可能"
80
+ statusTone="success"
81
+ description="打刻、シフト、休暇申請を一つのワークスペースで管理します。"
82
+ metadata="attend.corp.example.jp · v4.2"
83
+ action={<Button>サービスを開く</Button>}
84
+ />
85
+ <ServiceLauncherCard
86
+ icon={ReceiptText}
87
+ title="経費精算"
88
+ statusLabel="利用可能"
89
+ statusTone="success"
90
+ description="領収書の提出から承認、仕訳連携までを扱います。"
91
+ metadata="keihi.corp.example.jp · v2.8"
92
+ action={<Button>サービスを開く</Button>}
93
+ />
94
+ <ServiceLauncherCard
95
+ icon={Calendar}
96
+ title="会議室予約"
97
+ statusLabel="利用可能"
98
+ statusTone="success"
99
+ description="拠点ごとの会議室と備品を予約します。"
100
+ metadata="rooms.corp.example.jp · v1.9"
101
+ action={<Button>サービスを開く</Button>}
102
+ />
103
+ </ResponsiveGrid>
104
+ </Section>
105
+
106
+ <Section
107
+ title="2 · statusTone の 6 値すべて"
108
+ why="success / warning / destructive / info / neutral / muted。tone は Badge の意味づけだけを決め、文言・判定・操作の可否はすべて利用側が渡します。"
109
+ >
110
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
111
+ <ServiceLauncherCard
112
+ icon={Mail}
113
+ titleLevel={3}
114
+ title="社内メール"
115
+ statusLabel="稼働中"
116
+ statusTone="success"
117
+ description="メールボックス、配布リスト、共有署名を管理します。"
118
+ metadata="mail.corp.example.jp"
119
+ action={<Button>サービスを開く</Button>}
120
+ />
121
+ <ServiceLauncherCard
122
+ icon={Server}
123
+ titleLevel={3}
124
+ title="ファイル共有"
125
+ statusLabel="メンテナンス中"
126
+ statusTone="warning"
127
+ description="部門フォルダと外部共有リンクを扱います。"
128
+ metadata="files.corp.example.jp"
129
+ action={<Button variant="outline">状況を見る</Button>}
130
+ />
131
+ <ServiceLauncherCard
132
+ icon={Database}
133
+ titleLevel={3}
134
+ title="データ連携基盤"
135
+ statusLabel="停止中"
136
+ statusTone="destructive"
137
+ description="外部システムとのデータ同期ジョブを管理します。"
138
+ metadata="sync.corp.example.jp"
139
+ action={<Button variant="outline">履歴を見る</Button>}
140
+ />
141
+ <ServiceLauncherCard
142
+ icon={ShieldCheck}
143
+ titleLevel={3}
144
+ title="権限管理"
145
+ statusLabel="管理者限定"
146
+ statusTone="info"
147
+ description="ロール、二要素認証ポリシー、監査イベントを確認します。"
148
+ metadata="iam.corp.example.jp"
149
+ action={<Button variant="outline">権限を確認</Button>}
150
+ />
151
+ <ServiceLauncherCard
152
+ icon={PlugZap}
153
+ titleLevel={3}
154
+ title="連携アプリ"
155
+ statusLabel="準備中"
156
+ statusTone="neutral"
157
+ description="サードパーティ連携の接続とスコープを設定します。"
158
+ metadata="connect.corp.example.jp"
159
+ action={<Button variant="outline">設定を開く</Button>}
160
+ />
161
+ <ServiceLauncherCard
162
+ icon={BookOpen}
163
+ titleLevel={3}
164
+ title="旧ナレッジベース"
165
+ statusLabel="アーカイブ"
166
+ statusTone="muted"
167
+ description="2024 年度までの運用手順を読み取り専用で保管しています。"
168
+ metadata="kb-archive.corp.example.jp"
169
+ action={<Button variant="outline">アーカイブを見る</Button>}
170
+ />
171
+ </ResponsiveGrid>
172
+ </Section>
173
+
174
+ <Section
175
+ title="3 · disabledReason · 操作が効かない理由は操作より先に置かれる"
176
+ why="無効化されたボタンは「なぜ」を一切伝えません。だから理由は DOM 順で操作より先に置かれ(WCAG 2.2 · 1.3.2)、タイルは data-unavailable を帯びてメダリオンが沈みます。disabledReason はボタンを無効化しません — それは利用側の Button prop のままです。"
177
+ >
178
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
179
+ <ServiceLauncherCard
180
+ icon={Boxes}
181
+ titleLevel={3}
182
+ title="在庫管理"
183
+ statusLabel="未申請"
184
+ statusTone="warning"
185
+ description="拠点別の在庫、入出庫、棚卸を管理します。"
186
+ metadata="zaiko.corp.example.jp"
187
+ disabledReason="この部門ではまだ利用申請が承認されていません。"
188
+ action={<Button disabled>サービスを開く</Button>}
189
+ />
190
+ <ServiceLauncherCard
191
+ icon={FileText}
192
+ titleLevel={3}
193
+ title="契約書管理"
194
+ statusLabel="権限なし"
195
+ statusTone="info"
196
+ description="契約書の保管、期限通知、電子署名の依頼を扱います。"
197
+ metadata="keiyaku.corp.example.jp"
198
+ disabledReason="閲覧には法務部門のロールが必要です。IT ヘルプデスクへ申請してください。"
199
+ action={<Button disabled>サービスを開く</Button>}
200
+ />
201
+ <ServiceLauncherCard
202
+ icon={Database}
203
+ titleLevel={3}
204
+ title="分析基盤"
205
+ statusLabel="停止中"
206
+ statusTone="destructive"
207
+ description="全社ダッシュボードとデータマートを提供します。"
208
+ metadata="bi.corp.example.jp"
209
+ disabledReason="このサービスは組織の管理者によって停止されています。"
210
+ action={<Button disabled>サービスを開く</Button>}
211
+ />
212
+ </ResponsiveGrid>
213
+ </Section>
214
+
215
+ <Section
216
+ title="4 · 読み込み中とカタログ導線"
217
+ why="ServiceLauncherCardSkeleton は同じ骨格の初期プレースホルダーで、label が必須・aria-busy を持ち、ライブリージョンは意図的に開きません。ServiceCatalogCta は実在する追加導線がある場合にだけ並べる相棒タイルで、既定の Plus 以外のグリフも受け取ります。"
218
+ >
219
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
220
+ <ServiceLauncherCardSkeleton label="サービスを読み込み中" />
221
+ <ServiceCatalogCta
222
+ title="アプリを追加"
223
+ action={<Button variant="outline">申請フォームを開く</Button>}
224
+ />
225
+ <ServiceCatalogCta
226
+ icon={LayoutGrid}
227
+ title="すべての社内アプリを見る"
228
+ action={<Button variant="ghost">一覧を開く</Button>}
229
+ />
230
+ </ResponsiveGrid>
231
+ </Section>
65
232
 
66
- {/* Remaining status tones — the label text is always the consumer's own copy; the
67
- * component maps only the tone to a semantic Badge. */}
68
- <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
69
- <ServiceLauncherCard
70
- icon={Database}
71
- titleLevel={3}
72
- title="データ連携基盤"
73
- statusLabel="停止中"
74
- statusTone="destructive"
75
- description="外部システムとのデータ同期ジョブを管理します。"
76
- metadata="sync.godx.jp · Enterprise"
77
- disabledReason="このサービスは組織の管理者によって停止されています。"
78
- action={<Button disabled>サービスを開く</Button>}
79
- />
80
- <ServiceLauncherCard
81
- icon={PlugZap}
82
- titleLevel={3}
83
- title="連携アプリ"
84
- statusLabel="準備中"
85
- statusTone="neutral"
86
- description="サードパーティ連携の接続とスコープを設定します。"
87
- action={<Button variant="outline">設定を開く</Button>}
88
- />
89
- <ServiceLauncherCard
90
- icon={BookPlus}
91
- titleLevel={3}
92
- title="ナレッジベース"
93
- statusLabel="アーカイブ"
94
- statusTone="muted"
95
- description="社内ドキュメントと運用手順を保管します。"
96
- metadata="kb.godx.jp · Free"
97
- action={<Button variant="outline">アーカイブを見る</Button>}
98
- />
99
- {/* The companion CTA takes an explicit glyph when the default `Plus` is too generic. */}
100
- <ServiceCatalogCta
101
- icon={LayoutGrid}
102
- title="すべてのサービスを見る"
103
- action={<Button variant="ghost">カタログ一覧</Button>}
104
- />
105
- </ResponsiveGrid>
233
+ <Section
234
+ title="5 · 端に触れる · 3 行の名称、説明なし、切れない長いホスト名"
235
+ why="整った例はタイルが崩れる場所を何も見せません。ここは崩れる側です:3 行になる名称と 2 語の名称が同じ行に並び、説明のないタイルが説明のあるタイルの隣に置かれ、空白のないホスト名が列幅を広げようとします。見苦しく見えるなら、このセクションは仕事をしています — 耐えるのはサンプルデータではなくコンポーネントの側です。"
236
+ >
237
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
238
+ <ServiceLauncherCard
239
+ icon={ShieldCheck}
240
+ titleLevel={3}
241
+ title="グローバル人事情報基盤・従業員セルフサービスポータル(アジア太平洋地域)"
242
+ statusLabel="利用可能"
243
+ statusTone="success"
244
+ description="人事マスタ、勤怠、給与、評価を一元管理する統合ワークスペースです。"
245
+ metadata="workforce-identity-administration.ap-northeast-1.corp.example.jp · v11.0"
246
+ action={<Button>サービスを開く</Button>}
247
+ />
248
+ <ServiceLauncherCard
249
+ icon={Clock3}
250
+ titleLevel={3}
251
+ title="日報"
252
+ statusLabel="利用可能"
253
+ statusTone="success"
254
+ metadata="nippo.corp.example.jp"
255
+ action={<Button>サービスを開く</Button>}
256
+ />
257
+ <ServiceLauncherCard
258
+ icon={Server}
259
+ titleLevel={3}
260
+ title="Global Workforce Identity & Entitlement Administration Console"
261
+ statusLabel="Attention required"
262
+ statusTone="warning"
263
+ description="Review pending entitlement requests before the next access certification."
264
+ metadata="workforce-identity-administration.ap-southeast-1.corp.example.com"
265
+ disabledReason="Your organization has no approved request for this service yet."
266
+ action={<Button disabled>Open service</Button>}
267
+ />
268
+ </ResponsiveGrid>
269
+ </Section>
106
270
 
107
- {/* Long JA / EN / VI names + an unbreakable hostname: the tile must wrap, never widen its
108
- * grid column. Same 3 → 2 → 1 grid, no page-local CSS anywhere. */}
109
- <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
110
- <ServiceLauncherCard
111
- icon={ShieldCheck}
112
- titleLevel={3}
113
- title="グローバル人事情報基盤・従業員セルフサービスポータル(アジア太平洋地域)"
114
- statusLabel="LIVE"
115
- statusTone="success"
116
- description="人事マスタ、勤怠、給与、評価を一元管理する統合ワークスペースです。"
117
- metadata="workforce-identity-administration.ap-northeast-1.godx.jp · Enterprise"
118
- action={<Button>サービスを開く</Button>}
119
- />
120
- <ServiceLauncherCard
121
- icon={ShieldCheck}
122
- titleLevel={3}
123
- title="Acme Global Workforce Identity & Entitlement Administration Console"
124
- statusLabel="Attention required"
125
- statusTone="warning"
126
- description="Review pending entitlement requests before the next access certification."
127
- metadata="workforce-identity-administration.ap-northeast-1.godx.jp · Enterprise"
128
- disabledReason="Your organization has no active subscription for this service yet."
129
- action={<Button disabled>Open service</Button>}
130
- />
131
- <ServiceLauncherCard
132
- icon={ShieldCheck}
133
- titleLevel={3}
134
- title="Cổng quản trị định danh và phân quyền nhân sự toàn cầu của doanh nghiệp"
135
- statusLabel="Chỉ quản trị viên"
136
- statusTone="info"
137
- description="Xem lại quyền truy cập, chính sách xác thực hai yếu tố và nhật ký kiểm toán."
138
- metadata="workforce-identity-administration.ap-southeast-1.godx.jp · Enterprise"
139
- action={<Button variant="outline">Xem quyền truy cập</Button>}
140
- />
141
- </ResponsiveGrid>
271
+ <Section
272
+ title="6 · 最小のタイル"
273
+ why="必須は icon・title・action の 3 つだけです。statusLabel を省けば Badge の行ごと消え、description と metadata を省いても骨格は保たれます — 隣の満載のタイルと高さを比べてください。"
274
+ >
275
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>
276
+ <ServiceLauncherCard
277
+ icon={BookOpen}
278
+ titleLevel={3}
279
+ title="社内 Wiki"
280
+ action={<Button variant="outline">開く</Button>}
281
+ />
282
+ <ServiceLauncherCard
283
+ icon={Mail}
284
+ titleLevel={3}
285
+ title="問い合わせ窓口"
286
+ description="社内のシステム相談はこちらで受け付けています。"
287
+ action={<Button variant="outline">開く</Button>}
288
+ />
289
+ <ServiceLauncherCard
290
+ icon={Calendar}
291
+ titleLevel={3}
292
+ title="全社カレンダー"
293
+ statusLabel="利用可能"
294
+ statusTone="success"
295
+ description="全社行事、休業日、拠点別の予定を共有します。"
296
+ metadata="calendar.corp.example.jp · v3.1"
297
+ disabledReason="読み取り専用で公開されています。"
298
+ action={<Button variant="outline">開く</Button>}
299
+ />
300
+ </ResponsiveGrid>
301
+ </Section>
142
302
  </Flex>
143
303
  </PageContainer>
144
304
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "28.6.0",
4
- "godxUiMcp": "28.6.0",
3
+ "version": "28.7.0",
4
+ "godxUiMcp": "28.7.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -370,6 +370,7 @@
370
370
  "check:text-ink-clip": "node scripts/check-text-ink-clip.mjs",
371
371
  "check:token-scale-bypass": "node scripts/check-token-scale-bypass.mjs",
372
372
  "check:token-tiers": "node scripts/check-token-tiers.mjs",
373
+ "check:token-width-wins": "node scripts/check-token-width-wins.mjs",
373
374
  "check:typography": "node scripts/check-typography.mjs",
374
375
  "check:use-client": "node scripts/check-use-client.mjs",
375
376
  "check:visual-audit": "node scripts/visual-audit-smoke.mjs",
@@ -408,10 +409,10 @@
408
409
  "typecheck:docs": "node node_modules/typescript-7/bin/tsc --noEmit -p tsconfig.docs.json",
409
410
  "typecheck:mcp": "node node_modules/typescript-7/bin/tsc --noEmit -p mcp/tsconfig.json",
410
411
  "typecheck:ts6": "tsc --noEmit -p tsconfig.json",
411
- "verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
412
+ "verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-width-wins && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
412
413
  "verify:browser": "pnpm check:contrast && pnpm check:text-ink-clip && pnpm check:frame-overflow && pnpm check:font-fallback-metrics && pnpm check:visual-audit",
413
414
  "verify:ci": "pnpm verify:ci:static && pnpm check:frame-contracts && pnpm test",
414
- "verify:ci:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm run audit && pnpm check:frame-coverage-report && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets && pnpm check:absorbed-names && pnpm check:agent-catalog",
415
+ "verify:ci:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-width-wins && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm run audit && pnpm check:frame-coverage-report && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets && pnpm check:absorbed-names && pnpm check:agent-catalog",
415
416
  "verify:publish-tree": "pnpm build && pnpm check:packed-public-contract && pnpm check:use-client && pnpm check:dist-tokens-resolve",
416
417
  "verify:release": "pnpm verify:static && pnpm check:frame-contracts && pnpm check:frame-coverage",
417
418
  "verify:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm test",
@@ -95,6 +95,32 @@ function collectInPage() {
95
95
  const cs = getComputedStyle(el);
96
96
  return r.width > 0 && r.height > 0 && cs.visibility !== "hidden" && cs.display !== "none";
97
97
  };
98
+ // `visible` above is ELEMENT-LOCAL — it reads the node's own box and its own visibility/display,
99
+ // and an ancestor can still clip the node out of the rendered page entirely. The visually-hidden
100
+ // idiom (Tailwind `sr-only`, react-aria `<VisuallyHidden>`) is exactly that: a 1x1 container with
101
+ // `overflow: hidden` + `clip: rect(0 0 0 0)` wrapping a FULL-SIZE child. The child's own
102
+ // getBoundingClientRect stays full size, so an element-local test calls it visible.
103
+ //
104
+ // gh#818: every `<Select>` carries react-aria's native `<select>` in one of those containers —
105
+ // the form-submit + browser-autofill fallback, `aria-hidden`, `tabindex="-1"`, painted nowhere.
106
+ // Its box is the width of its concatenated option labels (85x22 for a locale picker), so
107
+ // `target-size-min` reported one per Select on every consumer page, and the consumer read it as
108
+ // a second, undersized language control sitting beside the styled trigger. Measured in Chromium:
109
+ // `document.elementFromPoint` at that `<select>`'s own centre never returns the select, and at
110
+ // the trigger's centre +/-11px it returns the trigger — there is one target, not two.
111
+ //
112
+ // SC 2.5.8 bounds TARGETS: "the region of the display that will accept a pointer action". A node
113
+ // clipped to a 1px sliver accepts none at any size, so it is not a target and must not be
114
+ // measured as one — enlarging it would be the wrong fix twice over.
115
+ const clippedAway = (el) => {
116
+ for (let a = el.parentElement; a; a = a.parentElement) {
117
+ const cs = getComputedStyle(a);
118
+ if (cs.overflow === "visible" && cs.clip === "auto" && cs.clipPath === "none") continue;
119
+ const r = a.getBoundingClientRect();
120
+ if (r.width <= 1 || r.height <= 1) return true;
121
+ }
122
+ return false;
123
+ };
98
124
 
99
125
  // Accent surfaces — buttons / primary CTAs / banners whose background carries colour.
100
126
  const accents = [];
@@ -116,7 +142,9 @@ function collectInPage() {
116
142
  for (const el of document.querySelectorAll(
117
143
  "a[href], button, [role=button], input:not([type=hidden]), select, [tabindex]:not([tabindex='-1'])",
118
144
  )) {
119
- if (!visible(el)) continue;
145
+ // Dropped BEFORE the list is built, not filtered after: a node nobody can point at must not
146
+ // count as a neighbour either, or it spends the Spacing exception of the real target it sits on.
147
+ if (!visible(el) || clippedAway(el)) continue;
120
148
  const r = el.getBoundingClientRect();
121
149
  const name = (el.getAttribute("aria-label") || el.textContent || "").trim().slice(0, 40);
122
150
  rawTargets.push({