@godxjp/ui 33.15.0 → 33.17.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 (88) hide show
  1. package/README.md +4 -4
  2. package/agent/START-HERE.md +7 -7
  3. package/agent/components/AppShell.json +1 -1
  4. package/agent/components/AreaChart.json +6 -0
  5. package/agent/components/BarChart.json +6 -0
  6. package/agent/components/Card.json +1 -1
  7. package/agent/components/DashboardGrid.json +76 -0
  8. package/agent/components/DataTable.json +1 -1
  9. package/agent/components/Heading.json +6 -0
  10. package/agent/components/HeatmapChart.json +6 -0
  11. package/agent/components/LineChart.json +6 -0
  12. package/agent/components/Pagination.json +1 -1
  13. package/agent/components/PieChart.json +6 -0
  14. package/agent/components/Table.json +2 -1
  15. package/agent/components-index.json +9 -3
  16. package/agent/components.json +118 -5
  17. package/agent/index.json +8 -8
  18. package/agent/llms.txt +6 -6
  19. package/agent/rules.json +1 -1
  20. package/agent/tokens.json +74 -2
  21. package/agent/vocabulary.json +12 -0
  22. package/dist/components/charts/chart-cartesian.d.ts +3 -2
  23. package/dist/components/charts/chart-cartesian.js +146 -46
  24. package/dist/components/charts/chart-datum-selection.d.ts +15 -0
  25. package/dist/components/charts/chart-datum-selection.js +97 -0
  26. package/dist/components/charts/chart-frame.d.ts +2 -1
  27. package/dist/components/charts/chart-frame.js +2 -1
  28. package/dist/components/charts/heatmap-chart.d.ts +1 -1
  29. package/dist/components/charts/heatmap-chart.js +20 -0
  30. package/dist/components/charts/index.d.ts +1 -1
  31. package/dist/components/charts/pie-chart.d.ts +2 -1
  32. package/dist/components/charts/pie-chart.js +36 -1
  33. package/dist/components/charts/recharts-peer.d.ts +1 -1
  34. package/dist/components/charts/recharts-peer.js +6 -0
  35. package/dist/components/data-display/index.d.ts +1 -1
  36. package/dist/components/data-display/index.js +10 -1
  37. package/dist/components/data-display/table.d.ts +9 -0
  38. package/dist/components/data-display/table.js +3 -0
  39. package/dist/components/general/typography.d.ts +1 -0
  40. package/dist/components/general/typography.js +3 -0
  41. package/dist/contracts/measurement.json +1 -1
  42. package/dist/i18n/messages/en.json +10 -0
  43. package/dist/i18n/messages/ja.json +10 -0
  44. package/dist/i18n/messages/vi.json +10 -0
  45. package/dist/lab/dashboard-grid-layout.d.ts +4 -0
  46. package/dist/lab/dashboard-grid-layout.js +36 -0
  47. package/dist/lab/dashboard-grid.d.ts +5 -0
  48. package/dist/lab/dashboard-grid.js +258 -0
  49. package/dist/lab/index.d.ts +2 -0
  50. package/dist/lab/index.js +2 -0
  51. package/dist/lib/hooks.d.ts +8 -0
  52. package/dist/lib/hooks.js +20 -21
  53. package/dist/props/components/charts.prop.d.ts +11 -0
  54. package/dist/props/components/dashboard-grid.prop.d.ts +27 -0
  55. package/dist/props/components/dashboard-grid.prop.js +0 -0
  56. package/dist/props/components/general.prop.d.ts +8 -1
  57. package/dist/props/components/index.d.ts +1 -1
  58. package/dist/props/registry.d.ts +89 -6
  59. package/dist/props/registry.js +85 -0
  60. package/dist/props/vocabulary/index.d.ts +1 -1
  61. package/dist/props/vocabulary/interaction.prop.d.ts +6 -0
  62. package/dist/styles/badge-layout.css +1 -0
  63. package/dist/styles/base.css +8 -0
  64. package/dist/styles/card-layout.css +8 -0
  65. package/dist/styles/chart-layout.css +28 -0
  66. package/dist/styles/control.css +82 -0
  67. package/dist/styles/dialog-layout.css +7 -3
  68. package/dist/styles/layers.json +10 -1
  69. package/dist/styles/layout.css +2 -0
  70. package/dist/styles/logo-layout.css +18 -12
  71. package/dist/styles/navigation-layout.css +2 -0
  72. package/dist/styles/shell-layout.css +40 -1
  73. package/dist/styles/table-layout.css +30 -0
  74. package/dist/styles/text-layout.css +5 -0
  75. package/dist/styles.css +1 -1
  76. package/dist/theme/famgia.service.css +10 -8
  77. package/dist/themes/godx-tokens.css +20 -16
  78. package/dist/tokens/base.css +2 -0
  79. package/dist/tokens/components/dashboard-grid.css +14 -0
  80. package/dist/tokens/components/feedback.css +2 -0
  81. package/dist/tokens/components/logo.css +6 -4
  82. package/dist/tokens/components/table.css +5 -1
  83. package/dist/tokens/derived.css +28 -24
  84. package/dist/tokens/foundation.css +62 -56
  85. package/dist/tokens/semantic/layout.css +5 -3
  86. package/docs/COMPOSITION-VS-COMPONENT.md +15 -14
  87. package/docs/TOKENS.md +33 -0
  88. package/package.json +2 -2
