@godxjp/ui 32.0.5 → 32.1.1

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 (62) hide show
  1. package/README.md +2 -2
  2. package/agent/START-HERE.md +7 -7
  3. package/agent/components/Calendar.json +1 -1
  4. package/agent/components/Checkbox.json +1 -1
  5. package/agent/components/CheckboxGroup.json +6 -0
  6. package/agent/components/CommandPalette.json +6 -0
  7. package/agent/components/DatePicker.json +6 -0
  8. package/agent/components/DropdownMenu.json +5 -0
  9. package/agent/components/ListRow.json +2 -1
  10. package/agent/components/PasswordStrength.json +3 -3
  11. package/agent/components/SearchInput.json +6 -0
  12. package/agent/components/Select.json +8 -2
  13. package/agent/components/Toaster.json +15 -4
  14. package/agent/components/Toggle.json +3 -2
  15. package/agent/components/ToggleGroup.json +1 -1
  16. package/agent/components-index.json +1 -1
  17. package/agent/components.json +63 -15
  18. package/agent/index.json +6 -6
  19. package/agent/llms.txt +6 -6
  20. package/agent/patterns/card-grid-selection.json +21 -0
  21. package/agent/patterns/dependent-selects.json +20 -0
  22. package/agent/patterns-index.json +39 -0
  23. package/agent/patterns.json +41 -0
  24. package/agent/tokens.json +60 -0
  25. package/dist/components/data-entry/checkbox-group.d.ts +1 -1
  26. package/dist/components/data-entry/checkbox-group.js +58 -19
  27. package/dist/components/data-entry/command-palette.d.ts +21 -1
  28. package/dist/components/data-entry/command-palette.js +34 -34
  29. package/dist/components/data-entry/date-picker.js +21 -1
  30. package/dist/components/data-entry/index.d.ts +1 -1
  31. package/dist/components/data-entry/password-strength.d.ts +2 -0
  32. package/dist/components/data-entry/password-strength.js +8 -7
  33. package/dist/components/data-entry/search-input.d.ts +1 -1
  34. package/dist/components/data-entry/search-input.js +22 -2
  35. package/dist/components/data-entry/search-select.js +68 -13
  36. package/dist/components/data-entry/toggle.d.ts +2 -2
  37. package/dist/components/data-entry/toggle.js +5 -1
  38. package/dist/components/feedback/sonner.d.ts +7 -1
  39. package/dist/components/feedback/sonner.js +22 -2
  40. package/dist/components/feedback/use-toast.d.ts +3 -1
  41. package/dist/components/feedback/use-toast.js +10 -1
  42. package/dist/components/navigation/dropdown-menu.d.ts +7 -1
  43. package/dist/components/navigation/dropdown-menu.js +18 -2
  44. package/dist/contracts/measurement.json +1 -1
  45. package/dist/i18n/messages/en.json +19 -1
  46. package/dist/i18n/messages/ja.json +18 -1
  47. package/dist/i18n/messages/vi.json +18 -1
  48. package/dist/lib/shortcut.d.ts +17 -0
  49. package/dist/lib/shortcut.js +37 -0
  50. package/dist/props/components/data-entry.prop.d.ts +36 -0
  51. package/dist/styles/control.css +35 -3
  52. package/dist/styles/data-display-layout.css +7 -0
  53. package/dist/styles/layers.json +6 -1
  54. package/dist/styles/navigation-layout.css +26 -0
  55. package/dist/styles/table-layout.css +14 -0
  56. package/dist/styles/toggle.css +14 -0
  57. package/dist/styles.css +1 -1
  58. package/dist/tokens/components/control.css +10 -0
  59. package/dist/tokens/components/navigation.css +6 -0
  60. package/dist/tokens/foundation.css +2 -0
  61. package/package.json +2 -2
  62. package/scripts/ui-audit.mjs +4 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@ npm i @godxjp/ui
18
18
 
19
19
  <!-- agent-catalog-counts:start -->
20
20
 
21
- The catalog — 161 components, 2183 tokens, 43 cardinal rules — is published in two forms from one
21
+ The catalog — 161 components, 2193 tokens, 43 cardinal rules — is published in two forms from one
22
22
  source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
23
23
  <!-- agent-catalog-counts:end -->
24
24
 
