@godxjp/ui-mcp 0.9.0 → 0.10.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.
package/dist/index.js CHANGED
@@ -191,6 +191,75 @@ import { Button } from "@godxjp/ui/general";
191
191
  storyPath: "layout/Inline.stories.tsx",
192
192
  rules: [2]
193
193
  },
194
+ {
195
+ name: "Flex",
196
+ group: "layout",
197
+ tagline: "Token-spaced flex primitive with explicit direction, alignment, justification, and wrapping controls.",
198
+ props: [
199
+ {
200
+ name: "direction",
201
+ type: '"row" | "col"',
202
+ defaultValue: '"col"',
203
+ description: "Main axis direction. Use row for horizontal runs, col for vertical stacks."
204
+ },
205
+ {
206
+ name: "gap",
207
+ type: '"xs" | "sm" | "md" | "lg" | "xl"',
208
+ defaultValue: '"md"',
209
+ description: "Token gap between children, shared with Stack and Inline."
210
+ },
211
+ {
212
+ name: "align",
213
+ type: '"start" | "center" | "end" | "stretch" | "baseline"',
214
+ description: "Cross-axis alignment, emitted as a data attribute for the layout CSS."
215
+ },
216
+ {
217
+ name: "justify",
218
+ type: '"start" | "center" | "end" | "between" | "around" | "evenly"',
219
+ description: "Main-axis distribution, emitted as a data attribute for the layout CSS."
220
+ },
221
+ {
222
+ name: "wrap",
223
+ type: "boolean",
224
+ defaultValue: "false",
225
+ description: "Allows children to wrap onto additional flex lines."
226
+ }
227
+ ],
228
+ usage: [
229
+ 'DO import from `@godxjp/ui/layout` and reach for Flex when the axis, alignment, justification, or wrap behavior is part of the component contract: `import { Flex } from "@godxjp/ui/layout"`.',
230
+ "DO keep spacing on the `gap` prop instead of raw `gap-*`, `space-*`, or padding utilities. Flex uses the same token scale as Stack and Inline, so spacing remains tied to the design system.",
231
+ 'DO use `direction="row"` with `wrap` for responsive control rows, chip clusters, and action groups that need more control than Inline exposes.',
232
+ 'DO use `direction="col"` for vertical groupings that need explicit `align` or `justify` behavior. If all you need is a vertical gap, Stack is the thinner alias.',
233
+ "DON'T override the axis with `className` after choosing a direction prop. Keep the layout intent in props so catalog guidance and data attributes stay accurate.",
234
+ "Flex is a plain div with React.HTMLAttributes<HTMLDivElement>; pass `id`, `role`, `aria-*`, `data-*`, and structural className values as needed, but do not use it as a semantic form or button wrapper."
235
+ ],
236
+ useCases: [
237
+ "Toolbar internals where controls should sit in a row, wrap on narrow widths, and stay vertically centered.",
238
+ "Card headers that need title content on the left and actions on the right via `justify='between'` without hand-rolling flex utility classes.",
239
+ "Empty-state or loading blocks that center content on both axes using `align='center'` and `justify='center'`.",
240
+ "Form sub-sections where a vertical group needs stretched children or centered helper content beyond what Stack exposes.",
241
+ "Badge, chip, or tag clusters where wrapping is required but the caller also needs a larger token gap than Inline's default.",
242
+ "Low-level layout composition inside custom components where Stack and Inline are too opinionated but raw flex classes would duplicate the primitive."
243
+ ],
244
+ related: [
245
+ "Stack \u2014 thin `direction='col'` alias of Flex. Use Stack for ordinary vertical block spacing; use Flex when you need align, justify, or wrap control.",
246
+ "Inline \u2014 thin `direction='row'` alias of Flex with wrapping and centered alignment. Use Inline for simple horizontal groups; use Flex for explicit axis/distribution control.",
247
+ "ResponsiveGrid \u2014 use for equal-width, multi-column tile layouts. Flex arranges children on one flex axis and does not provide column-count behavior.",
248
+ "PageContainer \u2014 page scaffold and padding context. Flex is an inner layout primitive used inside page sections, cards, dialogs, and toolbars."
249
+ ],
250
+ example: `import { Flex } from "@godxjp/ui/layout";
251
+ import { Button } from "@godxjp/ui/general";
252
+
253
+ <Flex direction="row" gap="sm" align="center" justify="between" wrap>
254
+ <SearchSummary />
255
+ <Flex direction="row" gap="xs" align="center" wrap>
256
+ <Button variant="outline">\u30EA\u30BB\u30C3\u30C8</Button>
257
+ <Button>\u9069\u7528</Button>
258
+ </Flex>
259
+ </Flex>`,
260
+ storyPath: "layout/Flex.stories.tsx",
261
+ rules: [2, 40]
262
+ },
194
263
  {
195
264
  name: "ResponsiveGrid",
196
265
  group: "layout",
@@ -811,7 +880,7 @@ function MyShell({ children }: { content: React.ReactNode }) {
811
880
  ],
812
881
  useCases: [
813
882
  'Primary form submission in a Dialog or Sheet (e.g. `<Button type="submit" disabled={form.processing}>\u4FDD\u5B58</Button>`) \u2014 the `disabled` prop greys it out and blocks pointer events, preventing double-submit during async operations.',
814
- 'Destructive confirmation inside a Dialog \u2014 pair `variant="destructive"` Button as the confirm action and `variant="outline"` as Cancel; never use `variant="default"` for a delete action.',
883
+ 'Destructive confirmation inside a Dialog \u2014 pair `tone="destructive"` Button as the confirm action and `variant="outline"` as Cancel; never use `variant="default"` for a delete action.',
815
884
  'Icon-only toolbar actions in a DataTable column (edit, delete, copy) using `size="icon-sm"` + `variant="ghost"` + a Lucide icon child \u2014 gives equal-width square targets that don\'t distort the row.',
816
885
  "Navigation links styled as buttons (e.g. 'New Invoice', 'Back to list') using `asChild` + Inertia `<Link>` \u2014 preserves SPA navigation while using the button's visual treatment.",
817
886
  "Async mutation trigger in an accounting workflow (e.g. 'Sync from MF', 'Export CSV') \u2014 disable on processing state; pair with `MutationFeedback` for error/retry UI rather than inline `try/catch` alerts.",
@@ -1224,7 +1293,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1224
1293
  ],
1225
1294
  useCases: [
1226
1295
  'Category or tier labels on table rows \u2014 e.g. plan tier (`<Badge variant="secondary">Pro</Badge>`), document type (`<Badge variant="outline">Invoice</Badge>`), or locale tag (`<Badge variant="secondary">EN</Badge>`).',
1227
- 'Approval or review state in an accounting list where the value is not a lifecycle key in Badge\'s STATUS_MAP \u2014 e.g. a custom approval tier like `<Badge variant="success">\u627F\u8A8D\u6E08</Badge>` or `<Badge variant="warning">\u8981\u78BA\u8A8D</Badge>`.',
1296
+ 'Approval or review state in an accounting list where the value is not a lifecycle key in Badge\'s STATUS_MAP \u2014 e.g. a custom approval tier like `<Badge tone="success">\u627F\u8A8D\u6E08</Badge>` or `<Badge tone="warning">\u8981\u78BA\u8A8D</Badge>`.',
1228
1297
  "Inline count or highlight next to a heading or nav item \u2014 e.g. `<Badge variant=\"destructive\">3</Badge>` beside 'Overdue invoices' to draw attention to a non-zero count.",
1229
1298
  'Feature flags or experiment variant labels on admin records \u2014 e.g. `<Badge variant="outline">A/B</Badge>` alongside a campaign row to indicate it is in a split test.',
1230
1299
  "Read-only metadata chips inside a Descriptions.Item or Card header where a lifecycle icon would be visually heavy \u2014 e.g. currency code, payment method, or region tag."
@@ -1236,7 +1305,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1236
1305
 
1237
1306
  <Badge variant="secondary">A/B</Badge>
1238
1307
  <Badge status="active">\u516C\u958B\u4E2D</Badge>
1239
- <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" variant="success" icon={null}>\u30D7\u30EC\u30DF\u30A2\u30E0</Badge>`,
1308
+ <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null}>\u30D7\u30EC\u30DF\u30A2\u30E0</Badge>`,
1240
1309
  storyPath: "data-display/Badge.stories.tsx",
1241
1310
  rules: [35]
1242
1311
  },
@@ -1356,7 +1425,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1356
1425
  "DON'T use Progress for editable numeric input or range selection \u2014 it has no callbacks, no interactivity, and no form `name` prop. Use Slider (bounded range input) or Input (free-form number) for data-entry scenarios."
1357
1426
  ],
1358
1427
  useCases: [
1359
- 'Budget utilisation in an accounting dashboard \u2014 show how much of a monthly budget has been consumed, switching to `variant="warning"` when the figure crosses 80%.',
1428
+ 'Budget utilisation in an accounting dashboard \u2014 show how much of a monthly budget has been consumed, switching to `tone="warning"` when the figure crosses 80%.',
1360
1429
  'Invoice payment progress \u2014 display the proportion of an invoice total that has been settled (e.g. partial payments), with a label like `"\xA545,000 / \xA560,000 \u652F\u6255\u6E08"` computed before passing `value`.',
1361
1430
  "Storage or quota indicator in an admin panel \u2014 visualise disk usage, API quota, or seat licence consumption against a fixed limit.",
1362
1431
  "Sync / import job completion feedback \u2014 surface the completion percentage of a long-running background job (polling the server) without giving the user an interactive control.",
@@ -1675,7 +1744,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1675
1744
  example: `import { FormField, Input } from "@godxjp/ui/data-entry";
1676
1745
 
1677
1746
  <FormField id="coupon-name" label="\u30AF\u30FC\u30DD\u30F3\u540D" required error={errors.name} helper="\u6700\u592750\u6587\u5B57">
1678
- <Input id="coupon-name" placeholder="\u6625\u306E\u82B1\u7C89\u75C7\u5BFE\u7B5615%OFF" value={name} onChange={(e) => setName(e.target.value)} />
1747
+ <Input id="coupon-name" placeholder="\u6625\u306E\u82B1\u7C89\u75C7\u5BFE\u7B5615%OFF" value={name} onValueChange={(e) => setName(e.target.value)} />
1679
1748
  </FormField>`,
1680
1749
  storyPath: "data-entry/FormField.stories.tsx",
1681
1750
  rules: [23]
@@ -1718,7 +1787,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1718
1787
  ],
1719
1788
  example: `import { Input } from "@godxjp/ui/data-entry";
1720
1789
 
1721
- <Input id="qty" type="number" placeholder="\u4F8B: 500" value={value} onChange={(e) => setValue(e.target.value)} />`,
1790
+ <Input id="qty" type="number" placeholder="\u4F8B: 500" value={value} onValueChange={(e) => setValue(e.target.value)} />`,
1722
1791
  storyPath: "data-entry/Input.stories.tsx",
1723
1792
  rules: []
1724
1793
  },
