@godxjp/ui 30.9.0 → 31.0.2

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 (52) hide show
  1. package/README.md +2 -1
  2. package/agent/START-HERE.md +1 -1
  3. package/agent/components/PrefetchLink.json +3 -3
  4. package/agent/components-index.json +1 -1
  5. package/agent/components.json +3 -3
  6. package/agent/index.json +2 -2
  7. package/agent/llms.txt +3 -3
  8. package/agent/patterns/settings-page-responsive.json +1 -1
  9. package/agent/patterns.json +1 -1
  10. package/dist/components/query/index.d.ts +0 -2
  11. package/dist/components/query/index.js +0 -2
  12. package/dist/components/react-router/index.d.ts +2 -0
  13. package/dist/components/react-router/index.js +4 -0
  14. package/dist/contracts/measurement.json +1 -1
  15. package/dist/styles/alert-layout.css +4 -2
  16. package/dist/styles/layers.json +1 -1
  17. package/docs/CONSUMER-RULES.md +1 -1
  18. package/docs/data-display/permission-matrix.tsx +30 -28
  19. package/docs/data-entry/cascader.tsx +22 -20
  20. package/docs/data-entry/date-picker.tsx +47 -43
  21. package/docs/data-entry/form-field/examples/a11y-contract.tsx +80 -76
  22. package/docs/data-entry/form-field/examples/create-form.tsx +33 -31
  23. package/docs/data-entry/form-field/index.tsx +61 -57
  24. package/docs/data-entry/form.tsx +112 -106
  25. package/docs/data-entry/input.tsx +297 -270
  26. package/docs/data-entry/label.tsx +11 -9
  27. package/docs/data-entry/number-input.tsx +86 -78
  28. package/docs/data-entry/radio-group.tsx +33 -26
  29. package/docs/data-entry/rating.tsx +29 -25
  30. package/docs/data-entry/select-matrix.tsx +49 -47
  31. package/docs/data-entry/select.tsx +38 -35
  32. package/docs/data-entry/slider.tsx +45 -41
  33. package/docs/data-entry/textarea.tsx +44 -42
  34. package/docs/data-entry/time-picker.tsx +51 -47
  35. package/docs/feedback/sheet.tsx +84 -79
  36. package/docs/foundation/theme-editor.tsx +133 -124
  37. package/docs/general/button/examples/form-actions.tsx +17 -15
  38. package/docs/layout/auth-recovery/examples/password-recovery.tsx +31 -29
  39. package/docs/layout/auth-shell-registration.tsx +75 -72
  40. package/docs/layout/auth-shell-variants.tsx +55 -51
  41. package/docs/layout/space-compact.tsx +11 -8
  42. package/docs/{query → react-router}/prefetch-link.tsx +1 -1
  43. package/docs/showcase/caimono-price-comparison.tsx +59 -51
  44. package/docs/showcase/case3-approval-workflow.tsx +27 -25
  45. package/docs/showcase/case4-login.tsx +37 -34
  46. package/docs/showcase/permission-matrix.tsx +44 -42
  47. package/docs/showcase/theme-customization.tsx +108 -99
  48. package/package.json +6 -2
  49. package/scripts/consumer-rule.md +14 -0
  50. package/scripts/ui-audit.mjs +99 -2
  51. /package/dist/components/{query → react-router}/prefetch-link.d.ts +0 -0
  52. /package/dist/components/{query → react-router}/prefetch-link.js +0 -0
package/README.md CHANGED
@@ -112,7 +112,8 @@ A value is defined **once** as a CSS var (`--primary`), mapped to a utility in t
112
112
  | **Data Entry** | `@godxjp/ui/data-entry` | `Input`, `Select`, `FormField`, `Field`, `DatePicker`, `TimePicker`, `Combobox`, `Switch`, `Toggle`, `Upload`, `Cascader`, `TreeSelect`, `ColorPicker`, `Slider`, `PasswordInput`, `PasswordStrength`, `InputOTP`, `Rating`, `TagInput` |
113
113
  | **Data Display** | `@godxjp/ui/data-display` | `Table`, `DataTable`, `Card`, `StatCard`, `Badge`, `Avatar`, `Descriptions`, `Timeline`, `EmptyState`, `Progress`, `QrCode`, `CredentialReveal`, `Accordion`, `HoverCard`, `Carousel`, `Popover`, `Collapsible` |
114
114
  | **Feedback** | `@godxjp/ui/feedback` | `Dialog`, `AlertDialog`, `Sheet` (side), `Drawer` (bottom-sheet), `Toast`, `Skeleton`, `Alert`, `Tooltip` |