@@ -55,7 +55,7 @@ static files straight from this public repo — no hosting, no deploy step:
55
55
  | [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 47 KB | all 161 as name + group + tagline — **fetch this first** |
56
56
  | [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–36 KB | one file per component, each with its `importPath` — **this is the route to take** |
57
57
  | [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.2 MB | all 161 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
58
- | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 841 KB | all 2183 design tokens, their values and why they exist |
58
+ | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 845 KB | all 2193 design tokens, their values and why they exist |
59
59
  | [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
60
60
  | [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 21 KB | the 43 cardinal rules |
61
61
  <!-- prettier-ignore-end -->
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 32.0.5.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 32.1.1.** 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.
@@ -43,7 +43,7 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
43
43
  **You cannot run a process** (ChatGPT web · Claude.ai · anything fetching URLs)
44
44
  → These files are for you. Fetch in this order:
45
45
 
46
- 0. `patterns-index.json` — 22 whole-task patterns as name + tagline + tags. **If your
46
+ 0. `patterns-index.json` — 24 whole-task patterns as name + tagline + tags. **If your
47
47
  task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
48
48
  filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
49
49
  copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
@@ -56,10 +56,10 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
56
56
  its `importPath`, and its examples. Fetch only the handful you picked in step 1.
57
57
  3. `rules.json` — 43 cardinal rules. The ones about raw HTML and hardcoded colour are not
58
58
  style advice.
59
- 4. `tokens.json` — 2183 design tokens, each tagged with its `tier`. **If you were handed a
60
- brand, read the 210 `foundation` entries first** — `--primary`, `--background`,
59
+ 4. `tokens.json` — 2193 design tokens, each tagged with its `tier`. **If you were handed a
60
+ brand, read the 211 `foundation` entries first** — `--primary`, `--background`,
61
61
  `--radius`, `--font-size-base` are the handful everything else derives from. The
62
- 1861 `component` entries are per-part knobs; reach for one only when a role is
62
+ 1870 `component` entries are per-part knobs; reach for one only when a role is
63
63
  right everywhere except one component.
64
64
  5. `anti-ai-tells.json` — 26 shapes that make generated UI look generated, each with the
65
65
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
@@ -143,9 +143,9 @@ has stopped following the brand.
143
143
 
144
144
  | `tier` | count | what it is | set it? |
145
145
  |---|---|---|---|
146
- | `foundation` | 210 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** Handed a brand colour, this is where it goes: `:root { --primary: <H> <S>% <L>%; }` (HSL components, no `hsl()` wrapper) |
146
+ | `foundation` | 211 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** Handed a brand colour, this is where it goes: `:root { --primary: <H> <S>% <L>%; }` (HSL components, no `hsl()` wrapper) |
147
147
  | `semantic` | 112 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
148
- | `component` | 1861 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
148
+ | `component` | 1870 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
149
149
 
150
150
  A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
151
151
  value, so the real default is computed where the element paints it. Set it and yours wins.
@@ -22,7 +22,7 @@
22
22
  },
23
23
  {
24
24
  "defaultValue": "\"auto\"",
25
- "description": "How the grid claims horizontal space. `auto` (default) shrink-wraps to seven fixed day columns — the shape a picker popover needs, because the panel is shrink-to-fit and takes ITS width from the calendar inside it. Use `full` for an EMBEDDED calendar (a shift board, a booking month) that is the content of a Card rather than a dropdown: it stacks the months and lets the day cells share the row. Measured at a 1200px container: auto → root 248px / cells 32px; full → root 1200px / cells 168px; the DatePicker popover stays 250px either way. Do NOT reach for `className=\"w-full\"` instead — it widens the root and leaves the grid at 224px pinned left.",
25
+ "description": "How the grid claims horizontal space. `auto` (default) shrink-wraps to seven fixed day columns — the shape a picker popover needs, because the panel is shrink-to-fit and takes ITS width from the calendar inside it. Use `full` for an EMBEDDED calendar (a shift board, a booking month) that is the content of a Card rather than a dropdown: it stacks the months and lets the day cells share the row, each reading from the start edge with an inset (`--calendar-full-day-padding-inline` / `-block`, default --space-2 / --space-1, so the numeral clears the `text-edge-inset` audit; gh#1258). Measured at a 1200px container: auto → root 248px / cells 32px; full → root 1200px / cells 168px; the DatePicker popover stays 250px either way. Do NOT reach for `className=\"w-full\"` instead — it widens the root and leaves the grid at 224px pinned left.",
26
26
  "name": "width",
27
27
  "type": "\"auto\" | \"full\""
28
28
  },
@@ -60,7 +60,7 @@
60
60
  "DO use the controlled pattern (`checked` + `onCheckedChange`) for any form-bound checkbox. `onCheckedChange` receives `boolean | 'indeterminate'` — always coerce with `!!v` or an explicit guard before storing in state.",
61
61
  "DO use `Checkbox.Group` (alias for CheckboxGroup) with the `options` prop when you have ≥2 choices from an array — it renders each item inside a `Field` (label + optional description), generates stable ids automatically, and manages the `string[]` value array. NEVER hand-roll a loop of bare `<Checkbox>` elements for a multi-select list.",
62
62
  "DO pass `name` on `Checkbox.Group` (not on individual checkboxes) when the group must submit as form fields — the group propagates the name to each internal checkbox so the browser serialises all checked values under that key.",
63
- "DON'T use `checked='indeterminate'` on `Checkbox.Group` children — indeterminate is only meaningful on a parent 'select-all' control you wire manually; the group itself does not auto-compute it.",
63
+ "DON'T use `checked='indeterminate'` on `Checkbox.Group` children, and DON'T hand-wire a select-all box above a group — pass `selectAll` (or `selectAll={{ label: '…' }}`) on `Checkbox.Group` with `options`. It renders a leading box derived from the value (checked / `mixed` / clear), a muted `{n} of {m} selected` count (`common.selectedOfTotal`, numbers via Intl.NumberFormat) and a Separator; one click is ONE `onValueChange` with every enabled value; disabled options are never counted, added or removed; the box has no `name`, so a native submit never carries it.",
64
64
  "DON'T wrap a standalone Checkbox in `Field` manually — `Field` is the internal composition primitive that `Checkbox.Group` uses. For a single boolean with a label, pass the label as children (`<Checkbox …>label</Checkbox>`, the catalog example) — it renders the same row. Use `<Field id='x' label='…' description='…'><Checkbox id='x' … /></Field>` only when the row needs a description line — Field owns the label-to-control id wiring, the description slot and the row rhythm, and a hand-rolled flex row owns none of them; for a full labelled-checkbox with description, use `Field` directly only if you need a one-off item outside a group."
65
65
  ],
66
66
  "useCases": [
@@ -60,6 +60,11 @@
60
60
  "description": "Fires with the checked values array.",
61
61
  "name": "onValueChange",
62
62
  "type": "(value: string[]) => void"
63
+ },
64
+ {
65
+ "description": "`options` mode only. Renders a leading select-all box whose state is derived from the value — checked when every enabled option is selected, `mixed` (indeterminate) on a partial selection, clear otherwise — followed by a muted `{n} of {m} selected` count (`common.selectedOfTotal`, numbers via Intl.NumberFormat) and a Separator. One click is ONE `onValueChange`: from mixed or clear it adds every enabled value, from checked it removes them. Disabled options are never counted, added or removed. The box has no `name`, so a native submit never carries it. `{ label }` replaces the default `dataEntry.checkboxGroup.selectAll` text.",
66
+ "name": "selectAll",
67
+ "type": "boolean | { label?: React.ReactNode }"
63
68
  }
64
69
  ],
65
70
  "related": [
@@ -77,6 +82,7 @@
77
82
  ],
78
83
  "tagline": "Multi-select checkbox list from an options array or manual children — use `options` prop for the data-driven path, never hand-roll individual Checkbox items in a group.",
79
84
  "usage": [
85
+ "DO pass `selectAll` instead of hand-wiring a parent checkbox above the group — the derived tri-state, the n-of-m count and the disabled-option rule are built in: `<CheckboxGroup options={channels} defaultValue={['email']} selectAll />`.",
80
86
  "DO use the `options` prop for any data-driven list — it auto-generates IDs, handles checked state, and wires up Field (label + description) for each item. NEVER hand-roll individual `<Checkbox>` elements inside a loop when you have an options array.",
81
87
  "DO pass `name` when inside an HTML form so each checkbox submits its value under the same field name, giving the server a multi-value array. Without `name`, native form submission silently drops all values.",
82
88
  "Controlled vs uncontrolled: pass `value` + `onChange` together for controlled usage (e.g. react-hook-form). Pass `defaultValue` alone for uncontrolled usage. Do NOT mix both — if `value` is provided, `defaultValue` is ignored and onChange must update value externally or the UI freezes.",
@@ -81,6 +81,11 @@
81
81
  "description": "Consumer-supplied error content.",
82
82
  "name": "error",
83
83
  "type": "ReactNode"
84
+ },
85
+ {
86
+ "description": "Recent queries (gh#1249), rendered as the FIRST group only while the query is empty, followed by a trailing \"Clear recent searches\" row. Choosing a query calls `onSelect(query)` and closes; the clear row calls `onClear()` and keeps the palette open. The library never reads or writes `localStorage` — the consumer stores, de-duplicates and caps `items`; duplicates are rendered as given. `label` replaces the default heading (`dataEntry.commandPalette.recent`).",
87
+ "name": "recent",
88
+ "type": "{ items: string[]; onSelect: (query: string) => void; onClear: () => void; label?: ReactNode }"
84
89
  }
85
90
  ],
86
91
  "rules": [],
@@ -88,6 +93,7 @@
88
93
  "tagline": "Searchable command dialog with controlled or uncontrolled open state and consumer-owned selection.",
89
94
  "usage": [
90
95
  "Provide localized labels and real command groups; the component does not fetch commands.",
96
+ "Recent searches: keep the list in your own store (state, URL, server, localStorage), de-duplicate it, and pass it as `recent` — the palette only renders it while the query is empty.",
91
97
  "Use either `open` plus `onOpenChange` or `defaultOpen`; do not mirror both state models.",
92
98
  "Search-as-you-type against an API: read the query from `onSearchChange`, pass the results back as `groups`, and set `shouldFilter={false}` so the rows are not scored a second time against the same string.",
93
99
  "The empty-state contract: with `shouldFilter` (default) cmdk decides — items exist, the query matches none. With `shouldFilter={false}` the PALETTE decides from props — `labels.empty` renders when `groups` carries no items, and never while `loading` or `error` is set. Hold `loading` for the whole in-flight window: a request that has not answered yet is not an empty result, and asserting over cmdk's own empty node races an async group that populates a frame late."
@@ -132,6 +132,12 @@
132
132
  "name": "allowEmpty",
133
133
  "type": "[boolean, boolean]"
134
134
  },
135
+ {
136
+ "defaultValue": "true",
137
+ "description": "With `range`: a polite live summary (`aria-live=\"polite\"`, always mounted) under the DAY grid — the start date alone while only the start is held, then `Intl.DateTimeFormat#formatRange` of both edges plus the INCLUSIVE day count through `Intl.PluralRules` (1 → 7 reads \"Mar 1 – 7, 2026 · 7 days\"; ja 「7日間」). Muted `Text size=\"xs\" tabular`, no new token. The month / quarter / year period grids ignore it. Independently of this flag, while only the start is chosen the `to` edge's placeholder becomes \"Select end date\" (`dataEntry.dateRangePicker.selectEnd`). `false` removes the summary.",
138
+ "name": "showSummary",
139
+ "type": "boolean"
140
+ },
135
141
  {
136
142
  "defaultValue": "true",
137
143
  "description": "Normalise the selection into ascending order (antd `order`). ONE rule seen through two value shapes: a `range` picked backwards is SWAPPED, a `multiple` selection is SORTED. Pass false to keep pick order.",
@@ -51,6 +51,11 @@
51
51
  "description": "On DropdownMenuContent. Ant Design `arrow` — paints the pointer at the anchored edge (default off). Sized from --dropdown-arrow-{width,height,background} rather than Radix's built-in 10×5.",
52
52
  "name": "arrow",
53
53
  "type": "boolean"
54
+ },
55
+ {
56
+ "description": "On DropdownMenuItem (gh#1246). A muted second line under the label — the two-line row, parity with `MegaMenuLinkProp.description`. Rendered as react-aria `<Text slot=\"description\">` (`data-slot=\"dropdown-menu-item-description\"`), so the row's accessible NAME stays the label and the description becomes its `aria-describedby`. A string label is handed to the collection as `textValue`, so typeahead still reaches the row by its label; pass `textValue` yourself when the label is not a plain string. Sized by --menu-item-description-{font-size,space-block}; rows without one keep the one-line height.",
57
+ "name": "description",
58
+ "type": "React.ReactNode"
54
59
  }
55
60
  ],
