@godxjp/ui 28.5.0 → 28.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/components/data-display/data-table.d.ts +10 -2
  2. package/dist/components/data-display/data-table.js +12 -5
  3. package/dist/components/data-display/scroll-area.js +16 -2
  4. package/dist/components/data-display/service-launcher-card.d.ts +25 -0
  5. package/dist/components/data-display/table.d.ts +38 -1
  6. package/dist/components/data-display/table.js +57 -39
  7. package/dist/components/data-entry/field.d.ts +1 -1
  8. package/dist/components/data-entry/field.js +35 -4
  9. package/dist/components/data-entry/form-field.js +29 -12
  10. package/dist/components/data-entry/input.d.ts +1 -1
  11. package/dist/components/layout/index.d.ts +1 -1
  12. package/dist/components/layout/nav-list.d.ts +9 -1
  13. package/dist/components/layout/nav-list.js +22 -11
  14. package/dist/components/layout/sidebar.d.ts +16 -2
  15. package/dist/components/layout/sidebar.js +1 -0
  16. package/dist/components/navigation/app-setting-picker.js +20 -3
  17. package/dist/components/ui/password-input.d.ts +1 -1
  18. package/dist/components/ui/table.js +1 -0
  19. package/dist/contracts/measurement.json +1 -1
  20. package/dist/i18n/messages/en.json +5 -1
  21. package/dist/i18n/messages/ja.json +5 -1
  22. package/dist/i18n/messages/vi.json +5 -1
  23. package/dist/lib/control-styles.d.ts +18 -3
  24. package/dist/lib/control-styles.js +1 -1
  25. package/dist/lib/hooks.d.ts +23 -0
  26. package/dist/lib/hooks.js +27 -0
  27. package/dist/props/components/data-display.prop.d.ts +14 -0
  28. package/dist/props/components/data-entry.prop.d.ts +36 -2
  29. package/dist/props/components/layout.prop.d.ts +16 -5
  30. package/dist/props/registry.d.ts +2 -2
  31. package/dist/props/registry.js +10 -2
  32. package/dist/styles/control.css +12 -0
  33. package/dist/styles/data-display-layout.css +25 -2
  34. package/dist/styles/dialog-layout.css +12 -0
  35. package/dist/styles/form-layout.css +22 -0
  36. package/dist/styles/layout.css +1 -2
  37. package/dist/styles/navigation-layout.css +18 -19
  38. package/dist/tokens/components/data-display.css +2 -1
  39. package/dist/tokens/components/feedback.css +2 -0
  40. package/dist/tokens/components/form.css +2 -0
  41. package/dist/tokens/components/navigation.css +2 -0
  42. package/docs/COMPOSITION-VS-COMPONENT.md +3 -0
  43. package/docs/DESIGN-AUTHORITY.md +25 -5
  44. package/docs/FRAME-COVERAGE-REPORT.md +1 -1
  45. package/docs/data-display/collapsible.tsx +5 -5
  46. package/docs/data-display/descriptions.tsx +188 -109
  47. package/docs/data-display/legend.tsx +17 -4
  48. package/docs/data-display/scroll-area.tsx +106 -5
  49. package/docs/data-display/service-launcher-card.tsx +275 -115
  50. package/docs/data-display/stat-card.tsx +319 -64
  51. package/docs/data-entry/form.tsx +700 -6
  52. package/docs/data-entry/input.tsx +663 -53
  53. package/docs/data-entry/radio-group.tsx +216 -2
  54. package/docs/data-entry/switch.tsx +210 -4
  55. package/docs/data-entry/upload-crop-dialog.tsx +24 -25
  56. package/docs/layout/nav-list.tsx +556 -35
  57. package/package.json +9 -7
  58. package/scripts/visual-audit.mjs +29 -1
@@ -10,6 +10,8 @@
10
10
 
11
11
  --form-block-gap: var(--space-4);
12
12
 
13
+ --form-feedback-icon-size: var(--icon-size-sm);
14
+
13
15
  --form-field-row-gap: var(--space-3);
14
16
 
15
17
  --form-grid-row-gap: var(--form-field-row-gap);
@@ -187,6 +187,8 @@
187
187
  --app-setting-picker-density-width: 10rem;
188
188
  --app-setting-picker-font-size-width: 9rem;