115
- | **Query** | `@godxjp/ui/query` | `DataState`, `InfiniteQueryState`, `PrefetchLink` (adapter subpath — pulls TanStack Query) |
115
+ | **Query** | `@godxjp/ui/query` | `DataState`, `InfiniteQueryState` (adapter subpath — pulls TanStack Query; no router) |
116
+ | **React Router** | `@godxjp/ui/react-router` | `PrefetchLink` — imports the optional `react-router-dom` peer, so it is kept out of `/query` (gh#996) |
116
117
  | **Navigation** | `@godxjp/ui/navigation` | `Tabs`, `Toolbar`, `DropdownMenu`, `Steps`, `Pagination`, `Breadcrumb`, `AppSettingPicker` |
117
118
  | **App** | `@godxjp/ui/app` | `AppProvider`, `useDateTime` (adapter — i18n/datetime singleton) |
118
119
  | **Datetime** | `@godxjp/ui/datetime` | `formatDate` (mandatory for display) |
@@ -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` 30.9.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.0.2.** 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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "example": "import { PrefetchLink } from \"@godxjp/ui/query\";\nimport { fetchInvoice } from \"@/api/invoices\";\n\n// Inside a table row or list item:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n staleTime={60_000}\n className=\"font-medium hover:underline\"\n>\n {invoice.number}\n</PrefetchLink>\n\n// Disable prefetch for rows where data is not yet stable:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n prefetchOn=\"none\"\n>\n {invoice.number}\n</PrefetchLink>",
2
+ "example": "import { PrefetchLink } from \"@godxjp/ui/react-router\";\nimport { fetchInvoice } from \"@/api/invoices\";\n\n// Inside a table row or list item:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n staleTime={60_000}\n className=\"font-medium hover:underline\"\n>\n {invoice.number}\n</PrefetchLink>\n\n// Disable prefetch for rows where data is not yet stable:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n prefetchOn=\"none\"\n>\n {invoice.number}\n</PrefetchLink>",
3
3
  "group": "data-display",
4
- "importPath": "@godxjp/ui/query",
4
+ "importPath": "@godxjp/ui/react-router",
5
5
  "name": "PrefetchLink",
6
6
  "props": [
7
7
  {
@@ -46,7 +46,7 @@
46
46
  31
47
47
  ],
48
48
  "storyPath": "data-display/PrefetchLink.stories.tsx",
49
- "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context.",
49
+ "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context. Imported from `@godxjp/ui/react-router` (not `/query`) because it imports the optional `react-router-dom` peer; in `/query` it broke every router-less app's build (gh#996).",
50
50
  "usage": [
51
51
  "DO: provide a queryKey that exactly matches the destination page's useQuery key — a mismatch means the prefetch populates a different cache slot and the page still loads cold.",
52
52
  "DO: keep queryFn lightweight and side-effect-free; it runs speculatively on hover. Avoid mutations or write operations inside queryFn.",
@@ -590,7 +590,7 @@
590
590
  {
591
591
  "group": "data-display",
592
592
  "name": "PrefetchLink",
593
- "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context."
593
+ "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context. Imported from `@godxjp/ui/react-router` (not `/query`) because it imports the optional `react-router-dom` peer; in `/query` it broke every router-less app's build (gh#996)."
594
594
  },
595
595
  {
596
596
  "group": "data-display",
@@ -11201,9 +11201,9 @@
11201
11201
  ]
11202
11202
  },
11203
11203
  {
11204
- "example": "import { PrefetchLink } from \"@godxjp/ui/query\";\nimport { fetchInvoice } from \"@/api/invoices\";\n\n// Inside a table row or list item:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n staleTime={60_000}\n className=\"font-medium hover:underline\"\n>\n {invoice.number}\n</PrefetchLink>\n\n// Disable prefetch for rows where data is not yet stable:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n prefetchOn=\"none\"\n>\n {invoice.number}\n</PrefetchLink>",
11204
+ "example": "import { PrefetchLink } from \"@godxjp/ui/react-router\";\nimport { fetchInvoice } from \"@/api/invoices\";\n\n// Inside a table row or list item:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n staleTime={60_000}\n className=\"font-medium hover:underline\"\n>\n {invoice.number}\n</PrefetchLink>\n\n// Disable prefetch for rows where data is not yet stable:\n<PrefetchLink\n to={`/invoices/${invoice.id}`}\n queryKey={[\"invoice\", invoice.id]}\n queryFn={() => fetchInvoice(invoice.id)}\n prefetchOn=\"none\"\n>\n {invoice.number}\n</PrefetchLink>",
11205
11205
  "group": "data-display",
11206
- "importPath": "@godxjp/ui/query",
11206
+ "importPath": "@godxjp/ui/react-router",
11207
11207
  "name": "PrefetchLink",
11208
11208
  "props": [
11209
11209
  {
@@ -11248,7 +11248,7 @@
11248
11248
  31
11249
11249
  ],
11250
11250
  "storyPath": "data-display/PrefetchLink.stories.tsx",
11251
- "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context.",
11251
+ "tagline": "React Router Link that fires prefetchQuery on hover/focus so detail pages feel instant — requires a TanStack Query QueryClient in context. Imported from `@godxjp/ui/react-router` (not `/query`) because it imports the optional `react-router-dom` peer; in `/query` it broke every router-less app's build (gh#996).",
11252
11252
  "usage": [
11253
11253
  "DO: provide a queryKey that exactly matches the destination page's useQuery key — a mismatch means the prefetch populates a different cache slot and the page still loads cold.",
11254
11254
  "DO: keep queryFn lightweight and side-effect-free; it runs speculatively on hover. Avoid mutations or write operations inside queryFn.",
package/agent/index.json CHANGED
@@ -48,7 +48,7 @@
48
48
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
49
49
  "read": {
50
50
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
51
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v30.9.0/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.0.2/agent/index.json"
52
52
  },
53
53
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
54
54
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
@@ -62,5 +62,5 @@
62
62
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
63
63
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
64
64
  },
65
- "version": "30.9.0"
65
+ "version": "31.0.2"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
3
  > A Japanese-enterprise React design system: 175 components, 2074 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 30.9.0.
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.0.2.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@30.9.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.0.2`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -26,7 +26,7 @@ that can only fetch URLs.
26
26
  ## Pinning
27
27
 
28
28
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
29
- the tag: `.../godx-jp/godxjp-ui/v30.9.0/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.0.2/agent/...`. A catalog that does not match the installed
30
30
  package describes props that are absent, or hides props that are present, and says nothing either way.
31
31
 
32
32
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
@@ -4,7 +4,7 @@
4
4
  "settings-page",
5
5
  "settings-layout"
6
6
  ],