56
61
  "related": [
@@ -81,7 +81,8 @@
81
81
  "DO put the row's action in `trailing` (a `ghost`/`outline` Button, a DropdownMenu trigger, a Switch, or a status Badge). DO pass `as=\"li\"` when the rows live inside a semantic list, and build that list as `<Flex as=\"ul\" marker=\"none\">` — `ui-audit` flags a raw `<ul>`/`role=\"list\"` as `no-hand-rolled-list`.",
82
82
  "DO use `unread` for a notification list — the dot is a SHAPE with localized `sr-only` text (\"Unread\"/\"Read\"), so it never reads as colour alone, and the row surface reads `--list-row-unread-background` (default `hsl(var(--muted))` — chosen so the xs muted description line stays WCAG AA on the emphasized surface; `--accent` would drop it to 4.23:1). DON'T substitute a `Badge` — that renders a labelled pill, not a compact status dot.",
83
83
  "DO pass `density=\"compact\"` for the canonical invitation / history row — an Avatar, a title (+ description) and one or two small trailing Buttons that must read as ONE line inside a narrow card (≈326px content at 390px), or a history row whose status Badge + ISO-8601 date belongs beside the title. Measured at 390px: 62px tall vs 126px at the default density (where the actions wrapped), history row 41px vs 114px. DON'T reach for it just to \"make things tighter\" on a roomy page — the default density is the entity-row measure.",
84
- "DON'T add one-off `min-width`/wrapping CSS in the consumer app for a long title + two trailing Buttons. The row already shrinks and WRAPS: the content column keeps only `min(var(--list-row-body-min-width), 100%)` and the trailing actions drop onto their own line below the threshold. Retune the threshold with `--list-row-body-min-width` (default 12rem) and the action gap with `--list-row-trailing-gap`; pass `overflow=\"wrap\"` (usually with `align=\"start\"`) when the title must stay fully readable at 390px instead of truncating."
84
+ "DON'T add one-off `min-width`/wrapping CSS in the consumer app for a long title + two trailing Buttons. The row already shrinks and WRAPS: the content column keeps only `min(var(--list-row-body-min-width), 100%)` and the trailing actions drop onto their own line below the threshold. Retune the threshold with `--list-row-body-min-width` (default 12rem) and the action gap with `--list-row-trailing-gap`; pass `overflow=\"wrap\"` (usually with `align=\"start\"`) when the title must stay fully readable at 390px instead of truncating.",
85
+ "DO expect rows placed DIRECTLY in a padded (non-flush) `CardContent` — or in a `Flex as=\"ul\"` there — to drop their own inline padding: the card already holds the inset, so the row text lines up with the CardTitle instead of sitting 16px inside it. Inside `CardContent flush` (the documented home, with edge-to-edge dividers) the row keeps `--list-row-padding-x`."
85
86
  ],
86
87
  "useCases": [
87
88
  "Account security page — a list of active sessions (device + last-seen as title/description, a destructive 'Revoke' Button in trailing).",
@@ -5,7 +5,7 @@
5
5
  "name": "PasswordStrength",
6
6
  "props": [
7
7
  {
8
- "description": "The current password value to evaluate.",
8
+ "description": "The current password value to evaluate. While it is empty (or whitespace only) the meter is NEUTRAL (gh#1247): no segment lit, no tone, the label reads `labels.empty` (\"Enter a password\" / 「パスワードを入力」), every checklist item is `data-state=\"pending\"`, and the live region stays silent — no \"Weak\" verdict before anything is typed.",
9
9
  "name": "value",
10
10
  "required": true,
11
11
  "type": "string"
@@ -28,9 +28,9 @@
28
28
  "type": "number"
29
29
  },
30
30
  {
31
- "description": "Every visible and screen-reader string, each optional (gh#1191). Unset entries come from the kit's own ja / en / vi catalogue for the active locale, so most apps pass nothing: the score words, the checklist line per rule (length / upper / lower / number / symbol), the sr-only passed / failed prefixes, and the polite live-region sentence (`announcement(strength)`). Pass only the ones your product words differently.",
31
+ "description": "Every visible and screen-reader string, each optional (gh#1191). Unset entries come from the kit's own ja / en / vi catalogue for the active locale, so most apps pass nothing: the score words, the neutral empty-field line (`empty`, gh#1247), the checklist line per rule (length / upper / lower / number / symbol), the sr-only passed / failed prefixes, and the polite live-region sentence (`announcement(strength)`). Pass only the ones your product words differently.",
32
32
  "name": "labels",
33
- "type": "{ weak?: string; fair?: string; strong?: string; rules?: Partial<Record<PasswordRule, string>>; passed?: string; failed?: string; announcement?: (strength: string) => string }"
33
+ "type": "{ weak?: string; fair?: string; strong?: string; empty?: string; rules?: Partial<Record<PasswordRule, string>>; passed?: string; failed?: string; announcement?: (strength: string) => string }"
34
34
  }
35
35
  ],
36
36
  "related": [
@@ -68,6 +68,12 @@
68
68
  "name": "disabled",
69
69
  "type": "boolean"
70
70
  },
71
+ {
72
+ "defaultValue": "false",
73
+ "description": "A global shortcut that FOCUSES the field (gh#1250). `false` (default) binds none — two search boxes on one page must not fight over one key; `true` is ⌘K on Apple platforms and Ctrl+K elsewhere (never both, so Ctrl+K keeps its editing meaning on a Mac); a combo string (`\"mod+/\"`, `\"mod+shift+f\"`) is read exactly as CommandPalette `shortcut`. A trailing aria-hidden key cap shows the combo while the field is empty — the clear ✕ takes the slot once there is text. Never fires during IME composition, nor while `disabled`.",
74
+ "name": "shortcut",
75
+ "type": "boolean | string"
76
+ },
71
77
  {
72
78
  "description": "Class on the `<input>` itself. SearchInput renders a WRAPPER (label, icon, clear button, input), so `className` lands on that wrapper and never reaches the field — this is the second handle, for the case where the field and its chrome need different treatment. Layout and colour still belong to tokens; use it for the rare geometry a token cannot reach.",
73
79
  "name": "inputClassName",
@@ -24,7 +24,7 @@
24
24
  "type": "number"
25
25
  },
26
26
  {
27
- "description": "Collapse extra selected labels.",
27
+ "description": "Collapse extra selected labels into a \"+N\" chip. Without it (mode=\"multiple\"/\"tags\"), the chips WRAP and the trigger grows to hold them, antd's default — a chip is never clipped out of sight. Set it when the trigger must stay one line (a dense rail or table cell).",
28
28
  "name": "maxTagCount",
29
29
  "type": "number"
30
30
  },
@@ -288,7 +288,7 @@
288
288
  "type": "React.ReactNode"
289
289
  },
