@godxjp/ui 19.2.0 → 19.4.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.
Files changed (144) hide show
  1. package/dist/components/data-display/badge.d.ts +26 -2
  2. package/dist/components/data-display/badge.js +20 -5
  3. package/dist/components/data-display/index.d.ts +2 -0
  4. package/dist/components/data-display/index.js +2 -0
  5. package/dist/components/data-display/popover.d.ts +13 -1
  6. package/dist/components/data-display/popover.js +3 -1
  7. package/dist/components/data-display/table.d.ts +19 -2
  8. package/dist/components/data-display/table.js +4 -1
  9. package/dist/components/data-display/timeline-grid.d.ts +18 -0
  10. package/dist/components/data-display/timeline-grid.js +222 -0
  11. package/dist/components/data-entry/cascader.js +4 -6
  12. package/dist/components/data-entry/checkbox.js +1 -1
  13. package/dist/components/data-entry/search-select.js +5 -7
  14. package/dist/components/data-entry/select.js +32 -28
  15. package/dist/components/data-entry/tree-select.js +4 -6
  16. package/dist/components/general/inert-background.d.ts +5 -0
  17. package/dist/components/general/inert-background.js +48 -0
  18. package/dist/components/layout/auth-shell.d.ts +4 -2
  19. package/dist/components/layout/auth-shell.js +7 -1
  20. package/dist/components/layout/centered-shell.d.ts +2 -2
  21. package/dist/components/layout/flex.d.ts +1 -1
  22. package/dist/components/layout/flex.js +2 -1
  23. package/dist/components/layout/index.d.ts +3 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/mobile-shell.d.ts +21 -0
  26. package/dist/components/layout/mobile-shell.js +43 -0
  27. package/dist/components/layout/sidebar-link.d.ts +1 -1
  28. package/dist/components/navigation/context-menu.js +13 -9
  29. package/dist/components/navigation/dropdown-menu.js +17 -13
  30. package/dist/inertia/index.d.ts +1 -1
  31. package/dist/lib/variants.d.ts +3 -3
  32. package/dist/lib/variants.js +1 -0
  33. package/dist/props/components/data-display.prop.d.ts +72 -0
  34. package/dist/props/components/data-entry.prop.d.ts +1 -1
  35. package/dist/props/components/layout.prop.d.ts +93 -6
  36. package/dist/props/registry.d.ts +113 -2
  37. package/dist/props/registry.js +155 -1
  38. package/dist/props/vocabulary/data.prop.d.ts +6 -0
  39. package/dist/props/vocabulary/index.d.ts +3 -3
  40. package/dist/props/vocabulary/layout.prop.d.ts +17 -2
  41. package/dist/props/vocabulary/shared.prop.d.ts +7 -0
  42. package/dist/styles/badge-layout.css +15 -0
  43. package/dist/styles/control.css +18 -1
  44. package/dist/styles/data-display-layout.css +167 -0
  45. package/dist/styles/layout.css +7 -0
  46. package/dist/styles/shell-layout.css +148 -0
  47. package/dist/styles/table-layout.css +23 -4
  48. package/dist/tokens/components/badge.css +8 -0
  49. package/dist/tokens/components/control.css +1 -0
  50. package/dist/tokens/components/data-display.css +20 -0
  51. package/dist/tokens/components/shell.css +22 -0
  52. package/dist/tokens/components/table.css +2 -0
  53. package/docs/SPACING.md +19 -6
  54. package/docs/charts/compact-bar-trend.tsx +5 -6
  55. package/docs/data-display/avatar.tsx +7 -9
  56. package/docs/data-display/badge.tsx +37 -1
  57. package/docs/data-display/card/index.tsx +6 -4
  58. package/docs/data-display/carousel.tsx +8 -4
  59. package/docs/data-display/data-table/examples/approval-queue.tsx +1 -1
  60. package/docs/data-display/data-table/examples/grid-features.tsx +1 -1
  61. package/docs/data-display/data-table/index.tsx +1 -1
  62. package/docs/data-display/permission-matrix.tsx +15 -11
  63. package/docs/data-display/popover.tsx +2 -1
  64. package/docs/data-display/scroll-area.tsx +108 -76
  65. package/docs/data-display/table.tsx +2 -2
  66. package/docs/data-display/timeline-grid.tsx +119 -0
  67. package/docs/data-entry/branch-scope-picker.tsx +1 -1
  68. package/docs/data-entry/calendar.tsx +1 -1
  69. package/docs/data-entry/checkbox.tsx +1 -1
  70. package/docs/data-entry/country-picker-recipe.tsx +11 -6
  71. package/docs/data-entry/form-field/index.tsx +33 -29
  72. package/docs/data-entry/form-root.tsx +1 -1
  73. package/docs/data-entry/input-otp.tsx +1 -1
  74. package/docs/data-entry/password-input.tsx +1 -5
  75. package/docs/data-entry/select.tsx +7 -7
  76. package/docs/data-entry/textarea.tsx +3 -3
  77. package/docs/data-entry/toggle-count.tsx +1 -1
  78. package/docs/feedback/alert.tsx +1 -1
  79. package/docs/feedback/banner.tsx +1 -1
  80. package/docs/feedback/sheet.tsx +112 -102
  81. package/docs/feedback/skeleton.tsx +2 -2
  82. package/docs/foundation/colors.tsx +8 -6
  83. package/docs/foundation/email-tokens.tsx +83 -66
  84. package/docs/foundation/radius-shadow.tsx +13 -11
  85. package/docs/general/activity.tsx +1 -1
  86. package/docs/layout/app-shell.tsx +2 -2
  87. package/docs/layout/aspect-ratio.tsx +30 -14
  88. package/docs/layout/auth-account-summary.tsx +1 -1
  89. package/docs/layout/auth-recovery/examples/mobile-390.tsx +1 -1
  90. package/docs/layout/auth-recovery/examples/password-recovery.tsx +0 -2
  91. package/docs/layout/auth-shell-context.tsx +1 -1
  92. package/docs/layout/auth-shell-device.tsx +2 -2
  93. package/docs/layout/auth-shell-registration.tsx +1 -1
  94. package/docs/layout/auth-shell.tsx +1 -1
  95. package/docs/layout/flex.tsx +6 -2
  96. package/docs/layout/master-detail.tsx +2 -2
  97. package/docs/layout/mobile-shell.tsx +101 -0
  98. package/docs/layout/page-container.tsx +15 -7
  99. package/docs/layout/responsive-grid.tsx +12 -4
  100. package/docs/layout/separator.tsx +2 -2
  101. package/docs/layout/service-role-panel.tsx +1 -1
  102. package/docs/layout/sidebar.tsx +175 -152
  103. package/docs/layout/split-pane.tsx +45 -28
  104. package/docs/layout/topbar.tsx +19 -23
  105. package/docs/navigation/app-setting-picker.tsx +5 -4
  106. package/docs/navigation/breadcrumb.tsx +12 -5
  107. package/docs/navigation/context-menu.tsx +10 -4
  108. package/docs/navigation/filter-bar.tsx +2 -2
  109. package/docs/navigation/navigation-menu.tsx +89 -96
  110. package/docs/navigation/pagination.tsx +2 -2
  111. package/docs/navigation/steps.tsx +13 -11
  112. package/docs/navigation/tabs.tsx +27 -21
  113. package/docs/navigation/toolbar.tsx +15 -15
  114. package/docs/providers/format-date.tsx +2 -2
  115. package/docs/showcase/acme-portal.tsx +41 -28
  116. package/docs/showcase/acme-website.tsx +67 -61
  117. package/docs/showcase/case1-warehouse-dashboard.tsx +46 -31
  118. package/docs/showcase/case2-employee-me.tsx +33 -36
  119. package/docs/showcase/case3-approval-workflow.tsx +34 -32
  120. package/docs/showcase/case4-login.tsx +95 -101
  121. package/docs/showcase/case5-shift-calendar.tsx +182 -281
  122. package/docs/showcase/case6-agency-handy.tsx +514 -470
  123. package/docs/showcase/futurelastic-web.tsx +32 -29
  124. package/docs/showcase/org-switcher.tsx +20 -13
  125. package/docs/showcase/permission-matrix.tsx +20 -17
  126. package/docs/showcase/public-landing.tsx +1 -1
  127. package/docs/showcase/settings-account-sections.tsx +2 -2
  128. package/docs/showcase/settings-security-mfa.tsx +3 -3
  129. package/docs/showcase/table-approval-queue.tsx +1 -1
  130. package/docs/showcase/table-bulk-actions.tsx +49 -52
  131. package/docs/showcase/table-conditional-format.tsx +9 -9
  132. package/docs/showcase/table-crud-list.tsx +4 -4
  133. package/docs/showcase/table-density.tsx +49 -42
  134. package/docs/showcase/table-expandable-rows.tsx +112 -100
  135. package/docs/showcase/table-filter-chips.tsx +78 -71
  136. package/docs/showcase/table-footer-totals.tsx +30 -23
  137. package/docs/showcase/table-grouped-subtotals.tsx +31 -25
  138. package/docs/showcase/table-master-detail.tsx +27 -24
  139. package/docs/showcase/table-pagination.tsx +34 -27
  140. package/docs/showcase/table-states.tsx +42 -34
  141. package/docs/showcase/table-sticky-columns.tsx +11 -7
  142. package/docs/showcase/table-tree-rows.tsx +22 -15
  143. package/docs/showcase/table-view-tabs.tsx +12 -14
  144. package/package.json +7 -7