7
- "code": "// Research basis: GitHub, Google Account, Microsoft Account, Atlassian Account settings +\n// the IBM Carbon form grid. Every destination is a URL, so deep links and browser back/forward\n// work for free and the active item is derived from the route — never from local tab state.\n//\n// Desktop (>= lg): persistent vertical local nav on the left + bounded content on the right.\n// Mobile (< lg): the SAME routes become a horizontal, scrollable tab strip above the content.\n// Styling uses ONLY real semantic token classes (bg-muted / text-foreground / text-muted-foreground)\n// and responsive/visibility utilities — the library ships NO bespoke settings shell/nav CSS classes.\nimport { NavLink, Outlet, Navigate } from \"react-router-dom\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { FormField, Input } from \"@godxjp/ui/data-entry\";\nimport { Button } from \"@godxjp/ui/general\";\n\nconst SECTIONS = [\n { to: \"general\", label: \"基本情報\" },\n { to: \"security\", label: \"セキュリティ\" },\n { to: \"notifications\", label: \"通知\" },\n] as const;\n\n// One link renderer for BOTH breakpoints. NavLink gives us the active state (and sets\n// aria-current=\"page\") straight from the URL; semantic tokens only, no invented classes.\nconst link = ({ isActive }: { isActive: boolean }) =>\n [\n \"rounded-md px-3 py-2 text-sm no-underline transition-colors whitespace-nowrap\",\n isActive\n ? \"bg-muted text-foreground font-medium\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \");\n\nexport function SettingsLayout() {\n return (\n <Flex direction=\"col\" gap=\"lg\" className=\"lg:flex-row lg:items-start\">\n {/* < lg: horizontal, scrollable route tabs. Flex owns the row and the gap — hideFrom /\n hideBelow own the breakpoint, so neither nav needs a hand-rolled flex or gap utility. */}\n <Flex as=\"nav\" aria-label=\"設定\" gap={1} hideFrom=\"lg\" className=\"overflow-x-auto\">\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* >= lg: persistent vertical local nav (bounded width, does not shrink). */}\n <Flex as=\"nav\" aria-label=\"設定\" direction=\"col\" gap={1} hideBelow=\"lg\" width={224} shrink={false}>\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* Bounded content region (~42rem) — the routed section renders here. min-w-0 lets it shrink. */}\n <main className=\"min-w-0 max-w-2xl flex-1\"><Outlet /></main>\n </Flex>\n );\n}\n\n// A settings section. Horizontal label/control rows on desktop (collapse to stacked on mobile),\n// controls bounded to a semantic width. FormField OWNS the label↔control relationship — do NOT\n// hand-roll <Label> + <input>. Save feedback = Button loading prop + a toast in onSuccess.\nexport function GeneralSettingsSection({\n defaults, onSave, saving, error,\n}: { defaults: { name: string; email: string }; onSave: (v: FormData) => void; saving: boolean; error?: string }) {\n return (\n <form onSubmit={(e) => { e.preventDefault(); onSave(new FormData(e.currentTarget)); }}>\n <Flex direction=\"col\" gap=\"md\">\n <FormField id=\"ws-name\" label=\"ワークスペース名\" layout=\"horizontal\" controlWidth=\"24rem\"\n helper=\"請求書や共有リンクに表示されます。\" error={error}>\n <Input id=\"ws-name\" name=\"name\" defaultValue={defaults.name} />\n </FormField>\n <FormField id=\"ws-email\" label=\"連絡先メール\" layout=\"horizontal\" controlWidth=\"24rem\">\n <Input id=\"ws-email\" name=\"email\" type=\"email\" defaultValue={defaults.email} />\n </FormField>\n <Flex gap=\"sm\"><Button type=\"submit\" loading={saving}>変更を保存</Button></Flex>\n </Flex>\n </form>\n );\n}\n\n// Router wiring — each destination is addressable, so deep links + back/forward just work:\n// <Route path=\"settings\" element={<SettingsLayout/>}>\n// <Route index element={<Navigate to=\"general\" replace/>} />\n// <Route path=\"general\" element={<GeneralSettingsSection .../>} />\n// <Route path=\"security\" element={<SecuritySection/>} />\n// <Route path=\"notifications\" element={<NotificationsSection/>} />\n// </Route>\n\n// ── DO / DON'T ────────────────────────────────────────────────────────────────────────────\n// ✅ Tabs (navigation) are fine for a SMALL number (2–4) of PEER views inside ONE task/section\n// — e.g. \"プロフィール\" ⇄ \"環境設定\" within a single account page — where losing the URL is OK.\n// ⛔ Tabs as the PRIMARY IA for broad settings (Account / Security / Billing / Members / …):\n// use route-backed local navigation so every area is deep-linkable and back/forward works.\n// ⛔ Do NOT wrap the whole settings page in a Card, and do NOT make desktop a horizontal tab bar\n// — persistent local nav scales to many sections; a tab strip does not.\n// ⛔ Do NOT invent CSS class names for the shell/nav/content — compose Flex + semantic tokens.",
7
+ "code": "// Research basis: GitHub, Google Account, Microsoft Account, Atlassian Account settings +\n// the IBM Carbon form grid. Every destination is a URL, so deep links and browser back/forward\n// work for free and the active item is derived from the route — never from local tab state.\n//\n// Desktop (>= lg): persistent vertical local nav on the left + bounded content on the right.\n// Mobile (< lg): the SAME routes become a horizontal, scrollable tab strip above the content.\n// Styling uses ONLY real semantic token classes (bg-muted / text-foreground / text-muted-foreground)\n// and responsive/visibility utilities — the library ships NO bespoke settings shell/nav CSS classes.\nimport { NavLink, Outlet, Navigate } from \"react-router-dom\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { Form, FormField, Input } from \"@godxjp/ui/data-entry\";\nimport { Button } from \"@godxjp/ui/general\";\n\nconst SECTIONS = [\n { to: \"general\", label: \"基本情報\" },\n { to: \"security\", label: \"セキュリティ\" },\n { to: \"notifications\", label: \"通知\" },\n] as const;\n\n// One link renderer for BOTH breakpoints. NavLink gives us the active state (and sets\n// aria-current=\"page\") straight from the URL; semantic tokens only, no invented classes.\nconst link = ({ isActive }: { isActive: boolean }) =>\n [\n \"rounded-md px-3 py-2 text-sm no-underline transition-colors whitespace-nowrap\",\n isActive\n ? \"bg-muted text-foreground font-medium\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \");\n\nexport function SettingsLayout() {\n return (\n <Flex direction=\"col\" gap=\"lg\" className=\"lg:flex-row lg:items-start\">\n {/* < lg: horizontal, scrollable route tabs. Flex owns the row and the gap — hideFrom /\n hideBelow own the breakpoint, so neither nav needs a hand-rolled flex or gap utility. */}\n <Flex as=\"nav\" aria-label=\"設定\" gap={1} hideFrom=\"lg\" className=\"overflow-x-auto\">\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* >= lg: persistent vertical local nav (bounded width, does not shrink). */}\n <Flex as=\"nav\" aria-label=\"設定\" direction=\"col\" gap={1} hideBelow=\"lg\" width={224} shrink={false}>\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* Bounded content region (~42rem) — the routed section renders here. min-w-0 lets it shrink. */}\n <main className=\"min-w-0 max-w-2xl flex-1\"><Outlet /></main>\n </Flex>\n );\n}\n\n// A settings section. Horizontal label/control rows on desktop (collapse to stacked on mobile),\n// controls bounded to a semantic width — set ONCE on the <Form>, which every FormField reads\n// (gh#998). FormField OWNS the label↔control relationship — do NOT hand-roll <Label> + <input>. Save feedback = Button loading prop + a toast in onSuccess.\nexport function GeneralSettingsSection({\n defaults, onSave, saving, error,\n}: { defaults: { name: string; email: string }; onSave: (v: FormData) => void; saving: boolean; error?: string }) {\n return (\n <Form layout=\"horizontal\" controlWidth=\"24rem\"\n onSubmit={(e) => { e.preventDefault(); onSave(new FormData(e.currentTarget)); }}>\n <FormField id=\"ws-name\" label=\"ワークスペース名\"\n helper=\"請求書や共有リンクに表示されます。\" error={error}>\n <Input id=\"ws-name\" name=\"name\" defaultValue={defaults.name} />\n </FormField>\n <FormField id=\"ws-email\" label=\"連絡先メール\">\n <Input id=\"ws-email\" name=\"email\" type=\"email\" defaultValue={defaults.email} />\n </FormField>\n <Flex gap=\"sm\"><Button type=\"submit\" loading={saving}>変更を保存</Button></Flex>\n </Form>\n );\n}\n\n// Router wiring — each destination is addressable, so deep links + back/forward just work:\n// <Route path=\"settings\" element={<SettingsLayout/>}>\n// <Route index element={<Navigate to=\"general\" replace/>} />\n// <Route path=\"general\" element={<GeneralSettingsSection .../>} />\n// <Route path=\"security\" element={<SecuritySection/>} />\n// <Route path=\"notifications\" element={<NotificationsSection/>} />\n// </Route>\n\n// ── DO / DON'T ────────────────────────────────────────────────────────────────────────────\n// ✅ Tabs (navigation) are fine for a SMALL number (2–4) of PEER views inside ONE task/section\n// — e.g. \"プロフィール\" ⇄ \"環境設定\" within a single account page — where losing the URL is OK.\n// ⛔ Tabs as the PRIMARY IA for broad settings (Account / Security / Billing / Members / …):\n// use route-backed local navigation so every area is deep-linkable and back/forward works.\n// ⛔ Do NOT wrap the whole settings page in a Card, and do NOT make desktop a horizontal tab bar\n// — persistent local nav scales to many sections; a tab strip does not.\n// ⛔ Do NOT invent CSS class names for the shell/nav/content — compose Flex + semantic tokens.",
8
8
  "name": "settings-page-responsive",