290
290
  {
291
- "description": "antd `suffixIcon` — replaces the trailing chevron; `null` removes the indicator entirely (antd's own replacement for the deprecated showArrow). While a value is clearable the ✕ owns that seat, as in antd.",
291
+ "description": "antd `suffixIcon` — replaces the trailing chevron; `null` removes the indicator entirely (antd's own replacement for the deprecated showArrow) — and, with no clear and no loading, the trigger stops reserving the chevron's end padding, so a `width=\"auto\"` trigger ends at its value. While a value is clearable the ✕ owns that seat, as in antd.",
292
292
  "name": "suffixIcon",
293
293
  "type": "React.ReactNode"
294
294
  },
@@ -349,6 +349,12 @@
349
349
  "name": "maxTagTextLength",
350
350
  "type": "number"
351
351
  },
352
+ {
353
+ "defaultValue": "false",
354
+ "description": "multiple/tags — hold the panel's picks as a DRAFT until OK commits them (gh#1248; antd's `needConfirm`, the same word TimePicker/DatePicker use). Row toggles move a pending set and report nothing; the footer's OK (Button size sm) fires ONE onValueChange with the whole set and closes; its Clear empties the pending set; Escape or an outside click discards the draft and the trigger keeps the committed values. onSelect/onDeselect do not fire for draft toggles. A chip's ✕ and Backspace on the closed trigger still commit at once. Default false: every toggle commits.",
355
+ "name": "needConfirm",
356
+ "type": "boolean"
357
+ },
352
358
  {
353
359
  "description": "antd `tagRender` (multiple/tags) — render one chip yourself. Exactly the shape TagInput's tagRender takes. The onClose handed in is the same remover the built-in ✕ calls, so a custom chip cannot end up unremovable; supplying tagRender withdraws the built-in ✕.",
354
360
  "name": "tagRender",
@@ -1,5 +1,5 @@
1
1
  {
2
- "example": "// app root — mount once\nimport { Toaster } from \"@godxjp/ui/feedback\";\n<>{children}<Toaster richColors /></>\n\n// anywhere — import toast from \"sonner\"\nimport { toast } from \"sonner\";\ntoast.success(\"クーポンを公開しました\");\ntoast.error(\"保存に失敗しました\");",
2
+ "example": "// app root — mount once\nimport { Toaster } from \"@godxjp/ui/feedback\";\n<>{children}<Toaster richColors /></>\n\n// anywhere\nimport { toast } from \"@godxjp/ui/feedback\";\ntoast.success(\"クーポンを公開しました\"); // auto-dismiss after --toast-duration\ntoast.error(\"保存に失敗しました\"); // also carries a close button",
3
3
  "group": "feedback",
4
4
  "importPath": "@godxjp/ui/feedback",
5
5
  "name": "Toaster",
@@ -14,6 +14,16 @@
14
14
  "description": "Enable Sonner rich variant colours.",
15
15
  "name": "richColors",
16
16
  "type": "boolean"
17
+ },
18
+ {
19
+ "description": "Auto-dismiss time in ms for every toast. Omit it to let the `--toast-duration` token (default 4000ms) decide — read once on mount from the ThemeScope host or `<html>` (gh#1253). A per-call `toast(…, { duration })` still wins.",
20
+ "name": "duration",
21
+ "type": "number"
22
+ },
23
+ {
24
+ "description": "Sonner's switch for a close button on EVERY toast. Leave it unset for the default: `toast.error` / `toast.warning` from `@godxjp/ui/feedback` carry a close button, `toast.success` / `toast.info` stay auto-dismiss only (gh#1253). The button is named via `t(\"feedback.toast.close\")`.",
25
+ "name": "closeButton",
26
+ "type": "boolean"
17
27
  }
18
28
  ],
19
29
  "related": [
@@ -22,11 +32,12 @@
22
32
  "Dialog — use when the user must make a conscious decision (confirm delete, resolve conflict) before proceeding. Toaster toasts are fire-and-forget; Dialog blocks until the user responds."
23
33
  ],
24
34
  "rules": [],
25
- "tagline": "Mount once at app root to enable toasts. IMPORTANT: trigger toasts via `import { toast } from \"sonner\"` — NOT from @godxjp/ui.",
35
+ "tagline": "Mount once at app root to enable toasts. Trigger them with `import { toast } from \"@godxjp/ui/feedback\"` — Sonner's API, with a close button on `toast.error` / `toast.warning` by default.",
26
36
  "usage": [
27
37
  "DO: Mount exactly ONE `<Toaster richColors />` at the app root (e.g. inside your layout or AppShell children). Multiple mounts create duplicate toast stacks — there is no provider context, only DOM portals.",
28
- "DO: Import `toast` from `\"sonner\"` directly (not from `@godxjp/ui`) to fire toasts anywhere: `toast.success(…)`, `toast.error(…)`, `toast.warning(…)`, `toast.info(…)`, `toast.loading(…)`, `toast.promise(…)`.",
29
- "DON'T: Try to import a `toast` helper from `@godxjp/ui/feedback` — it does not exist. The component re-exports only the `Toaster` mount; the imperative API lives in the `sonner` package.",
38
+ "DO: Import `toast` from `\"@godxjp/ui/feedback\"` to fire toasts anywhere: `toast.success(…)`, `toast.error(…)`, `toast.warning(…)`, `toast.info(…)`, `toast.loading(…)`, `toast.promise(…)`. It is Sonner's own API with one house default: `toast.error` and `toast.warning` carry a close button (opt out per call with `{ closeButton: false }`), success/info stay auto-dismiss only (gh#1253).",
39
+ "DON'T: Import `toast` from `\"sonner\"` directly — it works, but it skips the close-button default on error/warning. Also note `toast.promise` settling into an error does not gain the button; pass `closeButton` there yourself if the failure must stay dismissible.",
40
+ "DO: Tune how long toasts stay with the `--toast-duration` token in the theme (default 4000ms) rather than a `duration` prop on every mount. Never truncate a toast's body — it wraps.",
30
41
  "DO: Let the wrapper handle theming — it uses `useDocumentTheme()` to sync with the document `dark` class and `prefers-color-scheme` automatically. Never pass a hardcoded `theme` prop unless you are deliberately overriding.",
31
42
  "DON'T: Use `Toaster` for persistent errors or blocking confirmations. Toasts auto-dismiss; they are not a substitute for `Alert` (inline persistent warnings) or `Dialog` (decisions requiring user input).",
32
43
  "DO: Pass `position` to relocate the stack if a persistent sidebar/footer would obscure the default `bottom-right`. The wrapper already sets a safe `mobileOffset`; don't add redundant mobile offsets unless your layout differs."
@@ -16,9 +16,9 @@
16
16
  },