@@ -83,6 +83,7 @@ const THEME = `
83
83
  [data-tenant="futurelastic"] .fl-lead { font-size: 1.25rem; line-height: 1.65; color: hsl(var(--muted-foreground)); max-width: 620px; }
84
84
  [data-tenant="futurelastic"] .fl-note { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-top: 2.5rem; }
85
85
  [data-tenant="futurelastic"] .fl-head { max-width: 640px; margin-inline: auto; text-align: center; }
86
+ [data-tenant="futurelastic"] .fl-hero-actions { margin-top: 1.75rem; }
86
87
  [data-tenant="futurelastic"] .fl-cta-inner { position: relative; max-width: 680px; margin-inline: auto; }
87
88
  [data-tenant="futurelastic"] .fl-footer-tagline { max-width: 260px; }
88
89
  [data-tenant="futurelastic"] .fl-logocloud { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem 2.5rem; }
@@ -121,7 +122,7 @@ function Navbar() {
121
122
  <span className="fl-brand">
122
123
  futur<span className="fl-gold">elastic</span>
123
124
  </span>
124
- <Flex direction="row" gap="xs" align="center" className="ms-4">
125
+ <Flex direction="row" gap="xs" align="center">
125
126
  {links.map((l, i) => (
126
127
  <Button key={l} variant="ghost" size="sm" aria-current={i === 0 ? "page" : undefined}>
127
128
  {l}
@@ -154,7 +155,7 @@ function Hero() {
154
155
  Futurelastic là pháp nhân holding của nhóm công ty công nghệ &amp; AI vận hành xuyên Nhật
155
156
  Bản và Việt Nam, từ logistics, fintech đến F&amp;B và thương mại.
156
157
  </p>
157
- <Flex direction="row" gap="md" className="mt-7">
158
+ <Flex direction="row" gap="md" className="fl-hero-actions">
158
159
  <Button size="lg">Khám phá hệ sinh thái</Button>
159
160
  <Button variant="outline" size="lg">
160
161
  Liên hệ hợp tác
@@ -176,8 +177,8 @@ function LogoCloud() {
176
177
  ];
177
178
  return (
178
179
  <section className="fl-section-tint">
179
- <div className={SHELL}>
180
- <Text as="p" size="xs" tone="muted" className="mb-6 text-center tracking-widest uppercase">
180
+ <Flex direction="col" gap="lg" className={SHELL}>
181
+ <Text as="p" size="xs" tone="muted" className="text-center tracking-widest uppercase">
181
182
  Các pháp nhân vận hành dưới Futurelastic
182
183
  </Text>
183
184
  <div className="fl-logocloud">
@@ -187,7 +188,7 @@ function LogoCloud() {
187
188
  </span>
188
189
  ))}
189
190
  </div>
190
- </div>
191
+ </Flex>
191
192
  </section>
192
193
  );
193
194
  }
@@ -235,8 +236,8 @@ function Bento() {
235
236
  ];
236
237
  return (
237
238
  <section className="fl-section" id="ventures">
238
- <div className={SHELL}>
239
- <div className="fl-head mb-12">
239
+ <Flex direction="col" gap="xl" className={SHELL}>
240
+ <div className="fl-head">
240
241
  <div className="fl-eyebrow">Hệ sinh thái</div>
241
242
  <h2 className="fl-h2">Một mái nhà, nhiều mũi nhọn</h2>
242
243
  <p className="fl-lead mx-auto">
@@ -247,18 +248,20 @@ function Bento() {
247
248
  {cells.map((c) => (
248
249
  <Card key={c.title} className={`fl-cell ${c.span ?? ""}`}>
249
250
  <CardContent>
250
- <Medallion icon={c.icon} />
251
- <Text as="div" weight="bold" size="lg" className="mt-5">
252
- {c.title}
253
- </Text>
254
- <Text as="p" size="sm" tone="muted" className="mt-2">
255
- {c.text}
256
- </Text>
251
+ <Flex direction="col" gap="lg">
252
+ <Medallion icon={c.icon} />
253
+ <Text as="div" weight="bold" size="lg">
254
+ {c.title}
255
+ </Text>
256
+ <Text as="p" size="sm" tone="muted">
257
+ {c.text}
258
+ </Text>
259
+ </Flex>
257
260
  </CardContent>
258
261
  </Card>
259
262
  ))}
260
263
  </div>
261
- </div>
264
+ </Flex>
262
265
  </section>
263
266
  );
264
267
  }
@@ -275,12 +278,12 @@ function Stats() {
275
278
  <div className={SHELL}>
276
279
  <div className="fl-stats">
277
280
  {stats.map(([n, l]) => (
278
- <div key={l} className="text-center">
281
+ <Flex key={l} direction="col" gap="sm" className="text-center">
279
282
  <div className="fl-stat-num">{n}</div>
280
- <Text as="div" size="sm" tone="muted" className="mt-2">
283
+ <Text as="div" size="sm" tone="muted">
281
284
  {l}
282
285
  </Text>
283
- </div>
286
+ </Flex>
284
287
  ))}
285
288
  </div>
286
289
  </div>
@@ -294,20 +297,20 @@ function Cta() {
294
297
  <div className={SHELL}>
295
298
  <div className="fl-cta">
296
299
  <div className="fl-cta-glow" />
297
- <div className="fl-cta-inner">
300
+ <Flex direction="col" gap="xl" className="fl-cta-inner">
298
301
  <h2 className="fl-h2">Cùng xây điều mới?</h2>
299
- <p className="fl-lead mx-auto mt-3">
302
+ <p className="fl-lead mx-auto">
300
303
  Chúng tôi tìm đối tác, nhân tài và cơ hội M&amp;A trong công nghệ, logistics và
301
304
  F&amp;B tại Nhật Bản &amp; Việt Nam.
302
305
  </p>
303
- <Flex direction="row" gap="md" className="mt-7 justify-center">
306
+ <Flex direction="row" gap="md" className="justify-center">
304
307
  <Button size="lg">Đặt lịch trao đổi</Button>
305
308
  <Button variant="ghost" size="lg">
306
309
  Đọc blog
307
310
  <ArrowRight aria-hidden="true" />
308
311
  </Button>
309
312
  </Flex>
310
- </div>
313
+ </Flex>
311
314
  </div>
312
315
  </div>
313
316
  </section>
@@ -323,26 +326,26 @@ function Footer() {
323
326
  return (
324
327
  <footer className="fl-footer">
325
328
  <div className={`${SHELL} fl-footer-grid`}>
326
- <div>
329
+ <Flex direction="col" gap="md">
327
330
  <span className="fl-brand">
328
331
  futur<span className="fl-gold">elastic</span>
329
332
  </span>
330
- <Text as="p" size="sm" tone="muted" className="fl-footer-tagline mt-4">
333
+ <Text as="p" size="sm" tone="muted" className="fl-footer-tagline">
331
334
  Tech · AI · Holdings · building the elastic infrastructure of the future across Japan
332
335
  &amp; Vietnam.
333
336
  </Text>
334
- </div>
337
+ </Flex>
335
338
  {cols.map(([h, items]) => (
336
- <div key={h}>
339
+ <Flex direction="col" gap="md" key={h}>
337
340
  <Text
338
341
  as="div"
339
342
  size="2xs"
340
343
  weight="bold"
341
- className="text-foreground mb-3 tracking-widest uppercase"
344
+ className="text-foreground tracking-widest uppercase"
342
345
  >
343
346
  {h}
344
347
  </Text>
345
- <Flex direction="col" gap="xs" className="mt-2">
348
+ <Flex direction="col" gap="xs">
346
349
  {items.map((i) => (
347
350
  <Button
348
351
  key={i}
@@ -354,7 +357,7 @@ function Footer() {
354
357
  </Button>
355
358
  ))}
356
359
  </Flex>
357
- </div>
360
+ </Flex>
358
361
  ))}
359
362
  </div>
360
363
  <div className={`${SHELL} fl-footer-bottom`}>
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Showcase · OrgSwitcher — sidebar organization/tenant switcher (Slack/Linear pattern)
3
3
  *
4
- * Requested by dxs-platform (godxjp-ui#196) as a "first-class OrgSwitcher component".
4
+ * Requested by dxs-platform as a "first-class OrgSwitcher component".
5
5
  * GATE 0 — Framework-Component Test verdict: **COMPOSITION PATTERN**, not a framework
6
6
  * component. It owns NO new behaviour — search/keyboard/filtering come from `Command`
7
7
  * (cmdk), open/focus-trap/dismiss from `Popover`, the trigger from `Button` + `Avatar`.
@@ -44,6 +44,7 @@ import {
44
44
  AppShell,
45
45
  Flex,
46
46
  PageContainer,
47
+ Separator,
47
48
  Sidebar,
48
49
  type SidebarSectionProp,
49
50
  Topbar,
@@ -116,25 +117,29 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
116
117
  type="button"
117
118
  variant="ghost"
118
119
  aria-label={`Tổ chức hiện tại: ${current.name}. Nhấn để đổi tổ chức`}
119
- className="w-full justify-between gap-2 px-2"
120
+ className="w-full justify-between"
120
121
  >
121
- <Flex align="center" gap="sm" className="min-w-0">
122
+ {/* Inside a Button, i.e. inside a <button>, whose content model is phrasing content
123
+ only — so every Flex on this branch renders as a <span> (gh#354). */}
124
+ <Flex as="span" align="center" gap="sm" className="min-w-0">
122
125
  <Avatar className="size-7 rounded-md">
123
126
  <AvatarFallback>{monogram(current.name)}</AvatarFallback>
124
127
  </Avatar>
125
- <span className="flex min-w-0 flex-col text-start">
128
+ <Flex as="span" direction="col" gap="none" className="min-w-0 text-start">
126
129
  <Text as="span" size="sm" weight="medium" truncate>
127
130
  {current.name}
128
131
  </Text>
129
132
  <Text as="span" size="xs" tone="muted" truncate>
130
133
  {current.role}
131
134
  </Text>
132
- </span>
135
+ </Flex>
133
136
  </Flex>
134
137
  <ChevronsUpDown aria-hidden className="size-4 shrink-0 opacity-60" />
135
138
  </Button>
136
139
  </PopoverTrigger>
137
- <PopoverContent align="start" sideOffset={6} className="w-64 p-0">
140
+ {/* `flush` the Command list owns its own inset, so its rows and separators run edge to
141
+ edge inside the popover instead of being indented by the panel padding. */}
142
+ <PopoverContent flush align="start" sideOffset={6} className="w-64">
138
143
  <Command label="Chọn tổ chức">
139
144
  <CommandInput
140
145
  placeholder="Tìm tổ chức…"
@@ -154,18 +159,18 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
154
159
  keywords={[org.id]}
155
160
  onSelect={() => choose(org.id)}
156
161
  >
157
- <Flex align="center" gap="sm" className="w-full min-w-0">
162
+ <Flex as="span" align="center" gap="sm" className="w-full min-w-0">
158
163
  <Avatar className="size-6 rounded">
159
164
  <AvatarFallback>{monogram(org.name)}</AvatarFallback>
160
165
  </Avatar>
161
- <span className="flex min-w-0 flex-col text-start">
166
+ <Flex as="span" direction="col" gap="none" className="min-w-0 text-start">
162
167
  <Text as="span" size="sm" truncate>
163
168
  {org.name}
164
169
  </Text>
165
170
  <Text as="span" size="xs" tone="muted" truncate>
166
171
  {org.role}
167
172
  </Text>
168
- </span>
173
+ </Flex>
169
174
  {active ? (
170
175
  <Check aria-hidden className="text-primary ms-auto size-4 shrink-0" />
171
176
  ) : null}
@@ -176,12 +181,14 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
176
181
  })}
177
182
  </CommandGroup>
178
183
  </CommandList>
179
- <div className="border-t p-1">
184
+ <Separator />
185
+ {/* Menu-style footer: the two actions read as one list, so no seam between them. */}
186
+ <Flex direction="col" gap="none">
180
187
  <Button
181
188
  type="button"
182
189
  variant="ghost"
183
190
  size="sm"
184
- className="w-full justify-start gap-2"
191
+ className="w-full justify-start"
185
192
  onClick={() => {
186
193
  onCreate();
187
194
  setOpen(false);
@@ -194,7 +201,7 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
194
201
  type="button"
195
202
  variant="ghost"
196
203
  size="sm"
197
- className="w-full justify-start gap-2"
204
+ className="w-full justify-start"
198
205
  onClick={() => {
199
206
  onJoin();
200
207
  setOpen(false);
@@ -203,7 +210,7 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
203
210
  <TicketPlus aria-hidden className="size-4" />
204
211
  Tham gia bằng mã mời
205
212
  </Button>
206
- </div>
213
+ </Flex>
207
214
  </Command>
208
215
  </PopoverContent>
209
216
  </Popover>
@@ -4,12 +4,12 @@
4
4
  * A role × permission matrix with a STICKY first column (権限), ✓/— cells, a
5
5
  * two-role COMPARE mode, and a 差分のみ (differences-only) filter — the pattern
6
6
  * DXS role tabs (SCR-203) and the org role editor (SCR-111b) each hand-rolled
7
- * (godxjp-ui#194). Read-only by default.
7
+ *. Read-only by default.
8
8
  *
9
- * SINCE gh#257 the grid itself IS a package export — `PermissionMatrix` from
9
+ * SINCE the grid itself IS a package export — `PermissionMatrix` from
10
10
  * `@godxjp/ui/data-display` (see docs/data-display/permission-matrix.tsx). The
11
11
  * original Gate-0 "composition pattern" verdict was OUTCOME-corrected the same
12
- * way ErrorSurface was (gh#251): a consumer cannot import a docs page, so the
12
+ * way ErrorSurface was: a consumer cannot import a docs page, so the
13
13
  * canonical grid needed an importable home. Prefer the export in app code.
14
14
  *
15
15
  * This showcase remains as the COMPOSED form of the same screen — the full
@@ -34,6 +34,7 @@ import { Check, Minus } from "lucide-react";
34
34
  import {
35
35
  Badge,
36
36
  Card,
37
+ CardAction,
37
38
  CardContent,
38
39
  CardHeader,
39
40
  CardTitle,
@@ -128,10 +129,10 @@ function GrantCell({ granted }: { granted: boolean }) {
128
129
  <span className="sr-only">許可</span>
129
130
  </Badge>
130
131
  ) : (
131
- <span className="text-muted-foreground inline-flex items-center justify-center">
132
+ <Flex align="center" justify="center" gap="none" className="text-muted-foreground">
132
133
  <Minus aria-hidden="true" className="size-4" />
133
134
  <span className="sr-only">不許可</span>
134
- </span>
135
+ </Flex>
135
136
  );
136
137
  }
137
138
 
@@ -217,11 +218,13 @@ export default function Demo() {
217
218
 
218
219
  {/* ── マトリクス本体 ── */}
219
220
  <Card>
220
- <CardHeader className="flex flex-row items-center justify-between">
221
+ <CardHeader>
221
222
  <CardTitle level={2}>ロール × 権限</CardTitle>
222
- <Text size="xs" tone="muted" tabular>
223
- 権限 {rows.length} / {PERMISSIONS.length} 件 · ロール {ROLES.length}
224
- </Text>
223
+ <CardAction>
224
+ <Text size="xs" tone="muted" tabular>
225
+ 権限 {rows.length} / {PERMISSIONS.length} 件 · ロール {ROLES.length}
226
+ </Text>
227
+ </CardAction>
225
228
  </CardHeader>
226
229
  <CardContent flush>
227
230
  <div className="overflow-x-auto">
@@ -237,19 +240,19 @@ export default function Demo() {
237
240
  className={cn("text-center", isCompared && "bg-primary/[0.06]")}
238
241
  aria-label={isCompared ? `${role.name}(比較対象)` : role.name}
239
242
  >
240
- <span className="inline-flex flex-col items-center leading-tight">
241
- <span className="inline-flex items-center gap-1">
243
+ <Flex direction="col" align="center" gap="none" className="leading-tight">
244
+ <Flex align="center" gap="xs">
242
245
  <Text weight="medium">{role.name}</Text>
243
246
  {isCompared && (
244
247
  <Badge tone="primary" variant="outline">
245
248
  {role.id === compareA ? "A" : "B"}
246
249
  </Badge>
247
250
  )}
248
- </span>
251
+ </Flex>
249
252
  <Text size="2xs" tone="muted">
250
253
  {role.hint} · {countGrants(PERMISSIONS, GRANTS, role.id)}件
251
254
  </Text>
252
- </span>
255
+ </Flex>
253
256
  </TableHead>
254
257
  );
255
258
  })}
@@ -267,14 +270,14 @@ export default function Demo() {
267
270
  >
268
271
  <TableCell className={cn(PIN_START, "w-64 min-w-64 align-middle")}>
269
272
  <Flex direction="col" className="leading-tight">
270
- <span className="inline-flex items-center gap-1.5">
273
+ <Flex align="center" gap="xs">
271
274
  <Text weight="medium">{perm.name}</Text>
272
275
  {isDiffRow && (
273
276
  <Badge tone="warning" variant="outline">
274
277
  差分
275
278
  </Badge>
276
279
  )}
277
- </span>
280
+ </Flex>
278
281
  <Text size="2xs" tone="muted">
279
282
  {perm.category} · {perm.description}
280
283
  </Text>
@@ -319,9 +322,9 @@ export default function Demo() {
319
322
  </Text>
320
323
  </Flex>
321
324
  <Flex direction="row" align="center" gap="xs">
322
- <span className="text-muted-foreground inline-flex">
325
+ <Flex align="center" gap="none" className="text-muted-foreground">
323
326
  <Minus aria-hidden="true" className="size-4" />
324
- </span>
327
+ </Flex>
325
328
  <Text size="xs" tone="muted">
326
329
  不許可
327
330
  </Text>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Showcase · Public landing (gh#252) — the PUBLIC marketing/product landing page composed from
2
+ * Showcase · Public landing — the PUBLIC marketing/product landing page composed from
3
3
  * real @godxjp/ui primitives with **zero page-local CSS**.
4
4
  *
5
5
  * Gate 0 (docs/COMPOSITION-VS-COMPONENT.md): a landing header / hero / section grid / legal footer
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Showcase · settings-account-sections — the canonical SETTINGS SECTION / ROW composition (gh#216)
2
+ * Showcase · settings-account-sections — the canonical SETTINGS SECTION / ROW composition
3
3
  *
4
4
  * ── Gate 0 verdict: COMPOSITION PATTERN, not a framework component ────────────────────────────
5
5
  * A "SettingsSection" / "SettingsRow" / "DangerZone" fails the Framework-Component Test
@@ -477,7 +477,7 @@ export default function SettingsAccountSectionsShowcase() {
477
477
  </Descriptions>
478
478
  </CardContent>
479
479
  <CardFooter separated>
480
- <Flex direction="col" gap="xs" className="sm:flex-row sm:items-center sm:gap-4">
480
+ <Flex direction="col" gap="md" className="sm:flex-row sm:items-center">
481
481
  <Button variant="outline" asChild>
482
482
  <a href="https://billing.example.com/portal" target="_blank" rel="noreferrer">
483
483
  <ExternalLink aria-hidden="true" />
@@ -7,13 +7,13 @@
7
7
  * ✗ wrong (hand-rolled) ✓ here (rule)
8
8
  * ─────────────────────────────────────────────────────────────────────────────────────────
9
9
  * factor row = a centered <div> with the ListRow — leading icon · title/description ·
10
- * title duplicated as its own description trailing action (no redundant copy) (#113)
10
+ * title duplicated as its own description trailing action (no redundant copy)
11
11
  * "Xoá" rendered as plain black text destructive-toned ghost button — delete is a
12
12
  * destructive action, color carries intent
13
13
  * every action a full-width stacked bar right-aligned, auto-width actions; ONE primary
14
14
  * (rule 217 explicitly forbids this) per card, secondary = outline (rule 217)
15
15
  * 📱 / ✉️ emoji as "icons" lucide icons (no emoji in product UI) (anti-tell)
16
- * brand glyph = a styled <span> (size/radius Avatar primitive — real brand mark (#116)
16
+ * brand glyph = a styled <span> (size/radius Avatar primitive — real brand mark
17
17
  * baked as literals)
18
18
  *
19
19
  * Built ENTIRELY from real @godxjp/ui primitives. reference-design DNA: small headings, 14/1.7 body,
@@ -130,7 +130,7 @@ export default function SecurityMfaShowcase() {
130
130
  </Text>
131
131
  </Flex>
132
132
  </CardContent>
133
- <CardFooter separated className="justify-end gap-2">
133
+ <CardFooter separated className="justify-end">
134
134
  <Button variant="ghost" size="sm">
135
135
  Tải xuống
136
136
  </Button>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Showcase · アクセス承認キュー / Access approval queue (gh#253) — the canonical five-column
2
+ * Showcase · アクセス承認キュー / Access approval queue — the canonical five-column
3
3
  * approval collection at 1440 · 1024 · 390 using ONLY public API.
4
4
  *
5
5
  * The whole responsive contract is `Table preset="action-collection"` plus a `priority` on each
@@ -202,61 +202,58 @@ function BulkActionBar({
202
202
  }) {
203
203
  const effective = spanAll ? total : count;
204
204
  return (
205
- <Flex
206
- role="region"
207
- aria-label="一括操作"
208
- wrap
209
- align="center"
210
- gap="md"
211
- className="bg-primary/5 border-primary/30 rounded-md border px-3 py-2"
212
- >
213
- {/* left: count + cross-page select-all escalation + safe batch actions */}
214
- <Flex direction="row" align="center" gap="sm" className="min-w-0 flex-1">
215
- <Text tone="primary" weight="medium" tabular className="whitespace-nowrap">
216
- {effective}件を選択中
217
- </Text>
218
-
219
- {/* cross-page select-all: only offered once the whole page is selected */}
220
- {pageAllSelected &&
221
- (spanAll ? (
222
- <Text size="xs" tone="muted">
223
- フィルタ条件の全{" "}
224
- <Text as="span" tabular>
225
- {total}
226
- </Text>{" "}
227
- 件を選択しました
205
+ <Card className="bg-primary/5 border-primary/30">
206
+ <CardContent solo>
207
+ <Flex role="region" aria-label="一括操作" wrap align="center" gap="md">
208
+ {/* left: count + cross-page select-all escalation + safe batch actions */}
209
+ <Flex direction="row" align="center" gap="sm" className="min-w-0 flex-1">
210
+ <Text tone="primary" weight="medium" tabular className="whitespace-nowrap">
211
+ {effective}件を選択中
228
212
  </Text>
229
- ) : (
230
- <Button variant="link" size="sm" onClick={onSelectSpanAll}>
231
- 全 <span className="tabular-nums">{total}</span> 件を選択
213
+
214
+ {/* cross-page select-all: only offered once the whole page is selected */}
215
+ {pageAllSelected &&
216
+ (spanAll ? (
217
+ <Text size="xs" tone="muted">
218
+ フィルタ条件の全{" "}
219
+ <Text as="span" tabular>
220
+ {total}
221
+ </Text>{" "}
222
+ 件を選択しました
223
+ </Text>
224
+ ) : (
225
+ <Button variant="link" size="sm" onClick={onSelectSpanAll}>
226
+ 全 <span className="tabular-nums">{total}</span> 件を選択
227
+ </Button>
228
+ ))}
229
+
230
+ <span className="bg-border h-4 w-px" aria-hidden="true" />
231
+
232
+ <Button variant="outline" size="sm" onClick={onApprove}>
233
+ <CheckCheck aria-hidden="true" />
234
+ 一括承認
232
235
  </Button>
233
- ))}
234
-
235
- <span className="bg-border h-4 w-px" aria-hidden="true" />
236
-
237
- <Button variant="outline" size="sm" onClick={onApprove}>
238
- <CheckCheck aria-hidden="true" />
239
- 一括承認
240
- </Button>
241
- <Button variant="outline" size="sm" onClick={onExport}>
242
- <Download aria-hidden="true" />
243
- CSV出力
244
- </Button>
245
- </Flex>
236
+ <Button variant="outline" size="sm" onClick={onExport}>
237
+ <Download aria-hidden="true" />
238
+ CSV出力
239
+ </Button>
240
+ </Flex>
246
241
 
247
- {/* right: ISOLATED destructive action, kept apart from the safe ones */}
248
- <Flex direction="row" align="center" gap="sm">
249
- <Button variant="destructive" size="sm" onClick={onDelete}>
250
- <Trash2 aria-hidden="true" />
251
- 一括削除
252
- </Button>
253
- <span className="bg-border h-4 w-px" aria-hidden="true" />
254
- <Button variant="ghost" size="sm" onClick={onClear} aria-label="選択を解除">
255
- <X aria-hidden="true" />
256
- 解除
257
- </Button>
258
- </Flex>
259
- </Flex>
242
+ {/* right: ISOLATED destructive action, kept apart from the safe ones */}
243
+ <Flex direction="row" align="center" gap="sm">
244
+ <Button variant="destructive" size="sm" onClick={onDelete}>
245
+ <Trash2 aria-hidden="true" />
246
+ 一括削除
247
+ </Button>
248
+ <span className="bg-border h-4 w-px" aria-hidden="true" />
249
+ <Button variant="ghost" size="sm" onClick={onClear} aria-label="選択を解除">
250
+ <X aria-hidden="true" />
251
+ 解除
252
+ </Button>
253
+ </Flex>
254
+ </Flex>
255
+ </CardContent>
256
+ </Card>
260
257
  );
261
258
  }
262
259
 
@@ -205,11 +205,11 @@ export default function Demo() {
205
205
  sortable: true,
206
206
  render: (r) =>
207
207
  isEarlyCell(r) ? (
208
- <span className="bg-attention/10 text-attention inline-flex rounded-sm px-1.5 py-0.5">
208
+ <Badge tone="warning" shape="sharp">
209
209
  <Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
210
210
  {hours(r.earlyLeaveHours)}
211
211
  </Text>
212
- </span>
212
+ </Badge>
213
213
  ) : (
214
214
  <Text tabular>{hours(r.earlyLeaveHours)}</Text>
215
215
  ),
@@ -221,11 +221,11 @@ export default function Demo() {
221
221
  sortable: true,
222
222
  render: (r) =>
223
223
  isOvertimeCell(r) ? (
224
- <span className="bg-warning/10 text-warning-foreground inline-flex rounded-sm px-1.5 py-0.5">
224
+ <Badge tone="warning" shape="sharp">
225
225
  <Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
226
226
  {hours(r.overtime)}
227
227
  </Text>
228
- </span>
228
+ </Badge>
229
229
  ) : (
230
230
  <Text tabular>{hours(r.overtime)}</Text>
231
231
  ),
@@ -247,7 +247,7 @@ export default function Demo() {
247
247
  <Flex direction="col" gap="lg">
248
248
  {/* ── 行レベル + セルレベルの条件付き書式(実 Table primitive で合成) ── */}
249
249
  <Card>
250
- <CardHeader className="gap-2">
250
+ <CardHeader>
251
251
  <Flex direction="row" wrap align="center" justify="between" gap="sm">
252
252
  <CardTitle level={2}>勤怠サマリ · 製造部(当月締め)</CardTitle>
253
253
  <Text size="xs" tone="muted" tabular>
@@ -312,11 +312,11 @@ export default function Demo() {
312
312
  {/* 早退セル: > 2.0h は attention(朱) で背景+前景強調 */}
313
313
  <TableCell className="text-end">
314
314
  {isEarlyCell(r) ? (
315
- <span className="bg-attention/10 text-attention inline-flex rounded-sm px-1.5 py-0.5">
315
+ <Badge tone="warning" shape="sharp">
316
316
  <Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
317
317
  {hours(r.earlyLeaveHours)}
318
318
  </Text>
319
- </span>
319
+ </Badge>
320
320
  ) : (
321
321
  <Text tone="muted" tabular>
322
322
  {hours(r.earlyLeaveHours)}
@@ -326,11 +326,11 @@ export default function Demo() {
326
326
  {/* 残業セル: ≥ 45h は warning(山吹) で注意 */}
327
327
  <TableCell className="text-end">
328
328
  {isOvertimeCell(r) ? (
329
- <span className="bg-warning/10 text-warning-foreground inline-flex rounded-sm px-1.5 py-0.5">
329
+ <Badge tone="warning" shape="sharp">
330
330
  <Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
331
331
  {hours(r.overtime)}
332
332
  </Text>
333
- </span>
333
+ </Badge>
334
334
  ) : (
335
335
  <Text tabular>{hours(r.overtime)}</Text>
336
336
  )}