@godxjp/ui 33.18.0 → 33.20.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/README.md CHANGED
@@ -18,7 +18,7 @@ npm i @godxjp/ui
18
18
 
19
19
  <!-- agent-catalog-counts:start -->
20
20
 
21
- The catalog — 171 components, 2263 tokens, 44 cardinal rules — is published in two forms from one
21
+ The catalog — 171 components, 2266 tokens, 44 cardinal rules — is published in two forms from one
22
22
  source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
23
23
  <!-- agent-catalog-counts:end -->
24
24
 
@@ -56,7 +56,7 @@ static files straight from this public repo — no hosting, no deploy step:
56
56
  | [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 54 KB | all 171 as name + group + tagline — **fetch this first** |
57
57
  | [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–38 KB | one file per component, each with its `importPath` — **this is the route to take** |
58
58
  | [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.3 MB | all 171 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
59
- | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 862 KB | all 2263 design tokens, their values and why they exist |
59
+ | [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 863 KB | all 2266 design tokens, their values and why they exist |
60
60
  | [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
61
61
  | [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 23 KB | the 44 cardinal rules |
62
62
  <!-- prettier-ignore-end -->
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 33.18.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 33.20.0.** If the project you are editing has a different
7
7
  version in its `package.json`, read the pinned catalog for THAT version instead
8
8
  (`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
9
9
  not exist yet; older, and it hides props that do. Neither failure announces itself.
@@ -61,10 +61,10 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
61
61
  its `importPath`, and its examples. Fetch only the handful you picked in step 1.
62
62
  3. `rules.json` — 44 cardinal rules. The ones about raw HTML and hardcoded colour are not
63
63
  style advice.
64
- 4. `tokens.json` — 2263 design tokens, each tagged with its `tier`. **If you were handed a
64
+ 4. `tokens.json` — 2266 design tokens, each tagged with its `tier`. **If you were handed a
65
65
  brand, read the 220 `foundation` entries first** — `--primary`, `--background`,
66
66
  `--radius`, `--font-size-base` are the handful everything else derives from. The
67
- 1930 `component` entries are per-part knobs; reach for one only when a role is
67
+ 1933 `component` entries are per-part knobs; reach for one only when a role is
68
68
  right everywhere except one component.
69
69
  5. `anti-ai-tells.json` — 35 shapes that make generated UI look generated, each with the
70
70
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
@@ -150,7 +150,7 @@ has stopped following the brand.
150
150
  |---|---|---|---|
151
151
  | `foundation` | 220 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** The default `--primary` is a neutral placeholder: set it (`:root { --primary: <H> <S>% <L>%; }`, HSL components, no `hsl()` wrapper) or import a theme preset such as `@godxjp/ui/themes/godx.css` |
152
152
  | `semantic` | 113 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
153
- | `component` | 1930 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
153
+ | `component` | 1933 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
154
154
 
155
155
  A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
156
156
  value, so the real default is computed where the element paints it. Set it and yours wins.
@@ -22,6 +22,12 @@
22
22
  "name": "layout",
23
23
  "type": "\"vertical\" | \"horizontal\""
24
24
  },
25
+ {
26
+ "defaultValue": "\"auto\"",
27
+ "description": "Horizontal labels only. Omitted/auto retains --descriptions-label-width (8rem), even with columns=1. content (also max-content, min-content or fit-content) uses a shared intrinsic grid track: longest label per visual column, capped by --descriptions-label-max-width (40%) so long labels wrap without clipping at 320px. Use columns=1 for one aligned label column across the entire list. WidthProp lengths and expressions override the width per list; numbers are px. Vertical layout ignores this prop. No consumer CSS override required.",
28
+ "name": "labelWidth",
29
+ "type": "WidthProp | \"content\""
30
+ },
25
31
  {
26
32
  "defaultValue": "\"start\"",
27
33
  "description": "Applies only in layout=\"horizontal\" — a vertical label sits above its value and end-aligning it there would read as a mistake, the same contract `Form` keeps.",
@@ -95,7 +95,7 @@
95
95
  {
96
96
  "description": "A FIXED column: number = px, string = any CSS length. Comes with flex: none — a width a sibling can still squeeze is a suggestion, and stacked rows would start at different x offsets. Leaves data-width-raw on the DOM so the measurement stays countable, like gapRaw/padRaw.",
97
97
  "name": "width",
98
- "type": "number | string"
98
+ "type": "WidthProp"
99
99
  },
100
100
  {
101
101
  "description": "CENTRE this box and CAP it at a page measure (--page-measure-narrow 42rem / -medium 48rem / -wide 72rem): margin-inline:auto + inline-size:100% + max-inline-size. THE public answer to \"full-bleed outside, measured column inside\" — a marketing <section> paints edge to edge and its inner Flex measure=\"wide\" holds the content on the page measure. Deliberately NOT on PageContainer: that owns page padding and a header/toolbar/footer scaffold, so a full-bleed page cannot consume it, and the refusal left the centred column owned by nobody — three showcases each hand-wrote the same four declarations (gh#839). It adds NO gutter: the page inset is pad={{ inline: 6 }}, and one band with two padding owners drifts. A MAX, so nothing binds below the cap at 390px. An explicit `width` wins. Do not give a READING column \"wide\" — prose stays narrow/medium, which bracket Bringhurst's 45-75 characters. Omitted emits no attribute at all.",
@@ -13,12 +13,12 @@
13
13
  {
14
14
  "description": "Label column width in horizontal layout (number→px). e.g. 120 or '8rem'.",
15
15
  "name": "labelWidth",
16
- "type": "number | string"
16
+ "type": "WidthProp"
17
17
  },
18
18
  {
19
- "description": "Cap the control width (number→px). Omit to fill the column. Caps the control only — the helper and error still span the field column.",
19
+ "description": "Cap the control width: number→px, or a CSS length ('20rem', '50%', 'calc(…)'). Omit to fill the column. Caps the control only — the helper and error still span the field column. A size word ('md', 'lg') is a type error, not a width (gh#1431).",
20
20
  "name": "controlWidth",
21
- "type": "number | string"
21
+ "type": "WidthProp"
22
22
  },
23
23
  {
24
24
  "defaultValue": "\"end\"",
@@ -61,12 +61,12 @@
61
61
  {
62
62
  "description": "Override the Form's label width for this field.",
63
63
  "name": "labelWidth",
64
- "type": "number | string"
64
+ "type": "WidthProp"
65
65
  },
66
66
  {
67
67
  "description": "Override the Form's control width for this field. Caps the control only — the helper and error still span the field column.",
68
68
  "name": "controlWidth",
69
- "type": "number | string"
69
+ "type": "WidthProp"
70
70
  },
71
71
  {
72
72
  "description": "Span N columns when inside a `columns` Form grid.",
@@ -23,7 +23,7 @@
23
23
  {
24
24
  "description": "On SheetContent (side left/right): desired panel width (number→px). Default w-3/4 sm:max-w-md.",
25
25
  "name": "width",
26
- "type": "number | string"
26
+ "type": "WidthProp"
27
27
  },
28
28
  {
29
29
  "description": "On SheetContent (same name as on DialogContent): fires on every close before focus returns to the trigger. Call event.preventDefault() and focus your own target (e.g. the new page's heading after a drawer navigation) to send focus there instead (gh#1134).",
@@ -5,9 +5,9 @@
5
5
  "name": "Tabs",
6
6
  "props": [
7
7
  {
8
- "description": "Optional data-driven tab list. When provided, Tabs renders all triggers and content panels. When Tabs owns the initial selection (no `value`, and no `defaultValue` naming an existing ENABLED item), it falls back to the first item that is NOT `disabled` — never a disabled one — and selects nothing if every item is disabled. `icon` is a leading glyph in the trigger (Ant Design `Tab.icon`). `closable` / `closeIcon` only apply under `variant=\"editable-card\"`; `closable: false` opts one tab out of removal (Ant Design `getRemovable`). `forceRender` (Ant Design `Tab.forceRender`) mounts THAT panel up front and keeps it mounted while another tab is selected, without switching the whole strip over with `destroyOnHidden={false}` — antd's per-item `destroyOnHidden` is deliberately not offered, because on this component \"kept mounted\" is one state and it would be a second spelling of `forceRender`. `count` (+ `overflowCount`, default 99; `showZero`, default true; `countLabel`) draws the counter pill beside the label — 「未対応 12」 — in the SAME vocabulary Button and Toggle publish and through the same helper, formatted with `Intl.NumberFormat` in the active locale. Pass `countLabel` to say what the number is: the pill is `aria-hidden` and an `sr-only` clause carries the digits, so the tab announces \"未対応, 12 件の課題\" and never the concatenated \"未対応12\". Prefer this over antd's answer, which is to put a Badge inside `label` — with the number inside the label the package owns neither its size nor its tone as the tab moves between selected / unselected / disabled, so every consumer aligns it differently. `href` makes the strip a ROUTE strip (gh#1364) — see `linkComponent`.",
8
+ "description": "Optional data-driven tab list. When provided, Tabs renders all triggers and content panels. When Tabs owns the initial selection (no `value`, and no `defaultValue` naming an existing ENABLED item), it falls back to the first item that is NOT `disabled` — never a disabled one — and selects nothing if every item is disabled. `icon` is a leading glyph in the trigger (Ant Design `Tab.icon`). `closable` / `closeIcon` only apply under `variant=\"editable-card\"`; `closable: false` opts one tab out of removal (Ant Design `getRemovable`). `forceRender` (Ant Design `Tab.forceRender`) mounts THAT panel up front and keeps it mounted while another tab is selected, without switching the whole strip over with `destroyOnHidden={false}` — antd's per-item `destroyOnHidden` is deliberately not offered, because on this component \"kept mounted\" is one state and it would be a second spelling of `forceRender`. `countTone=\"neutral\"` keeps passive counts muted in both selected and resting states; omitted/\"accent\" preserves the existing muted resting and accent selected appearance. The compound TabsTrigger accepts the same countTone. Neutral uses --tabs-count-neutral-background/color; accent uses the existing --tabs-count-background/color and --tabs-count-active-background/color knobs (all role mirrors). `count` (+ `overflowCount`, default 99; `showZero`, default true; `countLabel`) draws the counter pill beside the label — 「未対応 12」 — in the SAME vocabulary Button and Toggle publish and through the same helper, formatted with `Intl.NumberFormat` in the active locale. Pass `countLabel` to say what the number is: the pill is `aria-hidden` and an `sr-only` clause carries the digits, so the tab announces \"未対応, 12 件の課題\" and never the concatenated \"未対応12\". Prefer this over antd's answer, which is to put a Badge inside `label` — with the number inside the label the package owns neither its size nor its tone as the tab moves between selected / unselected / disabled, so every consumer aligns it differently. `href` makes the strip a ROUTE strip (gh#1364) — see `linkComponent`.",
9
9
  "name": "items",
10
- "type": "{ value: string; label: React.ReactNode; content?: React.ReactNode; href?: string; disabled?: boolean; icon?: React.ReactNode; closable?: boolean; closeIcon?: React.ReactNode; forceRender?: boolean; count?: number; overflowCount?: number; showZero?: boolean; countLabel?: string }[]"
10
+ "type": "{ value: string; label: React.ReactNode; content?: React.ReactNode; href?: string; disabled?: boolean; icon?: React.ReactNode; closable?: boolean; closeIcon?: React.ReactNode; forceRender?: boolean; count?: number; countTone?: \"neutral\" | \"accent\"; overflowCount?: number; showZero?: boolean; countLabel?: string }[]"
11
11
  },
12
12
  {
13
13
  "description": "Controlled active tab key.",
@@ -1020,7 +1020,7 @@
1020
1020
  {
1021
1021
  "description": "A FIXED column: number = px, string = any CSS length. Comes with flex: none — a width a sibling can still squeeze is a suggestion, and stacked rows would start at different x offsets. Leaves data-width-raw on the DOM so the measurement stays countable, like gapRaw/padRaw.",
1022
1022
  "name": "width",
1023
- "type": "number | string"
1023
+ "type": "WidthProp"
1024
1024
  },
1025
1025
  {
1026
1026
  "description": "CENTRE this box and CAP it at a page measure (--page-measure-narrow 42rem / -medium 48rem / -wide 72rem): margin-inline:auto + inline-size:100% + max-inline-size. THE public answer to \"full-bleed outside, measured column inside\" — a marketing <section> paints edge to edge and its inner Flex measure=\"wide\" holds the content on the page measure. Deliberately NOT on PageContainer: that owns page padding and a header/toolbar/footer scaffold, so a full-bleed page cannot consume it, and the refusal left the centred column owned by nobody — three showcases each hand-wrote the same four declarations (gh#839). It adds NO gutter: the page inset is pad={{ inline: 6 }}, and one band with two padding owners drifts. A MAX, so nothing binds below the cap at 390px. An explicit `width` wins. Do not give a READING column \"wide\" — prose stays narrow/medium, which bracket Bringhurst's 45-75 characters. Omitted emits no attribute at all.",
@@ -4729,6 +4729,12 @@
4729
4729
  "name": "layout",
4730
4730
  "type": "\"vertical\" | \"horizontal\""
4731
4731
  },
4732
+ {
4733
+ "defaultValue": "\"auto\"",
4734
+ "description": "Horizontal labels only. Omitted/auto retains --descriptions-label-width (8rem), even with columns=1. content (also max-content, min-content or fit-content) uses a shared intrinsic grid track: longest label per visual column, capped by --descriptions-label-max-width (40%) so long labels wrap without clipping at 320px. Use columns=1 for one aligned label column across the entire list. WidthProp lengths and expressions override the width per list; numbers are px. Vertical layout ignores this prop. No consumer CSS override required.",
4735
+ "name": "labelWidth",
4736
+ "type": "WidthProp | \"content\""
4737
+ },
4732
4738
  {
4733
4739
  "defaultValue": "\"start\"",
4734
4740
  "description": "Applies only in layout=\"horizontal\" — a vertical label sits above its value and end-aligning it there would read as a mistake, the same contract `Form` keeps.",
@@ -5960,12 +5966,12 @@
5960
5966
  {
5961
5967
  "description": "Label column width in horizontal layout (number→px). e.g. 120 or '8rem'.",
5962
5968
  "name": "labelWidth",
5963
- "type": "number | string"
5969
+ "type": "WidthProp"
5964
5970
  },
5965
5971
  {
5966
- "description": "Cap the control width (number→px). Omit to fill the column. Caps the control only — the helper and error still span the field column.",
5972
+ "description": "Cap the control width: number→px, or a CSS length ('20rem', '50%', 'calc(…)'). Omit to fill the column. Caps the control only — the helper and error still span the field column. A size word ('md', 'lg') is a type error, not a width (gh#1431).",
5967
5973
  "name": "controlWidth",
5968
- "type": "number | string"
5974
+ "type": "WidthProp"
5969
5975
  },
5970
5976
  {
5971
5977
  "defaultValue": "\"end\"",
@@ -6096,12 +6102,12 @@
6096
6102
  {
6097
6103
  "description": "Override the Form's label width for this field.",
6098
6104
  "name": "labelWidth",
6099
- "type": "number | string"
6105
+ "type": "WidthProp"
6100
6106
  },
6101
6107
  {
6102
6108
  "description": "Override the Form's control width for this field. Caps the control only — the helper and error still span the field column.",
6103
6109
  "name": "controlWidth",
6104
- "type": "number | string"
6110
+ "type": "WidthProp"
6105
6111
  },
6106
6112
  {
6107
6113
  "description": "Span N columns when inside a `columns` Form grid.",
@@ -8349,7 +8355,7 @@
8349
8355
  {
8350
8356
  "description": "On SheetContent (side left/right): desired panel width (number→px). Default w-3/4 sm:max-w-md.",
8351
8357
  "name": "width",
8352
- "type": "number | string"
8358
+ "type": "WidthProp"
8353
8359
  },
8354
8360
  {
8355
8361
  "description": "On SheetContent (same name as on DialogContent): fires on every close before focus returns to the trigger. Call event.preventDefault() and focus your own target (e.g. the new page's heading after a drawer navigation) to send focus there instead (gh#1134).",
@@ -8563,9 +8569,9 @@
8563
8569
  "name": "Tabs",
8564
8570
  "props": [
8565
8571
  {
8566
- "description": "Optional data-driven tab list. When provided, Tabs renders all triggers and content panels. When Tabs owns the initial selection (no `value`, and no `defaultValue` naming an existing ENABLED item), it falls back to the first item that is NOT `disabled` — never a disabled one — and selects nothing if every item is disabled. `icon` is a leading glyph in the trigger (Ant Design `Tab.icon`). `closable` / `closeIcon` only apply under `variant=\"editable-card\"`; `closable: false` opts one tab out of removal (Ant Design `getRemovable`). `forceRender` (Ant Design `Tab.forceRender`) mounts THAT panel up front and keeps it mounted while another tab is selected, without switching the whole strip over with `destroyOnHidden={false}` — antd's per-item `destroyOnHidden` is deliberately not offered, because on this component \"kept mounted\" is one state and it would be a second spelling of `forceRender`. `count` (+ `overflowCount`, default 99; `showZero`, default true; `countLabel`) draws the counter pill beside the label — 「未対応 12」 — in the SAME vocabulary Button and Toggle publish and through the same helper, formatted with `Intl.NumberFormat` in the active locale. Pass `countLabel` to say what the number is: the pill is `aria-hidden` and an `sr-only` clause carries the digits, so the tab announces \"未対応, 12 件の課題\" and never the concatenated \"未対応12\". Prefer this over antd's answer, which is to put a Badge inside `label` — with the number inside the label the package owns neither its size nor its tone as the tab moves between selected / unselected / disabled, so every consumer aligns it differently. `href` makes the strip a ROUTE strip (gh#1364) — see `linkComponent`.",
8572
+ "description": "Optional data-driven tab list. When provided, Tabs renders all triggers and content panels. When Tabs owns the initial selection (no `value`, and no `defaultValue` naming an existing ENABLED item), it falls back to the first item that is NOT `disabled` — never a disabled one — and selects nothing if every item is disabled. `icon` is a leading glyph in the trigger (Ant Design `Tab.icon`). `closable` / `closeIcon` only apply under `variant=\"editable-card\"`; `closable: false` opts one tab out of removal (Ant Design `getRemovable`). `forceRender` (Ant Design `Tab.forceRender`) mounts THAT panel up front and keeps it mounted while another tab is selected, without switching the whole strip over with `destroyOnHidden={false}` — antd's per-item `destroyOnHidden` is deliberately not offered, because on this component \"kept mounted\" is one state and it would be a second spelling of `forceRender`. `countTone=\"neutral\"` keeps passive counts muted in both selected and resting states; omitted/\"accent\" preserves the existing muted resting and accent selected appearance. The compound TabsTrigger accepts the same countTone. Neutral uses --tabs-count-neutral-background/color; accent uses the existing --tabs-count-background/color and --tabs-count-active-background/color knobs (all role mirrors). `count` (+ `overflowCount`, default 99; `showZero`, default true; `countLabel`) draws the counter pill beside the label — 「未対応 12」 — in the SAME vocabulary Button and Toggle publish and through the same helper, formatted with `Intl.NumberFormat` in the active locale. Pass `countLabel` to say what the number is: the pill is `aria-hidden` and an `sr-only` clause carries the digits, so the tab announces \"未対応, 12 件の課題\" and never the concatenated \"未対応12\". Prefer this over antd's answer, which is to put a Badge inside `label` — with the number inside the label the package owns neither its size nor its tone as the tab moves between selected / unselected / disabled, so every consumer aligns it differently. `href` makes the strip a ROUTE strip (gh#1364) — see `linkComponent`.",
8567
8573
  "name": "items",
8568
- "type": "{ value: string; label: React.ReactNode; content?: React.ReactNode; href?: string; disabled?: boolean; icon?: React.ReactNode; closable?: boolean; closeIcon?: React.ReactNode; forceRender?: boolean; count?: number; overflowCount?: number; showZero?: boolean; countLabel?: string }[]"
8574
+ "type": "{ value: string; label: React.ReactNode; content?: React.ReactNode; href?: string; disabled?: boolean; icon?: React.ReactNode; closable?: boolean; closeIcon?: React.ReactNode; forceRender?: boolean; count?: number; countTone?: \"neutral\" | \"accent\"; overflowCount?: number; showZero?: boolean; countLabel?: string }[]"
8569
8575
  },
8570
8576
  {
8571
8577
  "description": "Controlled active tab key.",
package/agent/index.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "components": 171,
5
5
  "patterns": 28,
6
6
  "rules": 44,
7
- "tokens": 2263,
7
+ "tokens": 2266,
8
8
  "vocabulary": 16
9
9
  },
10
10
  "design": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md",
@@ -54,19 +54,19 @@
54
54
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
55
55
  "read": {
56
56
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
57
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.18.0/agent/index.json"
57
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.20.0/agent/index.json"
58
58
  },
59
59
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
60
60
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
61
61
  "tokenTiers": {
62
62
  "component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
63
63
  "counts": {
64
- "component": 1930,
64
+ "component": 1933,
65
65
  "foundation": 220,
66
66
  "semantic": 113
67
67
  },
68
68
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
69
69
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
70
70
  },
71
- "version": "33.18.0"
71
+ "version": "33.20.0"
72
72
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
- > A Japanese-enterprise React design system: 171 components, 2263 design tokens,
4
- > 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.18.0.
3
+ > A Japanese-enterprise React design system: 171 components, 2266 design tokens,
4
+ > 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.20.0.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@33.18.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@33.20.0`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -19,7 +19,7 @@ that can only fetch URLs.
19
19
  - [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 54 KB — all 171 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
20
20
  - [components/&lt;Name&gt;.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–38 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
21
21
  - [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.3 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
22
- - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 220 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles, 1930 `component` knobs.
22
+ - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 220 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles, 1933 `component` knobs.
23
23
  - [vocabulary.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json): the controlled prop vocabulary — which prop name means what, across every component.
24
24
  - [rules.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json): 44 cardinal rules.
25
25
  - [anti-ai-tells.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/anti-ai-tells.json): 35 shapes that make generated UI look generated, each with its fix.
@@ -27,7 +27,7 @@ that can only fetch URLs.
27
27
  ## Pinning
28
28
 
29
29
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
30
- the tag: `.../godx-jp/godxjp-ui/v33.18.0/agent/...`. A catalog that does not match the installed
30
+ the tag: `.../godx-jp/godxjp-ui/v33.20.0/agent/...`. A catalog that does not match the installed
31
31
  package describes props that are absent, or hides props that are present, and says nothing either way.
32
32
 
33
33
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
package/agent/tokens.json CHANGED
@@ -6600,11 +6600,17 @@
6600
6600
  "value": "var(--icon-size-xs)"
6601
6601
  },
6602
6602
  {
6603
- "description": "Width of the label column when <Descriptions layout=\"horizontal\">. Labels align to this shared column so the values line up (the horizontal-detail look, mirroring <Form layout>). A rem value gives a fixed aligned column; set `max-content` to size each label to its text. (rule #44/#45 — a service theme tunes it here instead of forking CSS.)",
6603
+ "description": "Width of the label column when <Descriptions layout=\"horizontal\">. Labels align to this shared column so the values line up (the horizontal-detail look, mirroring <Form layout>). A rem value gives a fixed aligned column; use labelWidth=\"content\" for shared intrinsic sizing. (rule #44/#45 — a service theme tunes it here instead of forking CSS.)",
6604
6604
  "name": "--descriptions-label-width",
6605
6605
  "tier": "component",
6606
6606
  "value": "8rem"
6607
6607
  },
6608
+ {
6609
+ "description": "Cap intrinsic labels so long labels wrap and values retain space on narrow screens.",
6610
+ "name": "--descriptions-label-max-width",
6611
+ "tier": "component",
6612
+ "value": "40%"
6613
+ },
6608
6614
  {
6609
6615
  "description": "Row-to-row gap (gh#294). Default = the historical hardcoded `gap-y-3`, so nothing changes visually by default. A consumer composing Descriptions beside a Form/FormField (a read-only block above an editable field on the same card) retunes this to `var(--space-4)` to match Form's own field-to-field rhythm instead of the two blocks reading as visually unrelated.",
6610
6616
  "name": "--descriptions-row-gap",
@@ -9449,6 +9455,18 @@
9449
9455
  "tier": "component",
9450
9456
  "value": "var(--radius-pill)"
9451
9457
  },
9458
+ {
9459
+ "description": "default = hsl(var(--muted)) at the call site",
9460
+ "name": "--tabs-count-neutral-background",
9461
+ "tier": "component",
9462
+ "value": "initial"
9463
+ },
9464
+ {
9465
+ "description": "default = hsl(var(--muted-foreground)) at the call site",
9466
+ "name": "--tabs-count-neutral-color",
9467
+ "tier": "component",
9468
+ "value": "initial"
9469
+ },
9452
9470
  {
9453
9471
  "description": "default = hsl(var(--muted)) at the call site",
9454
9472
  "name": "--tabs-count-background",
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import type { WidthProp } from "../../props/vocabulary/shared.prop.js";
2
3
  import type { DescriptionsLayoutProp } from "../../props/vocabulary/interaction.prop.js";
3
4
  import type { DescriptionsColumnProp, DescriptionsItemsProp, DescriptionsSpanProp } from "../../props/vocabulary/data.prop.js";
4
5
  export type { DescriptionsLayoutProp };
@@ -16,6 +17,9 @@ export interface DescriptionsProps {
16
17
  * it there would read as a mistake, exactly like `Form`'s own contract.
17
18
  */
18
19
  labelAlign?: "start" | "end";
20
+ /** Horizontal label width. Omitted/auto keeps the theme width; content shares the longest
21
+ * label track per visual column. CSS lengths (numbers are px) override the theme per list. */
22
+ labelWidth?: WidthProp | "content";
19
23
  /**
20
24
  * Draw the grid as a bordered table (antd `bordered`): an outer frame, a rule between every
21
25
  * cell, and a shaded label cell. Colour and rhythm come from the `--descriptions-*` tokens.
@@ -29,7 +33,7 @@ export interface DescriptionsProps {
29
33
  className?: string;
30
34
  children?: React.ReactNode;
31
35
  }
32
- export declare function Descriptions({ columns, layout, labelAlign, bordered, items, className, children, }: DescriptionsProps): React.JSX.Element;
36
+ export declare function Descriptions({ columns, layout, labelAlign, labelWidth, bordered, items, className, children, }: DescriptionsProps): React.JSX.Element;
33
37
  export declare namespace Descriptions {
34
38
  export { DescriptionsItem as Item };
35
39
  }
@@ -5,7 +5,8 @@ import { cn } from "../../lib/utils.js";
5
5
  const DescriptionsLayoutContext = React.createContext({
6
6
  layout: "vertical",
7
7
  labelAlign: "start",
8
- bordered: false
8
+ bordered: false,
9
+ contentLabels: false
9
10
  });
10
11
  const BREAKPOINT_LADDER = ["sm", "md", "lg", "xl"];
11
12
  function responsiveVars(name, base, steps) {
@@ -25,6 +26,7 @@ function Descriptions({
25
26
  columns = 2,
26
27
  layout = "vertical",
27
28
  labelAlign = "start",
29
+ labelWidth,
28
30
  bordered = false,
29
31
  items,
30
32
  className,
@@ -32,21 +34,31 @@ function Descriptions({
32
34
  }) {
33
35
  const legacyColumns = typeof columns === "number" ? LEGACY_COLUMN_CLASS[columns] : void 0;
34
36
  const responsiveColumns = legacyColumns === void 0 ? typeof columns === "number" ? { sm: Math.min(columns, 2), lg: columns } : columns : void 0;
37
+ const intrinsicWidth = labelWidth === "content" || labelWidth === "max-content" || labelWidth === "min-content" || labelWidth === "fit-content";
38
+ const contentLabels = layout === "horizontal" && intrinsicWidth;
39
+ const labelStyle = layout === "horizontal" && labelWidth !== void 0 && labelWidth !== "auto" && !intrinsicWidth ? {
40
+ "--descriptions-label-width": typeof labelWidth === "number" ? `${labelWidth}px` : labelWidth
41
+ } : {};
42
+ const contentColumns = contentLabels && !responsiveColumns ? typeof columns === "number" ? columns === 3 ? { sm: 2, lg: 3 } : columns === 2 ? { sm: 2 } : {} : columns : responsiveColumns;
35
43
  const context = React.useMemo(
36
- () => ({ layout, labelAlign, bordered }),
37
- [layout, labelAlign, bordered]
44
+ () => ({ layout, labelAlign, bordered, contentLabels }),
45
+ [layout, labelAlign, bordered, contentLabels]
38
46
  );
39
47
  return /* @__PURE__ */ jsx(DescriptionsLayoutContext.Provider, { value: context, children: /* @__PURE__ */ jsxs(
40
48
  "dl",
41
49
  {
42
50
  "data-slot": "descriptions",
43
51
  "data-columns": typeof columns === "number" ? columns : void 0,
44
- "data-columns-responsive": responsiveColumns ? "" : void 0,
52
+ "data-columns-responsive": responsiveColumns || contentLabels ? "" : void 0,
53
+ "data-label-width": contentLabels ? "content" : void 0,
45
54
  "data-bordered": bordered ? "" : void 0,
46
- style: responsiveColumns ? responsiveVars("--descriptions-column-count", 1, responsiveColumns) : void 0,
55
+ style: {
56
+ ...contentColumns ? responsiveVars("--descriptions-column-count", 1, contentColumns) : {},
57
+ ...labelStyle
58
+ },
47
59
  className: cn(
48
60
  "grid gap-x-[var(--descriptions-column-gap)] gap-y-[var(--descriptions-row-gap)]",
49
- legacyColumns,
61
+ !contentLabels && legacyColumns,
50
62
  className
51
63
  ),
52
64
  children: [
@@ -71,9 +83,9 @@ const LEGACY_SPAN_CLASS = {
71
83
  3: "sm:col-span-2 lg:col-span-3"
72
84
  };
73
85
  function DescriptionsItem({ label, mono, span, className, children }) {
74
- const { layout, labelAlign } = React.useContext(DescriptionsLayoutContext);
75
- const legacySpan = typeof span === "number" ? LEGACY_SPAN_CLASS[span] : void 0;
76
- const responsiveSpan = span !== void 0 && span !== "filled" && legacySpan === void 0 ? typeof span === "number" ? { sm: span } : span : void 0;
86
+ const { layout, labelAlign, contentLabels } = React.useContext(DescriptionsLayoutContext);
87
+ const legacySpan = !contentLabels && typeof span === "number" ? LEGACY_SPAN_CLASS[span] : void 0;
88
+ const responsiveSpan = span !== void 0 && span !== "filled" && legacySpan === void 0 ? typeof span === "number" ? span === 3 ? { sm: 2, lg: 3 } : { sm: span } : span : void 0;
77
89
  const endAlignedLabel = layout === "horizontal" && labelAlign === "end";
78
90
  return /* @__PURE__ */ jsxs(
79
91
  "div",
@@ -55,9 +55,10 @@ type TogglePressedFields = {
55
55
  * accessible name (gh#734) — without a second counting API growing beside this one (gh#762).
56
56
  * It defaults to `toggle`, so every existing call emits the identical markup it always has.
57
57
  */
58
- declare function useCounterPill({ count, overflowCount, showZero, countLabel, ariaLabel, slot, }: ToggleCountFields & {
58
+ declare function useCounterPill({ count, overflowCount, showZero, countLabel, ariaLabel, slot, countTone, }: ToggleCountFields & {
59
59
  ariaLabel?: string;
60
60
  slot?: string;
61
+ countTone?: "neutral" | "accent";
61
62
  }): {
62
63
  pill: null;
63
64
  resolvedAriaLabel: string | undefined;
@@ -58,7 +58,8 @@ function useCounterPill({
58
58
  showZero = true,
59
59
  countLabel,
60
60
  ariaLabel,
61
- slot = "toggle"
61
+ slot = "toggle",
62
+ countTone
62
63
  }) {
63
64
  const { locale } = useTranslation();
64
65
  const visible = count != null && (count !== 0 || showZero);
@@ -73,7 +74,16 @@ function useCounterPill({
73
74
  const spoken = countLabel ? `${formatted} ${countLabel}` : formatted;
74
75
  return {
75
76
  pill: /* @__PURE__ */ jsxs(Fragment, { children: [
76
- /* @__PURE__ */ jsx("span", { "data-slot": `${slot}-count`, className: `ui-${slot}-count`, "aria-hidden": "true", children: formatted }),
77
+ /* @__PURE__ */ jsx(
78
+ "span",
79
+ {
80
+ "data-slot": `${slot}-count`,
81
+ "data-count-tone": countTone,
82
+ className: `ui-${slot}-count`,
83
+ "aria-hidden": "true",
84
+ children: formatted
85
+ }
86
+ ),
77
87
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${spoken}` })
78
88
  ] }),
79
89
  resolvedAriaLabel: ariaLabel == null ? ariaLabel : `${ariaLabel}, ${spoken}`
@@ -24,7 +24,7 @@ export declare const TabsTrigger: React.ForwardRefExoticComponent<Omit<Omit<Reac
24
24
  value: string;
25
25
  /** RAC `isDisabled`; ALSO written to the button so `disabled:` utilities keep matching. */
26
26
  disabled?: boolean;
27
- } & Pick<TabItemProp, "count" | "countLabel" | "overflowCount" | "showZero"> & React.RefAttributes<HTMLButtonElement>>;
27
+ } & Pick<TabItemProp, "count" | "countLabel" | "countTone" | "overflowCount" | "showZero"> & React.RefAttributes<HTMLButtonElement>>;
28
28
  export declare const TabsContent: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "value"> & {
29
29
  /** The panel's key — must match a `TabsTrigger value`. RAC `id`. */
30
30
  value: string;
@@ -37,9 +37,17 @@ function TabCount({
37
37
  count,
38
38
  overflowCount,
39
39
  showZero,
40
- countLabel
40
+ countLabel,
41
+ countTone
41
42
  }) {
42
- const { pill } = useCounterPill({ count, overflowCount, showZero, countLabel, slot: "tabs" });
43
+ const { pill } = useCounterPill({
44
+ count,
45
+ overflowCount,
46
+ showZero,
47
+ countLabel,
48
+ countTone,
49
+ slot: "tabs"
50
+ });
43
51
  return pill;
44
52
  }
45
53
  const TABS_LIST_BASE_CLASS_NAME = "group/tabs-list text-muted-foreground inline-flex w-fit max-w-full min-w-0 items-center justify-center-safe rounded-lg p-1 group-data-[orientation=vertical]/tabs:flex-col data-[orientation=horizontal]:[scrollbar-width:none] data-[orientation=horizontal]:overflow-x-auto data-[orientation=horizontal]:overflow-y-hidden data-[variant=line]:gap-[var(--tabs-list-line-space-gap)] data-[variant=line]:rounded-none data-[variant=line]:bg-transparent [&[data-orientation=horizontal]::-webkit-scrollbar]:hidden";
@@ -344,7 +352,8 @@ function Tabs({
344
352
  count: item.count,
345
353
  overflowCount: item.overflowCount,
346
354
  showZero: item.showZero,
347
- countLabel: item.countLabel
355
+ countLabel: item.countLabel,
356
+ countTone: item.countTone
348
357
  }
349
358
  ),
350
359
  removable ? (
@@ -612,7 +621,8 @@ function TabsRouteStrip({
612
621
  count: item.count,
613
622
  overflowCount: item.overflowCount,
614
623
  showZero: item.showZero,
615
- countLabel: item.countLabel
624
+ countLabel: item.countLabel,
625
+ countTone: item.countTone
616
626
  }
617
627
  )
618
628
  ]
@@ -717,6 +727,7 @@ const TabsTrigger = React.forwardRef(
717
727
  overflowCount,
718
728
  showZero,
719
729
  countLabel,
730
+ countTone,
720
731
  children,
721
732
  onKeyDown,
722
733
  onClick,
@@ -810,7 +821,8 @@ const TabsTrigger = React.forwardRef(
810
821
  count,
811
822
  overflowCount,
812
823
  showZero,
813
- countLabel
824
+ countLabel,
825
+ countTone
814
826
  }
815
827
  )
816
828
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "33.18.0",
3
+ "version": "33.20.0",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -59,7 +59,7 @@ export type ProseProp = {
59
59
  className?: ClassNameProp;
60
60
  children?: ChildrenProp;
61
61
  };
62
- import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ExtraProp, ColumnDefProp, GetRowIdProp, GetRowLabelProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, PendingProp, ToneProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, GapProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp, DisabledProp, OnClickProp, ValueProp, DefaultValueProp, OnValueChangeProp, TypographyCopyConfigProp } from "../vocabulary/index.js";
62
+ import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ExtraProp, ColumnDefProp, GetRowIdProp, GetRowLabelProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, PendingProp, ToneProp, WidthProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, GapProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp, DisabledProp, OnClickProp, ValueProp, DefaultValueProp, OnValueChangeProp, TypographyCopyConfigProp } from "../vocabulary/index.js";
63
63
  import type { TreeFieldNamesProp, TreeOptionProp } from "./data-entry.prop.js";
64
64
  /**
65
65
  * One key in a `Legend`: a tone, and the words that tone stands for.
@@ -206,6 +206,8 @@ export type DescriptionsProp = {
206
206
  layout?: DescriptionsLayoutProp;
207
207
  /** Label text alignment inside the label column. Applies only to `layout="horizontal"`. */
208
208
  labelAlign?: "start" | "end";
209
+ /** Horizontal labels: auto keeps the theme width; content shares an intrinsic track across rows. */
210
+ labelWidth?: WidthProp | "content";
209
211
  /** Draw the grid as a bordered table with shaded label cells (antd `bordered`). */
210
212
  bordered?: boolean;
211
213
  /** Declarative items (antd `items`) — the alternative to composing `Descriptions.Item`. */
@@ -355,6 +355,8 @@ export type TabItemProp = {
355
355
  * lands somewhere different. The slot is what lets the tab state drive the pill.
356
356
  */
357
357
  count?: number;
358
+ /** neutral stays muted even when selected; accent preserves the existing selection emphasis. */
359
+ countTone?: "neutral" | "accent";
358
360
  /** Cap for `count` — beyond it the pill shows `{overflowCount}+` (e.g. `99+`). Default `99`. */
359
361
  overflowCount?: number;
360
362
  /** Render the pill when `count` is 0. Default `true`, matching Button and Toggle. */
@@ -122,7 +122,7 @@ export declare const VOCABULARY_REGISTRY: {
122
122
  readonly WidthProp: {
123
123
  readonly file: "vocabulary/shared.prop.ts";
124
124
  readonly category: "shared";
125
- readonly description: "Explicit layout length (number→px | CSS string) — NOT the SizeProp tier";
125
+ readonly description: 'Explicit layout length (number→px | CSS length string: 20rem, 50%, calc()/min()/var()) — NOT the SizeProp tier; "md" is a type error';
126
126
  };
127
127
  readonly ControlWidthProp: {
128
128
  readonly file: "vocabulary/shared.prop.ts";
@@ -2189,7 +2189,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
2189
2189
  readonly DescriptionsProp: {
2190
2190
  readonly group: "data-display";
2191
2191
  readonly file: "components/data-display.prop.ts";
2192
- readonly vocabulary: readonly ["ClassNameProp"];
2192
+ readonly vocabulary: readonly ["ClassNameProp", {
2193
+ readonly field: "labelWidth";
2194
+ readonly local: true;
2195
+ readonly reason: "WidthProp CSS lengths plus content for shared intrinsic label tracks; auto preserves the theme width (gh#1441).";
2196
+ }];
2193
2197
  };
2194
2198
  readonly DescriptionsItemProp: {
2195
2199
  readonly group: "data-display";
@@ -3034,6 +3038,10 @@ export declare const COMPONENT_PROP_REGISTRY: {
3034
3038
  readonly group: "navigation";
3035
3039
  readonly file: "components/navigation.prop.ts";
3036
3040
  readonly vocabulary: readonly ["ValueProp", "LabelProp", "ChildrenProp", "DisabledProp", "IconProp", {
3041
+ readonly field: "countTone";
3042
+ readonly local: true;
3043
+ readonly reason: "Counter intent: neutral (shared ToneProp term, as Badge) or accent (existing selected emphasis). Per-item and compound trigger parity, gh#1442.";
3044
+ }, {
3037
3045
  readonly field: "count";
3038
3046
  readonly local: true;
3039
3047
  readonly reason: "Counter-pill vocabulary shared VERBATIM with ButtonProp and ToggleProp (count/overflowCount/showZero) and drawn by the same `useCounterPill`, so a counted tab, a counted chip and a counted button are one thing — gh#312 / gh#734 / gh#762.";