17
17
  {
18
18
  "defaultValue": "\"default\"",
19
- "description": "Visual style. `default` is a 1px TRANSPARENT border (a toolbar affordance — no resting surface at all), `outline` a hairline on --background, and `soft` (gh#734) the REST FILL a chip needs: hsl(var(--secondary)), byte-identical to the fill `Badge variant=\"secondary\"` and `Button variant=\"secondary\"` already paint, so a chip, a badge and a button on one row are one family. `soft` is antd Tag's resting surface and is what makes a FILTER CHIP legible before it is pressed — with `default` or `outline` the chip reads as transparent, which is the reported defect. Hover is the opaque --secondary-hover (not a translucent tint, which would shift between a page and a Card); pressed still wins over both.",
19
+ "description": "Visual style. `default` is a 1px TRANSPARENT border (a toolbar affordance — no resting surface at all), `outline` a hairline on --background, and `soft` (gh#734) the REST FILL a chip needs: hsl(var(--secondary)), byte-identical to the fill `Badge variant=\"secondary\"` and `Button variant=\"secondary\"` already paint, so a chip, a badge and a button on one row are one family. `soft` is antd Tag's resting surface and is what makes a FILTER CHIP legible before it is pressed — with `default` or `outline` the chip reads as transparent, which is the reported defect. Hover is the opaque --secondary-hover (not a translucent tint, which would shift between a page and a Card); pressed still wins over both. `quiet` (gh#1257) is the LOW-KEY pressed state for a mark beside content (a comment star, a reaction): the transparent rest of `default`, and pressed as a --secondary fill with --foreground text and the primary hairline, instead of a solid primary block; knobs `--toggle-quiet-on-{background,color,count-background,count-color}`.",
20
20
  "name": "variant",
21
- "type": "\"default\" | \"outline\" | \"soft\""
21
+ "type": "\"default\" | \"outline\" | \"soft\" | \"quiet\""
22
22
  },
23
23
  {
24
24
  "defaultValue": "\"md\"",
@@ -68,6 +68,7 @@
68
68
  "storyPath": "data-entry/toggle.tsx",
69
69
  "tagline": "React Aria toggle button with default/outline/soft variants and tokenized sizes.",
70
70
  "usage": [
71
+ "DO use `variant=\"quiet\"` for a pressed state that should stay LOW-KEY — a comment star, a like/reaction, a pin beside a row. The solid primary fill is for a selected filter or mode; on a mark beside content it reads as the loudest thing on the screen. No app CSS needed (gh#1257).",
71
72
  "DO provide an accessible label when the toggle only contains an icon.",
72
73
  "DON'T use Toggle for multi-option selection; use ToggleGroup.",
73
74
  "DO reach for `count` for a faceted filter chip, a counted segmented toggle or a reaction chip — Toggle is the primitive that owns BOTH the count and the pressed state.",
@@ -29,7 +29,7 @@
29
29
  "defaultValue": "\"default\"",
30
30
  "description": "Visual style, PROVIDED TO EVERY ITEM via context — set it once on the group, not on each ToggleGroupItem. An explicit `variant` on an item still wins. The default is applied per item by toggleVariants, so an unset group emits no `data-variant` at all. `soft` (gh#734) is the chip fill: use it for a tag-filter panel, where `default`/`outline` leave the unselected chips reading as transparent.",
31
31
  "name": "variant",
32
- "type": "\"default\" | \"outline\" | \"soft\""
32
+ "type": "\"default\" | \"outline\" | \"soft\" | \"quiet\""
33
33
  },
34
34
  {
35
35
  "defaultValue": "\"default\"",
@@ -453,7 +453,7 @@
453
453
  {
454
454
  "group": "feedback",
455
455
  "name": "Toaster",
456
- "tagline": "Mount once at app root to enable toasts. IMPORTANT: trigger toasts via `import { toast } from \"sonner\"` — NOT from @godxjp/ui."
456
+ "tagline": "Mount once at app root to enable toasts. Trigger them with `import { toast } from \"@godxjp/ui/feedback\"` — Sonner's API, with a close button on `toast.error` / `toast.warning` by default."
457
457
  },
458
458
  {
459
459
  "group": "navigation",
@@ -4353,7 +4353,8 @@
4353
4353
  "DO put the row's action in `trailing` (a `ghost`/`outline` Button, a DropdownMenu trigger, a Switch, or a status Badge). DO pass `as=\"li\"` when the rows live inside a semantic list, and build that list as `<Flex as=\"ul\" marker=\"none\">` — `ui-audit` flags a raw `<ul>`/`role=\"list\"` as `no-hand-rolled-list`.",
4354
4354
  "DO use `unread` for a notification list — the dot is a SHAPE with localized `sr-only` text (\"Unread\"/\"Read\"), so it never reads as colour alone, and the row surface reads `--list-row-unread-background` (default `hsl(var(--muted))` — chosen so the xs muted description line stays WCAG AA on the emphasized surface; `--accent` would drop it to 4.23:1). DON'T substitute a `Badge` — that renders a labelled pill, not a compact status dot.",
4355
4355
  "DO pass `density=\"compact\"` for the canonical invitation / history row — an Avatar, a title (+ description) and one or two small trailing Buttons that must read as ONE line inside a narrow card (≈326px content at 390px), or a history row whose status Badge + ISO-8601 date belongs beside the title. Measured at 390px: 62px tall vs 126px at the default density (where the actions wrapped), history row 41px vs 114px. DON'T reach for it just to \"make things tighter\" on a roomy page — the default density is the entity-row measure.",
4356
- "DON'T add one-off `min-width`/wrapping CSS in the consumer app for a long title + two trailing Buttons. The row already shrinks and WRAPS: the content column keeps only `min(var(--list-row-body-min-width), 100%)` and the trailing actions drop onto their own line below the threshold. Retune the threshold with `--list-row-body-min-width` (default 12rem) and the action gap with `--list-row-trailing-gap`; pass `overflow=\"wrap\"` (usually with `align=\"start\"`) when the title must stay fully readable at 390px instead of truncating."
4356
+ "DON'T add one-off `min-width`/wrapping CSS in the consumer app for a long title + two trailing Buttons. The row already shrinks and WRAPS: the content column keeps only `min(var(--list-row-body-min-width), 100%)` and the trailing actions drop onto their own line below the threshold. Retune the threshold with `--list-row-body-min-width` (default 12rem) and the action gap with `--list-row-trailing-gap`; pass `overflow=\"wrap\"` (usually with `align=\"start\"`) when the title must stay fully readable at 390px instead of truncating.",
4357
+ "DO expect rows placed DIRECTLY in a padded (non-flush) `CardContent` — or in a `Flex as=\"ul\"` there — to drop their own inline padding: the card already holds the inset, so the row text lines up with the CardTitle instead of sitting 16px inside it. Inside `CardContent flush` (the documented home, with edge-to-edge dividers) the row keeps `--list-row-padding-x`."
4357
4358
  ],
4358
4359
  "useCases": [
4359
4360
  "Account security page — a list of active sessions (device + last-seen as title/description, a destructive 'Revoke' Button in trailing).",
@@ -6242,6 +6243,12 @@
6242
6243
  "name": "disabled",
6243
6244
  "type": "boolean"
6244
6245
  },
6246
+ {
6247
+ "defaultValue": "false",
6248
+ "description": "A global shortcut that FOCUSES the field (gh#1250). `false` (default) binds none — two search boxes on one page must not fight over one key; `true` is ⌘K on Apple platforms and Ctrl+K elsewhere (never both, so Ctrl+K keeps its editing meaning on a Mac); a combo string (`\"mod+/\"`, `\"mod+shift+f\"`) is read exactly as CommandPalette `shortcut`. A trailing aria-hidden key cap shows the combo while the field is empty — the clear ✕ takes the slot once there is text. Never fires during IME composition, nor while `disabled`.",
6249
+ "name": "shortcut",
6250
+ "type": "boolean | string"
6251
+ },
6245
6252
  {
6246
6253
  "description": "Class on the `<input>` itself. SearchInput renders a WRAPPER (label, icon, clear button, input), so `className` lands on that wrapper and never reaches the field — this is the second handle, for the case where the field and its chrome need different treatment. Layout and colour still belong to tokens; use it for the rare geometry a token cannot reach.",
6247
6254
  "name": "inputClassName",
@@ -6425,7 +6432,7 @@
6425
6432
  "type": "number"
6426
6433
  },
6427
6434
  {
6428
- "description": "Collapse extra selected labels.",
6435
+ "description": "Collapse extra selected labels into a \"+N\" chip. Without it (mode=\"multiple\"/\"tags\"), the chips WRAP and the trigger grows to hold them, antd's default — a chip is never clipped out of sight. Set it when the trigger must stay one line (a dense rail or table cell).",
6429
6436
  "name": "maxTagCount",
6430
6437
  "type": "number"
6431
6438
  },
@@ -6689,7 +6696,7 @@
6689
6696
  "type": "React.ReactNode"
6690
6697
  },
6691
6698
  {
6692
- "description": "antd `suffixIcon` — replaces the trailing chevron; `null` removes the indicator entirely (antd's own replacement for the deprecated showArrow). While a value is clearable the ✕ owns that seat, as in antd.",
6699
+ "description": "antd `suffixIcon` — replaces the trailing chevron; `null` removes the indicator entirely (antd's own replacement for the deprecated showArrow) — and, with no clear and no loading, the trigger stops reserving the chevron's end padding, so a `width=\"auto\"` trigger ends at its value. While a value is clearable the ✕ owns that seat, as in antd.",
6693
6700
  "name": "suffixIcon",
6694
6701
  "type": "React.ReactNode"
6695
6702
  },
@@ -6750,6 +6757,12 @@
6750
6757
  "name": "maxTagTextLength",
6751
6758
  "type": "number"
6752
6759
  },
