@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
@@ -25,7 +25,7 @@ import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
25
25
  import { buildEmailSpecimenHtml } from "./_email-specimen";
26
26
 
27
27
  /**
28
- * Email tokens · the `@godxjp/ui/email` contract (issue #227).
28
+ * Email tokens · the `@godxjp/ui/email` contract.
29
29
  *
30
30
  * A transactional email is rendered by Blade/Twig/MJML, by clients that strip `<style>`, ignore
31
31
  * CSS custom properties and block remote images — so neither the React components nor the CSS
@@ -139,8 +139,12 @@ export default function Demo() {
139
139
  <iframe
140
140
  title="Transactional email specimen at 480px"
141
141
  srcDoc={specimen}
142
- className="border-border bg-background w-full rounded-md border"
143
- style={{ height: 560 }}
142
+ className="bg-background w-full"
143
+ style={{
144
+ border: "1px solid hsl(var(--border))",
145
+ borderRadius: "var(--radius-md)",
146
+ height: 560,
147
+ }}
144
148
  />
145
149
  </CardContent>
146
150
  </Card>
@@ -160,8 +164,13 @@ export default function Demo() {
160
164
  <iframe
161
165
  title="Transactional email specimen at 360px"
162
166
  srcDoc={specimen}
163
- className="border-border bg-background rounded-md border"
164
- style={{ width: 360, height: 620 }}
167
+ className="bg-background"
168
+ style={{
169
+ border: "1px solid hsl(var(--border))",
170
+ borderRadius: "var(--radius-md)",
171
+ width: 360,
172
+ height: 620,
173
+ }}
165
174
  />
166
175
  </CardContent>
167
176
  </Card>
@@ -182,39 +191,41 @@ export default function Demo() {
182
191
  </CardDescription>
183
192
  </CardHeader>
184
193
  <CardContent>
185
- <ResponsiveGrid columns={{ sm: 1, md: 3 }}>
186
- {[
187
- { label: "Inline <svg>", markup: EMAIL_BRAND_MARK.svg },
188
- {
189
- // Prose label, not markup: it names the email delivery form. The real delivery
190
- // on the next line is a self-contained data: URI (email clients block remote
191
- // images) and it does carry alt="GoDX".
192
- // ui-audit-disable-next-line img-needs-alt
193
- label: "data: URL in <img>",
194
- markup: `<img src="${EMAIL_BRAND_MARK.dataUri}" width="${EMAIL_BRAND_MARK.widthPx}" height="${EMAIL_BRAND_MARK.heightPx}" alt="GoDX">`,
195
- },
196
- // Prose label, not markup: a raw <table> is the only brand-mark fallback that
197
- // renders in email clients without SVG support, so the label names it literally.
198
- // ui-audit-disable-next-line no-raw-table
199
- { label: "<table> fallback", markup: EMAIL_BRAND_MARK.tableHtml },
200
- ].map((item) => (
201
- <Flex key={item.label} direction="col" gap="sm">
202
- <div
203
- // eslint-disable-next-line react/no-danger
204
- dangerouslySetInnerHTML={{ __html: item.markup }}
205
- />
206
- <Text size="sm" tone="muted">
207
- {item.label}
208
- </Text>
209
- </Flex>
210
- ))}
211
- </ResponsiveGrid>
212
- <Flex direction="col" gap="xs" className="mt-4">
213
- <Text size="sm" tone="muted">
214
- viewBox {EMAIL_BRAND_MARK.viewBox} · {EMAIL_BRAND_MARK.width}×
215
- {EMAIL_BRAND_MARK.height} · capsule {EMAIL_COLORS.brand} · glyph{" "}
216
- {EMAIL_COLORS.brandForeground}
217
- </Text>
194
+ <Flex direction="col" gap="md">
195
+ <ResponsiveGrid columns={{ sm: 1, md: 3 }}>
196
+ {[
197
+ { label: "Inline <svg>", markup: EMAIL_BRAND_MARK.svg },
198
+ {
199
+ // Prose label, not markup: it names the email delivery form. The real delivery
200
+ // on the next line is a self-contained data: URI (email clients block remote
201
+ // images) and it does carry alt="GoDX".
202
+ // ui-audit-disable-next-line img-needs-alt
203
+ label: "data: URL in <img>",
204
+ markup: `<img src="${EMAIL_BRAND_MARK.dataUri}" width="${EMAIL_BRAND_MARK.widthPx}" height="${EMAIL_BRAND_MARK.heightPx}" alt="GoDX">`,
205
+ },
206
+ // Prose label, not markup: a raw <table> is the only brand-mark fallback that
207
+ // renders in email clients without SVG support, so the label names it literally.
208
+ // ui-audit-disable-next-line no-raw-table
209
+ { label: "<table> fallback", markup: EMAIL_BRAND_MARK.tableHtml },
210
+ ].map((item) => (
211
+ <Flex key={item.label} direction="col" gap="sm">
212
+ <div
213
+ // eslint-disable-next-line react/no-danger
214
+ dangerouslySetInnerHTML={{ __html: item.markup }}
215
+ />
216
+ <Text size="sm" tone="muted">
217
+ {item.label}
218
+ </Text>
219
+ </Flex>
220
+ ))}
221
+ </ResponsiveGrid>
222
+ <Flex direction="col" gap="xs">
223
+ <Text size="sm" tone="muted">
224
+ viewBox {EMAIL_BRAND_MARK.viewBox} · {EMAIL_BRAND_MARK.width}×
225
+ {EMAIL_BRAND_MARK.height} · capsule {EMAIL_COLORS.brand} · glyph{" "}
226
+ {EMAIL_COLORS.brandForeground}
227
+ </Text>
228
+ </Flex>
218
229
  </Flex>
219
230
  </CardContent>
220
231
  </Card>
@@ -235,8 +246,12 @@ export default function Demo() {
235
246
  {colorRows.map((row) => (
236
247
  <Flex key={row.token} direction="col" gap="xs">
237
248
  <div
238
- className="border-border h-12 rounded-md border"
239
- style={{ backgroundColor: row.hex }}
249
+ className="h-12"
250
+ style={{
251
+ border: "1px solid hsl(var(--border))",
252
+ borderRadius: "var(--radius-md)",
253
+ backgroundColor: row.hex,
254
+ }}
240
255
  />
241
256
  <Text size="sm">{row.token}</Text>
242
257
  <Flex gap="xs" align="center">
@@ -271,33 +286,35 @@ export default function Demo() {
271
286
  </CardDescription>
272
287
  </CardHeader>
273
288
  <CardContent>
274
- <Flex direction="col" gap="xs" className="mb-4">
275
- {fontStacks.map((stack) => (
276
- <Flex key={stack.label} direction="col" gap="xs">
277
- <Badge tone="neutral" variant="outline">
278
- {stack.label}
279
- </Badge>
280
- <Text size="sm" tone="muted">
281
- {stack.value}
282
- </Text>
283
- </Flex>
284
- ))}
289
+ <Flex direction="col" gap="md">
290
+ <Flex direction="col" gap="xs">
291
+ {fontStacks.map((stack) => (
292
+ <Flex key={stack.label} direction="col" gap="xs">
293
+ <Badge tone="neutral" variant="outline">
294
+ {stack.label}
295
+ </Badge>
296
+ <Text size="sm" tone="muted">
297
+ {stack.value}
298
+ </Text>
299
+ </Flex>
300
+ ))}
301
+ </Flex>
302
+ <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 4 }}>
303
+ {geometryGroups.map((group) => (
304
+ <Flex key={group.title} direction="col" gap="xs">
305
+ <Text weight="medium">{group.title}</Text>
306
+ {group.rows.map(([label, value]) => (
307
+ <Flex key={label} justify="between" gap="sm">
308
+ <Text size="sm" tone="muted">
309
+ {label}
310
+ </Text>
311
+ <Text size="sm">{value}</Text>
312
+ </Flex>
313
+ ))}
314
+ </Flex>
315
+ ))}
316
+ </ResponsiveGrid>
285
317
  </Flex>
286
- <ResponsiveGrid columns={{ sm: 1, md: 2, lg: 4 }}>
287
- {geometryGroups.map((group) => (
288
- <Flex key={group.title} direction="col" gap="xs">
289
- <Text weight="medium">{group.title}</Text>
290
- {group.rows.map(([label, value]) => (
291
- <Flex key={label} justify="between" gap="sm">
292
- <Text size="sm" tone="muted">
293
- {label}
294
- </Text>
295
- <Text size="sm">{value}</Text>
296
- </Flex>
297
- ))}
298
- </Flex>
299
- ))}
300
- </ResponsiveGrid>
301
318
  </CardContent>
302
319
  </Card>
303
320
  </Flex>
@@ -127,17 +127,19 @@ export default function Demo() {
127
127
  </Text>
128
128
  </Flex>
129
129
  <Flex direction="col" gap="xs">
130
- <div
131
- className="bg-popover text-popover-foreground border-border rounded-md border p-4"
132
- style={{ boxShadow: "var(--shadow-lg)" }}
133
- >
134
- <Text as="div" weight="medium">
135
- Floating(ポップオーバー)
136
- </Text>
137
- <Text as="div" size="xs" tone="muted">
138
- --shadow-lg lifts it off the page
139
- </Text>
140
- </div>
130
+ <Card>
131
+ <CardContent flush>
132
+ {/* ui-audit-disable-next-line no-utility-spacing — a tile showing the shadow token itself; the box IS the subject */}
133
+ <div className="p-4" style={{ boxShadow: "var(--shadow-lg)" }}>
134
+ <Text as="div" weight="medium">
135
+ Floating(ポップオーバー)
136
+ </Text>
137
+ <Text as="div" size="xs" tone="muted">
138
+ --shadow-lg lifts it off the page
139
+ </Text>
140
+ </div>
141
+ </CardContent>
142
+ </Card>
141
143
  <Text as="div" size="xs" tone="muted">