package/README.md CHANGED
@@ -18,7 +18,7 @@ npm i @godxjp/ui
18
18
 
19
19
  <!-- agent-catalog-counts:start -->
20
20
 
21
- The catalog — 170 components, 2251 tokens, 44 cardinal rules — is published in two forms from one
21
+ The catalog — 171 components, 2263 tokens, 44 cardinal rules — is published in two forms from one
22
22
  source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
23
23
  <!-- agent-catalog-counts:end -->
24
24
 
@@ -53,10 +53,10 @@ static files straight from this public repo — no hosting, no deploy step:
53
53
  | [`agent/llms.txt`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/llms.txt) | 3 KB | the [llms.txt](https://llmstxt.org/) entry point |
54
54
  | [`agent/DESIGN.md`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md) | 18 KB | the visual spec: tokens, colour scarcity, admin screen anatomy, do/don't |
55
55
  | [`agent/index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json) | 3 KB | manifest: version, counts, every file's URL |
56
- | [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 53 KB | all 170 as name + group + tagline — **fetch this first** |
56
+ | [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 54 KB | all 171 as name + group + tagline — **fetch this first** |
57
57
  | [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–38 KB | one file per component, each with its `importPath` — **this is the route to take** |
58
- | [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.3 MB | all 170 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
59
- | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 860 KB | all 2251 design tokens, their values and why they exist |
58
+ | [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.3 MB | all 171 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
59
+ | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 862 KB | all 2263 design tokens, their values and why they exist |
60
60
  | [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
61
61
  | [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 23 KB | the 44 cardinal rules |
62
62
  <!-- prettier-ignore-end -->
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 33.15.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 33.17.0.** If the project you are editing has a different
7
7
  version in its `package.json`, read the pinned catalog for THAT version instead
8
8
  (`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
9
9
  not exist yet; older, and it hides props that do. Neither failure announces itself.
@@ -52,7 +52,7 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
52
52
  task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
53
53
  filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
54
54
  copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
55
- 1. `components-index.json` — 53 KB, all 170 components as name + group +
55
+ 1. `components-index.json` — 54 KB, all 171 components as name + group +
56
56
  tagline. Read this when you already know the SHAPE you need. Each entry may carry `absorbed`:
57
57
  names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
58
58
  `SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
@@ -61,10 +61,10 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
61
61
  its `importPath`, and its examples. Fetch only the handful you picked in step 1.
62
62
  3. `rules.json` — 44 cardinal rules. The ones about raw HTML and hardcoded colour are not
63
63
  style advice.
64
- 4. `tokens.json` — 2251 design tokens, each tagged with its `tier`. **If you were handed a
65
- brand, read the 218 `foundation` entries first** — `--primary`, `--background`,
64
+ 4. `tokens.json` — 2263 design tokens, each tagged with its `tier`. **If you were handed a
65
+ brand, read the 220 `foundation` entries first** — `--primary`, `--background`,
66
66
  `--radius`, `--font-size-base` are the handful everything else derives from. The
67
- 1920 `component` entries are per-part knobs; reach for one only when a role is
67
+ 1930 `component` entries are per-part knobs; reach for one only when a role is
68
68
  right everywhere except one component.
69
69
  5. `anti-ai-tells.json` — 35 shapes that make generated UI look generated, each with the
70
70
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
@@ -148,9 +148,9 @@ has stopped following the brand.
148
148
 
149
149
  | `tier` | count | what it is | set it? |
150
150
  |---|---|---|---|
151
- | `foundation` | 218 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** The default `--primary` is a neutral placeholder: set it (`:root { --primary: <H> <S>% <L>%; }`, HSL components, no `hsl()` wrapper) or import a theme preset such as `@godxjp/ui/themes/godx.css` |
151
+ | `foundation` | 220 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** The default `--primary` is a neutral placeholder: set it (`:root { --primary: <H> <S>% <L>%; }`, HSL components, no `hsl()` wrapper) or import a theme preset such as `@godxjp/ui/themes/godx.css` |
152
152
  | `semantic` | 113 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
153
- | `component` | 1920 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
153
+ | `component` | 1930 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
154
154
 
155
155
  A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
156
156
  value, so the real default is computed where the element paints it. Set it and yours wins.
@@ -135,7 +135,7 @@
135
135
  23
136
136
  ],
137
137
  "storyPath": "layout/app-shell.tsx",
138
- "tagline": "Root application shell — composes sidebar, topbar rail, main content area, and optional footer. Keyboard focus in clipped chrome stays inset; the main scroll region shows an inset brand ring by default (--region-focus-ring-width).",
138
+ "tagline": "Root application shell — composes sidebar, topbar rail, main content area, and optional footer. In print, navigation, topbar, breadcrumb and footer are hidden; main content flows across the paper width on the light --background. Put every consumer-authored dark theme override inside @media not print, with light declarations outside it, matching the package themes. Keyboard focus in clipped chrome stays inset; the main scroll region shows an inset brand ring by default (--region-focus-ring-width).",
139
139
  "usage": [
140
140
  "DO put a sidebar collapse toggle IN the rail, not floating in the bar (ADR C10): the shell does not render one, so pass it through `Sidebar action={(collapsed) => <Button variant=\"ghost\" size=\"icon\" aria-label={t(\"toggleSidebar\")} aria-expanded={!collapsed} …/>}` (or the `footer` render-prop), which keeps it attached to the rail in both states. A toggle in `Topbar start` under `topbarSpan=\"full\"` lands beside the brand, detached from the rail it controls. The shell has no edge-mounted toggle (a control straddling the rail border); `Sidebar action` keeps it inside the rail head, which is the supported placement. Below the navigation boundary the drawer has its own trigger; render no collapse toggle there.",
141
141
  "useAppShellNavigationMode() returns drawer/docked at the same 56.25rem boundary as AppShell. The root also publishes data-navigation. Use this instead of a consumer media query.",
@@ -4,6 +4,11 @@
4
4
  "importPath": "@godxjp/ui/charts",
5
5
  "name": "AreaChart",
6
6
  "props": [
7
+ {
8
+ "description": "Opt-in pointer and Enter/Space selection. seriesKey is the series dataKey; index is the original data index. Arrows navigate; Home/End move to ends. Finite data only (pie: positive slices). No handler preserves the non-selectable chart. Dashboard owns filter state.",
9
+ "name": "onDatumSelect",
10
+ "type": "(datum: ChartDatum, context: { seriesKey: string; index: number }) => void"
11
+ },
7
12
  {
8
13
  "description": "Row data: one category per row with a numeric value per series.",
9
14
  "name": "data",
@@ -108,6 +113,7 @@
108
113
  "rules": [],
109
114
  "tagline": "Magnitude over an ordered category axis — overlay or `stacked` areas, optional `curved` smoothing, localized formatting + text alternative. Data-visualization graph / plot.",
110
115
  "usage": [
116
+ "DO use onDatumSelect for accessible drill-through; keep filter state in the screen. Marks have localized names, roving focus and the brand ring. Line/area display points while interactive. RTL mirrors horizontal arrow navigation.",
111
117
  "DO import from the charts entry: `import { AreaChart } from \"@godxjp/ui/charts\";` (recharts optional peer required).",
112
118
  "DO import only the chart a screen uses — `import { AreaChart } from \"@godxjp/ui/charts/area-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
113
119
  "DO use `stacked` to show how parts accumulate into a total over time.",
@@ -4,6 +4,11 @@
4
4
  "importPath": "@godxjp/ui/charts",
5
5
  "name": "BarChart",
6
6
  "props": [
7
+ {
8
+ "description": "Opt-in pointer and Enter/Space selection. seriesKey is the series dataKey; index is the original data index. Arrows navigate; Home/End move to ends. Finite data only (pie: positive slices). No handler preserves the non-selectable chart. Dashboard owns filter state.",
9
+ "name": "onDatumSelect",
10
+ "type": "(datum: ChartDatum, context: { seriesKey: string; index: number }) => void"
11
+ },
7
12
  {
8
13
  "description": "Row data: one category per row with a numeric value per series.",
9
14
  "name": "data",
@@ -103,6 +108,7 @@
103
108
  "rules": [],
104
109
  "tagline": "Compare a value across categories — grouped or `stacked`, vertical or `horizontal`, with localized ticks/tooltips and a built-in text alternative. Data-visualization graph / plot / diagram.",
105
110
  "usage": [
111
+ "DO use onDatumSelect for accessible drill-through; keep filter state in the screen. Marks have localized names, roving focus and the brand ring. Line/area display points while interactive. RTL mirrors horizontal arrow navigation.",
106
112
  "DO import from the charts entry: `import { BarChart } from \"@godxjp/ui/charts\";` (recharts optional peer required).",
107
113
  "DO import only the chart a screen uses — `import { BarChart } from \"@godxjp/ui/charts/bar-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
108
114
  "DO use `horizontal` when category labels are long (they read better on the y-axis).",
@@ -84,7 +84,7 @@
84
84
  "CardHeader",
85
85
  "CardTitle"
86
86
  ],
87
- "tagline": "Surface container with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. ⚠️ The bare <Card> has NO inner padding — body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props — the Card renders the strip and the children become the selected tab's body. For a non-tab TOOL strip inside the card (composer or editor tools) use <CardBar extra={…}> — a positionable bar that auto-draws its separator from its position (top→bottom border, bottom→top border, middle→both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card. List FILTERS (search, filter selects, filter chips) are NOT a CardBar: CardBar's main slot scrolls sideways, and a filter strip never does — they go in a FilterBar ABOVE the card, which wraps (cardinal rule #51, pattern `list-filters`).",
87
+ "tagline": "Surface container that requests break-inside: avoid in print, with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. ⚠️ The bare <Card> has NO inner padding — body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props — the Card renders the strip and the children become the selected tab's body. For a non-tab TOOL strip inside the card (composer or editor tools) use <CardBar extra={…}> — a positionable bar that auto-draws its separator from its position (top→bottom border, bottom→top border, middle→both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card. List FILTERS (search, filter selects, filter chips) are NOT a CardBar: CardBar's main slot scrolls sideways, and a filter strip never does — they go in a FilterBar ABOVE the card, which wraps (cardinal rule #51, pattern `list-filters`).",
88
88
  "usage": [
89
89
  "DO always wrap body content in <CardContent> — the bare <Card> div has zero inner padding; content renders flush against card edges without it. Never add className=\"p-4\" directly on <Card> as a substitute.",
90
90
  "DO put titles/descriptions in <CardHeader>/<CardTitle>/<CardDescription>. Use <CardHeader banded> for a visually separated muted-background header band (mirrors <CardFooter separated>). Pair with <CardAction> inside a flex-row CardHeader for header-level action buttons.",
@@ -0,0 +1,76 @@
1
+ {
2
+ "docPath": "docs/lab/dashboard-grid.tsx",
3
+ "example": "import { DashboardGrid } from \"@godxjp/ui/lab\";\n\n<DashboardGrid items={[{ id: \"revenue\", label: \"Revenue\", content: \"Revenue\" }]} defaultLayout={[{ id: \"revenue\", x: 0, y: 0, w: 2, h: 3 }]} columns={{ base: 1, lg: 4 }} editable />",
4
+ "group": "layout",
5
+ "importPath": "@godxjp/ui/lab",
6
+ "name": "DashboardGrid",
7
+ "props": [
8
+ {
9
+ "description": "Stable ids, visible header labels (also accessible region names) and widget body content. Wrap padded bodies in CardContent, tables in CardContent flush; DashboardGrid owns the Card and header. DOM reading order remains items order.",
10
+ "name": "items",
11
+ "required": true,
12
+ "type": "{ id: string; label: string; content: ReactNode }[]"
13
+ },
14
+ {
15
+ "description": "Controlled layout: {id,x,y,w,h}, integer cells, zero-based x from inline-start.",
16
+ "name": "layout",
17
+ "type": "DashboardGridItemProp[]"
18
+ },
19
+ {
20
+ "description": "Uncontrolled initial layout.",
21
+ "name": "defaultLayout",
22
+ "type": "DashboardGridItemProp[]"
23
+ },
24
+ {
25
+ "description": "One callback per changed drop; Escape restores without saving.",
26
+ "name": "onLayoutChange",
27
+ "type": "(layout: DashboardGridItemProp[]) => void"
28
+ },
29
+ {
30
+ "description": "Default 12. Responsive viewport breakpoints; a single column stacks at natural content heights. Narrow projection never overwrites saved layout.",
31
+ "name": "columns",
32
+ "type": "number | { base: number; sm?: number; md?: number; lg?: number; xl?: number }"
33
+ },
34
+ {
35
+ "description": "Positive pixel row track size. Omitted uses --dashboard-grid-row-height.",
36
+ "name": "rowHeight",
37
+ "type": "number"
38
+ },
39
+ {
40
+ "description": "Default false. Inset header grip and bottom-end resize corner (on hover/focus); header space is reserved in view mode to prevent content shift. Enter/Space grab/drop, arrows move, Shift+arrows resize, Escape cancels.",
41
+ "name": "editable",
42
+ "type": "boolean"
43
+ },
44
+ {
45
+ "description": "Root class.",
46
+ "name": "className",
47
+ "type": "string"
48
+ },
49
+ {
50
+ "description": "Root id.",
51
+ "name": "id",
52
+ "type": "string"
53
+ }
54
+ ],
55
+ "related": [
56
+ "ResponsiveGrid — static flow layout.",
57
+ "SortableList — ordering without cell geometry."
58
+ ],
59
+ "rules": [
60
+ 44,
61
+ 45
62
+ ],
63
+ "storyPath": "lab/dashboard-grid.tsx",
64
+ "tagline": "BI widget layout editor with deterministic push-down collisions and vertical compaction. Static CSS grid in view mode; temporary pointer/touch/keyboard previews commit once on drop.",
65
+ "tier": "lab",
66
+ "usage": [
67
+ "C7 exception approved by the owner, BI precedents (Tableau/Power BI/Grafana/react-grid-layout). Approved 2026-10-23. Lab §2.1: same gates as core; promotion only once ≥2 repos import it.",
68
+ "Use stable unique ids and order items by reading priority. Persist only onLayoutChange.",
69
+ "Coordinates are normalized and compacted. View mode exposes content semantics, not an ARIA data grid.",
70
+ "RTL mirrors x and horizontal keyboard/pointer deltas. Touch-action none is confined to handles.",
71
+ "Theme --dashboard-grid-gap, row-height, handle-size, handle-inset, item-radius and preview-ring-color."
72
+ ],
73
+ "useCases": [
74
+ "BI dashboards with rearrangeable and resizable widgets."
75
+ ]
76
+ }
@@ -86,7 +86,7 @@
86
86
  },
87
87
  {
88
88
  "defaultValue": "'default'",
89
- "description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: at EVERY width the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*: primary 18% · secondary 22% · meta 12% · actions a 3.5rem floor, the unmarked column takes the rest) under table-layout: fixed, and below collapseBelow they switch to the compact tier (24% · 22% · 20% · 2.75rem) — collapseBelow picks the TIER, it does not turn the measures on (gh#1408). Cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
89
+ "description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: at EVERY width the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*: primary 18% · secondary 22% · meta 12% · actions a 3.5rem floor, the unmarked column takes the rest) under table-layout: fixed, and below collapseBelow they switch to the compact tier (24% · 22% · 20% · 2.75rem) — collapseBelow picks the TIER, it does not turn the measures on (gh#1408). Cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with zero gap and a single separator between records (--table-stacked-collection-*; --table-stacked-collection-card-gap defaults to 0 and remains themeable). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
90
90
  "name": "preset",
91
91
  "type": "'default' | 'action-collection' | 'stacked-record-collection'"
92
92
  },
@@ -44,6 +44,12 @@
44
44
  "description": "Single-line ellipsis.",
45
45
  "name": "truncate",
46
46
  "type": "boolean"
47
+ },
48
+ {
49
+ "defaultValue": "\"display\"",
50
+ "description": "Heading face. `editorial` sets this heading in --font-family-editorial — the opt-in serif a public/editorial page uses for its hero and section headings while every UI heading stays in the display face. Until a theme names --font-family-editorial it falls back to --font-family-display, so the prop is inert by default. Prefer it over moving --font-family-display, which re-faces EVERY heading (card titles, empty states, form sections).",
51
+ "name": "family",
52
+ "type": "\"display\" | \"editorial\""
47
53
  }
48
54
  ],
49
55
  "rules": [
@@ -4,6 +4,11 @@
4
4
  "importPath": "@godxjp/ui/charts/heatmap-chart",
5
5
  "name": "HeatmapChart",
6
6
  "props": [
7
+ {
8
+ "description": "Opt-in pointer and Enter/Space selection. seriesKey is String(column key); index is the original data index. Arrows navigate; Home/End move to ends. Finite data only (pie: positive slices). No handler preserves the non-selectable chart. Dashboard owns filter state.",
9
+ "name": "onDatumSelect",
10
+ "type": "(datum: HeatmapDatum, context: { seriesKey: string; index: number }) => void"
11
+ },
7
12
  {
8
13
  "description": "Row categories, top to bottom (the y axis — weekdays). `label` is already translated.",
9
14
  "name": "rows",
@@ -81,6 +86,7 @@
81
86
  "storyPath": "charts/heatmap-chart.tsx",
82
87
  "tagline": "DEPENDENCY-FREE rows × columns heatmap — weekday × hour revenue / order counts, cell intensity from the sequential CVD-safe --chart-heat-1..5 ramp. Empty ≠ zero (dashed outline + 'no data' text), optional top-N ring, per-cell tooltip, APG grid keyboard, the table itself is the text alternative. Needs NO recharts. Matrix / punch card / activity heatmap / busy-hours chart.",
83
88
  "usage": [
89
+ "DO use onDatumSelect for accessible drill-through; keep filter state in the screen. Marks have localized names, roving focus and the brand ring. Line/area display points while interactive. RTL mirrors horizontal arrow navigation.",
84
90
  "DO import from the isolated entry: `import { HeatmapChart } from \"@godxjp/ui/charts/heatmap-chart\";` — it never links the optional `recharts` peer.",
85
91
  "DO derive axis labels with Intl in the active locale — weekdays via `Intl.DateTimeFormat(locale, { weekday: 'short' })`, hours via `Intl.NumberFormat(locale)` — and pass money through `numberFormat`, never a hand-built '¥' string.",
86
92
  "DO leave a slot OUT of `data` (or pass `value: null`) when there is no measurement — a closed hour, a day before the store opened. Pass `0` only for a real zero. The two render differently (dashed outline vs filled) and read differently ('No data' vs '¥0').",
@@ -4,6 +4,11 @@
4
4
  "importPath": "@godxjp/ui/charts",
5
5
  "name": "LineChart",
6
6
  "props": [
7
+ {
8
+ "description": "Opt-in pointer and Enter/Space selection. seriesKey is the series dataKey; index is the original data index. Arrows navigate; Home/End move to ends. Finite data only (pie: positive slices). No handler preserves the non-selectable chart. Dashboard owns filter state.",
9
+ "name": "onDatumSelect",
10
+ "type": "(datum: ChartDatum, context: { seriesKey: string; index: number }) => void"
11
+ },
7
12
  {
8
13
  "description": "Row data: one category per row with a numeric value per series.",
9
14
  "name": "data",
@@ -103,6 +108,7 @@
103
108
  "rules": [],
104
109
  "tagline": "Trends over an ordered category axis — one or more series, locale-formatted ticks/tooltips, screen-reader text alternative built in. Data-visualization graph / plot.",
105
110
  "usage": [
111
+ "DO use onDatumSelect for accessible drill-through; keep filter state in the screen. Marks have localized names, roving focus and the brand ring. Line/area display points while interactive. RTL mirrors horizontal arrow navigation.",
106
112
  "DO import from the tree-shaken charts entry: `import { LineChart } from \"@godxjp/ui/charts\";`. Importing any other subpath never pulls in recharts.",
107
113
  "DO import only the chart a screen uses — `import { LineChart } from \"@godxjp/ui/charts/line-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
108
114
  "DO install the `recharts` optional peer dependency in the consuming app — charts are the only part of @godxjp/ui that needs it, so apps without charts never pay for it.",
@@ -109,7 +109,7 @@
109
109
  "PaginationNext",
110
110
  "PaginationPrevious"
111
111
  ],
112
- "tagline": "Offset/page-based pagination bar. Sits below a table card. Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
112
+ "tagline": "Offset/page-based pagination bar. Its container owns the inset: no outer inline padding, including inside CardContent, CardFooter or PageContainer footer. Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
113
113
  "usage": [
114
114
  "DO always control Pagination externally: store `value` (page) and `pageSize` in React state (or URL params), and update both in the `onValueChange(page, pageSize)` callback. Pagination is fully controlled — it has no internal state and will not move unless `value` changes.",
115
115
  "DO let Pagination hide itself for zero items and single pages (`hideOnSinglePage`, default true) — it is navigation between multiple result pages. Render it inside a table footer only in the DATA state: never during loading, empty, error, or an unmet prerequisite. Pass `hideOnSinglePage={false}` only when you still want the bar on one page to keep `showTotal` visible.",
@@ -4,6 +4,11 @@
4
4
  "importPath": "@godxjp/ui/charts",
5
5
  "name": "PieChart",
6
6
  "props": [
7
+ {
8
+ "description": "Opt-in pointer and Enter/Space selection. seriesKey is dataKey; index is the original data index. Arrows navigate; Home/End move to ends. Finite data only (pie: positive slices). No handler preserves the non-selectable chart. Dashboard owns filter state.",
9
+ "name": "onDatumSelect",
10
+ "type": "(datum: ChartDatum, context: { seriesKey: string; index: number }) => void"
11
+ },
7
12
  {
8
13
  "description": "Row data: one slice per row.",
9
14
  "name": "data",
@@ -85,6 +90,7 @@
85
90
  "rules": [],
86
91
  "tagline": "Part-to-whole composition across a small set of slices — `donut` option, localized tooltips, and a screen-reader breakdown of every slice. Data-visualization graph / diagram.",
87
92
  "usage": [
93
+ "DO use onDatumSelect for accessible drill-through; keep filter state in the screen. Marks have localized names, roving focus and the brand ring. Line/area display points while interactive. RTL mirrors horizontal arrow navigation.",
88
94
  "DO import from the charts entry: `import { PieChart } from \"@godxjp/ui/charts\";` (recharts optional peer required).",
89
95
  "DO import only the chart a screen uses — `import { PieChart } from \"@godxjp/ui/charts/pie-chart\";` — when the `./charts` barrel should not link the whole chart family. Without the `recharts` peer the build then fails ONCE, naming the package and the fix.",
90
96
  "DO keep slices few (≈2–6) — pies are unreadable past a handful; use BarChart for many categories.",
@@ -69,7 +69,7 @@
69
69
  },
70
70
  {
71
71
  "defaultValue": "\"default\"",
72
- "description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
72
+ "description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with zero gap and a single separator between records (--table-stacked-collection-*; --table-stacked-collection-card-gap defaults to 0 and remains themeable). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
73
73
  "name": "preset",
74
74
  "type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
75
75
  },
@@ -95,6 +95,7 @@
95
95
  "storyPath": "data-display/table.tsx",
96
96
  "subParts": [
97
97
  "TableBody",
98
+ "TableCaption",
98
99
  "TableCell",
99
100
  "TableHead",
100
101
  "TableHeader",
@@ -72,7 +72,7 @@
72
72
  {
73
73
  "group": "layout",
74
74
  "name": "AppShell",
75
- "tagline": "Root application shell — composes sidebar, topbar rail, main content area, and optional footer. Keyboard focus in clipped chrome stays inset; the main scroll region shows an inset brand ring by default (--region-focus-ring-width)."
75
+ "tagline": "Root application shell — composes sidebar, topbar rail, main content area, and optional footer. In print, navigation, topbar, breadcrumb and footer are hidden; main content flows across the paper width on the light --background. Put every consumer-authored dark theme override inside @media not print, with light declarations outside it, matching the package themes. Keyboard focus in clipped chrome stays inset; the main scroll region shows an inset brand ring by default (--region-focus-ring-width)."
76
76
  },
77
77
  {
78
78
  "absorbed": [
@@ -215,7 +215,7 @@
215
215
  {
216
216
  "group": "data-display",
217
217
  "name": "Card",
218
- "tagline": "Surface container with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. ⚠️ The bare <Card> has NO inner padding — body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props — the Card renders the strip and the children become the selected tab's body. For a non-tab TOOL strip inside the card (composer or editor tools) use <CardBar extra={…}> — a positionable bar that auto-draws its separator from its position (top→bottom border, bottom→top border, middle→both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card. List FILTERS (search, filter selects, filter chips) are NOT a CardBar: CardBar's main slot scrolls sideways, and a filter strip never does — they go in a FilterBar ABOVE the card, which wraps (cardinal rule #51, pattern `list-filters`)."
218
+ "tagline": "Surface container that requests break-inside: avoid in print, with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. ⚠️ The bare <Card> has NO inner padding — body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props — the Card renders the strip and the children become the selected tab's body. For a non-tab TOOL strip inside the card (composer or editor tools) use <CardBar extra={…}> — a positionable bar that auto-draws its separator from its position (top→bottom border, bottom→top border, middle→both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card. List FILTERS (search, filter selects, filter chips) are NOT a CardBar: CardBar's main slot scrolls sideways, and a filter strip never does — they go in a FilterBar ABOVE the card, which wraps (cardinal rule #51, pattern `list-filters`)."
219
219
  },
220
220
  {
221
221
  "group": "data-display",
@@ -498,7 +498,7 @@
498
498
  {
499
499
  "group": "navigation",
500
500
  "name": "Pagination",
501
- "tagline": "Offset/page-based pagination bar. Sits below a table card. Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout."
501
+ "tagline": "Offset/page-based pagination bar. Its container owns the inset: no outer inline padding, including inside CardContent, CardFooter or PageContainer footer. Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout."
502
502
  },
503
503
  {
504
504
  "group": "navigation",
@@ -777,6 +777,12 @@
777
777
  "tagline": "Page icon / reaction picker in a Popover (gh#1164): search by the active language's CLDR keywords (ja / en / vi — emojibase-data, loaded lazily for that locale only), categories, per-user recents, a WAI-ARIA grid (one tab stop, arrows, Home / End, Enter), and \"remove icon\". No skin tones.",
778
778
  "tier": "lab"
779
779
  },
780
+ {
781
+ "group": "layout",
782
+ "name": "DashboardGrid",
783
+ "tagline": "BI widget layout editor with deterministic push-down collisions and vertical compaction. Static CSS grid in view mode; temporary pointer/touch/keyboard previews commit once on drop.",
784
+ "tier": "lab"
785
+ },
780
786
  {
781
787
  "group": "data-entry",
782
788
  "name": "SortableList",