6760
+ {
6761
+ "defaultValue": "false",
6762
+ "description": "multiple/tags — hold the panel's picks as a DRAFT until OK commits them (gh#1248; antd's `needConfirm`, the same word TimePicker/DatePicker use). Row toggles move a pending set and report nothing; the footer's OK (Button size sm) fires ONE onValueChange with the whole set and closes; its Clear empties the pending set; Escape or an outside click discards the draft and the trigger keeps the committed values. onSelect/onDeselect do not fire for draft toggles. A chip's ✕ and Backspace on the closed trigger still commit at once. Default false: every toggle commits.",
6763
+ "name": "needConfirm",
6764
+ "type": "boolean"
6765
+ },
6753
6766
  {
6754
6767
  "description": "antd `tagRender` (multiple/tags) — render one chip yourself. Exactly the shape TagInput's tagRender takes. The onClose handed in is the same remover the built-in ✕ calls, so a custom chip cannot end up unremovable; supplying tagRender withdraws the built-in ✕.",
6755
6768
  "name": "tagRender",
@@ -7139,7 +7152,7 @@
7139
7152
  "DO use the controlled pattern (`checked` + `onCheckedChange`) for any form-bound checkbox. `onCheckedChange` receives `boolean | 'indeterminate'` — always coerce with `!!v` or an explicit guard before storing in state.",
7140
7153
  "DO use `Checkbox.Group` (alias for CheckboxGroup) with the `options` prop when you have ≥2 choices from an array — it renders each item inside a `Field` (label + optional description), generates stable ids automatically, and manages the `string[]` value array. NEVER hand-roll a loop of bare `<Checkbox>` elements for a multi-select list.",
7141
7154
  "DO pass `name` on `Checkbox.Group` (not on individual checkboxes) when the group must submit as form fields — the group propagates the name to each internal checkbox so the browser serialises all checked values under that key.",
7142
- "DON'T use `checked='indeterminate'` on `Checkbox.Group` children — indeterminate is only meaningful on a parent 'select-all' control you wire manually; the group itself does not auto-compute it.",
7155
+ "DON'T use `checked='indeterminate'` on `Checkbox.Group` children, and DON'T hand-wire a select-all box above a group — pass `selectAll` (or `selectAll={{ label: '…' }}`) on `Checkbox.Group` with `options`. It renders a leading box derived from the value (checked / `mixed` / clear), a muted `{n} of {m} selected` count (`common.selectedOfTotal`, numbers via Intl.NumberFormat) and a Separator; one click is ONE `onValueChange` with every enabled value; disabled options are never counted, added or removed; the box has no `name`, so a native submit never carries it.",
7143
7156
  "DON'T wrap a standalone Checkbox in `Field` manually — `Field` is the internal composition primitive that `Checkbox.Group` uses. For a single boolean with a label, pass the label as children (`<Checkbox …>label</Checkbox>`, the catalog example) — it renders the same row. Use `<Field id='x' label='…' description='…'><Checkbox id='x' … /></Field>` only when the row needs a description line — Field owns the label-to-control id wiring, the description slot and the row rhythm, and a hand-rolled flex row owns none of them; for a full labelled-checkbox with description, use `Field` directly only if you need a one-off item outside a group."
7144
7157
  ],
7145
7158
  "useCases": [
@@ -7376,6 +7389,12 @@
7376
7389
  "name": "allowEmpty",
7377
7390
  "type": "[boolean, boolean]"
7378
7391
  },
7392
+ {
7393
+ "defaultValue": "true",
7394
+ "description": "With `range`: a polite live summary (`aria-live=\"polite\"`, always mounted) under the DAY grid — the start date alone while only the start is held, then `Intl.DateTimeFormat#formatRange` of both edges plus the INCLUSIVE day count through `Intl.PluralRules` (1 → 7 reads \"Mar 1 – 7, 2026 · 7 days\"; ja 「7日間」). Muted `Text size=\"xs\" tabular`, no new token. The month / quarter / year period grids ignore it. Independently of this flag, while only the start is chosen the `to` edge's placeholder becomes \"Select end date\" (`dataEntry.dateRangePicker.selectEnd`). `false` removes the summary.",
7395
+ "name": "showSummary",
7396
+ "type": "boolean"
7397
+ },
7379
7398
  {
7380
7399
  "defaultValue": "true",
7381
7400
  "description": "Normalise the selection into ascending order (antd `order`). ONE rule seen through two value shapes: a `range` picked backwards is SWAPPED, a `multiple` selection is SORTED. Pass false to keep pick order.",
@@ -7886,7 +7905,7 @@
7886
7905
  ]
7887
7906
  },
7888
7907
  {
7889
- "example": "// app root — mount once\nimport { Toaster } from \"@godxjp/ui/feedback\";\n<>{children}<Toaster richColors /></>\n\n// anywhere — import toast from \"sonner\"\nimport { toast } from \"sonner\";\ntoast.success(\"クーポンを公開しました\");\ntoast.error(\"保存に失敗しました\");",
7908
+ "example": "// app root — mount once\nimport { Toaster } from \"@godxjp/ui/feedback\";\n<>{children}<Toaster richColors /></>\n\n// anywhere\nimport { toast } from \"@godxjp/ui/feedback\";\ntoast.success(\"クーポンを公開しました\"); // auto-dismiss after --toast-duration\ntoast.error(\"保存に失敗しました\"); // also carries a close button",
7890
7909
  "group": "feedback",
7891
7910
  "importPath": "@godxjp/ui/feedback",
7892
7911
  "name": "Toaster",
@@ -7901,6 +7920,16 @@
7901
7920
  "description": "Enable Sonner rich variant colours.",
7902
7921
  "name": "richColors",
7903
7922
  "type": "boolean"
7923
+ },
7924
+ {
7925
+ "description": "Auto-dismiss time in ms for every toast. Omit it to let the `--toast-duration` token (default 4000ms) decide — read once on mount from the ThemeScope host or `<html>` (gh#1253). A per-call `toast(…, { duration })` still wins.",
7926
+ "name": "duration",
7927
+ "type": "number"
7928
+ },
7929
+ {
7930
+ "description": "Sonner's switch for a close button on EVERY toast. Leave it unset for the default: `toast.error` / `toast.warning` from `@godxjp/ui/feedback` carry a close button, `toast.success` / `toast.info` stay auto-dismiss only (gh#1253). The button is named via `t(\"feedback.toast.close\")`.",
7931
+ "name": "closeButton",
7932
+ "type": "boolean"
7904
7933
  }
7905
7934
  ],