142
144
  floating surface · elevated
143
145
  </Text>
@@ -12,7 +12,7 @@ import { Flex, PageContainer, Separator } from "@godxjp/ui/layout";
12
12
 
13
13
  /**
14
14
  * Activity · the official AMBIENT-motion primitive: a continuous, unbounded "something is happening
15
- * right now, elsewhere". The LOOP counterpart to `Reveal`'s one-shot entrance (gh#313).
15
+ * right now, elsewhere". The LOOP counterpart to `Reveal`'s one-shot entrance.
16
16
  *
17
17
  * Reads the DS motion tokens (`--activity-interval`, `--activity-stagger-step`,
18
18
  * `--activity-mark-offset`, `--activity-color`, `--ease-standard`) so a consumer never hand-rolls a
@@ -138,7 +138,7 @@ export default function Demo() {
138
138
  {/* Decorative brand mark — hidden below `sm` where the hamburger already anchors the
139
139
  start of a narrow mobile topbar, so functional controls (entity switcher, search)
140
140
  keep priority and the bar never overflows. */}
141
- <Avatar className="hidden rounded-md sm:inline-flex">
141
+ <Avatar className="rounded-md">
142
142
  <AvatarFallback className="bg-primary text-primary-foreground font-bold">
143
143
  C
144
144
  </AvatarFallback>
