@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
@@ -38,11 +38,12 @@ import {
38
38
  import { Button, Text } from "@godxjp/ui/general";
39
39
  import {
40
40
  Badge,
41
- type BadgeProps,
42
41
  Card,
42
+ CardAction,
43
43
  CardContent,
44
44
  CardHeader,
45
45
  CardTitle,
46
+ ListRow,
46
47
  Progress,
47
48
  StatCard,
48
49
  Table,
@@ -52,6 +53,7 @@ import {
52
53
  TableHeader,
53
54
  TableRow,
54
55
  Timeline,
56
+ type BadgeProps,
55
57
  type TimelineItem,
56
58
  } from "@godxjp/ui/data-display";
57
59
 
@@ -228,30 +230,37 @@ export default function WarehouseDashboardShowcase() {
228
230
  {/* Queue + Activity */}
229
231
  <ResponsiveGrid columns={{ sm: 1, md: 1, lg: 2 }}>
230
232
  <Card className="self-start">
231
- <CardHeader className="flex flex-row items-center justify-between">
233
+ <CardHeader>
232
234
  <CardTitle level={2}>Cần xử lý</CardTitle>
233
- <Text size="xs" tone="muted">
234
- {QUEUE.length} hàng đợi
235
- </Text>
235
+ <CardAction>
236
+ <Text size="xs" tone="muted">
237
+ {QUEUE.length} hàng đợi
238
+ </Text>
239
+ </CardAction>
236
240
  </CardHeader>
237
241
  <CardContent flush>
238
242
  <ul className="divide-border divide-y">
239
243
  {QUEUE.map((q) => {
240
244
  return (
241
- <li key={q.title} className="flex items-center gap-3 px-4 py-3">
242
- <Badge tone={q.tone} variant="outline" icon={q.icon} aria-label={q.title} />
243
- <div className="min-w-0 flex-1">
244
- <Text as="div" size="sm" weight="medium" truncate>
245
- {q.title}
246
- </Text>
247
- <Text as="div" size="2xs" tone="muted" truncate>
248
- {q.meta}
249
- </Text>
250
- </div>
251
- <Button variant="outline" size="sm">
252
- {q.action}
253
- </Button>
254
- </li>
245
+ <ListRow
246
+ as="li"
247
+ key={q.title}
248
+ leading={
249
+ <Badge
250
+ tone={q.tone}
251
+ variant="outline"
252
+ icon={q.icon}
253
+ aria-label={q.title}
254
+ />
255
+ }
256
+ title={q.title}
257
+ description={q.meta}
258
+ trailing={
259
+ <Button variant="outline" size="sm">
260
+ {q.action}
261
+ </Button>
262
+ }
263
+ />
255
264
  );
256
265
  })}
257
266
  </ul>
@@ -259,11 +268,13 @@ export default function WarehouseDashboardShowcase() {
259
268
  </Card>
260
269
 
261
270
  <Card className="self-start">
262
- <CardHeader className="flex flex-row items-center justify-between">
271
+ <CardHeader>
263
272
  <CardTitle level={2}>Hoạt động gần đây</CardTitle>
264
- <Text size="xs" tone="muted">
265
- 24h qua
266
- </Text>
273
+ <CardAction>
274
+ <Text size="xs" tone="muted">
275
+ 24h qua
276
+ </Text>
277
+ </CardAction>
267
278
  </CardHeader>
268
279
  <CardContent>
269
280
  <Timeline items={ACTIVITY} />
@@ -274,11 +285,13 @@ export default function WarehouseDashboardShowcase() {
274
285
  {/* Capacity + Recent shipments */}
275
286
  <ResponsiveGrid columns={{ sm: 1, md: 1, lg: 2 }}>
276
287
  <Card className="self-start">
277
- <CardHeader className="flex flex-row items-center justify-between">
288
+ <CardHeader>
278
289
  <CardTitle level={2}>Dung lượng giá hàng</CardTitle>
279
- <Button variant="ghost" size="sm">
280
- Xem tất cả
281
- </Button>
290
+ <CardAction>
291
+ <Button variant="ghost" size="sm">
292
+ Xem tất cả
293
+ </Button>
294
+ </CardAction>
282
295
  </CardHeader>
283
296
  <CardContent>
284
297
  <Flex direction="col" gap="md">
@@ -298,11 +311,13 @@ export default function WarehouseDashboardShowcase() {
298
311
  </Card>
299
312
 
300
313
  <Card className="self-start">
301
- <CardHeader className="flex flex-row items-center justify-between">
314
+ <CardHeader>
302
315
  <CardTitle level={2}>Chuyến gần nhất</CardTitle>
303
- <Button variant="ghost" size="sm">
304
- Tất cả
305
- </Button>
316
+ <CardAction>
317
+ <Button variant="ghost" size="sm">
318
+ Tất cả
319
+ </Button>
320
+ </CardAction>
306
321
  </CardHeader>
307
322
  <CardContent flush>
308
323
  <Table>
@@ -39,11 +39,12 @@ import { Button, Text } from "@godxjp/ui/general";
39
39
  import { Alert, AlertDescription, AlertTitle } from "@godxjp/ui/feedback";
40
40
  import {
41
41
  Badge,
42
- type BadgeProps,
43
42
  Card,
43
+ CardAction,
44
44
  CardContent,
45
45
  CardHeader,
46
46
  CardTitle,
47
+ Descriptions,
47
48
  StatCard,
48
49
  Table,
49
50
  TableBody,
@@ -51,12 +52,14 @@ import {
51
52
  TableHead,
52
53
  TableHeader,
53
54
  TableRow,
55
+ type BadgeProps,
54
56
  } from "@godxjp/ui/data-display";
55
57
  import {
56
58
  AppShell,
57
59
  Flex,
58
60
  PageContainer,
59
61
  ResponsiveGrid,
62
+ Separator,
60
63
  Sidebar,
61
64
  type SidebarSectionProp,
62
65
  } from "@godxjp/ui/layout";
@@ -149,26 +152,28 @@ function PunchCard() {
149
152
 
150
153
  return (
151
154
  <Card density="cozy" className="self-start" accent={state === "break" ? "warning" : undefined}>
152
- <CardHeader className="flex flex-row items-center justify-between gap-3">
155
+ <CardHeader>
153
156
  <CardTitle level={2} className="whitespace-nowrap">
154
157
  打刻 · Chấm công
155
158
  </CardTitle>
156
- <Badge tone={status.tone} variant="outline" className="whitespace-nowrap">
157
- {status.label}
158
- </Badge>
159
+ <CardAction>
160
+ <Badge tone={status.tone} variant="outline" className="whitespace-nowrap">
161
+ {status.label}
162
+ </Badge>
163
+ </CardAction>
159
164
  </CardHeader>
160
165
  <CardContent>
161
166
  <Flex direction="col" gap="md">
162
167
  {/* Big tabular clock — the signature employee block. */}
163
- <div>
168
+ <Flex direction="col" gap="xs">
164
169
  {/* Hero numeral at the system's max step (2xl) — the golden scale tops here by design. */}
165
170
  <Text as="div" size="2xl" weight="bold" tabular className="leading-none">
166
171
  {clock}
167
172
  </Text>
168
- <Text as="div" size="sm" tone="muted" className="mt-1">
173
+ <Text as="div" size="sm" tone="muted">
169
174
  {status.meta}
170
175
  </Text>
171
- </div>
176
+ </Flex>
172
177
 
173
178
  {/* Checkout-locked notice (attention, not destructive — it's a fixable hold). */}
174
179
  {checkoutLocked ? (
@@ -225,8 +230,9 @@ function PunchCard() {
225
230
  </Flex>
226
231
 
227
232
  {/* Demo state stepper — lets a reader walk every FSM state at rest. */}
228
- <Flex direction="row" wrap gap="xs" className="border-t pt-3">
229
- <Text size="2xs" tone="muted" className="me-1 self-center">
233
+ <Separator />
234
+ <Flex direction="row" wrap gap="xs">
235
+ <Text size="2xs" tone="muted" className="self-center">
230
236
  状態:
231
237
  </Text>
232
238
  {(["off", "working", "break", "closed"] as PunchState[]).map((s) => (
@@ -334,7 +340,7 @@ export default function EmployeeMeShowcase() {
334
340
  <PageContainer title="ダッシュボード" subtitle="田中 美咲 · 渋谷店 · 5月度" density="default">
335
341
  <Flex direction="col" gap="lg">
336
342
  {/* Punch (left) + month KPI 2×2 (right) — stack <lg, side-by-side ≥lg */}
337
- <div className="grid grid-cols-1 gap-4 lg:grid-cols-3 lg:items-start">
343
+ <ResponsiveGrid columns={{ sm: 1, lg: 3 }} gap="md">
338
344
  <div className="lg:col-span-1">
339
345
  <PunchCard />
340
346
  </div>
@@ -353,18 +359,20 @@ export default function EmployeeMeShowcase() {
353
359
  ))}
354
360
  </ResponsiveGrid>
355
361
  </div>
356
- </div>
362
+ </ResponsiveGrid>
357
363
 
358
364
  {/* 7-day attendance (left, wide) + today summary (right) */}
359
- <div className="grid grid-cols-1 gap-4 lg:grid-cols-3 lg:items-start">
365
+ <ResponsiveGrid columns={{ sm: 1, lg: 3 }} gap="md">
360
366
  <Card className="self-start lg:col-span-2">
361
- <CardHeader className="flex flex-row items-center justify-between gap-3">
367
+ <CardHeader>
362
368
  <CardTitle level={2} className="whitespace-nowrap">
363
369
  最近7日 · 7 ngày gần nhất
364
370
  </CardTitle>
365
- <Text size="xs" tone="muted" className="whitespace-nowrap">
366
- 勤務 47.4h · 残業 1.5h
367
- </Text>
371
+ <CardAction>
372
+ <Text size="xs" tone="muted" className="whitespace-nowrap">
373
+ 勤務 47.4h · 残業 1.5h
374
+ </Text>
375
+ </CardAction>
368
376
  </CardHeader>
369
377
  <CardContent flush>
370
378
  <Table>
@@ -383,9 +391,7 @@ export default function EmployeeMeShowcase() {
383
391
  <TableRow key={d.date}>
384
392
  <TableCell className="whitespace-nowrap tabular-nums">
385
393
  {d.date}
386
- <Text tone="muted" className="ms-1.5">
387
- ({d.weekday})
388
- </Text>
394
+ <Text tone="muted">({d.weekday})</Text>
389
395
  </TableCell>
390
396
  <TableCell className="text-end tabular-nums">{d.work}</TableCell>
391
397
  <TableCell className="text-end tabular-nums">{d.ot}</TableCell>
@@ -412,28 +418,19 @@ export default function EmployeeMeShowcase() {
412
418
  本日のサマリー · Hôm nay
413
419
  </CardTitle>
414
420
  </CardHeader>
415
- <CardContent flush>
416
- <dl className="divide-border divide-y">
421
+ <CardContent>
422
+ <Descriptions columns={1} layout="horizontal" labelAlign="start">
417
423
  {SUMMARY.map((s) => (
418
- <Flex
419
- key={s.label}
420
- align="center"
421
- justify="between"
422
- gap="md"
423
- className="px-4 py-2.5"
424
- >
425
- <Text as="dt" size="sm" tone="muted" className="whitespace-nowrap">
426
- {s.label}
427
- </Text>
428
- <Text as="dd" size="sm" weight="medium" align="end" tabular>
424
+ <Descriptions.Item key={s.label} label={s.label}>
425
+ <Text size="sm" weight="medium" tabular>
429
426
  {s.value}
430
427
  </Text>
431
- </Flex>
428
+ </Descriptions.Item>
432
429
  ))}
433
- </dl>
430
+ </Descriptions>
434
431
  </CardContent>
435
432
  </Card>
436
- </div>
433
+ </ResponsiveGrid>
437
434
  </Flex>
438
435
  </PageContainer>
439
436
  </AppShell>
@@ -352,38 +352,40 @@ export default function Demo() {
352
352
  {/* ── 4-column filter bar ───────────────────────────────────────── */}
353
353
  <Card className="self-start">
354
354
  <CardContent>
355
- <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 4 }}>
356
- <FormField id="f-type" label="区分">
357
- <Select
358
- options={TYPE_OPTIONS}
359
- value={type}
360
- onValueChange={setType}
361
- placeholder="すべて"
362
- />
363
- </FormField>
364
- <FormField id="f-status" label="状態">
365
- <Select
366
- options={STATUS_OPTIONS}
367
- value={status}
368
- onValueChange={setStatus}
369
- placeholder="すべて"
370
- />
371
- </FormField>
372
- <FormField id="f-from" label="期間 (開始)">
373
- <DatePicker value={from} onValueChange={setFrom} placeholder="YYYY-MM-DD" />
374
- </FormField>
375
- <FormField id="f-to" label="期間 (終了)">
376
- <DatePicker value={to} onValueChange={setTo} placeholder="YYYY-MM-DD" />
377
- </FormField>
378
- </ResponsiveGrid>
379
- {hasActiveFilters && (
380
- <Flex direction="row" justify="end" className="mt-3">
381
- <Button variant="ghost" size="sm" onClick={clearFilters}>
382
- <X aria-hidden="true" />
383
- 条件をクリア
384
- </Button>
385
- </Flex>
386
- )}
355
+ <Flex direction="col" gap="md">
356
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 4 }}>
357
+ <FormField id="f-type" label="区分">
358
+ <Select
359
+ options={TYPE_OPTIONS}
360
+ value={type}
361
+ onValueChange={setType}
362
+ placeholder="すべて"
363
+ />
364
+ </FormField>
365
+ <FormField id="f-status" label="状態">
366
+ <Select
367
+ options={STATUS_OPTIONS}
368
+ value={status}
369
+ onValueChange={setStatus}
370
+ placeholder="すべて"
371
+ />
372
+ </FormField>
373
+ <FormField id="f-from" label="期間 (開始)">
374
+ <DatePicker value={from} onValueChange={setFrom} placeholder="YYYY-MM-DD" />
375
+ </FormField>
376
+ <FormField id="f-to" label="期間 (終了)">
377
+ <DatePicker value={to} onValueChange={setTo} placeholder="YYYY-MM-DD" />
378
+ </FormField>
379
+ </ResponsiveGrid>
380
+ {hasActiveFilters && (
381
+ <Flex direction="row" justify="end">
382
+ <Button variant="ghost" size="sm" onClick={clearFilters}>
383
+ <X aria-hidden="true" />
384
+ 条件をクリア
385
+ </Button>
386
+ </Flex>
387
+ )}
388
+ </Flex>
387
389
  </CardContent>
388
390
  </Card>
389
391
 
@@ -7,9 +7,10 @@
7
7
  * not a transcription of the prototype DOM.
8
8
  *
9
9
  * Composition map (prototype block → @godxjp/ui primitive):
10
- * no shell ............... a bare centered page (login has no AppShell)
11
- * comfortable density .... `.ui-density-comfortable` wrapper → 44px touch floor
12
- * locale + theme toggle .. Select (locale) + ToggleGroup (theme) top-right
10
+ * page shell ............. AuthShell (the unauthenticated root shell)
11
+ * comfortable density .... AuthShell's own control tier → 44px touch floor
12
+ * locale + theme toggle .. AuthShell `actions` (banner, inline end)
13
+ * two-column measure ..... AuthShell `measure="wide"` → 64rem content slot
13
14
  * split brand panel ...... Card composition, hidden < lg (mobile-first)
14
15
  * auth card .............. Card (shadow-lg — the one resting shadow login allows)
15
16
  * SSO buttons ............ Button(outline) full-width + brand SVG slot
@@ -47,8 +48,9 @@ import {
47
48
  CardHeader,
48
49
  CardTitle,
49
50
  CardDescription,
51
+ Descriptions,
50
52
  } from "@godxjp/ui/data-display";
51
- import { Flex, ResponsiveGrid, Separator } from "@godxjp/ui/layout";
53
+ import { AuthShell, Flex, ResponsiveGrid, Separator } from "@godxjp/ui/layout";
52
54
 
53
55
  // ── The single allowed multi-color brand mark (Google "G") ─────────────────────
54
56
  // Icons normally inherit currentColor; a third-party brand mark is the documented
@@ -114,13 +116,19 @@ export default function LoginShowcase() {
114
116
  const [theme, setTheme] = React.useState("light");
115
117
 
116
118
  return (
117
- <div className="ui-density-comfortable bg-muted/40 text-foreground min-h-screen">
118
- {/* Locale + theme toggle, pinned top-right. Stacks under brand on narrow. */}
119
- <Flex align="center" justify="between" gap="md" className="px-4 py-3 sm:px-6">
119
+ <AuthShell
120
+ /* The 64rem content slot the split (brand panel + card) login needs. */
121
+ measure="wide"
122
+ className="text-foreground"
123
+ /* Brand mark in the banner below lg; from lg the split panel carries it. */
124
+ brand={
120
125
  <div className="lg:hidden">
121
126
  <BrandLockup />
122
127
  </div>
123
- <Flex align="center" gap="sm" className="ms-auto">
128
+ }
129
+ /* Page-level controls, pinned to the banner's inline end. */
130
+ actions={
131
+ <>
124
132
  <Select value={locale} onValueChange={setLocale}>
125
133
  <SelectTrigger size="sm" className="w-32" aria-label="言語を選択">
126
134
  <Languages aria-hidden="true" className="text-muted-foreground" />
@@ -152,20 +160,16 @@ export default function LoginShowcase() {
152
160
  <Monitor aria-hidden="true" />
153
161
  </ToggleGroupItem>
154
162
  </ToggleGroup>
155
- </Flex>
156
- </Flex>
157
-
163
+ </>
164
+ }
165
+ >
158
166
  {/* Centered auth area. Split brand panel + card sit side-by-side from lg. */}
159
- <div className="mx-auto flex min-h-[calc(100vh-64px)] w-full max-w-5xl items-center px-4 py-8 sm:px-6">
160
- <ResponsiveGrid
161
- columns={{ sm: 1, lg: 2, md: 1 }}
162
- gap="xl"
163
- className="mx-auto w-full items-stretch"
164
- >
165
- {/* Split brand panel — hidden on mobile (mobile-first), shown from lg. */}
166
- <aside className="hidden lg:flex">
167
- <Card className="bg-primary/5 w-full">
168
- <CardContent solo className="flex h-full flex-col justify-between gap-8">
167
+ <ResponsiveGrid columns={{ sm: 1, lg: 2, md: 1 }} gap="xl" className="w-full items-stretch">
168
+ {/* Split brand panel — hidden on mobile (mobile-first), shown from lg. */}
169
+ <aside className="hidden lg:block">
170
+ <Card className="bg-primary/5 h-full w-full">
171
+ <CardContent solo className="h-full">
172
+ <Flex direction="col" justify="between" gap="xl">
169
173
  <BrandLockup size="lg" />
170
174
  <Flex direction="col" gap="md">
171
175
  <Heading level={2} className="leading-snug">
@@ -176,49 +180,43 @@ export default function LoginShowcase() {
176
180
  多拠点・多テナントに対応し、現場と管理をつなぎます。
177
181
  </Text>
178
182
  </Flex>
179
- <dl className="border-border grid grid-cols-3 gap-4 border-t pt-6">
180
- <div>
181
- <Text as="dt" size="2xs" tone="muted">
182
- 導入企業
183
- </Text>
184
- <Text as="dd" size="lg" weight="bold" tabular>
183
+ <Separator />
184
+ <Descriptions columns={3}>
185
+ <Descriptions.Item label="導入企業">
186
+ <Text size="lg" weight="bold" tabular>
185
187
  1,240
186
188
  </Text>
187
- </div>
188
- <div>
189
- <Text as="dt" size="2xs" tone="muted">
190
- 稼働拠点
191
- </Text>
192
- <Text as="dd" size="lg" weight="bold" tabular>
189
+ </Descriptions.Item>
190
+ <Descriptions.Item label="稼働拠点">
191
+ <Text size="lg" weight="bold" tabular>
193
192
  8,600
194
193
  </Text>
195
- </div>
196
- <div>
197
- <Text as="dt" size="2xs" tone="muted">
198
- 月間打刻
199
- </Text>
200
- <Text as="dd" size="lg" weight="bold" tabular>
194
+ </Descriptions.Item>
195
+ <Descriptions.Item label="月間打刻">
196
+ <Text size="lg" weight="bold" tabular>
201
197
  21M
202
198
  </Text>
203
- </div>
204
- </dl>
205
- </CardContent>
206
- </Card>
207
- </aside>
208
-
209
- {/* Auth card — the one surface allowed a resting shadow (shadow-lg). */}
210
- <Card className="mx-auto w-full max-w-sm self-center shadow-lg lg:mx-0">
211
- <CardHeader className="flex flex-col gap-1.5 pb-2 text-center lg:text-start">
212
- <Flex justify="center" className="lg:hidden">
213
- <BrandLockup />
199
+ </Descriptions.Item>
200
+ </Descriptions>
214
201
  </Flex>
215
- <CardTitle className="text-lg font-semibold">ログイン</CardTitle>
216
- <CardDescription className="text-muted-foreground text-xs">
217
- 勤怠管理プラットフォームへようこそ
218
- </CardDescription>
219
- </CardHeader>
202
+ </CardContent>
203
+ </Card>
204
+ </aside>
220
205
 
221
- <CardContent solo className="flex flex-col gap-5">
206
+ {/* Auth card — the one surface allowed a resting shadow (shadow-lg). */}
207
+ <Card className="mx-auto w-full max-w-sm self-center shadow-lg">
208
+ <CardHeader className="text-center lg:text-start">
209
+ <Flex justify="center" className="lg:hidden">
210
+ <BrandLockup />
211
+ </Flex>
212
+ <CardTitle className="text-lg font-semibold">ログイン</CardTitle>
213
+ <CardDescription className="text-muted-foreground text-xs">
214
+ 勤怠管理プラットフォームへようこそ
215
+ </CardDescription>
216
+ </CardHeader>
217
+
218
+ <CardContent solo>
219
+ <Flex direction="col" gap="lg">
222
220
  {/* SSO buttons — outline, full-width, 44px (comfortable). */}
223
221
  <Flex direction="col" gap="sm">
224
222
  <Button variant="outline" className="w-full justify-center">
@@ -241,67 +239,63 @@ export default function LoginShowcase() {
241
239
 
242
240
  {/* Email — uncontrolled, seeded so the filled state shows at rest. */}
243
241
  <form
244
- className="flex flex-col gap-4"
245
242
  onSubmit={(e) => {
246
243
  e.preventDefault();
247
244
  }}
248
245
  >
249
- <FormField id="login-email" label="メールアドレス" required>
250
- <Input
251
- type="email"
252
- name="email"
253
- autoComplete="email"
254
- inputMode="email"
255
- placeholder="name@example.com"
256
- defaultValue="m.tanaka@famgia.com"
257
- />
258
- </FormField>
246
+ <Flex direction="col" gap="md">
247
+ <FormField id="login-email" label="メールアドレス" required>
248
+ <Input
249
+ type="email"
250
+ name="email"
251
+ autoComplete="email"
252
+ inputMode="email"
253
+ placeholder="name@example.com"
254
+ defaultValue="m.tanaka@famgia.com"
255
+ />
256
+ </FormField>
259
257
 
260
- {/* Password — labelAddon hosts the right-aligned "忘れた場合" link. */}
261
- <FormField
262
- id="login-password"
263
- label="パスワード"
264
- required
265
- labelAddon={
266
- <Button
267
- type="button"
268
- variant="link"
269
- size="sm"
270
- className="ms-auto h-auto p-0 text-xs"
271
- >
272
- お忘れの場合
273
- </Button>
274
- }
275
- >
276
- <PasswordInput
277
- name="password"
278
- autoComplete="current-password"
279
- placeholder="パスワードを入力"
280
- defaultValue="example-pass"
281
- />
282
- </FormField>
258
+ {/* Password — labelAddon hosts the right-aligned "忘れた場合" link. */}
259
+ <FormField
260
+ id="login-password"
261
+ label="パスワード"
262
+ required
263
+ labelAddon={
264
+ <Button type="button" variant="link" size="sm" className="ms-auto text-xs">
265
+ お忘れの場合
266
+ </Button>
267
+ }
268
+ >
269
+ <PasswordInput
270
+ name="password"
271
+ autoComplete="current-password"
272
+ placeholder="パスワードを入力"
273
+ defaultValue="example-pass"
274
+ />
275
+ </FormField>
283
276
 
284
- {/* The single --primary action of the view. */}
285
- <Button type="submit" className="w-full justify-center">
286
- ログイン
287
- </Button>
277
+ {/* The single --primary action of the view. */}
278
+ <Button type="submit" className="w-full justify-center">
279
+ ログイン
280
+ </Button>
281
+ </Flex>
288
282
  </form>
289
283
 
290
284
  <Text as="p" size="2xs" tone="muted" align="center" className="leading-relaxed">
291
285
  ログインすると{" "}
292
- <Button variant="link" size="sm" className="h-auto p-0 text-[var(--font-size-2xs)]">
286
+ <Button variant="link" size="sm" className="text-[var(--font-size-2xs)]">
293
287
  利用規約
294
288
  </Button>{" "}
295
289
  ·{" "}
296
- <Button variant="link" size="sm" className="h-auto p-0 text-[var(--font-size-2xs)]">
290
+ <Button variant="link" size="sm" className="text-[var(--font-size-2xs)]">
297
291
  プライバシーポリシー
298
292
  </Button>{" "}
299
293
  に同意したものとみなされます。
300
294
  </Text>
301
- </CardContent>
302
- </Card>
303
- </ResponsiveGrid>
304
- </div>
305
- </div>
295
+ </Flex>
296
+ </CardContent>
297
+ </Card>
298
+ </ResponsiveGrid>
299
+ </AuthShell>
306
300
  );
307
301
  }