7906
7935
  "related": [
@@ -7909,11 +7938,12 @@
7909
7938
  "Dialog — use when the user must make a conscious decision (confirm delete, resolve conflict) before proceeding. Toaster toasts are fire-and-forget; Dialog blocks until the user responds."
7910
7939
  ],
7911
7940
  "rules": [],
7912
- "tagline": "Mount once at app root to enable toasts. IMPORTANT: trigger toasts via `import { toast } from \"sonner\"` — NOT from @godxjp/ui.",
7941
+ "tagline": "Mount once at app root to enable toasts. Trigger them with `import { toast } from \"@godxjp/ui/feedback\"` — Sonner's API, with a close button on `toast.error` / `toast.warning` by default.",
7913
7942
  "usage": [
7914
7943
  "DO: Mount exactly ONE `<Toaster richColors />` at the app root (e.g. inside your layout or AppShell children). Multiple mounts create duplicate toast stacks — there is no provider context, only DOM portals.",
7915
- "DO: Import `toast` from `\"sonner\"` directly (not from `@godxjp/ui`) to fire toasts anywhere: `toast.success(…)`, `toast.error(…)`, `toast.warning(…)`, `toast.info(…)`, `toast.loading(…)`, `toast.promise(…)`.",
7916
- "DON'T: Try to import a `toast` helper from `@godxjp/ui/feedback` — it does not exist. The component re-exports only the `Toaster` mount; the imperative API lives in the `sonner` package.",
7944
+ "DO: Import `toast` from `\"@godxjp/ui/feedback\"` to fire toasts anywhere: `toast.success(…)`, `toast.error(…)`, `toast.warning(…)`, `toast.info(…)`, `toast.loading(…)`, `toast.promise(…)`. It is Sonner's own API with one house default: `toast.error` and `toast.warning` carry a close button (opt out per call with `{ closeButton: false }`), success/info stay auto-dismiss only (gh#1253).",
7945
+ "DON'T: Import `toast` from `\"sonner\"` directly — it works, but it skips the close-button default on error/warning. Also note `toast.promise` settling into an error does not gain the button; pass `closeButton` there yourself if the failure must stay dismissible.",
7946
+ "DO: Tune how long toasts stay with the `--toast-duration` token in the theme (default 4000ms) rather than a `duration` prop on every mount. Never truncate a toast's body — it wraps.",
7917
7947
  "DO: Let the wrapper handle theming — it uses `useDocumentTheme()` to sync with the document `dark` class and `prefers-color-scheme` automatically. Never pass a hardcoded `theme` prop unless you are deliberately overriding.",
7918
7948
  "DON'T: Use `Toaster` for persistent errors or blocking confirmations. Toasts auto-dismiss; they are not a substitute for `Alert` (inline persistent warnings) or `Dialog` (decisions requiring user input).",
7919
7949
  "DO: Pass `position` to relocate the stack if a persistent sidebar/footer would obscure the default `bottom-right`. The wrapper already sets a safe `mobileOffset`; don't add redundant mobile offsets unless your layout differs."
@@ -8284,6 +8314,11 @@
8284
8314
  "description": "On DropdownMenuContent. Ant Design `arrow` — paints the pointer at the anchored edge (default off). Sized from --dropdown-arrow-{width,height,background} rather than Radix's built-in 10×5.",
8285
8315
  "name": "arrow",
8286
8316
  "type": "boolean"
8317
+ },
8318
+ {
8319
+ "description": "On DropdownMenuItem (gh#1246). A muted second line under the label — the two-line row, parity with `MegaMenuLinkProp.description`. Rendered as react-aria `<Text slot=\"description\">` (`data-slot=\"dropdown-menu-item-description\"`), so the row's accessible NAME stays the label and the description becomes its `aria-describedby`. A string label is handed to the collection as `textValue`, so typeahead still reaches the row by its label; pass `textValue` yourself when the label is not a plain string. Sized by --menu-item-description-{font-size,space-block}; rows without one keep the one-line height.",
8320
+ "name": "description",
8321
+ "type": "React.ReactNode"
8287
8322
  }
8288
8323
  ],
8289
8324
  "related": [
@@ -10166,7 +10201,7 @@
10166
10201
  },
10167
10202
  {
10168
10203
  "defaultValue": "\"auto\"",
10169
- "description": "How the grid claims horizontal space. `auto` (default) shrink-wraps to seven fixed day columns — the shape a picker popover needs, because the panel is shrink-to-fit and takes ITS width from the calendar inside it. Use `full` for an EMBEDDED calendar (a shift board, a booking month) that is the content of a Card rather than a dropdown: it stacks the months and lets the day cells share the row. Measured at a 1200px container: auto → root 248px / cells 32px; full → root 1200px / cells 168px; the DatePicker popover stays 250px either way. Do NOT reach for `className=\"w-full\"` instead — it widens the root and leaves the grid at 224px pinned left.",
10204
+ "description": "How the grid claims horizontal space. `auto` (default) shrink-wraps to seven fixed day columns — the shape a picker popover needs, because the panel is shrink-to-fit and takes ITS width from the calendar inside it. Use `full` for an EMBEDDED calendar (a shift board, a booking month) that is the content of a Card rather than a dropdown: it stacks the months and lets the day cells share the row, each reading from the start edge with an inset (`--calendar-full-day-padding-inline` / `-block`, default --space-2 / --space-1, so the numeral clears the `text-edge-inset` audit; gh#1258). Measured at a 1200px container: auto → root 248px / cells 32px; full → root 1200px / cells 168px; the DatePicker popover stays 250px either way. Do NOT reach for `className=\"w-full\"` instead — it widens the root and leaves the grid at 224px pinned left.",
10170
10205
  "name": "width",
10171
10206
  "type": "\"auto\" | \"full\""
10172
10207
  },
@@ -10631,6 +10666,11 @@
10631
10666
  "description": "Fires with the checked values array.",
10632
10667
  "name": "onValueChange",
10633
10668
  "type": "(value: string[]) => void"
10669
+ },
10670
+ {
10671
+ "description": "`options` mode only. Renders a leading select-all box whose state is derived from the value — checked when every enabled option is selected, `mixed` (indeterminate) on a partial selection, clear otherwise — followed by a muted `{n} of {m} selected` count (`common.selectedOfTotal`, numbers via Intl.NumberFormat) and a Separator. One click is ONE `onValueChange`: from mixed or clear it adds every enabled value, from checked it removes them. Disabled options are never counted, added or removed. The box has no `name`, so a native submit never carries it. `{ label }` replaces the default `dataEntry.checkboxGroup.selectAll` text.",
10672
+ "name": "selectAll",
10673
+ "type": "boolean | { label?: React.ReactNode }"
10634
10674
  }
10635
10675
  ],
10636
10676
  "related": [
@@ -10648,6 +10688,7 @@
10648
10688
  ],
10649
10689
  "tagline": "Multi-select checkbox list from an options array or manual children — use `options` prop for the data-driven path, never hand-roll individual Checkbox items in a group.",
10650
10690
  "usage": [
10691
+ "DO pass `selectAll` instead of hand-wiring a parent checkbox above the group — the derived tri-state, the n-of-m count and the disabled-option rule are built in: `<CheckboxGroup options={channels} defaultValue={['email']} selectAll />`.",
10651
10692
  "DO use the `options` prop for any data-driven list — it auto-generates IDs, handles checked state, and wires up Field (label + description) for each item. NEVER hand-roll individual `<Checkbox>` elements inside a loop when you have an options array.",
10652
10693
  "DO pass `name` when inside an HTML form so each checkbox submits its value under the same field name, giving the server a multi-value array. Without `name`, native form submission silently drops all values.",
10653
10694
  "Controlled vs uncontrolled: pass `value` + `onChange` together for controlled usage (e.g. react-hook-form). Pass `defaultValue` alone for uncontrolled usage. Do NOT mix both — if `value` is provided, `defaultValue` is ignored and onChange must update value externally or the UI freezes.",
@@ -11730,9 +11771,9 @@
11730
11771
  },