@@ -159,7 +159,7 @@ export default function Demo() {
159
159
  <DropdownMenuSeparator />
160
160
  {ENTITIES.map((e) => (
161
161
  <DropdownMenuItem key={e.id} onSelect={() => setActiveEntity(e.name)}>
162
- <Building2 className="me-2 size-4" />
162
+ <Building2 className="size-4" />
163
163
  {e.name}
164
164
  </DropdownMenuItem>
165
165
  ))}
@@ -27,9 +27,13 @@ export default function Demo() {
27
27
  </CardHeader>
28
28
  <CardContent>
29
29
  <AspectRatio ratio={16 / 9}>
30
- <div className="bg-muted flex h-full w-full items-center justify-center rounded-md">
31
- <Text tone="muted">16:9 動画プレビュー(サンプル)</Text>
32
- </div>
30
+ <Card variant="muted" className="h-full w-full">
31
+ <CardContent flush>
32
+ <Flex align="center" justify="center" gap="xs">
33
+ <Text tone="muted">16:9 動画プレビュー(サンプル)</Text>
34
+ </Flex>
35
+ </CardContent>
36
+ </Card>
33
37
  </AspectRatio>
34
38
  </CardContent>
35
39
  </Card>
@@ -45,9 +49,13 @@ export default function Demo() {
45
49
  <CardContent>
46
50
  <div className="max-w-xs">
47
51
  <AspectRatio ratio={4 / 3}>
48
- <div className="bg-muted border-border flex h-full w-full items-center justify-center rounded-md border">
49
- <Text tone="muted">領収書スキャン 4:3</Text>
50
- </div>
52
+ <Card variant="muted" className="h-full w-full">
53
+ <CardContent flush>
54
+ <Flex align="center" justify="center" gap="xs">
55
+ <Text tone="muted">領収書スキャン 4:3</Text>
56
+ </Flex>
57
+ </CardContent>
58
+ </Card>
51
59
  </AspectRatio>
52
60
  </div>
53
61
  </CardContent>
@@ -66,11 +74,15 @@ export default function Demo() {
66
74
  {["株式会社 A", "合同会社 B", "有限会社 C"].map((name) => (
67
75
  <div key={name} className="w-28">
68
76
  <AspectRatio ratio={1}>
69
- <div className="bg-muted border-border flex h-full w-full items-center justify-center rounded-md border">
70
- <Text size="xs" tone="muted" className="px-1 text-center">
71
- {name}
72
- </Text>
73
- </div>
77
+ <Card variant="muted" className="h-full w-full">
78
+ <CardContent flush>
79
+ <Flex align="center" justify="center" gap="xs">
80
+ <Text size="xs" tone="muted" className="text-center">
81
+ {name}
82
+ </Text>
83
+ </Flex>
84
+ </CardContent>
85
+ </Card>
74
86
  </AspectRatio>
75
87
  </div>
76
88
  ))}
@@ -90,9 +102,13 @@ export default function Demo() {
90
102
  </CardHeader>
91
103
  <CardContent>
92
104
  <AspectRatio ratio={21 / 9}>
93
- <div className="bg-muted border-border flex h-full w-full items-center justify-center rounded-md border">
94
- <Text tone="muted">月次売上推移グラフ(21:9)</Text>
95
- </div>
105
+ <Card variant="muted" className="h-full w-full">
106
+ <CardContent flush>
107
+ <Flex align="center" justify="center" gap="xs">
108
+ <Text tone="muted">月次売上推移グラフ(21:9)</Text>
109
+ </Flex>
110
+ </CardContent>
111
+ </Card>
96
112
  </AspectRatio>
97
113
  </CardContent>
98
114
  </Card>
@@ -8,7 +8,7 @@ import { AuthAccountSummary, AuthStack, PageContainer } from "@godxjp/ui/layout"
8
8
  * the account address, and exactly one ghost action. Everything else is consumer data.
9
9
  *
10
10
  * The address CLIPS with an ellipsis, so it declares its own line box rather than inheriting one
11
- * (gh#254): a tight inherited line-height makes the clip box shorter than the font's ascent plus
11
+ *: a tight inherited line-height makes the clip box shorter than the font's ascent plus
12
12
  * descent and shears Vietnamese tone marks and Latin descenders off, while the DOM text stays
13
13
  * correct. The Vietnamese row below is the regression case for that.
14
14
  */
@@ -9,7 +9,7 @@ import { AppSettingPicker } from "@godxjp/ui/navigation";
9
9
  /**
10
10
  * Viewport fixture — 390x844 (mobile). THE MOBILE CONTRACT, decided here.
11
11
  *
12
- * ⚠️ The 390 canonical reference supplied with gh#233 is a desktop 2x2 COMPOSITE that overflows and
12
+ * ⚠️ The 390 canonical reference supplied with is a desktop 2x2 COMPOSITE that overflows and
13
13
  * crops horizontally. It is not a route-level mobile artboard and it was NOT traced. The behaviour
14
14
  * below is a decided, documented contract — see the Overview page.
15
15
  *
@@ -28,8 +28,6 @@ export default function Demo() {
28
28
  const [password, setPassword] = useState("Godx-2026");
29
29
  const [confirmation, setConfirmation] = useState("Godx-202");
30
30
 
31
- // The address is masked by the SERVER before it reaches the panel — the library never
32
- // reconstructs an identifier, it only renders the string the consumer sends.
33
31
  const maskedEmail = "h*****@example.co.jp";
34
32
 
35
33
  return (
@@ -16,7 +16,7 @@ import { AuthFooter, AuthIdentity, AuthShell, Flex } from "@godxjp/ui/layout";
16
16
  import { AppSettingPicker } from "@godxjp/ui/navigation";
17
17
 
18
18
  /**
19
- * AuthShell preset="context-selection" (gh#217) — the canonical organisation / context picker.
19
+ * AuthShell preset="context-selection" — the canonical organisation / context picker.
20
20
  *
21
21
  * The preset owns the whole page measure: a 25rem card at 1440/1024 and an edge-to-edge card at
22
22
  * 390 (0 inline gutter), plus the vertical rhythm between the three direct sections of the auth
@@ -18,7 +18,7 @@ import {
18
18
  import { AppSettingPicker, Steps } from "@godxjp/ui/navigation";
19
19
 
20
20
  /**
21
- * AuthShell preset="device-authorization" (gh#220) — the canonical OAuth device-grant screen.
21
+ * AuthShell preset="device-authorization" — the canonical OAuth device-grant screen.
22
22
  *
23
23
  * The whole page measure is owned by the preset: a 380px card at 1440/1024 and a 5px inline page
24
24
  * gutter at 390 (card x=5px, width=380px). Nothing here sets a width, an inset or a colour — a
@@ -28,7 +28,7 @@ import { AppSettingPicker, Steps } from "@godxjp/ui/navigation";
28
28
  * `variant="canonical"` still owns control density + heading size; the preset only re-measures.
29
29
  * Verify at 1440x900 · 1024x900 · 390x844.
30
30
  *
31
- * gh#12 — the preset also owns the CODE FIELD now: `--otp-slot-{inline,block}-size` come from
31
+ * The preset also owns the CODE FIELD now: `--otp-slot-{inline,block}-size` come from
32
32
  * `--auth-shell-device-otp-slot-*`, so each 4-slot grouped box measures the canonical 112x54
33
33
  * instead of the 146x38 the square control tier produced. Nothing on this page sets it. The row is
34
34
  * centred with `align="center"` (not a wrapper div) and the progress marker is the canonical
@@ -33,7 +33,7 @@ import {
33
33
  import { AppSettingPicker } from "@godxjp/ui/navigation";
34
34
 
35
35
  /**
36
- * AuthShell preset="registration" (gh#256) — the canonical sign-up measure.
36
+ * AuthShell preset="registration" — the canonical sign-up measure.
37
37
  *
38
38
  * The preset owns the whole page geometry: a 360px (22.5rem) form measure at 1440/1024, a 15px
39
39
  * inline gutter at 390 (card x=15, width=360 — the same page rhythm as preset="login", so
@@ -14,7 +14,7 @@ import {
14
14
  * SCR-001 Login visual contract. `?state=standalone|one-line|wrapped` drives the package visual
15
15
  * test; all three keep the same card anchor without consumer CSS or fake requester data.
16
16
  *
17
- * The composition mirrors the documented consumer page exactly (gh#263): NO top brand bar (the
17
+ * The composition mirrors the documented consumer page exactly: NO top brand bar (the
18
18
  * canonical SCR-001 artboard has none — the in-flow AuthIdentity mark IS the brand), a header-less
19
19
  * `<CardContent solo>` body, `AuthFooter` as the third direct child (the login preset's third grid
20
20
  * row), and a plain 12px text-link row like the artboard's.
@@ -129,7 +129,8 @@ export default function Demo() {
129
129
  direction="row"
130
130
  gap="sm"
131
131
  align={a}
132
- className="bg-muted min-h-20 rounded-md p-2"
132
+ className="bg-muted min-h-20 rounded-md"
133
+ style={{ padding: "var(--space-2)" }}
133
134
  >
134
135
  <Text size="xs" tone="muted" className="w-16">
135
136
  {a}
@@ -166,7 +167,8 @@ export default function Demo() {
166
167
  gap="xs"
167
168
  align="center"
168
169
  justify={j}
169
- className="bg-muted flex-1 rounded-md p-2"
170
+ className="bg-muted flex-1 rounded-md"
171
+ style={{ padding: "var(--space-2)" }}
170
172
  >
171
173
  <Badge variant="outline">借方</Badge>
172
174
  <Badge variant="outline">貸方</Badge>
@@ -192,6 +194,7 @@ export default function Demo() {
192
194
  <Text size="xs" tone="muted">
193
195
  wrap=&#123;false&#125;(既定)
194
196
  </Text>
197
+ {/* ui-audit-disable-next-line no-utility-spacing — a tinted tile so the gap between children is visible; the box IS the subject */}
195
198
  <Flex direction="row" gap="xs" wrap={false} className="bg-muted rounded-md p-2">
196
199
  <Badge variant="outline">勘定科目</Badge>
197
200
  <Badge variant="outline">補助科目</Badge>
@@ -202,6 +205,7 @@ export default function Demo() {
202
205
  <Text size="xs" tone="muted">
203
206
  wrap=&#123;true&#125;
204
207
  </Text>
208
+ {/* ui-audit-disable-next-line no-utility-spacing — a tinted tile so the gap between children is visible; the box IS the subject */}
205
209
  <Flex direction="row" gap="xs" wrap className="bg-muted rounded-md p-2">
206
210
  <Badge variant="outline">勘定科目</Badge>
207
211
  <Badge variant="outline">補助科目</Badge>
@@ -102,7 +102,7 @@ const COLLAPSE_STEPS = ["token", "sm", "md", "lg", "xl", "false"] as const;
102
102
  const MASTER_VIEWPORTS = ["auto", "compact", "standard"] as const;
103
103
 
104
104
  /**
105
- * 実サービスの「長い」コレクション (gh#231)。auto のままだと一覧が数千 px に伸び、
105
+ * 実サービスの「長い」コレクション。auto のままだと一覧が数千 px に伸び、
106
106
  * 積み重なった 390px 画面では詳細がフォールドの遥か下に押し出される。
107
107
  */
108
108
  const MEMBERS = Array.from({ length: 60 }, (_, i) => ({
@@ -339,7 +339,7 @@ export default function Demo() {
339
339
  </Card>
340
340
  </MasterDetail>
341
341
 
342
- {/* ── 3. masterViewport — 長い実コレクションを境界付きビューポートに収める (gh#231)。
342
+ {/* ── 3. masterViewport — 長い実コレクションを境界付きビューポートに収める。
343
343
  auto: 一覧が 60 行ぶん伸び、390px では詳細がフォールドの遥か下に落ちる。
344
344
  compact / standard: --master-detail-master-viewport-* が block サイズを
345
345
  上限とし、コレクションは region の中でスクロールする。
@@ -0,0 +1,101 @@
1
+ import * as React from "react";
2
+ import { Inbox, Package, ScanLine, Truck } from "lucide-react";
3
+
4
+ import { Badge, Card, CardContent } from "@godxjp/ui/data-display";
5
+ import { Button, Heading, Text } from "@godxjp/ui/general";
6
+ import { Flex, MobileShell } from "@godxjp/ui/layout";
7
+
8
+ /**
9
+ * MobileShell — the handheld app shell: a status band, an app bar, ONE scroll region, a sticky
10
+ * action bar and a bottom tab bar. The two things a composed `Card` stack cannot do are exactly
11
+ * what the shell owns: the root is one screen tall so the DOCUMENT never scrolls (the tab bar can
12
+ * never slide away under a collapsing URL bar), and every band pads itself out of the device
13
+ * safe areas. Zero custom CSS — no `min-h-dvh`, no `position: sticky`, no page gutter by hand.
14
+ */
15
+ const ITEMS = [
16
+ { rc: "RC-204881", name: "洗顔フォーム", status: "未仕分け", tone: "warning" as const },
17
+ { rc: "RC-204882", name: "日焼け止め", status: "棚上", tone: "info" as const },
18
+ { rc: "RC-204879", name: "ビタミン B", status: "棚上", tone: "info" as const },
19
+ { rc: "RC-204875", name: "歯ブラシ", status: "梱包済み", tone: "muted" as const },
20
+ { rc: "RC-204871", name: "入浴剤", status: "棚上", tone: "info" as const },
21
+ { rc: "RC-204864", name: "解熱鎮痛薬", status: "梱包済み", tone: "muted" as const },
22
+ ];
23
+
24
+ const TABS = [
25
+ { id: "inbound", label: "入庫", icon: Inbox },
26
+ { id: "packing", label: "梱包", icon: Package },
27
+ { id: "outbound", label: "出庫", icon: Truck },
28
+ ];
29
+
30
+ export default function Demo() {
31
+ const [tab, setTab] = React.useState("inbound");
32
+
33
+ return (
34
+ <MobileShell
35
+ statusBar={
36
+ <>
37
+ <Text size="sm" weight="medium" tabular>
38
+ 9:41
39
+ </Text>
40
+ <Text size="sm" weight="medium" tone="muted" tabular>
41
+ Acme Handy
42
+ </Text>
43
+ </>
44
+ }
45
+ header={
46
+ <Flex align="center" justify="between" gap="xs" className="w-full">
47
+ <Heading level={3} as="h1">
48
+ 入庫
49
+ </Heading>
50
+ <Button variant="ghost" size="sm">
51
+ 選択
52
+ </Button>
53
+ </Flex>
54
+ }
55
+ actions={
56
+ <>
57
+ <Button className="flex-[2]">
58
+ <ScanLine aria-hidden="true" />
59
+ スキャン
60
+ </Button>
61
+ <Button variant="outline" className="flex-1">
62
+ 手入力
63
+ </Button>
64
+ </>
65
+ }
66
+ tabBar={TABS.map((t) => {
67
+ const Icon = t.icon;
68
+ return (
69
+ <Button
70
+ key={t.id}
71
+ variant="ghost"
72
+ onClick={() => setTab(t.id)}
73
+ aria-current={t.id === tab ? "page" : undefined}
74
+ className="h-full flex-col rounded-none"
75
+ >
76
+ <Icon aria-hidden="true" />
77
+ <Text size="2xs">{t.label}</Text>
78
+ </Button>
79
+ );
80
+ })}
81
+ >
82
+ <Flex direction="col" gap="sm">
83
+ {ITEMS.map((item) => (
84
+ <Card key={item.rc} density="tight">
85
+ <CardContent solo>
86
+ <Flex align="center" justify="between" gap="sm">
87
+ <Flex direction="col" gap="none">
88
+ <Text weight="medium">{item.name}</Text>
89
+ <Text size="xs" mono tone="muted" tabular>
90
+ {item.rc}
91
+ </Text>
92
+ </Flex>
93
+ <Badge tone={item.tone}>{item.status}</Badge>
94
+ </Flex>
95
+ </CardContent>
96
+ </Card>
97
+ ))}
98
+ </Flex>
99
+ </MobileShell>
100
+ );
101
+ }