9
9
  "tagline": "Route-backed settings: persistent desktop local navigation, compact mobile tab strip, deep links + browser history, and bounded form content — NO Card wrapper, NO undefined CSS.",
10
10
  "tags": [
@@ -34,7 +34,7 @@
34
34
  "settings-page",
35
35
  "settings-layout"
36
36
  ],
37
- "code": "// Research basis: GitHub, Google Account, Microsoft Account, Atlassian Account settings +\n// the IBM Carbon form grid. Every destination is a URL, so deep links and browser back/forward\n// work for free and the active item is derived from the route — never from local tab state.\n//\n// Desktop (>= lg): persistent vertical local nav on the left + bounded content on the right.\n// Mobile (< lg): the SAME routes become a horizontal, scrollable tab strip above the content.\n// Styling uses ONLY real semantic token classes (bg-muted / text-foreground / text-muted-foreground)\n// and responsive/visibility utilities — the library ships NO bespoke settings shell/nav CSS classes.\nimport { NavLink, Outlet, Navigate } from \"react-router-dom\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { FormField, Input } from \"@godxjp/ui/data-entry\";\nimport { Button } from \"@godxjp/ui/general\";\n\nconst SECTIONS = [\n { to: \"general\", label: \"基本情報\" },\n { to: \"security\", label: \"セキュリティ\" },\n { to: \"notifications\", label: \"通知\" },\n] as const;\n\n// One link renderer for BOTH breakpoints. NavLink gives us the active state (and sets\n// aria-current=\"page\") straight from the URL; semantic tokens only, no invented classes.\nconst link = ({ isActive }: { isActive: boolean }) =>\n [\n \"rounded-md px-3 py-2 text-sm no-underline transition-colors whitespace-nowrap\",\n isActive\n ? \"bg-muted text-foreground font-medium\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \");\n\nexport function SettingsLayout() {\n return (\n <Flex direction=\"col\" gap=\"lg\" className=\"lg:flex-row lg:items-start\">\n {/* < lg: horizontal, scrollable route tabs. Flex owns the row and the gap — hideFrom /\n hideBelow own the breakpoint, so neither nav needs a hand-rolled flex or gap utility. */}\n <Flex as=\"nav\" aria-label=\"設定\" gap={1} hideFrom=\"lg\" className=\"overflow-x-auto\">\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* >= lg: persistent vertical local nav (bounded width, does not shrink). */}\n <Flex as=\"nav\" aria-label=\"設定\" direction=\"col\" gap={1} hideBelow=\"lg\" width={224} shrink={false}>\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* Bounded content region (~42rem) — the routed section renders here. min-w-0 lets it shrink. */}\n <main className=\"min-w-0 max-w-2xl flex-1\"><Outlet /></main>\n </Flex>\n );\n}\n\n// A settings section. Horizontal label/control rows on desktop (collapse to stacked on mobile),\n// controls bounded to a semantic width. FormField OWNS the label↔control relationship — do NOT\n// hand-roll <Label> + <input>. Save feedback = Button loading prop + a toast in onSuccess.\nexport function GeneralSettingsSection({\n defaults, onSave, saving, error,\n}: { defaults: { name: string; email: string }; onSave: (v: FormData) => void; saving: boolean; error?: string }) {\n return (\n <form onSubmit={(e) => { e.preventDefault(); onSave(new FormData(e.currentTarget)); }}>\n <Flex direction=\"col\" gap=\"md\">\n <FormField id=\"ws-name\" label=\"ワークスペース名\" layout=\"horizontal\" controlWidth=\"24rem\"\n helper=\"請求書や共有リンクに表示されます。\" error={error}>\n <Input id=\"ws-name\" name=\"name\" defaultValue={defaults.name} />\n </FormField>\n <FormField id=\"ws-email\" label=\"連絡先メール\" layout=\"horizontal\" controlWidth=\"24rem\">\n <Input id=\"ws-email\" name=\"email\" type=\"email\" defaultValue={defaults.email} />\n </FormField>\n <Flex gap=\"sm\"><Button type=\"submit\" loading={saving}>変更を保存</Button></Flex>\n </Flex>\n </form>\n );\n}\n\n// Router wiring — each destination is addressable, so deep links + back/forward just work:\n// <Route path=\"settings\" element={<SettingsLayout/>}>\n// <Route index element={<Navigate to=\"general\" replace/>} />\n// <Route path=\"general\" element={<GeneralSettingsSection .../>} />\n// <Route path=\"security\" element={<SecuritySection/>} />\n// <Route path=\"notifications\" element={<NotificationsSection/>} />\n// </Route>\n\n// ── DO / DON'T ────────────────────────────────────────────────────────────────────────────\n// ✅ Tabs (navigation) are fine for a SMALL number (2–4) of PEER views inside ONE task/section\n// — e.g. \"プロフィール\" ⇄ \"環境設定\" within a single account page — where losing the URL is OK.\n// ⛔ Tabs as the PRIMARY IA for broad settings (Account / Security / Billing / Members / …):\n// use route-backed local navigation so every area is deep-linkable and back/forward works.\n// ⛔ Do NOT wrap the whole settings page in a Card, and do NOT make desktop a horizontal tab bar\n// — persistent local nav scales to many sections; a tab strip does not.\n// ⛔ Do NOT invent CSS class names for the shell/nav/content — compose Flex + semantic tokens.",
37
+ "code": "// Research basis: GitHub, Google Account, Microsoft Account, Atlassian Account settings +\n// the IBM Carbon form grid. Every destination is a URL, so deep links and browser back/forward\n// work for free and the active item is derived from the route — never from local tab state.\n//\n// Desktop (>= lg): persistent vertical local nav on the left + bounded content on the right.\n// Mobile (< lg): the SAME routes become a horizontal, scrollable tab strip above the content.\n// Styling uses ONLY real semantic token classes (bg-muted / text-foreground / text-muted-foreground)\n// and responsive/visibility utilities — the library ships NO bespoke settings shell/nav CSS classes.\nimport { NavLink, Outlet, Navigate } from \"react-router-dom\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { Form, FormField, Input } from \"@godxjp/ui/data-entry\";\nimport { Button } from \"@godxjp/ui/general\";\n\nconst SECTIONS = [\n { to: \"general\", label: \"基本情報\" },\n { to: \"security\", label: \"セキュリティ\" },\n { to: \"notifications\", label: \"通知\" },\n] as const;\n\n// One link renderer for BOTH breakpoints. NavLink gives us the active state (and sets\n// aria-current=\"page\") straight from the URL; semantic tokens only, no invented classes.\nconst link = ({ isActive }: { isActive: boolean }) =>\n [\n \"rounded-md px-3 py-2 text-sm no-underline transition-colors whitespace-nowrap\",\n isActive\n ? \"bg-muted text-foreground font-medium\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \");\n\nexport function SettingsLayout() {\n return (\n <Flex direction=\"col\" gap=\"lg\" className=\"lg:flex-row lg:items-start\">\n {/* < lg: horizontal, scrollable route tabs. Flex owns the row and the gap — hideFrom /\n hideBelow own the breakpoint, so neither nav needs a hand-rolled flex or gap utility. */}\n <Flex as=\"nav\" aria-label=\"設定\" gap={1} hideFrom=\"lg\" className=\"overflow-x-auto\">\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* >= lg: persistent vertical local nav (bounded width, does not shrink). */}\n <Flex as=\"nav\" aria-label=\"設定\" direction=\"col\" gap={1} hideBelow=\"lg\" width={224} shrink={false}>\n {SECTIONS.map((s) => (\n <NavLink key={s.to} to={s.to} className={link} end>{s.label}</NavLink>\n ))}\n </Flex>\n {/* Bounded content region (~42rem) — the routed section renders here. min-w-0 lets it shrink. */}\n <main className=\"min-w-0 max-w-2xl flex-1\"><Outlet /></main>\n </Flex>\n );\n}\n\n// A settings section. Horizontal label/control rows on desktop (collapse to stacked on mobile),\n// controls bounded to a semantic width — set ONCE on the <Form>, which every FormField reads\n// (gh#998). FormField OWNS the label↔control relationship — do NOT hand-roll <Label> + <input>. Save feedback = Button loading prop + a toast in onSuccess.\nexport function GeneralSettingsSection({\n defaults, onSave, saving, error,\n}: { defaults: { name: string; email: string }; onSave: (v: FormData) => void; saving: boolean; error?: string }) {\n return (\n <Form layout=\"horizontal\" controlWidth=\"24rem\"\n onSubmit={(e) => { e.preventDefault(); onSave(new FormData(e.currentTarget)); }}>\n <FormField id=\"ws-name\" label=\"ワークスペース名\"\n helper=\"請求書や共有リンクに表示されます。\" error={error}>\n <Input id=\"ws-name\" name=\"name\" defaultValue={defaults.name} />\n </FormField>\n <FormField id=\"ws-email\" label=\"連絡先メール\">\n <Input id=\"ws-email\" name=\"email\" type=\"email\" defaultValue={defaults.email} />\n </FormField>\n <Flex gap=\"sm\"><Button type=\"submit\" loading={saving}>変更を保存</Button></Flex>\n </Form>\n );\n}\n\n// Router wiring — each destination is addressable, so deep links + back/forward just work:\n// <Route path=\"settings\" element={<SettingsLayout/>}>\n// <Route index element={<Navigate to=\"general\" replace/>} />\n// <Route path=\"general\" element={<GeneralSettingsSection .../>} />\n// <Route path=\"security\" element={<SecuritySection/>} />\n// <Route path=\"notifications\" element={<NotificationsSection/>} />\n// </Route>\n\n// ── DO / DON'T ────────────────────────────────────────────────────────────────────────────\n// ✅ Tabs (navigation) are fine for a SMALL number (2–4) of PEER views inside ONE task/section\n// — e.g. \"プロフィール\" ⇄ \"環境設定\" within a single account page — where losing the URL is OK.\n// ⛔ Tabs as the PRIMARY IA for broad settings (Account / Security / Billing / Members / …):\n// use route-backed local navigation so every area is deep-linkable and back/forward works.\n// ⛔ Do NOT wrap the whole settings page in a Card, and do NOT make desktop a horizontal tab bar\n// — persistent local nav scales to many sections; a tab strip does not.\n// ⛔ Do NOT invent CSS class names for the shell/nav/content — compose Flex + semantic tokens.",
38
38
  "name": "settings-page-responsive",