11731
11772
  {
11732
11773
  "defaultValue": "\"default\"",
11733
- "description": "Visual style. `default` is a 1px TRANSPARENT border (a toolbar affordance — no resting surface at all), `outline` a hairline on --background, and `soft` (gh#734) the REST FILL a chip needs: hsl(var(--secondary)), byte-identical to the fill `Badge variant=\"secondary\"` and `Button variant=\"secondary\"` already paint, so a chip, a badge and a button on one row are one family. `soft` is antd Tag's resting surface and is what makes a FILTER CHIP legible before it is pressed — with `default` or `outline` the chip reads as transparent, which is the reported defect. Hover is the opaque --secondary-hover (not a translucent tint, which would shift between a page and a Card); pressed still wins over both.",
11774
+ "description": "Visual style. `default` is a 1px TRANSPARENT border (a toolbar affordance — no resting surface at all), `outline` a hairline on --background, and `soft` (gh#734) the REST FILL a chip needs: hsl(var(--secondary)), byte-identical to the fill `Badge variant=\"secondary\"` and `Button variant=\"secondary\"` already paint, so a chip, a badge and a button on one row are one family. `soft` is antd Tag's resting surface and is what makes a FILTER CHIP legible before it is pressed — with `default` or `outline` the chip reads as transparent, which is the reported defect. Hover is the opaque --secondary-hover (not a translucent tint, which would shift between a page and a Card); pressed still wins over both. `quiet` (gh#1257) is the LOW-KEY pressed state for a mark beside content (a comment star, a reaction): the transparent rest of `default`, and pressed as a --secondary fill with --foreground text and the primary hairline, instead of a solid primary block; knobs `--toggle-quiet-on-{background,color,count-background,count-color}`.",
11734
11775
  "name": "variant",
11735
- "type": "\"default\" | \"outline\" | \"soft\""
11776
+ "type": "\"default\" | \"outline\" | \"soft\" | \"quiet\""
11736
11777
  },
11737
11778
  {
11738
11779
  "defaultValue": "\"md\"",
@@ -11782,6 +11823,7 @@
11782
11823
  "storyPath": "data-entry/toggle.tsx",
11783
11824
  "tagline": "React Aria toggle button with default/outline/soft variants and tokenized sizes.",
11784
11825
  "usage": [
11826
+ "DO use `variant=\"quiet\"` for a pressed state that should stay LOW-KEY — a comment star, a like/reaction, a pin beside a row. The solid primary fill is for a selected filter or mode; on a mark beside content it reads as the loudest thing on the screen. No app CSS needed (gh#1257).",
11785
11827
  "DO provide an accessible label when the toggle only contains an icon.",
11786
11828
  "DON'T use Toggle for multi-option selection; use ToggleGroup.",
11787
11829
  "DO reach for `count` for a faceted filter chip, a counted segmented toggle or a reaction chip — Toggle is the primitive that owns BOTH the count and the pressed state.",
@@ -11833,7 +11875,7 @@
11833
11875
  "defaultValue": "\"default\"",
11834
11876
  "description": "Visual style, PROVIDED TO EVERY ITEM via context — set it once on the group, not on each ToggleGroupItem. An explicit `variant` on an item still wins. The default is applied per item by toggleVariants, so an unset group emits no `data-variant` at all. `soft` (gh#734) is the chip fill: use it for a tag-filter panel, where `default`/`outline` leave the unselected chips reading as transparent.",
11835
11877
  "name": "variant",
11836
- "type": "\"default\" | \"outline\" | \"soft\""
11878
+ "type": "\"default\" | \"outline\" | \"soft\" | \"quiet\""
11837
11879
  },
11838
11880
  {
11839
11881
  "defaultValue": "\"default\"",
@@ -12088,7 +12130,7 @@
12088
12130
  "name": "PasswordStrength",
12089
12131
  "props": [
12090
12132
  {
12091
- "description": "The current password value to evaluate.",
12133
+ "description": "The current password value to evaluate. While it is empty (or whitespace only) the meter is NEUTRAL (gh#1247): no segment lit, no tone, the label reads `labels.empty` (\"Enter a password\" / 「パスワードを入力」), every checklist item is `data-state=\"pending\"`, and the live region stays silent — no \"Weak\" verdict before anything is typed.",
12092
12134
  "name": "value",
12093
12135
  "required": true,
12094
12136
  "type": "string"
@@ -12111,9 +12153,9 @@
12111
12153
  "type": "number"
12112
12154
  },
12113
12155
  {
12114
- "description": "Every visible and screen-reader string, each optional (gh#1191). Unset entries come from the kit's own ja / en / vi catalogue for the active locale, so most apps pass nothing: the score words, the checklist line per rule (length / upper / lower / number / symbol), the sr-only passed / failed prefixes, and the polite live-region sentence (`announcement(strength)`). Pass only the ones your product words differently.",
12156
+ "description": "Every visible and screen-reader string, each optional (gh#1191). Unset entries come from the kit's own ja / en / vi catalogue for the active locale, so most apps pass nothing: the score words, the neutral empty-field line (`empty`, gh#1247), the checklist line per rule (length / upper / lower / number / symbol), the sr-only passed / failed prefixes, and the polite live-region sentence (`announcement(strength)`). Pass only the ones your product words differently.",
12115
12157
  "name": "labels",
12116
- "type": "{ weak?: string; fair?: string; strong?: string; rules?: Partial<Record<PasswordRule, string>>; passed?: string; failed?: string; announcement?: (strength: string) => string }"
12158
+ "type": "{ weak?: string; fair?: string; strong?: string; empty?: string; rules?: Partial<Record<PasswordRule, string>>; passed?: string; failed?: string; announcement?: (strength: string) => string }"
12117
12159
  }
12118
12160
  ],
12119
12161
  "related": [
@@ -13398,6 +13440,11 @@
13398
13440
  "description": "Consumer-supplied error content.",
13399
13441
  "name": "error",
13400
13442
  "type": "ReactNode"
13443
+ },
13444
+ {
13445
+ "description": "Recent queries (gh#1249), rendered as the FIRST group only while the query is empty, followed by a trailing \"Clear recent searches\" row. Choosing a query calls `onSelect(query)` and closes; the clear row calls `onClear()` and keeps the palette open. The library never reads or writes `localStorage` — the consumer stores, de-duplicates and caps `items`; duplicates are rendered as given. `label` replaces the default heading (`dataEntry.commandPalette.recent`).",
13446
+ "name": "recent",
13447
+ "type": "{ items: string[]; onSelect: (query: string) => void; onClear: () => void; label?: ReactNode }"
13401
13448
  }
13402
13449
  ],
13403
13450
  "rules": [],
@@ -13405,6 +13452,7 @@
13405
13452
  "tagline": "Searchable command dialog with controlled or uncontrolled open state and consumer-owned selection.",
13406
13453
  "usage": [
13407
13454
  "Provide localized labels and real command groups; the component does not fetch commands.",
13455
+ "Recent searches: keep the list in your own store (state, URL, server, localStorage), de-duplicate it, and pass it as `recent` — the palette only renders it while the query is empty.",
13408
13456
  "Use either `open` plus `onOpenChange` or `defaultOpen`; do not mirror both state models.",
13409
13457
  "Search-as-you-type against an API: read the query from `onSearchChange`, pass the results back as `groups`, and set `shouldFilter={false}` so the rows are not scored a second time against the same string.",
13410
13458
  "The empty-state contract: with `shouldFilter` (default) cmdk decides — items exist, the query matches none. With `shouldFilter={false}` the PALETTE decides from props — `labels.empty` renders when `groups` carries no items, and never while `loading` or `error` is set. Hold `loading` for the whole in-flight window: a request that has not answered yet is not an empty result, and asserting over cmdk's own empty node races an async group that populates a frame late."