189
189
 
190
+ --app-setting-picker-trigger-width: auto;
191
+
190
192
  --app-setting-picker-width-breakpoint: 40rem;
191
193
  --app-setting-picker-bar-border-width: 0;
192
194
  --app-setting-picker-bar-shadow: none;
@@ -77,6 +77,7 @@ new UI need
77
77
  | `Select` (incl. search/async) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
78
78
  | `DataTable`, `Dialog`, `Calendar`, `Switch`, `Combobox` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
79
79
  | `StatCard` (+ `icon`) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** (a reusable KPI tile with a stable API + tokens) |
80
+ | `ServiceLauncherCard` | ➖ | ❌ | ❌ | ✅ | ✅ | ➖ | ➖ | **Composition by the test — RETAINED as a recorded exception** (gh#814) |
80
81
  | Marketing **Hero** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | **Composition** — section, static, composable, brand-specific |
81
82
  | **Navbar** / **Footer** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | **Composition** — layout of `Text`/`Button`/`Flex` |
82
83
  | **PricingTable** / feature grid | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | **Composition** — `ResponsiveGrid` + `Card` |
@@ -85,6 +86,8 @@ new UI need
85
86
 
86
87
  `✅ pass · ❌ fail · ➖ borderline`. **StatCard** is the instructive borderline: C2 is weak (it owns little behavior), but it is a universal KPI tile with a controlled API, fully tokenized, broadly reused — so it earns its place. A **Hero** fails six of seven; it is unambiguously a composition.
87
88
 
89
+ **ServiceLauncherCard is the ONE recorded exception, and it is recorded so that it stays one.** It shipped before the test was run against it, and when the test was run it came back with two hard FAILs: **C2** — no state, no keyboard handling, no focus management, its only ARIA three static attributes — and **C3** — its own imports are `Card` + `CardContent` + `Badge` + a Lucide glyph, so "could I build this right now from primitives?" is yes. By §2 that makes it a composition pattern. It is kept anyway because `src/components/layout/app-launcher.tsx` consumes it: it is an internal building block of a component that **does** pass, so the question was never "should it exist" but "should it be PUBLIC", and removing a public export is breaking. Keeping it public was the cheaper call and the ledger lives on gh#814 and at the top of `src/components/data-display/service-launcher-card.tsx`. **Consumers should compose `Card` + `Badge` for a service tile** — reach for `ServiceLauncherCard` only to match `AppLauncher`'s own tiles — and this row is not precedent for adding another static tile to `src/components/`.
90
+
88
91
  ---
89
92
 
90
93
  ## 4. How to build a composition pattern correctly
@@ -648,11 +648,31 @@ because removing it would break the contents anchor.
648
648
 
649
649
  **Every other focusable non-interactive element in this library is a genuinely scrollable region**
650
650
  — `.app-main`, `.ui-mobile-shell-main`, `.ui-timeline-grid`, `.ui-code-block` when it overflows,
651
- MasterDetail's bounded master, the DataTable and Table scroll containers, ScrollArea, and the
652
- BranchScopePicker list. Each carries `tabIndex={0}` for axe `scrollable-region-focusable` and must
653
- stay reachable, so each keeps BOTH halves rather than becoming focusable-but-unpainted. The region
654
- ring (`--region-focus-ring-width`, off by default) now also multiplies by `--focus-outline`, so a
655
- service that opts it in still cannot paint while the library-wide switch is off.
651
+ MasterDetail's bounded master, the DataTable and Table scroll containers when they overflow,
652
+ ScrollArea, and the BranchScopePicker list. Each carries `tabIndex={0}` for axe
653
+ `scrollable-region-focusable` and must stay reachable, so each keeps BOTH halves rather than
654
+ becoming focusable-but-unpainted. The region ring (`--region-focus-ring-width`, off by default) now
655
+ also multiplies by `--focus-outline`, so a service that opts it in still cannot paint while the
656
+ library-wide switch is off.
657
+
658
+ ### A focusable scroll region is ROLED and NAMED, and only exists while it scrolls (gh#817)
659
+
660
+ Reaching the overflow is half the contract; knowing what you reached is the other half. A stop with
661
+ no role and no accessible name announces nothing at all, which is why `godx-jp/id` found `Table`'s
662
+ wrapper sitting at tab stop 27 as a bare `<div tabindex="0">`. So the Table and DataTable scroll
663
+ containers carry `role="group"` plus a name that defaults to the localized `dataTable.scrollRegion`
664
+ — a consumer is never forced to invent one per table, and `label` overrides it per instance.
665
+
666
+ `group`, not `region`: a NAMED `region` is a landmark, so a page with three tables would ship three
667
+ identically-named landmarks and fail axe `landmark-unique`. That collision is the reason the role
668
+ was left off in the first place; `group` is announced, takes a name, and is not a landmark.
669
+
670
+ And all three attributes are withheld until the box provably overflows (`useScrollsHorizontally`,
671
+ `src/lib/hooks.ts`) — a focus stop that scrolls nothing is noise. The measurement may only ever
672
+ REMOVE the stop: an unlaid-out box reports `clientWidth === 0` (SSR, jsdom, a `display:none`
673
+ ancestor), and reading that as "nothing overflows" would strand the overflow from every keyboard
674
+ user, a worse failure than the extra stop. Gated by
675
+ `src/components/data-display/__tests__/table-scroll-region-a11y-817.test.tsx`.
656
676
 
657
677
  ## How a decision gets made from here
658
678
 
@@ -173,7 +173,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
173
173
  | Carousel | ✓ | N/A | N/A | · | · | · | · | · | · | · |
174
174
  | AppSettingPicker | ✓ | N/A | · | · | · | · | · | · | · | · |
175
175
  | AppSettingToggle | ✓ | N/A | · | · | · | · | · | · | · | · |
176
- | Field | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
176
+ | Field | ✓ | N/A | · | N/A | · | · | · | · | · | · |
177
177
  | LineChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
178
178
  | BarChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
179
179
  | CompactBarTrend | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
@@ -10,7 +10,7 @@ import {
10
10
  } from "@godxjp/ui/data-display";
11
11
  import { Button, Text } from "@godxjp/ui/general";
12
12
  import { Flex, PageContainer } from "@godxjp/ui/layout";
13
- import { ChevronsUpDown } from "lucide-react";
13
+ import { ChevronDown } from "lucide-react";
14
14
  import { useState } from "react";
15
15
 
16
16
  /**
@@ -43,7 +43,7 @@ export default function Demo() {
43
43
  aria-label="開閉"
44
44
  className="transition-transform data-[state=open]:rotate-180"
45
45
  >
46
- <ChevronsUpDown />
46
+ <ChevronDown className="ui-disclosure-chevron" />
47
47
  </Button>
48
48
  </CollapsibleTrigger>
49
49
  </Flex>
@@ -74,7 +74,7 @@ export default function Demo() {
74
74
  aria-label="開閉"
75
75
  className="transition-transform data-[state=open]:rotate-180"
76
76
  >
77
- <ChevronsUpDown />
77
+ <ChevronDown className="ui-disclosure-chevron" />
78
78
  </Button>
79
79
  </CollapsibleTrigger>
80
80
  </Flex>
@@ -112,7 +112,7 @@ export default function Demo() {
112
112
  aria-label="開閉"
113
113
  className="transition-transform data-[state=open]:rotate-180"
114
114
  >
115
- <ChevronsUpDown />
115
+ <ChevronDown className="ui-disclosure-chevron" />
116
116
  </Button>
117
117
  </CollapsibleTrigger>
118
118
  </Flex>
@@ -144,7 +144,7 @@ export default function Demo() {
144
144
  aria-label="開閉"
145
145
  className="transition-transform data-[state=open]:rotate-180"
146
146
  >
147
- <ChevronsUpDown />
147
+ <ChevronDown className="ui-disclosure-chevron" />
148
148
  </Button>
149
149
  </CollapsibleTrigger>
150
150
  </Flex>
@@ -6,176 +6,255 @@ import {
6
6
  CardHeader,
7
7
  CardTitle,
8
8
  Descriptions,
9
+ Progress,
10
+ Thumbnail,
9
11
  } from "@godxjp/ui/data-display";
10
- import type { CSSProperties } from "react";
11
- import { Text } from "@godxjp/ui/general";
12
+ import { Button, Text } from "@godxjp/ui/general";
12
13
  import { Flex, PageContainer } from "@godxjp/ui/layout";
14
+ import { Download, ExternalLink, FileText } from "lucide-react";
13
15
 
14
16
  /**
15
- * Descriptions — responsive label/value grid for detail-page metadata. COMPOUND:
16
- * the value goes in Descriptions.Item children. Never hand-roll a dl/dt/dd grid.
17
- * Composed only from real @godxjp/ui components.
17
+ * Descriptions — the label/value grid every detail page is made of.
18
+ *
19
+ * WHY THIS PAGE IS LONG. It was five tidy cards of short strings, and the owner's verdict was that
20
+ * it shows nothing about whether the component holds up. That is right: a label/value grid is
21
+ * trivial when every label is four characters and every value is one line.
22
+ *
23
+ * A real detail screen is not that. It has a label that runs to three lines beside one that is two
24
+ * characters; a value that is an image, a file to download, a link, a status, a progress bar, a
25
+ * 40-character machine ID, or nothing at all; and it has to survive all of them in the same grid,
26
+ * at every breakpoint, bordered and not.
27
+ *
28
+ * So every section below is a case a real detail page hits. Where a section looks bad, the
29
+ * component owes an answer — the sample data is not there to flatter it.
18
30
  */
31
+ const ID = "01JBQ9X7M4K2R8VT3NZ6YHF0PA";
32
+
19
33
  export default function Demo() {
20
34
  return (
21
- <PageContainer title="Descriptions" subtitle="Label / value metadata grid for detail pages">
35
+ <PageContainer
36
+ title="Descriptions"
37
+ subtitle="label/value grid — every shape a detail page asks for"
38
+ >
22
39
  <Flex direction="col" gap="lg">
23
- {/* Default columns=2 — the canonical 2-column metadata grid.
24
- mono for the ID; span=2 + a long URL value shows full-row + break-all overflow. */}
25
40
  <Card>
26
41
  <CardHeader>
27
- <CardTitle level={2}>請求書の詳細</CardTitle>
42
+ <CardTitle level={2}>1 · columns × bordered × layout — the three axes</CardTitle>
28
43
  <CardDescription>
29
- 既定は
30
- columns=2。値は任意のノード(テキスト・Badge・金額)。狭い画面では1列に折り返します。
44
+ Every combination changes where the label sits and how the grid divides. These are the
45
+ four a project actually picks between.
31
46
  </CardDescription>
32
47
  </CardHeader>
33
48
  <CardContent>
34
- <Descriptions>
35
- <Descriptions.Item label="請求書番号" mono>
36
- INV-2024-0312
37
- </Descriptions.Item>
38
- <Descriptions.Item label="取引先">株式会社ベトヤ</Descriptions.Item>
39
- <Descriptions.Item label="状態">
40
- <Badge status="active" />
41
- </Descriptions.Item>
42
- <Descriptions.Item label="発行日">2024-04-12</Descriptions.Item>
43
- <Descriptions.Item label="小計">
44
- <Text tabular>¥438,182</Text>
45
- </Descriptions.Item>
46
- <Descriptions.Item label="消費税 (10%)">
47
- <Text tabular>¥43,818</Text>
48
- </Descriptions.Item>
49
- <Descriptions.Item label="合計">
50
- <Text weight="medium" tabular>
51
- ¥482,000
49
+ <Flex direction="col" gap="lg">
50
+ <Flex direction="col" gap="xs">
51
+ <Text size="sm" tone="muted">
52
+ columns=3 · bordered · horizontal — the dense admin default
52
53
  </Text>
54
+ <Descriptions columns={3} bordered layout="horizontal">
55
+ <Descriptions.Item label="取引先">株式会社山田製作所</Descriptions.Item>
56
+ <Descriptions.Item label="担当">佐藤 玲</Descriptions.Item>
57
+ <Descriptions.Item label="状態">
58
+ <Badge tone="success">有効</Badge>
59
+ </Descriptions.Item>
60
+ <Descriptions.Item label="登録日">2024-04-01</Descriptions.Item>
61
+ <Descriptions.Item label="与信枠">¥12,000,000</Descriptions.Item>
62
+ <Descriptions.Item label="支払条件">月末締め翌月末払い</Descriptions.Item>
63
+ </Descriptions>
64
+ </Flex>
65
+
66
+ <Flex direction="col" gap="xs">
67
+ <Text size="sm" tone="muted">
68
+ columns=2 · no border · vertical — the reading layout, label above value
69
+ </Text>
70
+ <Descriptions columns={2} layout="vertical">
71
+ <Descriptions.Item label="取引先">株式会社山田製作所</Descriptions.Item>
72
+ <Descriptions.Item label="担当">佐藤 玲</Descriptions.Item>
73
+ <Descriptions.Item label="与信枠">¥12,000,000</Descriptions.Item>
74
+ <Descriptions.Item label="支払条件">月末締め翌月末払い</Descriptions.Item>
75
+ </Descriptions>
76
+ </Flex>
77
+
78
+ <Flex direction="col" gap="xs">
79
+ <Text size="sm" tone="muted">
80
+ columns=1 · bordered — a narrow column or a phone
81
+ </Text>
82
+ <Descriptions columns={1} bordered>
83
+ <Descriptions.Item label="取引先">株式会社山田製作所</Descriptions.Item>
84
+ <Descriptions.Item label="状態">
85
+ <Badge tone="success">有効</Badge>
86
+ </Descriptions.Item>
87
+ </Descriptions>
88
+ </Flex>
89
+
90
+ <Flex direction="col" gap="xs">
91
+ <Text size="sm" tone="muted">
92
+ labelAlign=end — right-aligned labels, so the values start on one line
93
+ </Text>
94
+ <Descriptions columns={2} bordered labelAlign="end">
95
+ <Descriptions.Item label="ID">{ID}</Descriptions.Item>
96
+ <Descriptions.Item label="状態">
97
+ <Badge tone="success">有効</Badge>
98
+ </Descriptions.Item>
99
+ </Descriptions>
100
+ </Flex>
101
+ </Flex>
102
+ </CardContent>
103
+ </Card>
104
+
105
+ <Card>
106
+ <CardHeader>
107
+ <CardTitle level={2}>2 · responsive columns</CardTitle>
108
+ <CardDescription>
109
+ `columns` takes a breakpoint object. Narrow this frame with the Dimensions control: 1
110
+ column on a phone, 2 on a tablet, 3 on a desktop — the same markup throughout.
111
+ </CardDescription>
112
+ </CardHeader>
113
+ <CardContent>
114
+ <Descriptions columns={{ sm: 1, md: 2, lg: 3 }} bordered>
115
+ <Descriptions.Item label="会社名">株式会社山田製作所</Descriptions.Item>
116
+ <Descriptions.Item label="法人番号" mono>
117
+ 7010001071491
53
118
  </Descriptions.Item>
54
- <Descriptions.Item label="PDF リンク" mono span={2}>
55
- https://invoices.vetoya.example.co.jp/2024/04/INV-2024-0312-rev2.pdf
56
- </Descriptions.Item>
119
+ <Descriptions.Item label="業種">精密機器製造</Descriptions.Item>
120
+ <Descriptions.Item label="代表">山田 太郎</Descriptions.Item>
121
+ <Descriptions.Item label="従業員数">248 名</Descriptions.Item>
122
+ <Descriptions.Item label="設立">1974-06-12</Descriptions.Item>
57
123
  </Descriptions>
58
124
  </CardContent>
59
125
  </Card>
60
126
 
61
- {/* layout="horizontal" — label BESIDE value (token-aligned label column), like <Form layout>.
62
- Default layout is "vertical" (label over value, the cards above). */}
63
127
  <Card>
64
128
  <CardHeader>
65
- <CardTitle level={2}>横並び (layout=&quot;horizontal&quot;)</CardTitle>
129
+ <CardTitle level={2}>3 · the value is not a string</CardTitle>
66
130
  <CardDescription>
67
- ラベルを値の左に揃える。`--descriptions-label-width` でラベル列幅を調整。既定は
68
- layout=&quot;vertical&quot;(ラベルを上に積む、上のカード)。
131
+ The value slot takes any node. These are the ones a detail page actually puts there —
132
+ an image, a file, a link, a status, a proportion, a secret, money, a long memo.
69
133
  </CardDescription>
70
134
  </CardHeader>
71
135
  <CardContent>
72
- <Descriptions columns={1} layout="horizontal">
73
- <Descriptions.Item label="請求書番号" mono>
74
- INV-2024-0312
136
+ <Descriptions columns={{ sm: 1, md: 2 }} bordered>
137
+ <Descriptions.Item label="製品画像">
138
+ <Thumbnail src="/godxjp-ui/favicon.svg" alt="製品サムネイル" size="md" />
75
139
  </Descriptions.Item>
76
- <Descriptions.Item label="取引先">株式会社ベトヤ</Descriptions.Item>
77
140
  <Descriptions.Item label="状態">
78
- <Badge status="active" />
141
+ <Flex direction="row" gap="xs" wrap>
142
+ <Badge tone="success">出荷可</Badge>
143
+ <Badge tone="warning" variant="outline">
144
+ 在庫僅少
145
+ </Badge>
146
+ </Flex>
79
147
  </Descriptions.Item>
80
- <Descriptions.Item label="合計">
81
- <Text weight="medium" tabular>
82
- ¥482,000
83
- </Text>
148
+ <Descriptions.Item label="仕様書">
149
+ <Button variant="outline" size="sm">
150
+ <FileText className="size-4" />
151
+ 仕様書_v4.2.pdf
152
+ </Button>
153
+ </Descriptions.Item>
154
+ <Descriptions.Item label="検査成績書">
155
+ <Button variant="ghost" size="sm">
156
+ <Download className="size-4" />
157
+ ダウンロード(2.4 MB)
158
+ </Button>
159
+ </Descriptions.Item>
160
+ <Descriptions.Item label="外部リンク">
161
+ <Button variant="link" size="sm">
162
+ <ExternalLink className="size-4" />
163
+ サプライヤーポータル
164
+ </Button>
165
+ </Descriptions.Item>
166
+ <Descriptions.Item label="検収進捗">
167
+ <Progress value={72} tone="success" label="検収進捗 72%" />
168
+ </Descriptions.Item>
169
+ <Descriptions.Item label="単価">¥128,400</Descriptions.Item>
170
+ <Descriptions.Item label="ロットID" mono>
171
+ {ID}
84
172
  </Descriptions.Item>
85
173
  </Descriptions>
86
174
  </CardContent>
87
175
  </Card>
88
176
 
89
- {/* columns=1 — stacked detail panel: every item on its own row. */}
90
177
  <Card>
91
178
  <CardHeader>
92
- <CardTitle level={2}>取引先の詳細</CardTitle>
93
- <CardDescription>columns=1。各項目を縦積みにする詳細パネル向けの構成。</CardDescription>
179
+ <CardTitle level={2}>4 · span — a value that needs the whole row</CardTitle>
180
+ <CardDescription>
181
+ An address, a memo or a JSON blob does not belong in a one-third cell. `span` widens
182
+ the item; `span=&quot;filled&quot;` takes whatever is left on the row, so the grid
183
+ stays aligned instead of leaving a hole.
184
+ </CardDescription>
94
185
  </CardHeader>
95
186
  <CardContent>
96
- <Descriptions columns={1}>
97
- <Descriptions.Item label="会社名">株式会社ベトヤ</Descriptions.Item>
98
- <Descriptions.Item label="法人番号" mono>
99
- 1180001052731
187
+ <Descriptions columns={3} bordered>
188
+ <Descriptions.Item label="取引先">株式会社山田製作所</Descriptions.Item>
189
+ <Descriptions.Item label="担当">佐藤 玲</Descriptions.Item>
190
+ <Descriptions.Item label="電話">03-5555-0198</Descriptions.Item>
191
+ <Descriptions.Item label="本社所在地" span={3}>
192
+ 〒105-0011 東京都港区芝公園四丁目2番8号 東京タワー内 グローバルビジネスセンター 23F
193
+ 株式会社山田製作所 東日本統括本部
100
194
  </Descriptions.Item>
101
- <Descriptions.Item label="住所">
102
- 東京都渋谷区神宮前5-52-2 青山オーバルビル7F
195
+ <Descriptions.Item label="備考" span="filled">
196
+ 検収は毎月第2・第4火曜のみ。祝日と重なる場合は翌営業日に振替。先方の受入窓口は
197
+ 資材部・資材管理課(内線
198
+ 4821)。2026年4月以降は電子取引のみとなるため、紙の納品書は不可。
103
199
  </Descriptions.Item>
104
- <Descriptions.Item label="担当者">山田 太郎(経理部)</Descriptions.Item>
200
+ <Descriptions.Item label="与信枠">¥12,000,000</Descriptions.Item>
201
+ <Descriptions.Item label="使用中">¥8,214,930</Descriptions.Item>
105
202
  </Descriptions>
106
203
  </CardContent>
107
204
  </Card>
108
205
 
109
- {/* columns=3 — dense metadata grid; span=3 makes the JSON value occupy the full width. */}
110
206
  <Card>
111
207
  <CardHeader>
112
- <CardTitle level={2}>システムメタデータ</CardTitle>
208
+ <CardTitle level={2}>5 · the cases that break a label/value grid</CardTitle>
113
209
  <CardDescription>
114
- columns=3。識別子の多い密なメタデータ向け。mono と span でIDやJSONを整列。
210
+ This is the only section on the page that can tell you whether Descriptions is any
211
+ good. If a row here looks wrong, the component owes an answer — not the data.
115
212
  </CardDescription>
116
213
  </CardHeader>
117
214
  <CardContent>
118
- <Descriptions columns={3}>
119
- <Descriptions.Item label="リソースID" mono>
120
- inv_8f3a21c0
215
+ <Descriptions columns={{ sm: 1, md: 2, lg: 3 }} bordered>
216
+ <Descriptions.Item label="グローバル人事情報基盤・従業員セルフサービスポータル(アジア太平洋地域)連携ステータス">
217
+ <Badge tone="success">連携済</Badge>
121
218
  </Descriptions.Item>
122
- <Descriptions.Item label="リビジョン" mono>
123
- v3
219
+ <Descriptions.Item label="率">96.8%</Descriptions.Item>
220
+ <Descriptions.Item label="ID" mono>
221
+ urn:godx:entitlement:ap-northeast-1:7010001071491:workforce/identity/administration/v4
124
222
  </Descriptions.Item>
125
- <Descriptions.Item label="状態">
126
- <Badge status="active" />
127
- </Descriptions.Item>
128
- <Descriptions.Item label="作成日時" mono>
129
- 2024-04-12T09:31:00+09:00
130
- </Descriptions.Item>
131
- <Descriptions.Item label="更新日時" mono>
132
- 2024-04-12T14:07:55+09:00
223
+ <Descriptions.Item label="未設定">
224
+ <Text tone="muted">—</Text>
133
225
  </Descriptions.Item>
134
- <Descriptions.Item label="作成者">山田 太郎</Descriptions.Item>
135
- <Descriptions.Item label="メタデータ" mono span={3}>
136
- {'{ "channel": "web", "tax_rate": 0.1, "currency": "JPY" }'}
226
+ <Descriptions.Item label="空文字">{""}</Descriptions.Item>
227
+ <Descriptions.Item label="ゼロ">0</Descriptions.Item>
228
+ <Descriptions.Item label="累計取扱高">¥128,400,932,517</Descriptions.Item>
229
+ <Descriptions.Item label="差引">−¥412,880</Descriptions.Item>
230
+ <Descriptions.Item label="長い英数字" mono>
231
+ AKIAIOSFODNN7EXAMPLEWJALRXUTNFEMIK7MDENGBPXRFICYEXAMPLEKEY
137
232
  </Descriptions.Item>
138
233
  </Descriptions>
139
234
  </CardContent>
140
235
  </Card>
141
236
 
142
- {/* ── The RULED property panel (gh#414) ─────────────────────────────
143
- Two token knobs, both OFF by default, turn a horizontal Descriptions into the ruled
144
- panel a detail page usually wants. They exist so a service stops hand-rolling a
145
- `<Flex className="min-h-10 border-b">` row: the label column, the gaps and the value
146
- type step were already Descriptions'; only the rule and the band height were missing.
147
- The row gap goes to 0 in the same declaration — the gap is the space BETWEEN rules, so
148
- leaving it would draw detached hairlines instead of a ruled list. */}
149
237
  <Card>
150
238
  <CardHeader>
151
- <CardTitle level={2}>案件の詳細 · ruled rows</CardTitle>
152
- <CardDescription>
153
- 一行ごとに罫線を引いた属性パネル。--descriptions-row-border と
154
- --descriptions-row-min-height をテーマで一度設定するだけで、コンポーネントは同じです。
155
- </CardDescription>
239
+ <CardTitle level={2}>使い方の要点</CardTitle>
240
+ <CardDescription>この3点で、上のほとんどが決まります。</CardDescription>
156
241
  </CardHeader>
157
- {/* The knobs are declared on the CARD BODY, not on the grid: custom properties inherit,
158
- so one scope re-tunes every Descriptions inside it — which is how a service theme
159
- would set them (once, globally), not per call site. */}
160
- <CardContent
161
- style={
162
- {
163
- "--descriptions-row-border": "1px solid hsl(var(--border))",
164
- "--descriptions-row-min-height": "var(--band-height-lg)",
165
- "--descriptions-row-gap": "0px",
166
- } as CSSProperties
167
- }
168
- >
169
- <Descriptions columns={1} layout="horizontal">
170
- <Descriptions.Item label="案件名">2026年度 基幹システム更改</Descriptions.Item>
171
- <Descriptions.Item label="担当">山田 太郎</Descriptions.Item>
172
- <Descriptions.Item label="状態">
173
- <Badge status="active" />
174
- </Descriptions.Item>
175
- <Descriptions.Item label="契約ID" mono>
176
- CT-2026-0088
177
- </Descriptions.Item>
178
- </Descriptions>
242
+ <CardContent>
243
+ <Flex direction="col" gap="sm">
244
+ <Text as="div" size="sm">
245
+ 直下の子は <code>Descriptions.Item</code> だけです。生の <code>&lt;div&gt;</code> や
246
+ <code>&lt;dt&gt;/&lt;dd&gt;</code> を混ぜるとグリッドが崩れます。
247
+ </Text>
248
+ <Text as="div" size="sm">
249
+ 機械可読な値(ID・パス・鍵・通貨コード)には <code>mono</code> を付けてください。
250
+ 等幅になるだけでなく <code>break-all</code> が効き、上の 57
251
+ 文字の鍵がセルを突き破らずに折り返します。
252
+ </Text>
253
+ <Text as="div" size="sm">
254
+ 住所・備考・JSON のような長い値には <code>span</code> を。 1/3
255
+ セルに押し込むと、その行だけ高さが跳ねて表全体が読みにくくなります。
256
+ </Text>
257
+ </Flex>
179
258
  </CardContent>
180
259
  </Card>
181
260
  </Flex>
@@ -61,7 +61,20 @@ export default function Demo() {
61
61
  aria-label={row.name}
62
62
  />
63
63
  </Flex>
64
- <Flex width={150} justify="end" align="center" gapRaw={12}>
64
+ {/* A COLUMN OF NUMBERS NEEDS COLUMNS, not a row of shrink-to-fit boxes.
65
+ `tabular` makes DIGITS equal-width inside one Text; it cannot align two Texts
66
+ with different digit COUNTS. Laid out as a flex row and right-aligned as a
67
+ group, a row ending in "9" sat 8px right of one ending in "21" — measured on
68
+ the published site, and the reason this readout looked crooked. Fixed tracks
69
+ give each value its own cell, so every row lines up whatever the digits. */}
70
+ <div
71
+ className="grid items-center"
72
+ style={{
73
+ gridTemplateColumns: "2.5rem 2.5rem 2.5rem 4.5rem",
74
+ justifyItems: "end",
75
+ columnGap: "var(--space-2)",
76
+ }}
77
+ >
65
78
  <Text tone="destructive" weight="semibold" tabular>
66
79
  {row.over}
67
80
  </Text>
@@ -74,7 +87,7 @@ export default function Demo() {
74
87
  <Text tone="muted" tabular>
75
88
  計 {row.over + row.near + row.done}
76
89
  </Text>
77
- </Flex>
90
+ </div>
78
91
  </Flex>
79
92
  ))}
80
93
  </Flex>
@@ -108,8 +121,8 @@ export default function Demo() {
108
121
  <CardHeader>
109
122
  <CardTitle level={2}>Named legend</CardTitle>
110
123
  <CardDescription>
111
- aria-label を渡すと、その凡例が「何のキーか」を読み上げます。項目は list /
112
- listitem として並びます。
124
+ aria-label を渡すと、その凡例が「何のキーか」を読み上げます。項目は list / listitem
125
+ として並びます。
113
126
  </CardDescription>
114
127
  </CardHeader>
115
128
  <CardContent>