39
39
  "tagline": "Route-backed settings: persistent desktop local navigation, compact mobile tab strip, deep links + browser history, and bounded form content — NO Card wrapper, NO undefined CSS.",
40
40
  "tags": [
@@ -6,7 +6,5 @@ export { ButtonRefetch } from "./query-refetch-button.js";
6
6
  export type { ButtonRefetchProp, ButtonRefetchProps } from "./query-refetch-button.js";
7
7
  export { InfiniteQueryState, flattenItemPages } from "./infinite-query-state.js";
8
8
  export type { InfiniteQueryStateProp, InfiniteQueryStateProps } from "./infinite-query-state.js";
9
- export { PrefetchLink } from "./prefetch-link.js";
10
- export type { PrefetchLinkProp, PrefetchLinkProps } from "./prefetch-link.js";
11
9
  export { classifyQueryError, isRetryableQueryError } from "../../lib/query-error.js";
12
10
  export type { QueryErrorCategory, QueryErrorInfo } from "../../lib/query-error.js";
@@ -2,14 +2,12 @@ import { DataState } from "./data-state.js";
2
2
  import { AlertMutationFeedback } from "./mutation-feedback.js";
3
3
  import { ButtonRefetch } from "./query-refetch-button.js";
4
4
  import { InfiniteQueryState, flattenItemPages } from "./infinite-query-state.js";
5
- import { PrefetchLink } from "./prefetch-link.js";
6
5
  import { classifyQueryError, isRetryableQueryError } from "../../lib/query-error.js";
7
6
  export {
8
7
  AlertMutationFeedback,
9
8
  ButtonRefetch,
10
9
  DataState,
11
10
  InfiniteQueryState,
12
- PrefetchLink,
13
11
  classifyQueryError,
14
12
  flattenItemPages,
15
13
  isRetryableQueryError
@@ -0,0 +1,2 @@
1
+ export { PrefetchLink } from "./prefetch-link.js";
2
+ export type { PrefetchLinkProp, PrefetchLinkProps } from "./prefetch-link.js";
@@ -0,0 +1,4 @@
1
+ import { PrefetchLink } from "./prefetch-link.js";
2
+ export {
3
+ PrefetchLink
4
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "30.9.0",
3
+ "version": "31.0.2",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -2,6 +2,8 @@
2
2
 
3
3
  @layer components {
4
4
  [data-slot="alert"] {
5
+
6
+ container: alert / inline-size;
5
7
  position: relative;
6
8
  display: flex;
7
9
  align-items: flex-start;
@@ -133,7 +135,7 @@
133
135
  gap: var(--alert-inner-space-gap);
134
136
  }
135
137
 
136
- @media (min-width: 640px) {
138
+ @container alert (min-width: 40rem) {
137
139
  [data-slot="alert-body"]:has(> [data-slot="alert-actions"]) {
138
140
  display: grid;
139
141
  grid-template-columns: minmax(0, 1fr) auto;
@@ -190,7 +192,7 @@
190
192
  width: 100%;
191
193
  }
192
194
 
193
- @media (min-width: 640px) {
195
+ @container alert (min-width: 40rem) {
194
196
  [data-slot="alert-actions"] {
195
197
  width: auto;
196
198
  justify-content: flex-end;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "30.9.0",
2
+ "version": "31.0.2",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -6,7 +6,7 @@ Read this once; the audit enforces it. Everything else in `docs/` is for contrib
6
6
  2. Every page is `<PageContainer title subtitle extra footer>`; its sections are spaced by the page. Group items inside a section with `<Flex direction="col" gap>` or `<ResponsiveGrid>`.
7
7
  3. No Tailwind layout on your own elements: no `flex`, `grid`, `gap-*`, `p-*`, `m-*`, `space-*`. Rows are `<Flex>` (default row), stacks are `<Flex direction="col">`, grids are `<ResponsiveGrid>`.
8
8
  4. No hand-rolled surfaces: no `rounded-* border bg-*` divs. A box is `Card`, a pill is `Badge`, a person is `Avatar`, a row is `ListRow`, a label/value pair is `Descriptions`, an empty area is `EmptyState`, a read-only sample of a colour the USER chose is `Swatch`. A LIST of those rows is `<Flex as="ul" marker="none" direction="col" gap="none">` with `<ListRow as="li">` children — `marker="none"` keeps the `<ul>`, the `<li>` semantics and the gap token while dropping the bullet and the indent. Never a raw `<ul>`/`<ol>` (it carries no gap token), never `<div role="list">` + `<div role="listitem">`, and never a wrapper around each row: the divider is `:not(:last-child)` among siblings, so a row alone in its own wrapper is always the last one and EVERY divider disappears silently. A bulleted prose list is the same `<Flex as="ul">` without `marker`.
9
- 5. Real controls only: `Button`, `Input`, `Select`, `Textarea`, `Checkbox`… never raw `<button>`/`<input>`; a labelled control lives in `<FormField label>`. A Select outside a form takes `width="auto"`. **A disabled control's reason is visible text, never a tooltip** — see below.
9
+ 5. Real controls only: `Button`, `Input`, `Select`, `Textarea`, `Checkbox`… never raw `<button>`/`<input>`; a labelled control lives in `<FormField label>`. A Select outside a form takes `width="auto"`. **A disabled control's reason is visible text, never a tooltip** — see below. **Fields live in a `<Form layout="horizontal" labelWidth controlWidth>`** — never a group of `FormField`s without one, never a hand-rolled `<Flex>` row of fields (a row that belongs together is `<SpaceCompact>` or `<Form columns>`); size each control for its content with `controlWidth` (GOV.UK text-input width: a port ~7rem, a short enum ~10rem); three or more fields is a page, not a Dialog. The audit rules are `formfield-needs-form`, `dialog-form-too-big` and `select-width-hint`. **A case the kit cannot express is an issue, not a hand-roll:** open it on godx-jp/godxjp-ui, use the nearest valid composition, and mark it `// TODO(godxjp-ui#<n>)` (gh#998).
10
10
  6. Text is `<Text>` / `<Heading>` with `tone`, `size`, `weight`, `truncate`, `mono` — not `className="text-muted-foreground font-semibold"`. A STANDALONE glyph is `<Icon as={Lock} size="sm" tone="muted" />` from `@godxjp/ui/general`: a bare lucide icon carries its own `width="24" height="24"` and only four places re-size it for you (`Button`, a `DropdownMenuItem`, a `TopbarItem`, a `ListRow` leading slot), so everywhere else — in a `Text`, a table cell, an `<a>` — it draws at 24px beside 14px type. `size-4` and `w-[16px]` are closed to you by rules 3 and 8, and `size={16}` hard-codes a number the theme owns; `Icon` puts the glyph on the `--icon-size-*` scale (`2xs…4xl`) instead, and is `aria-hidden` unless you give it a `label` (then it is `role="img"` with that name). The audit rule is `lucide-icon-needs-size`.
11
11
  7. Colours are semantic tokens (`tone="destructive"`, `bg-primary`), never palette names, hex, or `bg-black` / `text-white`.
12
12
  8. Sizes come from props (`size`, `width`, `columns`), never `w-[240px]` / `max-h-[420px]`.
@@ -17,7 +17,7 @@ import {
17
17
  CardTitle,
18
18
  PermissionMatrix,
19
19
  } from "@godxjp/ui/data-display";
20
- import { FormField, Label, Select, Switch } from "@godxjp/ui/data-entry";
20
+ import { Form, FormField, Label, Select, Switch } from "@godxjp/ui/data-entry";
21
21
  import { Flex, PageContainer } from "@godxjp/ui/layout";
22
22
  import { grantKey } from "@godxjp/ui/lib/permission-grid";
23
23
 
@@ -114,35 +114,37 @@ export default function Demo() {
114
114
  </CardHeader>
115
115
  <CardContent>
116
116
  <Flex direction="col" gap="md">
117
- <Flex direction="row" wrap align="end" gap="md">
118
- {/* Bare Select has NO accessible name (axe button-name) — the compare
117
+ <Form>
118
+ <Flex direction="row" wrap align="end" gap="md">
119
+ {/* Bare Select has NO accessible name (axe button-name) — the compare
119
120
  pickers are labelled through FormField, exactly as the showcase does. */}
120
- <FormField label="比較 A">
121
- <Select
122
- value={compareA}
123
- onValueChange={setCompareA}
124
- options={roleOptions}
125
- className="w-40"
126
- />
127
- </FormField>
128
- <FormField label="比較 B">
129
- <Select
130
- value={compareB}
131
- onValueChange={setCompareB}
132
- options={roleOptions}
133
- className="w-40"
134
- />
135
- </FormField>
136
- <Flex direction="row" align="center" gap="sm">
137
- <Switch
138
- id="pm-doc-diff-only"
139
- size="sm"
140
- checked={diffOnly}
141
- onCheckedChange={setDiffOnly}
142
- />
143
- <Label htmlFor="pm-doc-diff-only">差分のみ</Label>
121
+ <FormField label="比較 A">
122
+ <Select
123
+ value={compareA}
124
+ onValueChange={setCompareA}
125
+ options={roleOptions}
126
+ className="w-40"
127
+ />
128
+ </FormField>
129
+ <FormField label="比較 B">
130
+ <Select
131
+ value={compareB}
132
+ onValueChange={setCompareB}
133
+ options={roleOptions}
134
+ className="w-40"
135
+ />
136
+ </FormField>
137
+ <Flex direction="row" align="center" gap="sm">
138
+ <Switch
139
+ id="pm-doc-diff-only"
140
+ size="sm"
141
+ checked={diffOnly}
142
+ onCheckedChange={setDiffOnly}
143
+ />
144
+ <Label htmlFor="pm-doc-diff-only">差分のみ</Label>
145
+ </Flex>
144
146
  </Flex>
145
- </Flex>
147
+ </Form>
146
148
  <Card variant="outline" className="overflow-hidden">
147
149
  <CardContent flush>
148
150
  <div>
@@ -1,7 +1,7 @@
1
1
  import { useState } from "react";
2
2
 
3
3
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
4
- import { Cascader, FormField } from "@godxjp/ui/data-entry";
4
+ import { Form, Cascader, FormField } from "@godxjp/ui/data-entry";
5
5
  import { Flex, PageContainer } from "@godxjp/ui/layout";
6
6
  import type { TreeOptionProp } from "@godxjp/ui/props";
7
7
 
@@ -351,25 +351,27 @@ export default function Demo() {
351
351
  </CardDescription>
352
352
  </CardHeader>
353
353
  <CardContent>
354
- <Flex direction="col" gap="md">
355
- <FormField id="disabled-empty" label="無効(未選択)">
356
- <Cascader
357
- id="disabled-empty"
358
- options={EXPENSE_CATEGORIES}
359
- disabled
360
- placeholder="カテゴリを選択..."
361
- />
362
- </FormField>
363
- <FormField id="disabled-selected" label="無効(選択済み)">
364
- <Cascader
365
- id="disabled-selected"
366
- options={EXPENSE_CATEGORIES}
367
- disabled
368
- defaultValue={["operating", "admin", "salary"]}
369
- placeholder="カテゴリを選択..."
370
- />
371
- </FormField>
372
- </Flex>
354
+ <Form>
355
+ <Flex direction="col" gap="md">
356
+ <FormField id="disabled-empty" label="無効(未選択)">
357
+ <Cascader
358
+ id="disabled-empty"
359
+ options={EXPENSE_CATEGORIES}
360
+ disabled
361
+ placeholder="カテゴリを選択..."
362
+ />
363
+ </FormField>
364
+ <FormField id="disabled-selected" label="無効(選択済み)">
365
+ <Cascader
366
+ id="disabled-selected"
367
+ options={EXPENSE_CATEGORIES}
368
+ disabled
369
+ defaultValue={["operating", "admin", "salary"]}
370
+ placeholder="カテゴリを選択..."
371
+ />
372
+ </FormField>
373
+ </Flex>
374
+ </Form>
373
375
  </CardContent>
374
376
  </Card>
375
377
  </Flex>
@@ -2,7 +2,7 @@ import { useState } from "react";
2
2
  import { ja } from "date-fns/locale";
3
3
 
4
4
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
5
- import { DatePicker, FormField } from "@godxjp/ui/data-entry";
5
+ import { Form, DatePicker, FormField } from "@godxjp/ui/data-entry";
6
6
  import { Flex, PageContainer } from "@godxjp/ui/layout";
7
7
  import type { DateRange } from "react-day-picker";
8
8
 
@@ -65,23 +65,25 @@ export default function Demo() {
65
65
  </CardDescription>
66
66
  </CardHeader>
67
67
  <CardContent>
68
- <Flex gap="lg" wrap>
69
- <FormField id="ruled-date" label="罫線あり (既定)">
70
- <DatePicker
71
- id="ruled-date"
72
- name="ruled_date"
73
- defaultValue={new Date(2026, 0, 15)}
74
- />
75
- </FormField>
76
- <FormField id="unruled-date" label="罫線なし (bordered={false})">
77
- <DatePicker
78
- id="unruled-date"
79
- name="unruled_date"
80
- defaultValue={new Date(2026, 0, 15)}
81
- bordered={false}
82
- />
83
- </FormField>
84
- </Flex>
68
+ <Form>
69
+ <Flex gap="lg" wrap>
70
+ <FormField id="ruled-date" label="罫線あり (既定)">
71
+ <DatePicker
72
+ id="ruled-date"
73
+ name="ruled_date"
74
+ defaultValue={new Date(2026, 0, 15)}
75
+ />
76
+ </FormField>
77
+ <FormField id="unruled-date" label="罫線なし (bordered={false})">
78
+ <DatePicker
79
+ id="unruled-date"
80
+ name="unruled_date"
81
+ defaultValue={new Date(2026, 0, 15)}
82
+ bordered={false}
83
+ />
84
+ </FormField>
85
+ </Flex>
86
+ </Form>
85
87
  </CardContent>
86
88
  </Card>
87
89
 
@@ -231,31 +233,33 @@ export default function Demo() {
231
233
  <CardTitle level={2}>表示形式と元号</CardTitle>
232
234
  </CardHeader>
233
235
  <CardContent>
234
- <Flex direction="col" gap="md">
235
- <FormField id="formatted-date" label="契約日">
236
- <DatePicker
237
- id="formatted-date"
238
- name="contract"
239
- defaultValue={new Date(2026, 8, 9)}
240
- format="yyyy年MM月dd日"
241
- />
242
- </FormField>
243
- <FormField id="era-date" label="和暦">
244
- <DatePicker
245
- id="era-date"
246
- name="era"
247
- defaultValue={new Date(2026, 8, 9)}
248
- format={{
249
- calendar: "japanese",
250
- era: "long",
251
- year: "numeric",
252
- month: "long",
253
- day: "numeric",
254
- }}
255
- inputReadOnly
256
- />
257
- </FormField>
258
- </Flex>
236
+ <Form>
237
+ <Flex direction="col" gap="md">
238
+ <FormField id="formatted-date" label="契約日">
239
+ <DatePicker
240
+ id="formatted-date"
241
+ name="contract"
242
+ defaultValue={new Date(2026, 8, 9)}
243
+ format="yyyy年MM月dd日"
244
+ />
245
+ </FormField>
246
+ <FormField id="era-date" label="和暦">
247
+ <DatePicker
248
+ id="era-date"
249
+ name="era"
250
+ defaultValue={new Date(2026, 8, 9)}
251
+ format={{
252
+ calendar: "japanese",
253
+ era: "long",
254
+ year: "numeric",
255
+ month: "long",
256
+ day: "numeric",
257
+ }}
258
+ inputReadOnly
259
+ />
260
+ </FormField>
261
+ </Flex>
262
+ </Form>
259
263
  </CardContent>
260
264
  </Card>
261
265
  <Card>