@@ -1916,7 +1985,7 @@ export function StatusSelect({ value, onChange }) {
1916
1985
  return (
1917
1986
  <Select
1918
1987
  value={value}
1919
- onChange={onChange}
1988
+ onValueChange={onChange}
1920
1989
  options={[
1921
1990
  { value: "draft", label: "Draft" },
1922
1991
  { value: "sent", label: "Sent" },
@@ -1935,7 +2004,7 @@ export function CurrencySelect({ value, onChange }) {
1935
2004
  return (
1936
2005
  <Select
1937
2006
  value={value}
1938
- onChange={onChange}
2007
+ onValueChange={onChange}
1939
2008
  showSearch
1940
2009
  options={[
1941
2010
  { value: "JPY", label: "Japanese Yen", group: "Asia" },
@@ -1961,7 +2030,7 @@ export function AccountSelect({ value, onChange, selectedLabel }) {
1961
2030
  return (
1962
2031
  <Select
1963
2032
  value={value}
1964
- onChange={onChange}
2033
+ onValueChange={onChange}
1965
2034
  loadOptions={loadOptions}
1966
2035
  selectedLabel={selectedLabel}
1967
2036
  placeholder="Search accounts\u2026"
@@ -2084,7 +2153,7 @@ export function PrioritySelect({ value, onValueChange }) {
2084
2153
  ],
2085
2154
  example: `import { Textarea } from "@godxjp/ui/data-entry";
2086
2155
 
2087
- <Textarea id="notes" rows={4} placeholder="\u81EA\u7531\u8A18\u8FF0" value={notes} onChange={(e) => setNotes(e.target.value)} />`,
2156
+ <Textarea id="notes" rows={4} placeholder="\u81EA\u7531\u8A18\u8FF0" value={notes} onValueChange={(e) => setNotes(e.target.value)} />`,
2088
2157
  storyPath: "data-entry/Textarea.stories.tsx",
2089
2158
  rules: []
2090
2159
  },
@@ -2320,7 +2389,7 @@ export function InvoiceDueDateField() {
2320
2389
  id="due-date"
2321
2390
  name="due_date"
2322
2391
  value={dueDate}
2323
- onChange={setDueDate}
2392
+ onValueChange={setDueDate}
2324
2393
  fromDate={new Date()}
2325
2394
  placeholder="yyyy-mm-dd"
2326
2395
  />
@@ -2465,7 +2534,7 @@ import { Button } from "@godxjp/ui/general";
2465
2534
  }
2466
2535
  ],
2467
2536
  usage: [
2468
- 'DO compose with sub-parts in order: wrap text content in `<Alert.Content>` (or bare `<AlertContent>`), then `<Alert.Title>` + `<Alert.Description>` inside it, then `<Alert.Actions>` for any retry/CTA buttons. Example: `<Alert variant="destructive"><Alert.Content><Alert.Title>Error</Alert.Title><Alert.Description>{msg}</Alert.Description></Alert.Content><Alert.Actions><Button \u2026/></Alert.Actions></Alert>`.',
2537
+ 'DO compose with sub-parts in order: wrap text content in `<Alert.Content>` (or bare `<AlertContent>`), then `<Alert.Title>` + `<Alert.Description>` inside it, then `<Alert.Actions>` for any retry/CTA buttons. Example: `<Alert tone="destructive"><Alert.Content><Alert.Title>Error</Alert.Title><Alert.Description>{msg}</Alert.Description></Alert.Content><Alert.Actions><Button \u2026/></Alert.Actions></Alert>`.',
2469
2538
  "DO use `Alert.QueryError` (alias `AlertQueryError`) for TanStack Query / API failure surfaces \u2014 it already renders humanError(error), an i18n title, and an optional Retry button. Never hand-roll that pattern.",
2470
2539
  'DON\'T pass raw action elements directly as top-level children of `<Alert>` without wrapping them in `<Alert.Actions>` \u2014 the layout slot only activates correctly via the `data-slot="alert-actions"` wrapper.',
2471
2540
  'DON\'T hand-roll a dismiss \u2715 button \u2014 pass `onDismiss` to `<Alert>` and the component renders its own accessible dismiss button with `aria-label="Dismiss"`. The `onDismiss` handler may return a Promise.',
@@ -2473,12 +2542,12 @@ import { Button } from "@godxjp/ui/general";
2473
2542
  "DO NOT use `Alert` for transient ephemeral feedback (e.g. 'saved successfully'). Use `toast()` from sonner + `<Toaster>` for that. `Alert` is for persistent, page-scoped banners that stay visible until the user acts or dismisses."
2474
2543
  ],
2475
2544
  useCases: [
2476
- 'Page-level error banner after a form submission fails server-side validation \u2014 `variant="destructive"` with `Alert.Title` summarising the error and `Alert.Description` listing field issues, paired with `onDismiss` so the user can clear it.',
2545
+ 'Page-level error banner after a form submission fails server-side validation \u2014 `tone="destructive"` with `Alert.Title` summarising the error and `Alert.Description` listing field issues, paired with `onDismiss` so the user can clear it.',
2477
2546
  "Inline warning at the top of an accounting invoice list when the OAuth token for the MF sync is about to expire \u2014 `variant=\"warning\"` with an `Alert.Actions` containing a 'Reconnect' Button.",
2478
- 'Success confirmation banner rendered after a bulk-import job completes and the user returns to the list page \u2014 `variant="success"` with `Alert.Description` showing the record count imported.',
2547
+ 'Success confirmation banner rendered after a bulk-import job completes and the user returns to the list page \u2014 `tone="success"` with `Alert.Description` showing the record count imported.',
2479
2548
  "TanStack Query data-fetch failure inside a Card body \u2014 use `<Alert.QueryError error={error} onRetry={refetch} />` instead of writing a custom error state.",
2480
2549
  "Informational notice at the top of a settings page when a feature is in beta or requires a plan upgrade \u2014 `variant=\"default\"` (Info icon) with a short description and an `Alert.Actions` 'Learn more' link.",
2481
- 'Dismissible billing-overdue notice at the top of the dashboard \u2014 `variant="destructive"` with `onDismiss` that sets a session flag so it does not reappear until the next login.'
2550
+ 'Dismissible billing-overdue notice at the top of the dashboard \u2014 `tone="destructive"` with `onDismiss` that sets a session flag so it does not reappear until the next login.'
2482
2551
  ],
2483
2552
  related: [
2484
2553
  "Toaster \u2014 use for transient, auto-dismissing feedback ('Record saved', 'Deleted'). Alert is for persistent page-scoped banners; Toaster is for fire-and-forget notifications triggered by toast() from sonner.",
@@ -2488,7 +2557,7 @@ import { Button } from "@godxjp/ui/general";
2488
2557
  ],
2489
2558
  example: `import { Alert, AlertTitle, AlertDescription } from "@godxjp/ui/feedback";
2490
2559
 
2491
- <Alert variant="warning">
2560
+ <Alert tone="warning">
2492
2561
  <AlertTitle>3 \u4EF6\u306E\u6253\u523B\u6F0F\u308C\u304C\u3042\u308A\u307E\u3059</AlertTitle>
2493
2562
  <AlertDescription>\u672C\u65E5\u4E2D\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002</AlertDescription>
2494
2563
  </Alert>`,
@@ -2809,7 +2878,7 @@ import { SearchInput, Select, SelectTrigger, SelectValue, SelectContent, SelectI
2809
2878
  ],
2810
2879
  example: `import { Pagination } from "@godxjp/ui/navigation";
2811
2880
 
2812
- <Pagination current={page} total={filtered.length} pageSize={10} showTotal onChange={(p) => setPage(p)} />`,
2881
+ <Pagination current={page} total={filtered.length} pageSize={10} showTotal onValueChange={(p) => setPage(p)} />`,
2813
2882
  storyPath: "navigation/Pagination.stories.tsx",
2814
2883
  rules: [40]
2815
2884
  },
@@ -2855,7 +2924,7 @@ import { Button } from "@godxjp/ui/general";
2855
2924
  <DropdownMenuContent>
2856
2925
  <DropdownMenuItem>\u7DE8\u96C6</DropdownMenuItem>
2857
2926
  <DropdownMenuSeparator />
2858
- <DropdownMenuItem variant="destructive">\u524A\u9664</DropdownMenuItem>
2927
+ <DropdownMenuItem tone="destructive">\u524A\u9664</DropdownMenuItem>
2859
2928
  </DropdownMenuContent>
2860
2929
  </DropdownMenu>`,
2861
2930
  storyPath: "navigation/DropdownMenu.stories.tsx",
@@ -3110,7 +3179,7 @@ export function ShiftStartField() {
3110
3179
  id="shift-start"
3111
3180
  name="shift_start"
3112
3181
  value={startTime}
3113
- onChange={setStartTime}
3182
+ onValueChange={setStartTime}
3114
3183
  minuteStep={15}
3115
3184
  className="w-36"
3116
3185
  />
@@ -3230,7 +3299,7 @@ export function InvoicePeriodFilter() {
3230
3299
  id="invoice-period"
3231
3300
  name="period"
3232
3301
  value={range}
3233
- onChange={setRange}
3302
+ onValueChange={setRange}
3234
3303
  fromDate={new Date(2020, 0, 1)}
3235
3304
  toDate={new Date(2030, 11, 31)}
3236
3305
  />
@@ -3386,7 +3455,7 @@ function RegionPicker() {
3386
3455
  <Cascader
3387
3456
  options={REGIONS}
3388
3457
  value={path}
3389
- onChange={(v) => setPath(v as string[])}
3458
+ onValueChange={(v) => setPath(v as string[])}
3390
3459
  showSearch
3391
3460
  placeholder="Select region\u2026"
3392
3461
  />
@@ -3402,7 +3471,7 @@ function MultiRegionPicker() {
3402
3471
  options={REGIONS}
3403
3472
  multiple
3404
3473
  value={paths}
3405
- onChange={(v) => setPaths(v as string[][])}
3474
+ onValueChange={(v) => setPaths(v as string[][])}
3406
3475
  showSearch
3407
3476
  />
3408
3477
  );
@@ -3419,7 +3488,7 @@ function MultiRegionPicker() {
3419
3488
  <Cascader
3420
3489
  options={REGIONS}
3421
3490
  changeOnSelect
3422
- onChange={(v) => console.log("path", v)}
3491
+ onValueChange={(v) => console.log("path", v)}
3423
3492
  />
3424
3493
  \`}`,
3425
3494
  storyPath: "data-entry/Cascader.stories.tsx",
@@ -3582,7 +3651,7 @@ export function AccountPicker() {
3582
3651
  id="account-picker"
3583
3652
  treeData={accountTree}
3584
3653
  value={account}
3585
- onChange={(v) => setAccount(v as string | undefined)}
3654
+ onValueChange={(v) => setAccount(v as string | undefined)}
3586
3655
  showSearch
3587
3656
  treeDefaultExpandAll
3588
3657
  placeholder="Select account\u2026"
@@ -3600,7 +3669,7 @@ export function DepartmentFilter() {
3600
3669
  id="dept-filter"
3601
3670
  treeData={accountTree}
3602
3671
  value={selected}
3603
- onChange={(v) => setSelected(v as string[])}
3672
+ onValueChange={(v) => setSelected(v as string[])}
3604
3673
  treeCheckable
3605
3674
  showCheckedStrategy={TreeSelect.SHOW_PARENT}
3606
3675
  showSearch
@@ -3673,7 +3742,7 @@ export function DepartmentFilter() {
3673
3742
  }
3674
3743
  ],
3675
3744
  usage: [
3676
- "DO own `targetKeys` in state and update it inside `onChange`: `const [targetKeys, setTargetKeys] = useState<string[]>([]); onChange={(next) => setTargetKeys(next)}`.",
3745
+ "DO own `targetKeys` in state and update it inside `onChange`: `const [targetKeys, setTargetKeys] = useState<string[]>([]); onValueChange={(next) => setTargetKeys(next)}`.",
3677
3746
  "DO NOT hand-roll a two-panel checkbox picker \u2014 Transfer ships the full shuttle UX (select-all header, indeterminate state, search, move buttons, empty state) out of the box.",
3678
3747
  "DO enable `showSearch` for lists longer than ~10 items; the built-in SearchInput filters by both `title` and `description` text content, including ReactNode content via `reactNodeText`.",
3679
3748
  "DO use `oneWay={true}` for append-only flows (e.g. adding permissions to a role) where items must never be moved back.",
@@ -3712,7 +3781,7 @@ export function AccountMapping() {
3712
3781
  <Transfer
3713
3782
  dataSource={ALL_ACCOUNTS}
3714
3783
  targetKeys={targetKeys}
3715
- onChange={(nextKeys) => setTargetKeys(nextKeys)}
3784
+ onValueChange={(nextKeys) => setTargetKeys(nextKeys)}
3716
3785
  titles={["Available Accounts", "Mapped Accounts"]}
3717
3786
  showSearch
3718
3787
  />
@@ -3842,7 +3911,7 @@ export function AvatarUploadForm() {
3842
3911
  <Upload
3843
3912
  variant="avatar-crop"
3844
3913
  value={items}
3845
- onChange={setItems}
3914
+ onValueChange={setItems}
3846
3915
  onUpload={handleUpload}
3847
3916
  maxSizeBytes={5 * 1024 * 1024}
3848
3917
  />
@@ -3859,7 +3928,7 @@ export function DocumentUploadDropzone() {
3859
3928
  <Upload
3860
3929
  variant="dropzone"
3861
3930
  value={items}
3862
- onChange={setItems}
3931
+ onValueChange={setItems}
3863
3932
  accept=".pdf,.xlsx"
3864
3933
  maxCount={10}
3865
3934
  maxSizeBytes={20 * 1024 * 1024}
@@ -3948,7 +4017,7 @@ export function AvatarField() {
3948
4017
 
3949
4018
  return (
3950
4019
  <>
3951
- <input type="file" accept="image/*" onChange={handleFileChange} />
4020
+ <input type="file" accept="image/*" onValueChange={handleFileChange} />
3952
4021
  <UploadCropDialog
3953
4022
  open={cropFile !== null}
3954
4023
  onOpenChange={(open) => { if (!open) setCropFile(null); }}
@@ -4004,7 +4073,7 @@ export function AvatarField() {
4004
4073
  }
4005
4074
  ],
4006
4075
  usage: [
4007
- "DO wrap in FormField when a label or validation message is needed \u2014 pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v} onChange={setV} /></FormField>`.",
4076
+ "DO wrap in FormField when a label or validation message is needed \u2014 pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v} onValueChange={setV} /></FormField>`.",
4008
4077
  "DO use controlled mode (value + onChange) \u2014 there is no defaultValue/uncontrolled path; always supply value.",
4009
4078
  "DON'T pass an invalid or empty string to value \u2014 the component will flash the invalid color on the preview swatch. Always initialize state to a valid 3- or 6-digit hex (e.g. '#2563eb').",
4010
4079
  "The hex Input is a live draft field \u2014 onChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onChange firing on every keystroke.",
@@ -4034,7 +4103,7 @@ export function BrandColorField() {
4034
4103
  <ColorPicker
4035
4104
  id="brand-color"
4036
4105
  value={color}
4037
- onChange={setColor}
4106
+ onValueChange={setColor}
4038
4107
  />
4039
4108
  </FormField>
4040
4109
  );
@@ -4043,7 +4112,7 @@ export function BrandColorField() {
4043
4112
  // Compact swatch-only variant (no hex input)
4044
4113
  export function SwatchOnly() {
4045
4114
  const [color, setColor] = useState("#16a34a");
4046
- return <ColorPicker value={color} onChange={setColor} showHexInput={false} />;
4115
+ return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;
4047
4116
  }
4048
4117
 
4049
4118
  // Disabled state
@@ -4976,7 +5045,7 @@ export function ControlledExample() {
4976
5045
  name="permissions"
4977
5046
  options={PERMISSIONS}
4978
5047
  value={selected}
4979
- onChange={setSelected}
5048
+ onValueChange={setSelected}
4980
5049
  />
4981
5050
  );
4982
5051
  }`,
@@ -5213,7 +5282,7 @@ function LegacyAccountPicker({ value, onChange }) {
5213
5282
  return (
5214
5283
  <SearchSelect
5215
5284
  value={value}
5216
- onChange={onChange}
5285
+ onValueChange={onChange}
5217
5286
  options={[
5218
5287
  { value: "acc-001", label: "Cash", sublabel: "Current assets", group: "Assets" },
5219
5288
  { value: "acc-002", label: "Accounts Receivable", group: "Assets" },
@@ -5237,7 +5306,7 @@ function LegacyVendorPicker({ value, currentVendorName, onChange }) {
5237
5306
  return (
5238
5307
  <SearchSelect
5239
5308
  value={value}
5240
- onChange={onChange}
5309
+ onValueChange={onChange}
5241
5310
  loadOptions={fetchVendors}
5242
5311
  selectedLabel={currentVendorName}
5243
5312
  placeholder="Select vendor"
@@ -5326,7 +5395,7 @@ import { Autocomplete } from "@godxjp/ui/data-entry";
5326
5395
 
5327
5396
  // \u2705 Replace with:
5328
5397
  // import { Select } from "@godxjp/ui/data-entry";
5329
- // <Select options={options} showSearch placeholder="Search\u2026" onChange={setValue} value={value} />
5398
+ // <Select options={options} showSearch placeholder="Search\u2026" onValueChange={setValue} value={value} />
5330
5399
 
5331
5400
  // Legacy usage (backward compat only):
5332
5401
  import { Autocomplete } from "@godxjp/ui/data-entry";
@@ -5840,10 +5909,10 @@ export function LegacyInvoiceHeader() {
5840
5909
  ],
5841
5910
  useCases: [
5842
5911
  "App shell / top-nav language switcher that persists the user's locale preference via AppProvider and localStorage without any extra state.",
5843
- "Settings page 'Language' field where locale is part of a form submitted to the backend \u2014 use controlled mode: value={form.locale} onChange={(v) => form.setLocale(v)}.",
5912
+ "Settings page 'Language' field where locale is part of a form submitted to the backend \u2014 use controlled mode: value={form.locale} onValueChange={(v) => form.setLocale(v)}.",
5844
5913
  "Onboarding wizard step that lets the user pick their language before the rest of the app is configured \u2014 mount with AppProvider persist={false} and a controlled value to keep state local to the wizard.",
5845
5914
  "Admin user-profile form where locale is one of several preferences (alongside timezone and date/time format) \u2014 pair with TimezonePicker, DateFormatPicker, TimeFormatPicker under the same AppProvider.",
5846
- "Storybook / test harness where AppProvider is not present \u2014 render in fully controlled mode: <LocalePicker value='en' onChange={fn} />.",
5915
+ "Storybook / test harness where AppProvider is not present \u2014 render in fully controlled mode: <LocalePicker value='en' onValueChange={fn} />.",
5847
5916
  "Localization QA tool that cycles through locales programmatically \u2014 drive via controlled value to switch the UI language without user interaction."
5848
5917
  ],
5849
5918
  related: [
@@ -5875,7 +5944,7 @@ export function LocaleField() {
5875
5944
  return (
5876
5945
  <div className="flex flex-col gap-1.5">
5877
5946
  <label htmlFor="locale-picker">Language</label>
5878
- <LocalePicker id="locale-picker" value={locale} onChange={setLocale} />
5947
+ <LocalePicker id="locale-picker" value={locale} onValueChange={setLocale} />
5879
5948
  </div>
5880
5949
  );
5881
5950
  }\`}`,
@@ -5916,7 +5985,7 @@ export function LocaleField() {
5916
5985
  ],
5917
5986
  usage: [
5918
5987
  "DO: Wrap with <AppProvider> and omit value/onChange \u2014 the picker reads and writes context automatically. This is the canonical zero-prop usage: <TimezonePicker />.",
5919
- "DO: Pass value + onChange for fully controlled standalone usage (e.g. a form field that posts the timezone string): <TimezonePicker value={tz} onChange={setTz} />. AppProvider is not required in this mode.",
5988
+ "DO: Pass value + onChange for fully controlled standalone usage (e.g. a form field that posts the timezone string): <TimezonePicker value={tz} onValueChange={setTz} />. AppProvider is not required in this mode.",
5920
5989
  "DON'T: Omit BOTH AppProvider context AND controlled props \u2014 the component throws at runtime: 'TimezonePicker requires <AppProvider> or controlled value + onChange'.",
5921
5990
  "DO: Pass options={['Asia/Tokyo', 'UTC']} to restrict the list. The current value is automatically prepended if it is missing from the list, so the picker never shows an empty/invalid selection.",
5922
5991
  "DON'T: Hand-roll a timezone <select> or a custom combobox \u2014 TimezonePicker already handles locale-aware labels (translated city + GMT offset), the full IANA list, and ARIA semantics.",
@@ -5945,7 +6014,7 @@ export function TimezoneField() {
5945
6014
  return (
5946
6015
  <TimezonePicker
5947
6016
  value={tz}
5948
- onChange={setTz}
6017
+ onValueChange={setTz}
5949
6018
  options={["Asia/Tokyo", "Asia/Ho_Chi_Minh", "UTC"]}
5950
6019
  />
5951
6020
  );
@@ -6053,7 +6122,7 @@ export function ExportDialog() {
6053
6122
  return (
6054
6123
  <div className="flex items-center gap-2">
6055
6124
  <label htmlFor="export-fmt">Export date format</label>
6056
- <DateFormatPicker id="export-fmt" value={fmt} onChange={setFmt} />
6125
+ <DateFormatPicker id="export-fmt" value={fmt} onValueChange={setFmt} />
6057
6126
  </div>
6058
6127
  );
6059
6128
  }\`}`,
@@ -6093,7 +6162,7 @@ export function ExportDialog() {
6093
6162
  ],
6094
6163
  usage: [
6095
6164
  "DO use inside <AppProvider> with no extra props to let it read/write the global time-format automatically: <AppProvider defaultTimeFormat='24h'><TimeFormatPicker /></AppProvider>",
6096
- "DO switch to fully controlled mode when you need to manage the value yourself \u2014 supply BOTH value and onChange, or the component will throw: <TimeFormatPicker value={fmt} onChange={setFmt} />",
6165
+ "DO switch to fully controlled mode when you need to manage the value yourself \u2014 supply BOTH value and onChange, or the component will throw: <TimeFormatPicker value={fmt} onValueChange={setFmt} />",
6097
6166
  "DON'T omit both AppProvider and controlled props \u2014 the component throws an Error at render time: 'TimeFormatPicker requires <AppProvider> or controlled value + onChange'. There is no silent fallback.",
6098
6167
  "DON'T hand-roll a time-format <select> \u2014 the locale-aware labels (e.g. '24 gi\u1EDD' for vi, '24-hour' for en) are generated internally from the i18n layer; reinventing this loses those translations.",
6099
6168
  "DO wire a <label htmlFor={id}> when using the id prop for accessibility; the SelectTrigger already sets aria-label from i18n but a visible label improves discoverability.",
@@ -6136,7 +6205,7 @@ export function SettingsForm() {
6136
6205
  return (
6137
6206
  <div>
6138
6207
  <label htmlFor="time-fmt">Time format</label>
6139
- <TimeFormatPicker id="time-fmt" value={fmt} onChange={setFmt} />
6208
+ <TimeFormatPicker id="time-fmt" value={fmt} onValueChange={setFmt} />
6140
6209
  </div>
6141
6210
  );
6142
6211
  }\`}
@@ -6568,194 +6637,85 @@ function componentsByGroup(group) {
6568
6637
  // src/data/prop-vocabulary.ts
6569
6638
  var PROP_VOCABULARY = [
6570
6639
  {
6571
- name: "SizeProp",
6572
- concept: "Dimensional scale for most primitives.",
6573
- values: ["small", "default", "large"],
6574
- usedBy: [
6575
- "InputSize",
6576
- "CheckboxGroupSize",
6577
- "ColorPickerSize",
6578
- "MediaUploadSize",
6579
- "ProgressSize",
6580
- "RadioGroupSize",
6581
- "RateSize",
6582
- "TransferSize",
6583
- "SegmentedControlSize (subset)",
6584
- "SpaceSize (+ number)",
6585
- "FlexGap (+ number)",
6586
- "GridGap (+ number)",
6587
- "MasonryGap (+ number)"
6588
- ]
6589
- },
6590
- {
6591
- name: "SizeWithXSProp",
6592
- concept: 'Extension of `SizeProp` with `"x-small"` for compact icon-bar / table-row contexts.',
6593
- values: ["x-small", "small", "default", "large"],
6594
- usedBy: ["ButtonSize"]
6640
+ name: "ValueProp<T = string>",
6641
+ concept: "Abstract controlled value.",
6642
+ values: ["generic"],
6643
+ usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Tabs", "SearchSelect"]
6595
6644
  },
6596
6645
  {
6597
- name: "IconSizeProp",
6598
- concept: "Icon-symbol shaped primitives (visual axis = glyph size, not height).",
6599
- values: ["sm", "md", "lg"],
6600
- usedBy: ["SpinnerSize", "IconButtonSize"]
6646
+ name: "DefaultValueProp<T = string>",
6647
+ concept: "Abstract uncontrolled initial value.",
6648
+ values: ["generic"],
6649
+ usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Tabs"]
6601
6650
  },
6602
6651
  {
6603
- name: "StatusProp",
6604
- concept: "Form-field validation state.",
6605
- values: ["default", "error", "warning", "success"],
6606
- usedBy: ["InputStatus"],
6607
- notes: 'Form errors use `"error"` (not `"destructive"`) \u2014 different concern from destructive actions.'
6652
+ name: "OnValueChangeProp<T = string>",
6653
+ concept: "Callback for abstract value changes. DOM events continue to use onChange.",
6654
+ values: ["(value: T) => void"],
6655
+ usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Transfer", "settings pickers"]
6608
6656
  },
6609
6657
  {
6610
- name: "ToneProp",
6611
- concept: "Same values as StatusProp \u2014 name chosen when describing surface colouring rather than validation.",
6612
- values: ["default", "error", "warning", "success"],
6613
- usedBy: ["(alias of StatusProp)"]
6614
- },
6615
- {
6616
- name: "HelpToneProp",
6617
- concept: "Help-line / Alert colour ladder. Adds `info` + `warn` to StatusProp.",
6618
- values: ["default", "info", "warn", "error", "success"],
6619
- usedBy: ["FieldHelpTone"]
6620
- },
6621
- {
6622
- name: "OrientationProp",
6623
- concept: "Layout axis.",
6624
- values: ["horizontal", "vertical"],
6625
- usedBy: [
6626
- "AnchorOrientation",
6627
- "MenuOrientation",
6628
- "RadioGroupOrientation",
6629
- "CheckboxGroupOrientation",
6630
- "SegmentedControlOrientation",
6631
- "StepsOrientation",
6632
- "TabsOrientation"
6633
- ]
6658
+ name: "OpenProp / DefaultOpenProp / OnOpenChangeProp",
6659
+ concept: "Disclosure state.",
6660
+ values: ["boolean", "(open: boolean) => void"],
6661
+ usedBy: ["Dialog", "Sheet", "Popover"]
6634
6662
  },
6635
6663
  {
6636
- name: "DensityProp",
6637
- concept: "Internal row-height / padding scale (distinct from SizeProp \u2014 density rescales chrome, size rescales the primitive's own visual axis).",
6638
- values: ["compact", "default", "comfortable"],
6639
- usedBy: ["TreeDensity", "TableDensity"]
6640
- },
6641
- {
6642
- name: "SideProp",
6643
- concept: "Edge a floating panel docks against.",
6644
- values: ["top", "right", "bottom", "left"],
6645
- usedBy: ["SheetSide", "TabsPlacement", "TableStickySide (subset)"]
6646
- },
6647
- {
6648
- name: "PlacementProp",
6649
- concept: "Extension of SideProp with a centred anchor (Tabs placement, Tour spotlight).",
6650
- values: ["top", "right", "bottom", "left", "center"],
6651
- usedBy: ["TourPlacement"]
6652
- },
6653
- {
6654
- name: "PaddingProp",
6655
- concept: "Outer gutter scale for surface containers.",
6656
- values: ["tight", "default", "cozy", "none"],
6657
- usedBy: ["CardPadding", "PageHeaderPadding", "PageContentPadding"]
6664
+ name: "SizeProp",
6665
+ concept: "Shared public size names.",
6666
+ values: ["xs", "sm", "md", "lg"],
6667
+ usedBy: ["Button", "Steps", "Switch"],
6668
+ notes: "Component-specific subsets must be documented. Old alias small is sm."
6658
6669
  },
6659
6670
  {
6660
- name: "AlignProp",
6661
- concept: "CSS-flexbox cross-axis alignment ladder.",
6662
- values: ["start", "end", "center", "stretch", "baseline"],
6663
- usedBy: ["FlexAlign"]
6671
+ name: "ToneProp",
6672
+ concept: "Semantic status/color intent.",
6673
+ values: ["default", "success", "warning", "destructive", "info", "muted", "neutral"],
6674
+ usedBy: ["Badge", "Alert"],
6675
+ notes: "Status values belong in tone, not variant."
6664
6676
  },
6665
6677
  {
6666
- name: "ColorProp",
6667
- concept: "Full semantic palette \u2014 every CSS variable slot.",
6668
- values: ["default", "info", "success", "warning", "destructive", "attention", "primary", "secondary"],
6669
- usedBy: ["TagPresetColor (\u2212 secondary)", "TimelineColor (\u2212 secondary)", "SpinnerTone (+ muted)"],
6670
- notes: "Each value maps 1:1 to a CSS variable (`--info`, `--success`, `--warning`, `--destructive`, `--attention`, `--primary`, `--secondary`). The `data-accent` axis rebinds `--primary`'s hue without renaming the slot."
6678
+ name: "GapProp",
6679
+ concept: "Shared layout gap scale.",
6680
+ values: ["xs", "sm", "md", "lg", "xl"],
6681
+ usedBy: ["Stack", "Inline"],
6682
+ notes: "Inline uses an Exclude<GapProp, 'xl'> subset."
6671
6683
  },
6672
6684
  {
6673
- name: "FeedbackColorProp",
6674
- concept: "Subset of ColorProp accepted by feedback primitives (no brand `primary` since they are themselves informational).",
6675
- values: ["default", "info", "success", "warning", "destructive"],
6676
- usedBy: ["AlertColor", "ResultColor", "ProgressColor"]
6685
+ name: "TitleProp",
6686
+ concept: "Primary heading text.",
6687
+ values: ["React.ReactNode"],
6688
+ usedBy: ["PageContainer", "PageHeader", "EmptyState", "Dialog"]
6677
6689
  },
6678
6690
  {
6679
- name: "LoadingProp",
6680
- concept: "Loading-state union \u2014 shared across Form / FormField / data-entry primitives.",
6681
- values: ["true", "false", '{ kind: "spinner" }', '{ kind: "skeleton" }', "{ kind, label }"],
6682
- usedBy: ["FormProps.loading", "FormFieldProps.loading"],
6683
- notes: 'Cascade: `<Form loading>` sets a default for every nested `<FormField>`. Per-field `loading` overrides Form\'s. `true` \u2192 spinner (default). `{ kind: "skeleton" }` \u2192 use for INITIAL fetch state. UX nuance: skeleton on init, spinner on save.'
6691
+ name: "DensityProp",
6692
+ concept: "Page/subtree density.",
6693
+ values: ["compact", "default", "comfortable"],
6694
+ usedBy: ["PageContainer"]
6684
6695
  }
6685
6696
  ];
6686
6697
  function findVocab(name) {
6687
- const normalized = name.trim().toLowerCase().replace(/prop$/i, "");
6688
- return PROP_VOCABULARY.find(
6689
- (v) => v.name.toLowerCase().replace(/prop$/i, "") === normalized
6690
- );
6698
+ const normalized = name.trim().toLowerCase().replace(/prop(?:<.*>)?$/i, "");
6699
+ return PROP_VOCABULARY.find((v) => v.name.toLowerCase().replace(/prop(?:<.*>)?$/i, "") === normalized);
6691
6700
  }
6692
6701
 
6693
6702
  // src/data/tokens.ts
6694
6703
  var TOKENS = [
6695
- // Color semantic slots (rebound by `data-theme` + `data-accent`)
6696
- { name: "--background", category: "color", role: "Base surface", axis: "data-theme" },
6697
- { name: "--foreground", category: "color", role: "Base text", axis: "data-theme" },
6698
- { name: "--card", category: "color", role: "Card surface", axis: "data-theme" },
6699
- { name: "--popover", category: "color", role: "Popover / dropdown surface", axis: "data-theme" },
6700
- { name: "--popover-foreground", category: "color", role: "Popover text", axis: "data-theme" },
6701
- { name: "--primary", category: "color", role: "Brand accent (Buttons, links)", axis: "data-accent" },
6702
- { name: "--primary-foreground", category: "color", role: "Text on primary surface", axis: "data-accent" },
6703
- { name: "--secondary", category: "color", role: "Secondary surface / text dimming", axis: "data-theme" },
6704
- { name: "--accent", category: "color", role: "Hover / focus tint" },
6705
- { name: "--muted", category: "color", role: "Muted surface" },
6706
- { name: "--muted-foreground", category: "color", role: "Muted text" },
6707
- { name: "--border", category: "color", role: "Default border color", axis: "data-theme" },
6708
- { name: "--input", category: "color", role: "Input field border" },
6709
- { name: "--ring", category: "color", role: "Focus ring", axis: "data-accent" },
6710
- { name: "--success", category: "color", role: "Success semantic slot" },
6711
- { name: "--warning", category: "color", role: "Warning semantic slot" },
6712
- { name: "--destructive", category: "color", role: "Danger / destructive action slot" },
6713
- { name: "--info", category: "color", role: "Info / neutral notice slot" },
6714
- { name: "--attention", category: "color", role: "Attention / non-destructive alert slot" },
6715
- // Spacing — fixed scale
6716
- { name: "--spacing-1", category: "spacing", role: "4px", value: "0.25rem" },
6717
- { name: "--spacing-2", category: "spacing", role: "8px", value: "0.5rem" },
6718
- { name: "--spacing-3", category: "spacing", role: "12px", value: "0.75rem" },
6719
- { name: "--spacing-4", category: "spacing", role: "16px", value: "1rem" },
6720
- { name: "--spacing-5", category: "spacing", role: "20px", value: "1.25rem" },
6721
- { name: "--spacing-6", category: "spacing", role: "24px", value: "1.5rem" },
6722
- { name: "--spacing-8", category: "spacing", role: "32px", value: "2rem" },
6723
- // Typography — fixed scale
6724
- { name: "--text-2xs", category: "typography", role: "10px", value: "0.625rem" },
6725
- { name: "--text-xs", category: "typography", role: "12px", value: "0.75rem" },
6726
- { name: "--text-sm", category: "typography", role: "14px", value: "0.875rem" },
6727
- { name: "--text-base", category: "typography", role: "16px", value: "1rem" },
6728
- { name: "--text-lg", category: "typography", role: "18px", value: "1.125rem" },
6729
- { name: "--text-xl", category: "typography", role: "20px", value: "1.25rem" },
6730
- { name: "--text-2xl", category: "typography", role: "24px", value: "1.5rem" },
6731
- { name: "--font-mono", category: "typography", role: "Monospace stack" },
6732
- // Radius — fixed scale
6733
- { name: "--radius-sm", category: "radius", role: "Small (chips, inputs)", value: "0.25rem" },
6734
- { name: "--radius-md", category: "radius", role: "Medium (cards)", value: "0.5rem" },
6735
- { name: "--radius-lg", category: "radius", role: "Large (dialogs)", value: "0.75rem" },
6736
- { name: "--radius-full", category: "radius", role: "Pill / circle", value: "9999px" },
6737
- // Breakpoints — mobile-first min-widths
6738
- { name: "--breakpoint-xs", category: "breakpoint", role: "Mobile-first base (\u22650px)", value: "0" },
6739
- { name: "--breakpoint-sm", category: "breakpoint", role: "Phone landscape / tablet portrait", value: "640px" },
6740
- { name: "--breakpoint-md", category: "breakpoint", role: "Tablet landscape", value: "768px" },
6741
- { name: "--breakpoint-lg", category: "breakpoint", role: "Laptop", value: "1024px" },
6742
- { name: "--breakpoint-xl", category: "breakpoint", role: "Desktop", value: "1280px" },
6743
- { name: "--breakpoint-xxl", category: "breakpoint", role: "Wide desktop", value: "1536px" },
6744
- // Density — rebound by `data-density`
6745
- { name: "--density-element", category: "density", role: "Element height (Input/Button)", axis: "data-density" },
6746
- { name: "--density-element-sm", category: "density", role: "Small element", axis: "data-density" },
6747
- { name: "--density-element-lg", category: "density", role: "Large element", axis: "data-density" },
6748
- { name: "--density-card", category: "density", role: "Card padding", axis: "data-density" },
6749
- { name: "--density-page", category: "density", role: "Page (PageContent) padding", axis: "data-density" },
6750
- { name: "--density-section", category: "density", role: "Section padding (cozy variant)", axis: "data-density" },
6751
- { name: "--header-height", category: "density", role: "Topbar height", axis: "data-density" },
6752
- { name: "--sidebar-width", category: "density", role: "Sidebar width (expanded)", axis: "data-density" },
6753
- { name: "--sidebar-width-collapsed", category: "density", role: "Sidebar icon-only width", axis: "data-density" },
6754
- { name: "--touch-target-min", category: "density", role: "Mobile touch target (does NOT scale)", value: "44px" },
6755
- // Motion — fixed timings
6756
- { name: "--transition-base", category: "motion", role: "Standard transition duration", value: "200ms" },
6757
- { name: "--ease-out", category: "motion", role: "Out easing curve", value: "cubic-bezier(0, 0, 0.2, 1)" },
6758
- { name: "--ease-in-out", category: "motion", role: "In-out easing", value: "cubic-bezier(0.4, 0, 0.2, 1)" }
6704
+ { name: "--wa-*", category: "primitive", tier: "primitive", role: "Neutral decorative Japanese accent primitives for charts/tags/decoration only." },
6705
+ { name: "--chart-1..6", category: "primitive", tier: "primitive", role: "Neutral decorative chart primitives; @theme chart colors reference these tokens." },
6706
+ { name: "--space-0..12", category: "primitive", tier: "primitive", role: "Raw spacing scale." },
6707
+ { name: "--font-size-*", category: "primitive", tier: "primitive", role: "Raw typography scale." },
6708
+ { name: "--primary", category: "semantic", tier: "semantic", role: "Brand/action color role." },
6709
+ { name: "--success", category: "semantic", tier: "semantic", role: "Success status role." },
6710
+ { name: "--warning", category: "semantic", tier: "semantic", role: "Warning status role." },
6711
+ { name: "--destructive", category: "semantic", tier: "semantic", role: "Destructive/error status role." },
6712
+ { name: "--info", category: "semantic", tier: "semantic", role: "Information status role." },
6713
+ { name: "--attention", category: "semantic", tier: "semantic", role: "Attention status role." },
6714
+ { name: "--badge-space-*", category: "component", tier: "component", role: "Badge spacing." },
6715
+ { name: "--card-*", category: "component", tier: "component", role: "Card surface, border, spacing, and typography." },
6716
+ { name: "--control-*", category: "component", tier: "component", role: "Shared form control heights, padding, icons, and focus chrome." },
6717
+ { name: "--table-*", category: "component", tier: "component", role: "Table row/cell sizing." },
6718
+ { name: "--dialog-* / --alert-* / --skeleton-*", category: "component", tier: "component", role: "Feedback component sizing and spacing." }
6759
6719
  ];
6760
6720
  function tokensByCategory(category) {
6761
6721
  return TOKENS.filter((t) => t.category === category);
@@ -6839,7 +6799,7 @@ var PATTERNS = [
6839
6799
  // 2) Badge renders grey with a \u25CB (no colour) for localized/tier labels
6840
6800
  // Cause: it auto-maps only English lifecycle keys. (@godxjp/ui >= 6.1)
6841
6801
  // \u274C <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" />
6842
- // \u2705 <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" variant="success" icon={null} /> // tier \u2192 pill, no icon
6802
+ // \u2705 <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null} /> // tier \u2192 pill, no icon
6843
6803
  // \u2705 <Badge status="active">\u516C\u958B\u4E2D</Badge> // lifecycle \u2192 keep icon
6844
6804
 
6845
6805
  // 3) Table text collapses to one char per line, or a chip wraps
@@ -6990,11 +6950,11 @@ export function DeleteProjectDialog({ open, onOpenChange, slug }: { open: boolea
6990
6950
  <DialogDescription>\u3053\u306E\u64CD\u4F5C\u306F\u53D6\u308A\u6D88\u305B\u307E\u305B\u3093\u3002\u78BA\u8A8D\u306E\u305F\u3081\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D "{slug}" \u3068\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</DialogDescription>
6991
6951
  </DialogHeader>
6992
6952
  <Stack gap="md">
6993
- <Input value={confirm} onChange={(e) => setConfirm(e.target.value)} placeholder={slug} />
6953
+ <Input value={confirm} onValueChange={(e) => setConfirm(e.target.value)} placeholder={slug} />
6994
6954
  </Stack>
6995
6955
  <DialogFooter>
6996
6956
  <Button variant="outline" onClick={() => onOpenChange(false)}>\u30AD\u30E3\u30F3\u30BB\u30EB</Button>
6997
- <Button variant="destructive" disabled={confirm !== slug} onClick={() => { toast.success("\u524A\u9664\u3057\u307E\u3057\u305F"); onOpenChange(false); }}>\u5B8C\u5168\u306B\u524A\u9664</Button>
6957
+ <Button tone="destructive" disabled={confirm !== slug} onClick={() => { toast.success("\u524A\u9664\u3057\u307E\u3057\u305F"); onOpenChange(false); }}>\u5B8C\u5168\u306B\u524A\u9664</Button>
6998
6958
  </DialogFooter>
6999
6959
  </DialogContent>
7000
6960
  </Dialog>
@@ -7060,7 +7020,7 @@ function Coupons({ coupons }: { coupons: Coupon[] }) {
7060
7020
  // ColumnDef = { key, header, render?, align?: "left"|"center"|"right", sortable?, width? }
7061
7021
  const columns: ColumnDef<Coupon>[] = [
7062
7022
  { key: "name", header: "\u30AF\u30FC\u30DD\u30F3\u540D", render: (c) => <span className="font-medium">{c.name}</span> },
7063
- { key: "scope", header: "\u30B9\u30B3\u30FC\u30D7", render: (c) => <Badge status={c.scope} variant="info" icon={null} /> },
7023
+ { key: "scope", header: "\u30B9\u30B3\u30FC\u30D7", render: (c) => <Badge status={c.scope} tone="info" icon={null} /> },
7064
7024
  { key: "status", header: "\u30B9\u30C6\u30FC\u30BF\u30B9", render: (c) => <Badge status={c.status} /> },
7065
7025
  { key: "valid", header: "\u6709\u52B9\u671F\u9593", render: (c) => \`\${formatDate(c.validFrom)} \u301C \${formatDate(c.validTo)}\` },
7066
7026
  { key: "usage", header: "\u5229\u7528\u6570", align: "right", render: (c) => c.usage.toLocaleString() },
@@ -7102,7 +7062,7 @@ function Coupons({ coupons }: { coupons: Coupon[] }) {
7102
7062
  </Card>
7103
7063
 
7104
7064
  {filtered.length > PAGE_SIZE && (
7105
- <Pagination current={page} total={filtered.length} pageSize={PAGE_SIZE} showTotal onChange={(p) => setPage(p)} />
7065
+ <Pagination current={page} total={filtered.length} pageSize={PAGE_SIZE} showTotal onValueChange={(p) => setPage(p)} />
7106
7066
  )}
7107
7067
  </Stack>
7108
7068
  </PageContainer>
@@ -7158,7 +7118,7 @@ function MemberShow({ id }: { id: string }) {
7158
7118
  {/* Descriptions is COMPOUND \u2014 value goes in children, not a prop */}
7159
7119
  <Descriptions columns={2}>
7160
7120
  <Descriptions.Item label="\u6C0F\u540D">{member.name}</Descriptions.Item>
7161
- <Descriptions.Item label="\u30E9\u30F3\u30AF"><Badge status={member.rank} variant="info" icon={null} /></Descriptions.Item>
7121
+ <Descriptions.Item label="\u30E9\u30F3\u30AF"><Badge status={member.rank} tone="info" icon={null} /></Descriptions.Item>
7162
7122
  <Descriptions.Item label="\u30B9\u30C6\u30FC\u30BF\u30B9"><Badge status={member.status} /></Descriptions.Item>
7163
7123
  <Descriptions.Item label="\u767B\u9332\u65E5">{formatDate(member.registeredAt)}</Descriptions.Item>
7164
7124
  </Descriptions>
@@ -7221,12 +7181,12 @@ const seeded = (n: number) => { const x = Math.sin((n + 1) * 99.71) * 1e4; retur
7221
7181
  <Badge status="active">\u516C\u958B\u4E2D</Badge> // green \u2713 \u516C\u958B\u4E2D
7222
7182
 
7223
7183
  // 2) Unknown label \u2014 set tone explicitly (no icon, since the key is unknown):
7224
- <Badge status="\u516C\u958B\u4E2D" variant="success" />
7184
+ <Badge status="\u516C\u958B\u4E2D" tone="success" />
7225
7185
 
7226
7186
  // 3) Tier / category \u2014 coloured pill, drop the misleading glyph with icon={null}:
7227
- <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" variant="success" icon={null} />
7228
- <Badge status="\u30B4\u30FC\u30EB\u30C9" variant="warning" icon={null} />
7229
- <Badge status="\u6CD5\u4EBA\u5171\u901A" variant="info" icon={null} />
7187
+ <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null} />
7188
+ <Badge status="\u30B4\u30FC\u30EB\u30C9" tone="warning" icon={null} />
7189
+ <Badge status="\u6CD5\u4EBA\u5171\u901A" tone="info" icon={null} />
7230
7190
 
7231
7191
  // tone: "success" | "warning" | "destructive" | "info" | "neutral" (import type BadgeTone)
7232
7192
  // RULE: a chip never wraps \u2014 it is pinned white-space: nowrap, so it stays one line in