@godxjp/ui-mcp 0.21.2 → 0.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -63,6 +63,11 @@ var COMPONENTS = [
63
63
  type: "boolean",
64
64
  defaultValue: "false",
65
65
  description: "Grow the body to fill the remaining shell height. Default false = top-packed, content-height (short pages leave no stretched void). Enable for a full-height DataTable, SplitPane, or a chat surface."
66
+ },
67
+ {
68
+ name: "linkComponent",
69
+ type: "React.ElementType",
70
+ description: "Link component used for breadcrumb / header links (e.g. an Inertia or React Router `Link`). Defaults to a native `<a>`."
66
71
  }
67
72
  ],
68
73
  usage: [
@@ -72,7 +77,8 @@ var COMPONENTS = [
72
77
  "DO: Pass `breadcrumb` as an ordered array of `{ label, to? }` objects from root to current page. The last item is automatically rendered without a link and receives `aria-current='page'`; earlier items with `to` become router `<Link>` elements. Never hand-roll a breadcrumb nav inside a PageContainer.",
73
78
  "DON'T: Use `density` to change individual control sizes \u2014 it cascades spacing across the entire page subtree. Set it once per page (e.g. `density='compact'` for data-dense list pages) and let all child components inherit it. Do not apply density classes manually.",
74
79
  "DON'T: Confuse PageContainer's prop names with the old PageHeader's prop names \u2014 PageContainer uses `subtitle` (not `description`) and `extra` (not `actions`). If you see those legacy names in old code, migrate them to PageContainer.",
75
- "DO: Leave `fill` off (the default) for ordinary pages \u2014 the body is content-height and top-packed, so a short page on a tall viewport leaves no stretched empty void below the content (the page background simply spans the shell). Only set `fill` when the body itself should occupy the full remaining height: a full-height DataTable, a SplitPane, or a chat surface whose message list scrolls and whose composer is pinned to the bottom via `footer` + `stickyFooter`. DON'T add a manual `min-h-screen` / `flex-1` wrapper or a spacer div to fight or fake this."
80
+ "DO: Leave `fill` off (the default) for ordinary pages \u2014 the body is content-height and top-packed, so a short page on a tall viewport leaves no stretched empty void below the content (the page background simply spans the shell). Only set `fill` when the body itself should occupy the full remaining height: a full-height DataTable, a SplitPane, or a chat surface whose message list scrolls and whose composer is pinned to the bottom via `footer` + `stickyFooter`. DON'T add a manual `min-h-screen` / `flex-1` wrapper or a spacer div to fight or fake this.",
81
+ "DO: Know the header draws NO bottom divider by default \u2014 it is governed by the semantic token `--page-header-divider` (default `none`). A service theme opts in once, globally, with `--page-header-divider: 1px solid hsl(var(--border));` in its theme CSS. Never re-create the divider with a `border-b` utility on the header or a `<Separator>` under the title; `variant='ghost'` stays divider-less regardless of the token."
76
82
  ],
77
83
  useCases: [
78
84
  "A master list page (e.g. invoices, journal entries, customers) where the header holds the page title, a 'New Invoice' button in `extra`, a breadcrumb trail, and a full-bleed DataTable as the body \u2014 use `variant='flush'` + `<PageContainer.Inset>` for the Toolbar above the table.",
@@ -275,6 +281,11 @@ import { StatCard } from "@godxjp/ui/data-display";
275
281
  name: "footer",
276
282
  type: "ReactNode",
277
283
  description: "App-level footer outside the main content area."
284
+ },
285
+ {
286
+ name: "breadcrumb",
287
+ type: "BreadcrumbProp",
288
+ description: "Breadcrumb trail rendered in the topbar header for back-navigation."
278
289
  }
279
290
  ],
280
291
  usage: [
@@ -509,6 +520,12 @@ export default function Shell() {
509
520
  defaultValue: "undefined",
510
521
  description: "Called when the search bar button (\u2318K) is clicked. Wire this to your command-palette or search dialog."
511
522
  },
523
+ {
524
+ name: "searchPlaceholder",
525
+ type: "string",
526
+ defaultValue: 'i18n "layout.topbar.searchPlaceholder" ("\u691C\u7D22\u2026")',
527
+ description: "Search-bar placeholder text \u2014 set a domain-specific hint (e.g. \u6848\u4EF6\u30FB\u53D7\u6CE8\u30FB\u9867\u5BA2\u3092\u691C\u7D22\u2026) instead of the generic default."
528
+ },
512
529
  {
513
530
  name: "onTweaksOpen",
514
531
  type: "() => void",
@@ -758,6 +775,23 @@ function MyShell({ children }: { content: React.ReactNode }) {
758
775
  name: "onClick",
759
776
  type: "React.MouseEventHandler<HTMLButtonElement>",
760
777
  description: "Click handler. Does not fire while `loading` or `disabled`."
778
+ },
779
+ {
780
+ name: "count",
781
+ type: "number",
782
+ description: 'Trailing borderless counter pill after the label (filter tabs / segmented toggles, e.g. "Ch\u1EDD bay 18"). Localized via `Intl.NumberFormat`; styled per variant \u2014 never nest a `Badge` in a Button for this. Ignored when `asChild`.'
783
+ },
784
+ {
785
+ name: "overflowCount",
786
+ type: "number",
787
+ defaultValue: "99",
788
+ description: "Cap for `count` (Ant Badge parity) \u2014 when `count` exceeds it the pill renders `{overflowCount}+` (e.g. `99+`)."
789
+ },
790
+ {
791
+ name: "showZero",
792
+ type: "boolean",
793
+ defaultValue: "true",
794
+ description: "Whether the pill renders when `count` is 0 (Ant Badge parity). Pass `false` to hide the pill at zero."
761
795
  }
762
796
  ],
763
797
  usage: [
@@ -826,6 +860,11 @@ import { Trash2 } from "lucide-react";
826
860
  type: '"span" | "p" | "div" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr"',
827
861
  defaultValue: '"span"',
828
862
  description: "Rendered element. `code`/`kbd` are monospace by default."
863
+ },
864
+ {
865
+ name: "htmlFor",
866
+ type: "string",
867
+ description: "When set, Text renders as a `<label>` bound to this control id (polymorphic label use)."
829
868
  }
830
869
  ],
831
870
  usage: [
@@ -1283,7 +1322,7 @@ export function Grid({ rows }: { rows: Row[] }) {
1283
1322
  {
1284
1323
  name: "StatCard",
1285
1324
  group: "data-display",
1286
- tagline: "KPI tile. \u26A0\uFE0F StatCard IS ALREADY a bordered Card \u2014 render it DIRECTLY in ResponsiveGrid. NEVER wrap it in <Card>/<CardContent> (that double-borders it \u2192 looks too thick). NO accent prop (accent is a Card prop).",
1325
+ tagline: "KPI tile. \u26A0\uFE0F StatCard IS ALREADY a bordered Card \u2014 render it DIRECTLY in ResponsiveGrid. NEVER wrap it in <Card>/<CardContent> (that double-borders it \u2192 looks too thick). Use `accent` for a semantic leading-edge rail on a KPI needing attention.",
1287
1326
  props: [
1288
1327
  { name: "label", type: "ReactNode", required: true, description: "Metric name." },
1289
1328
  {
@@ -1304,13 +1343,24 @@ export function Grid({ rows }: { rows: Row[] }) {
1304
1343
  defaultValue: '"stacked"',
1305
1344
  description: "stacked = label over value; inline = label left / value right."
1306
1345
  },
1307
- { name: "align", type: '"start" | "end"', description: "Align the metric group." }
1346
+ { name: "align", type: '"start" | "end"', description: "Align the metric group." },
1347
+ {
1348
+ name: "inverse",
1349
+ type: "boolean",
1350
+ defaultValue: "false",
1351
+ description: "Flip delta tone semantics for metrics where lower is better (cost, error rate): '-' renders green, '+' renders red."
1352
+ },
1353
+ {
1354
+ name: "accent",
1355
+ type: '"primary" | "success" | "warning" | "info" | "attention" | "destructive"',
1356
+ description: "Semantic 3px leading-edge rail (forwarded to the underlying Card) \u2014 flags a KPI that needs attention (e.g. attention = backlog with a deadline, destructive = overdue)."
1357
+ }
1308
1358
  ],
1309
1359
  usage: [
1310
1360
  "DO place StatCard directly as a child of ResponsiveGrid \u2014 it renders its own bordered Card shell internally, so no wrapping <Card> or <CardContent> is needed or allowed. Wrapping creates a double border.",
1311
1361
  "DO pass `delta` as a sign-prefixed string (e.g. '+12%' or '-3%') to get automatic color tone: '+' renders text-success, '-' renders text-destructive. For metrics where a negative delta is good (e.g. cost reduction, error rate), pass `inverse` so the tone is flipped correctly.",
1312
1362
  "DO use `hint` for secondary context (e.g. '\u5148\u6708\u6BD4 +3%', 'last 30 days'). In the default `stacked` layout hint renders below the value; in `inline` layout it renders beside the label.",
1313
- "DO NOT add an `accent` prop \u2014 accent is a Card prop and StatCard does not expose it. Passing accent has no effect and creates a false expectation.",
1363
+ "DO use `accent` (not a className border) for the semantic leading-edge rail \u2014 `attention` for a non-destructive backlog needing action, `destructive` only for overdue/exceeded states. Most tiles in a grid stay rail-less; an all-accented grid says nothing.",
1314
1364
  "DO NOT hand-roll a KPI tile using a plain <Card><CardContent>. StatCard is the correct primitive and token-aligns the label/value/hint/delta slots automatically.",
1315
1365
  "WHILE data is loading, replace each StatCard with a <SkeletonStat /> at the same grid position \u2014 never render an empty value string or a spinner inside StatCard itself."
1316
1366
  ],
@@ -1423,6 +1473,11 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1423
1473
  type: "ReactNode",
1424
1474
  required: true,
1425
1475
  description: "Descriptions.Item elements."
1476
+ },
1477
+ {
1478
+ name: "items",
1479
+ type: "DescriptionsItemProp[]",
1480
+ description: "Data-driven rows `{ label, value, span? }` (label/value are ReactNode) \u2014 the alternative to composing `Descriptions.Item` children."
1426
1481
  }
1427
1482
  ],
1428
1483
  usage: [
@@ -1908,6 +1963,17 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1908
1963
  name: "onChange",
1909
1964
  type: "React.ChangeEventHandler<HTMLInputElement>",
1910
1965
  description: "Native change handler."
1966
+ },
1967
+ {
1968
+ name: "allowClear",
1969
+ type: "boolean",
1970
+ defaultValue: "false",
1971
+ description: "Opt-in inline \u2715 that clears the field while it holds text (works controlled + uncontrolled). Off by default, so existing inputs are unchanged."
1972
+ },
1973
+ {
1974
+ name: "onClear",
1975
+ type: "() => void",
1976
+ description: "Called after the field is cleared via the inline \u2715 (requires `allowClear`)."
1911
1977
  }
1912
1978
  ],
1913
1979
  usage: [
@@ -2074,6 +2140,32 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
2074
2140
  type: "number",
2075
2141
  defaultValue: "250",
2076
2142
  description: "Debounce delay (ms)."
2143
+ },
2144
+ {
2145
+ name: "id",
2146
+ type: "string",
2147
+ description: "Input id; pair with `label` or an external `<label htmlFor>`."
2148
+ },
2149
+ {
2150
+ name: "label",
2151
+ type: "React.ReactNode",
2152
+ description: "Optional visible label rendered above the search box (falls back to an sr-only label)."
2153
+ },
2154
+ {
2155
+ name: "onChange",
2156
+ type: "(value: string) => void",
2157
+ description: "Fires on EVERY keystroke (immediate) \u2014 required to keep a controlled `value` responsive."
2158
+ },
2159
+ {
2160
+ name: "onSearchChange",
2161
+ type: "(value: string) => void",
2162
+ description: "Fires the DEBOUNCED search term after `debounceMs` \u2014 wire your query/filter here, not onChange."
2163
+ },
2164
+ {
2165
+ name: "debounceMs",
2166
+ type: "number",
2167
+ defaultValue: "250",
2168
+ description: "Debounce delay (ms) before `onSearchChange` / `onSearch` fires."
2077
2169
  }
2078
2170
  ],
2079
2171
  usage: [
@@ -2111,7 +2203,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
2111
2203
  {
2112
2204
  name: "options",
2113
2205
  type: "SearchSelectOptionProp[]",
2114
- description: "Static option list. Passing this (or loadOptions) switches Select from the compound API to the data-driven API. Each option has { value, label, sublabel?, group?, disabled? }. group buckets the option under an optgroup-style heading."
2206
+ description: "Static option list. Passing this (or loadOptions) switches Select from the compound API to the data-driven API. Each option has { value, label, sublabel?, icon?, group?, disabled? }. `icon` (avatar / flag / lucide node) renders before the label in the rows AND on the trigger once selected. group buckets the option under an optgroup-style heading."
2115
2207
  },
2116
2208
  {
2117
2209
  name: "loadOptions",
@@ -2143,13 +2235,23 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
2143
2235
  {
2144
2236
  name: "renderOption",
2145
2237
  type: "(option: SearchSelectOptionProp) => React.ReactNode",
2146
- description: "Custom per-option renderer (Ant-Design style). Defaults to label + optional sublabel."
2238
+ description: "Custom per-option renderer for the dropdown ROWS (Ant-Design style). Defaults to label + optional sublabel. Does not change the trigger \u2014 use `labelRender` for that."
2239
+ },
2240
+ {
2241
+ name: "labelRender",
2242
+ type: "(selected: { value: string; label: React.ReactNode; option?: SearchSelectOptionProp }) => React.ReactNode",
2243
+ description: "Custom renderer for the SELECTED value shown on the TRIGGER (Ant Design `labelRender`) \u2014 avatar + name + role badge, etc. `option` is undefined for an async preset whose page hasn't loaded. Only used while a value is selected; the placeholder still shows when empty."
2147
2244
  },
2148
2245
  {
2149
2246
  name: "selectedLabel",
2150
2247
  type: "string",
2151
2248
  description: "Display label for the current value when its option is not in the loaded page (async). Prevents a flash of the raw id."
2152
2249
  },
2250
+ {
2251
+ name: "selectedIcon",
2252
+ type: "React.ReactNode",
2253
+ description: "Leading icon shown on the trigger for the current value when its option isn't loaded yet (async preset) \u2014 the trigger counterpart of `selectedLabel`, so an edit form pre-filled from the server shows the avatar/flag at rest."
2254
+ },
2153
2255
  {
2154
2256
  name: "placeholder",
2155
2257
  type: "string",
@@ -2398,6 +2500,17 @@ export function PrioritySelect({ value, onValueChange }) {
2398
2500
  name: "onChange",
2399
2501
  type: "React.ChangeEventHandler<HTMLTextAreaElement>",
2400
2502
  description: "Change handler."
2503
+ },
2504
+ {
2505
+ name: "allowClear",
2506
+ type: "boolean",
2507
+ defaultValue: "false",
2508
+ description: "Opt-in inline \u2715 at the top-end that clears the field while it holds text (controlled + uncontrolled). Off by default."
2509
+ },
2510
+ {
2511
+ name: "onClear",
2512
+ type: "() => void",
2513
+ description: "Called after the field is cleared via the inline \u2715 (requires `allowClear`)."
2401
2514
  }
2402
2515
  ],
2403
2516
  usage: [
@@ -2532,6 +2645,21 @@ export function PrioritySelect({ value, onValueChange }) {
2532
2645
  type: '"horizontal" | "vertical"',
2533
2646
  defaultValue: '"vertical"',
2534
2647
  description: "Layout direction."
2648
+ },
2649
+ {
2650
+ name: "defaultValue",
2651
+ type: "string",
2652
+ description: "Uncontrolled initial selected value."
2653
+ },
2654
+ {
2655
+ name: "disabled",
2656
+ type: "boolean",
2657
+ description: "Disable the whole group."
2658
+ },
2659
+ {
2660
+ name: "name",
2661
+ type: "string",
2662
+ description: "Form field name for native submission."
2535
2663
  }
2536
2664
  ],
2537
2665
  usage: [
@@ -2565,6 +2693,184 @@ export function PrioritySelect({ value, onValueChange }) {
2565
2693
  storyPath: "data-entry/RadioGroup.stories.tsx",
2566
2694
  rules: [23]
2567
2695
  },
2696
+ {
2697
+ name: "MonthPicker",
2698
+ group: "data-entry",
2699
+ tagline: "Year/month (yyyy/MM) input with an Ant-Design-style month-grid popover \u2014 a year chevron header over a 3x4 grid of the twelve months. The input stays typeable; the grid is the visual affordance.",
2700
+ props: [
2701
+ {
2702
+ name: "value",
2703
+ type: "Date | undefined",
2704
+ description: "Controlled value \u2014 first day of the selected month. Pass undefined to clear."
2705
+ },
2706
+ {
2707
+ name: "defaultValue",
2708
+ type: "Date | undefined",
2709
+ description: "Uncontrolled initial value."
2710
+ },
2711
+ {
2712
+ name: "onValueChange",
2713
+ type: "(value: Date | undefined) => void",
2714
+ description: "Fires on a grid pick, on a complete typed yyyy/MM, and on clear."
2715
+ },
2716
+ {
2717
+ name: "placeholder",
2718
+ type: "string",
2719
+ defaultValue: '"yyyy/mm"',
2720
+ description: "Placeholder shown while empty."
2721
+ },
2722
+ {
2723
+ name: "disabled",
2724
+ type: "boolean",
2725
+ defaultValue: "false",
2726
+ description: "Disables the input, the clear button and the grid trigger."
2727
+ },
2728
+ {
2729
+ name: "className",
2730
+ type: "string",
2731
+ description: "Extra classes on the control shell (width/margin overrides)."
2732
+ },
2733
+ {
2734
+ name: "id",
2735
+ type: "string",
2736
+ description: "Wired to the inner input; auto-generated when omitted so the field always has an id."
2737
+ },
2738
+ {
2739
+ name: "name",
2740
+ type: "string",
2741
+ description: "Form field name \u2014 submits the display text (yyyy/MM)."
2742
+ },
2743
+ {
2744
+ name: "fromYear",
2745
+ type: "number",
2746
+ description: "Inclusive lower bound for the year navigation."
2747
+ },
2748
+ {
2749
+ name: "toYear",
2750
+ type: "number",
2751
+ description: "Inclusive upper bound for the year navigation."
2752
+ },
2753
+ {
2754
+ name: "allowClear",
2755
+ type: "boolean",
2756
+ defaultValue: "true",
2757
+ description: "Inline clear button while a value is set."
2758
+ }
2759
+ ],
2760
+ usage: [
2761
+ "DO use MonthPicker for every yyyy/MM (year-month) field \u2014 never a bare Input with a YYYY/MM helper text.",
2762
+ "DO wrap it in FormField like every other labelled control; FormField injects id/aria wiring.",
2763
+ "DO NOT compose two MonthPickers to fake a from~to range \u2014 that is MonthRangePicker (one control shell, like DateRangePicker)."
2764
+ ],
2765
+ related: [
2766
+ "DatePicker \u2014 full date (yyyy-MM-dd); MonthPicker when the day is meaningless (\u7DE0\u3081\u5E74\u6708, \u96C6\u8A08\u5E74\u6708, \u5546\u8AC7\u767A\u751F\u5E74\u6708).",
2767
+ "MonthRangePicker \u2014 use for a yyyy/MM from~to pair; one input-styled control, never two MonthPickers side-by-side."
2768
+ ],
2769
+ example: `import { useState } from "react";
2770
+ import { MonthPicker, FormField } from "@godxjp/ui/data-entry";
2771
+
2772
+ export function OrderMonthField() {
2773
+ const [ym, setYm] = useState<Date | undefined>(undefined);
2774
+
2775
+ return (
2776
+ <FormField label="\u53D7\u6CE8\u65E5\u5E74\u6708">
2777
+ <MonthPicker name="search_order_date_ym" value={ym} onValueChange={setYm} />
2778
+ </FormField>
2779
+ );
2780
+ }`,
2781
+ storyPath: "data-entry/MonthPicker.stories.tsx",
2782
+ rules: [3, 6, 13, 31, 43]
2783
+ },
2784
+ {
2785
+ name: "MonthRangePicker",
2786
+ group: "data-entry",
2787
+ tagline: "Year/month (yyyy/MM) RANGE rendered as ONE input-styled control `[ from \u2192 to \u2715 \u{1F4C5} ]` (Ant RangePicker convention, same shell as DateRangePicker) with an Ant-style month-grid popover. Both inputs stay typeable; picks are two-step with from \u2264 to always enforced.",
2788
+ props: [
2789
+ {
2790
+ name: "value",
2791
+ type: "DateRange | undefined",
2792
+ description: "Controlled range \u2014 both edges normalized to the first day of their month. Pass undefined to clear."
2793
+ },
2794
+ {
2795
+ name: "defaultValue",
2796
+ type: "DateRange | undefined",
2797
+ description: "Uncontrolled initial range."
2798
+ },
2799
+ {
2800
+ name: "onValueChange",
2801
+ type: "(value: DateRange | undefined) => void",
2802
+ description: "Fires on each grid step ({from, to: undefined} then the complete pair), on a complete typed yyyy/MM at either edge, and on clear. Never emits an inverted range \u2014 a backwards pick/typing is swapped so from \u2264 to."
2803
+ },
2804
+ {
2805
+ name: "placeholder",
2806
+ type: "string",
2807
+ defaultValue: '"yyyy/mm"',
2808
+ description: "Placeholder shown in both inputs while empty."
2809
+ },
2810
+ {
2811
+ name: "disabled",
2812
+ type: "boolean",
2813
+ defaultValue: "false",
2814
+ description: "Disables both inputs, the clear button and the grid trigger."
2815
+ },
2816
+ {
2817
+ name: "className",
2818
+ type: "string",
2819
+ description: "Extra classes on the control shell (width/margin overrides)."
2820
+ },
2821
+ {
2822
+ name: "id",
2823
+ type: "string",
2824
+ description: "Wired to the from input; the to input gets `${id}-to`. Auto-generated when omitted."
2825
+ },
2826
+ {
2827
+ name: "name",
2828
+ type: "string",
2829
+ description: "Form field name \u2014 emits the range as `${name}_from` / `${name}_to` yyyy/MM fields."
2830
+ },
2831
+ {
2832
+ name: "fromYear",
2833
+ type: "number",
2834
+ description: "Inclusive lower bound for the year navigation."
2835
+ },
2836
+ {
2837
+ name: "toYear",
2838
+ type: "number",
2839
+ description: "Inclusive upper bound for the year navigation."
2840
+ },
2841
+ {
2842
+ name: "allowClear",
2843
+ type: "boolean",
2844
+ defaultValue: "true",
2845
+ description: "Inline clear button (clears the WHOLE range) while a value is set."
2846
+ }
2847
+ ],
2848
+ usage: [
2849
+ "DO use MonthRangePicker for every yyyy/MM from~to pair \u2014 never two MonthPickers (or bare Inputs) separated by ~; a range is ONE control, exactly like DateRangePicker.",
2850
+ "DO rely on its built-in range validation: a backwards grid pick or typed pair is swap-normalized so the emitted range always satisfies from \u2264 to \u2014 do not re-validate order in the app.",
2851
+ "DO wrap it in FormField like every other labelled control; FormField injects id/aria wiring.",
2852
+ "Grid picks are two-step (from, then to) and reset-on-complete: picking while a complete range is held STARTS a new range, so the start month is never stuck."
2853
+ ],
2854
+ related: [
2855
+ "MonthPicker \u2014 single yyyy/MM value; MonthRangePicker when the field is a from~to pair (\u5546\u8AC7\u767A\u751F\u5E74\u6708\u306E\u7BC4\u56F2\u691C\u7D22, \u96C6\u8A08\u671F\u9593).",
2856
+ "DateRangePicker \u2014 full-date (yyyy-MM-dd) range with the same one-control shell; MonthRangePicker when the day is meaningless."
2857
+ ],
2858
+ example: `import { useState } from "react";
2859
+ import type { DateRange } from "react-day-picker";
2860
+ import { MonthRangePicker, FormField } from "@godxjp/ui/data-entry";
2861
+
2862
+ export function NegotiationYmField() {
2863
+ const [range, setRange] = useState<DateRange | undefined>(undefined);
2864
+
2865
+ return (
2866
+ <FormField label="\u5546\u8AC7\u767A\u751F\u5E74\u6708">
2867
+ <MonthRangePicker name="search_negotiation_ym" value={range} onValueChange={setRange} />
2868
+ </FormField>
2869
+ );
2870
+ }`,
2871
+ storyPath: "data-entry/MonthRangePicker.stories.tsx",
2872
+ rules: [3, 6, 13, 31, 43]
2873
+ },
2568
2874
  {
2569
2875
  name: "DatePicker",
2570
2876
  group: "data-entry",
@@ -2621,6 +2927,22 @@ export function PrioritySelect({ value, onValueChange }) {
2621
2927
  name: "toDate",
2622
2928
  type: "Date",
2623
2929
  description: "Latest selectable date in the calendar. Days after this date are disabled in the grid, and the calendar navigation ends at this month."
2930
+ },
2931
+ {
2932
+ name: "allowClear",
2933
+ type: "boolean",
2934
+ defaultValue: "true",
2935
+ description: "Inline \u2715 on the trigger that resets the value when one is set (Ant-style). Pass `false` to hide it (e.g. a required field)."
2936
+ },
2937
+ {
2938
+ name: "defaultValue",
2939
+ type: "Date",
2940
+ description: "Uncontrolled initial date."
2941
+ },
2942
+ {
2943
+ name: "onValueChange",
2944
+ type: "(value: Date | undefined) => void",
2945
+ description: "Fires with the selected date (undefined when cleared)."
2624
2946
  }
2625
2947
  ],
2626
2948
  usage: [
@@ -2889,6 +3211,11 @@ import { Button } from "@godxjp/ui/general";
2889
3211
  name: "icon",
2890
3212
  type: "LucideIcon | false",
2891
3213
  description: "Override or hide (false) the icon."
3214
+ },
3215
+ {
3216
+ name: "tone",
3217
+ type: '"success" | "warning" | "destructive" | "info" | "neutral"',
3218
+ description: "Semantic tone driving the colour + leading icon."
2892
3219
  }
2893
3220
  ],
2894
3221
  usage: [
@@ -3080,6 +3407,16 @@ toast.error("\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F");`,
3080
3407
  name: "onValueChange",
3081
3408
  type: "(page: number, pageSize: number) => void",
3082
3409
  description: "Page / page-size change handler."
3410
+ },
3411
+ {
3412
+ name: "pageSizeOptions",
3413
+ type: "number[]",
3414
+ description: "Selectable page sizes shown in the size changer."
3415
+ },
3416
+ {
3417
+ name: "showSizeChanger",
3418
+ type: "boolean",
3419
+ description: "Show the page-size selector beside the pager."
3083
3420
  }
3084
3421
  ],
3085
3422
  usage: [
@@ -3184,6 +3521,31 @@ import { Button } from "@godxjp/ui/general";
3184
3521
  type: '"horizontal" | "vertical"',
3185
3522
  defaultValue: '"horizontal"',
3186
3523
  description: "Layout direction."
3524
+ },
3525
+ {
3526
+ name: "status",
3527
+ type: '"wait" | "process" | "finish" | "error"',
3528
+ description: "Status of the CURRENT step (drives the active step colour)."
3529
+ },
3530
+ {
3531
+ name: "type",
3532
+ type: '"default" | "dot"',
3533
+ description: "Render full steps or compact dots."
3534
+ },
3535
+ {
3536
+ name: "size",
3537
+ type: '"md" | "sm"',
3538
+ description: "Step size."
3539
+ },
3540
+ {
3541
+ name: "titlePlacement",
3542
+ type: '"horizontal" | "vertical"',
3543
+ description: "Lay step titles beside or below the step icons."
3544
+ },
3545
+ {
3546
+ name: "onValueChange",
3547
+ type: "(value: number) => void",
3548
+ description: "Fires with the clicked step index (0-based)."
3187
3549
  }
3188
3550
  ],
3189
3551
  usage: [
@@ -3373,6 +3735,17 @@ formatDate(order.createdAt, { kind: "relative" }); // "3\u65E5\u524D"`,
3373
3735
  name: "className",
3374
3736
  type: "string",
3375
3737
  description: "Extra Tailwind classes applied to the outer wrapper `<div>`. Use for width overrides (e.g. `w-32`)."
3738
+ },
3739
+ {
3740
+ name: "allowClear",
3741
+ type: "boolean",
3742
+ defaultValue: "true",
3743
+ description: "Inline \u2715 on the trigger that resets the value when one is set (Ant-style). Pass `false` to hide it (e.g. a required field)."
3744
+ },
3745
+ {
3746
+ name: "onValueChange",
3747
+ type: "(value: string) => void",
3748
+ description: "Fires with the canonical 24h `HH:mm` string (empty when cleared)."
3376
3749
  }
3377
3750
  ],
3378
3751
  usage: [
@@ -3493,12 +3866,28 @@ export function CutoffTimeForm() {
3493
3866
  name: "className",
3494
3867
  type: "string",
3495
3868
  description: "Extra CSS classes applied to the root flex container (flex items-center gap-1). Use to constrain width or adjust layout; avoid overriding token colors."
3496
- }
3497
- ],
3498
- usage: [
3499
- "DO use controlled mode (`value` + `onChange`) in all form contexts \u2014 this component has no `defaultValue` prop; initialize state with `useState<DateRange | undefined>()`.",
3500
- "DO set `name` when the form is submitted natively or via Inertia useForm: the component emits `${name}_from` and `${name}_to` as ISO yyyy-MM-dd strings \u2014 read them as separate fields on the server.",
3501
- 'DO wrap in `<FormField id="..." label="...">` to attach the label; pass the same string to both `FormField`\'s `id` and `DateRangePicker`\'s `id` so the label targets the FROM input.',
3869
+ },
3870
+ {
3871
+ name: "allowClear",
3872
+ type: "boolean",
3873
+ defaultValue: "true",
3874
+ description: "Inline \u2715 on the trigger that resets the range when one is set (Ant-style). Pass `false` to hide it."
3875
+ },
3876
+ {
3877
+ name: "defaultValue",
3878
+ type: "DateRange",
3879
+ description: "Uncontrolled initial range."
3880
+ },
3881
+ {
3882
+ name: "onValueChange",
3883
+ type: "(value: DateRange | undefined) => void",
3884
+ description: "Fires with the selected range (undefined when cleared)."
3885
+ }
3886
+ ],
3887
+ usage: [
3888
+ "DO use controlled mode (`value` + `onChange`) in all form contexts \u2014 this component has no `defaultValue` prop; initialize state with `useState<DateRange | undefined>()`.",
3889
+ "DO set `name` when the form is submitted natively or via Inertia useForm: the component emits `${name}_from` and `${name}_to` as ISO yyyy-MM-dd strings \u2014 read them as separate fields on the server.",
3890
+ 'DO wrap in `<FormField id="..." label="...">` to attach the label; pass the same string to both `FormField`\'s `id` and `DateRangePicker`\'s `id` so the label targets the FROM input.',
3502
3891
  "DON'T compose two `<DatePicker>` components side-by-side to achieve a range \u2014 `DateRangePicker` handles range state, calendar highlight, and dual form submission in one atomic control.",
3503
3892
  "DON'T rely on the calendar popover alone for e2e testing \u2014 both inputs are real typeable `<input>` elements; fill them directly with ISO strings (e.g. `fill('#from-id', '2026-01-01')`) in Playwright/Pest browser tests.",
3504
3893
  "Use `fromDate` / `toDate` to constrain the selectable window (e.g. fiscal year bounds, invoice cutoff), not just visual decoration \u2014 they also disable out-of-range keyboard navigation in the calendar."
@@ -3972,6 +4361,11 @@ export function DepartmentFilter() {
3972
4361
  name: "onSelectChange",
3973
4362
  type: "(sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void",
3974
4363
  description: "Called whenever the checked selection in either panel changes. Provides updated arrays for source and target selections. Required when `selectedKeys` is controlled."
4364
+ },
4365
+ {
4366
+ name: "onValueChange",
4367
+ type: '(targetKeys: string[], direction: "left" | "right", moveKeys: string[]) => void',
4368
+ description: "Fires when items move between panels; you own `targetKeys` state."
3975
4369
  }
3976
4370
  ],
3977
4371
  usage: [
@@ -4094,6 +4488,11 @@ export function AccountMapping() {
4094
4488
  name: "children",
4095
4489
  type: "React.ReactNode",
4096
4490
  description: "Custom button label for variant='button'. Falls back to the i18n 'Upload file' string."
4491
+ },
4492
+ {
4493
+ name: "onValueChange",
4494
+ type: "(items: UploadFileItemProp[]) => void",
4495
+ description: "Fires with the current file list."
4097
4496
  }
4098
4497
  ],
4099
4498
  usage: [
@@ -4303,6 +4702,11 @@ export function AvatarField() {
4303
4702
  type: "string",
4304
4703
  defaultValue: "undefined",
4305
4704
  description: "DOM id applied to the hidden native <input type='color'>. Pass the FormField id here so the label's htmlFor targets this control correctly."
4705
+ },
4706
+ {
4707
+ name: "onValueChange",
4708
+ type: "(value: string) => void",
4709
+ description: "Fires with the committed hex string."
4306
4710
  }
4307
4711
  ],
4308
4712
  usage: [
@@ -5017,6 +5421,11 @@ function AccountQuickPick({ onSelect }: { onSelect: (id: string) => void }) {
5017
5421
  name: "children",
5018
5422
  type: "React.ReactNode",
5019
5423
  description: "Manual children mode: used when `options` is omitted or empty. Render Checkbox items directly as children. You are responsible for composing each Checkbox with a Field for correct label/description layout."
5424
+ },
5425
+ {
5426
+ name: "onValueChange",
5427
+ type: "(value: string[]) => void",
5428
+ description: "Fires with the checked values array."
5020
5429
  }
5021
5430
  ],
5022
5431
  usage: [
@@ -6814,6 +7223,18 @@ var TOKENS = [
6814
7223
  },
6815
7224
  { name: "--info", category: "semantic", tier: "semantic", role: "Information status role." },
6816
7225
  { name: "--attention", category: "semantic", tier: "semantic", role: "Attention status role." },
7226
+ {
7227
+ name: "--page-header-divider",
7228
+ category: "semantic",
7229
+ tier: "semantic",
7230
+ role: "PageContainer header bottom divider. Default none; a service theme opts in with `1px solid hsl(var(--border))`."
7231
+ },
7232
+ {
7233
+ name: "--page-header-pad-bottom",
7234
+ category: "semantic",
7235
+ tier: "semantic",
7236
+ role: "PageContainer header bottom inset. Defaults to page top padding minus the section gap so the title band is vertically balanced."
7237
+ },
6817
7238
  { name: "--badge-space-*", category: "component", tier: "component", role: "Badge spacing." },
6818
7239
  {
6819
7240
  name: "--card-*",
@@ -6828,6 +7249,18 @@ var TOKENS = [
6828
7249
  role: "Shared form control heights, padding, icons, and focus chrome."
6829
7250
  },
6830
7251
  { name: "--table-*", category: "component", tier: "component", role: "Table row/cell sizing." },
7252
+ {
7253
+ name: "--form-label-width",
7254
+ category: "component",
7255
+ tier: "component",
7256
+ role: "Label column width in horizontal Form layout. Default max-content; a service theme sets it once (e.g. 110px) \u2014 the labelWidth prop overrides per form/field."
7257
+ },
7258
+ {
7259
+ name: "--form-label-gap",
7260
+ category: "component",
7261
+ tier: "component",
7262
+ role: "Label\u2194control column gap in horizontal Form layout. Default 16px (--space-4)."
7263
+ },
6831
7264
  {
6832
7265
  name: "--dialog-* / --alert-* / --skeleton-*",
6833
7266
  category: "component",
@@ -6839,6 +7272,715 @@ function tokensByCategory(category) {
6839
7272
  return TOKENS.filter((t) => t.category === category);
6840
7273
  }
6841
7274
 
7275
+ // src/data/component-tokens.generated.ts
7276
+ var COMPONENT_TOKENS = [
7277
+ {
7278
+ "name": "--badge-space-gap",
7279
+ "value": "var(--space-inline-xs)",
7280
+ "description": "Badge component tokens."
7281
+ },
7282
+ {
7283
+ "name": "--badge-space-x",
7284
+ "value": "var(--space-2)",
7285
+ "description": "Badge component tokens."
7286
+ },
7287
+ {
7288
+ "name": "--badge-space-y",
7289
+ "value": "var(--space-1)",
7290
+ "description": "Badge component tokens."
7291
+ },
7292
+ {
7293
+ "name": "--badge-font-size",
7294
+ "value": "var(--font-size-xs)",
7295
+ "description": "Small-by-design (badge/pill/counter). A knob (rule #45) so a service can * re-tune badge text without touching the global --font-size-xs step."
7296
+ },
7297
+ {
7298
+ "name": "--card-space-inset",
7299
+ "value": "var(--space-section-active)",
7300
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7301
+ },
7302
+ {
7303
+ "name": "--card-space-header-y",
7304
+ "value": "var(--space-stack-sm)",
7305
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7306
+ },
7307
+ {
7308
+ "name": "--card-space-body-y",
7309
+ "value": "var(--space-section-active)",
7310
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7311
+ },
7312
+ {
7313
+ "name": "--card-space-footer-y",
7314
+ "value": "var(--space-stack-sm)",
7315
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7316
+ },
7317
+ {
7318
+ "name": "--card-space-gap",
7319
+ "value": "var(--space-stack-xs)",
7320
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7321
+ },
7322
+ {
7323
+ "name": "--card-title-font-size",
7324
+ "value": "var(--font-size-base)",
7325
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7326
+ },
7327
+ {
7328
+ "name": "--card-title-line-height",
7329
+ "value": "var(--line-height-tight)",
7330
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7331
+ },
7332
+ {
7333
+ "name": "--card-title-font-weight",
7334
+ "value": "var(--font-weight-semibold)",
7335
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7336
+ },
7337
+ {
7338
+ "name": "--card-description-font-size",
7339
+ "value": "var(--font-size-sm)",
7340
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7341
+ },
7342
+ {
7343
+ "name": "--card-description-line-height",
7344
+ "value": "var(--line-height-normal)",
7345
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7346
+ },
7347
+ {
7348
+ "name": "--card-background",
7349
+ "value": "var(--card)",
7350
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7351
+ },
7352
+ {
7353
+ "name": "--card-border",
7354
+ "value": "var(--border)",
7355
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7356
+ },
7357
+ {
7358
+ "name": "--card-header-background",
7359
+ "value": "var(--muted)",
7360
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7361
+ },
7362
+ {
7363
+ "name": "--card-header-background-alpha",
7364
+ "value": "0.55",
7365
+ "description": "Card component tokens: card chrome derives from semantic layout tokens."
7366
+ },
7367
+ {
7368
+ "name": "--card-header-border-bottom",
7369
+ "value": "1px solid hsl(var(--card-border))",
7370
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7371
+ },
7372
+ {
7373
+ "name": "--card-radius",
7374
+ "value": "var(--radius)",
7375
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7376
+ },
7377
+ {
7378
+ "name": "--stat-card-label-font-size",
7379
+ "value": "var(--font-size-xs)",
7380
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7381
+ },
7382
+ {
7383
+ "name": "--stat-card-label-font-weight",
7384
+ "value": "var(--font-weight-medium)",
7385
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7386
+ },
7387
+ {
7388
+ "name": "--stat-card-label-letter-spacing",
7389
+ "value": "0.04em",
7390
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7391
+ },
7392
+ {
7393
+ "name": "--stat-card-value-font-size",
7394
+ "value": "var(--font-size-2xl)",
7395
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7396
+ },
7397
+ {
7398
+ "name": "--stat-card-value-line-height",
7399
+ "value": "1.1",
7400
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7401
+ },
7402
+ {
7403
+ "name": "--stat-card-value-font-weight",
7404
+ "value": "var(--font-weight-semibold)",
7405
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7406
+ },
7407
+ {
7408
+ "name": "--stat-card-hint-font-size",
7409
+ "value": "var(--font-size-xs)",
7410
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7411
+ },
7412
+ {
7413
+ "name": "--stat-card-gap",
7414
+ "value": "var(--space-stack-xs)",
7415
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7416
+ },
7417
+ {
7418
+ "name": "--stat-card-icon-size",
7419
+ "value": "2.25rem",
7420
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7421
+ },
7422
+ {
7423
+ "name": "--stat-card-delta-font-size",
7424
+ "value": "var(--font-size-xs)",
7425
+ "description": "Banded-header divider \u2014 tokenised (rule #44) so a service theme can make it * dashed / heavier / none without forking CSS. Pair with * --card-header-background-alpha: 0 for a quiet borderless-band header."
7426
+ },
7427
+ {
7428
+ "name": "--control-height-compact",
7429
+ "value": "1.75rem",
7430
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7431
+ },
7432
+ {
7433
+ "name": "--control-height-default",
7434
+ "value": "2rem",
7435
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7436
+ },
7437
+ {
7438
+ "name": "--control-height-comfortable",
7439
+ "value": "2.75rem",
7440
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7441
+ },
7442
+ {
7443
+ "name": "--control-padding-x-compact",
7444
+ "value": "var(--space-2)",
7445
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7446
+ },
7447
+ {
7448
+ "name": "--control-padding-x-default",
7449
+ "value": "var(--space-3)",
7450
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7451
+ },
7452
+ {
7453
+ "name": "--control-padding-x-comfortable",
7454
+ "value": "var(--space-4)",
7455
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7456
+ },
7457
+ {
7458
+ "name": "--control-height",
7459
+ "value": "var(--control-height-default)",
7460
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes."
7461
+ },
7462
+ {
7463
+ "name": "--control-height-sm",
7464
+ "value": "calc(var(--control-height) - 0.25rem)",
7465
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7466
+ },
7467
+ {
7468
+ "name": "--control-height-lg",
7469
+ "value": "calc(var(--control-height) + 0.25rem)",
7470
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7471
+ },
7472
+ {
7473
+ "name": "--control-height-xs",
7474
+ "value": "calc(var(--control-height) - 0.5rem)",
7475
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7476
+ },
7477
+ {
7478
+ "name": "--control-padding-x",
7479
+ "value": "var(--control-padding-x-default)",
7480
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7481
+ },
7482
+ {
7483
+ "name": "--control-gap",
7484
+ "value": "var(--space-inline-sm)",
7485
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7486
+ },
7487
+ {
7488
+ "name": "--control-gap-sm",
7489
+ "value": "var(--space-inline-xs)",
7490
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7491
+ },
7492
+ {
7493
+ "name": "--control-radius",
7494
+ "value": "var(--radius)",
7495
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7496
+ },
7497
+ {
7498
+ "name": "--control-icon-size",
7499
+ "value": "1rem",
7500
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7501
+ },
7502
+ {
7503
+ "name": "--control-icon-size-sm",
7504
+ "value": "0.875rem",
7505
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7506
+ },
7507
+ {
7508
+ "name": "--control-focus-ring-width",
7509
+ "value": "2px",
7510
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7511
+ },
7512
+ {
7513
+ "name": "--checkbox-size",
7514
+ "value": "1rem",
7515
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7516
+ },
7517
+ {
7518
+ "name": "--checkbox-size-compact",
7519
+ "value": "0.875rem",
7520
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7521
+ },
7522
+ {
7523
+ "name": "--checkbox-size-comfortable",
7524
+ "value": "1.125rem",
7525
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7526
+ },
7527
+ {
7528
+ "name": "--choice-gap",
7529
+ "value": "var(--space-inline-sm)",
7530
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7531
+ },
7532
+ {
7533
+ "name": "--choice-group-gap-x",
7534
+ "value": "var(--space-6)",
7535
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7536
+ },
7537
+ {
7538
+ "name": "--choice-group-gap-y",
7539
+ "value": "var(--space-3)",
7540
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7541
+ },
7542
+ {
7543
+ "name": "--choice-description-gap",
7544
+ "value": "0.125rem",
7545
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7546
+ },
7547
+ {
7548
+ "name": "--choice-control-offset",
7549
+ "value": "0.125rem",
7550
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7551
+ },
7552
+ {
7553
+ "name": "--switch-width",
7554
+ "value": "2.25rem",
7555
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7556
+ },
7557
+ {
7558
+ "name": "--switch-width-compact",
7559
+ "value": "2rem",
7560
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7561
+ },
7562
+ {
7563
+ "name": "--switch-width-comfortable",
7564
+ "value": "2.5rem",
7565
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7566
+ },
7567
+ {
7568
+ "name": "--switch-height",
7569
+ "value": "1.25rem",
7570
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7571
+ },
7572
+ {
7573
+ "name": "--switch-height-compact",
7574
+ "value": "1.125rem",
7575
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7576
+ },
7577
+ {
7578
+ "name": "--switch-height-comfortable",
7579
+ "value": "1.375rem",
7580
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7581
+ },
7582
+ {
7583
+ "name": "--switch-thumb-size",
7584
+ "value": "1rem",
7585
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7586
+ },
7587
+ {
7588
+ "name": "--switch-thumb-size-compact",
7589
+ "value": "0.875rem",
7590
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7591
+ },
7592
+ {
7593
+ "name": "--switch-thumb-size-comfortable",
7594
+ "value": "1.125rem",
7595
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7596
+ },
7597
+ {
7598
+ "name": "--switch-thumb-translate",
7599
+ "value": "1rem",
7600
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7601
+ },
7602
+ {
7603
+ "name": "--switch-thumb-translate-compact",
7604
+ "value": "0.875rem",
7605
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7606
+ },
7607
+ {
7608
+ "name": "--switch-thumb-translate-comfortable",
7609
+ "value": "1.125rem",
7610
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7611
+ },
7612
+ {
7613
+ "name": "--slider-track-height",
7614
+ "value": "0.375rem",
7615
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7616
+ },
7617
+ {
7618
+ "name": "--slider-thumb-size",
7619
+ "value": "1rem",
7620
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7621
+ },
7622
+ {
7623
+ "name": "--color-picker-input-width",
7624
+ "value": "6.5rem",
7625
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7626
+ },
7627
+ {
7628
+ "name": "--command-list-max-height",
7629
+ "value": "min(300px, 50vh)",
7630
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7631
+ },
7632
+ {
7633
+ "name": "--command-input-padding-x",
7634
+ "value": "var(--space-3)",
7635
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7636
+ },
7637
+ {
7638
+ "name": "--command-group-padding",
7639
+ "value": "var(--space-1)",
7640
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7641
+ },
7642
+ {
7643
+ "name": "--command-item-padding-y",
7644
+ "value": "var(--space-2)",
7645
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7646
+ },
7647
+ {
7648
+ "name": "--command-item-padding-x",
7649
+ "value": "var(--space-2)",
7650
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7651
+ },
7652
+ {
7653
+ "name": "--search-input-edge-inset",
7654
+ "value": "var(--space-3)",
7655
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7656
+ },
7657
+ {
7658
+ "name": "--search-input-start-padding",
7659
+ "value": "calc( var(--search-input-edge-inset) + var(--control-icon-size) + var(--control-gap) )",
7660
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7661
+ },
7662
+ {
7663
+ "name": "--search-input-end-padding",
7664
+ "value": "calc( var(--search-input-edge-inset) + var(--control-icon-size) + var(--control-gap) )",
7665
+ "description": "Adjacent control sizes, derived from the active --control-height. * Default/compact use a 4px/8px step; density overrides comfortable * (which steps 44\u219236\u219232 per the design's spacing-density spec)."
7666
+ },
7667
+ {
7668
+ "name": "--control-height-compact",
7669
+ "value": "2.75rem",
7670
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7671
+ },
7672
+ {
7673
+ "name": "--control-height-default",
7674
+ "value": "2.75rem",
7675
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7676
+ },
7677
+ {
7678
+ "name": "--choice-description-font-size",
7679
+ "value": "var(--font-size-xs)",
7680
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7681
+ },
7682
+ {
7683
+ "name": "--color-picker-hex-font-size",
7684
+ "value": "var(--font-size-xs)",
7685
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7686
+ },
7687
+ {
7688
+ "name": "--command-group-heading-font-size",
7689
+ "value": "var(--font-size-xs)",
7690
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7691
+ },
7692
+ {
7693
+ "name": "--search-input-label-font-size",
7694
+ "value": "var(--font-size-xs)",
7695
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7696
+ },
7697
+ {
7698
+ "name": "--tag-input-chip-font-size",
7699
+ "value": "var(--font-size-xs)",
7700
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7701
+ },
7702
+ {
7703
+ "name": "--toggle-sm-font-size",
7704
+ "value": "var(--font-size-xs)",
7705
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7706
+ },
7707
+ {
7708
+ "name": "--button-sm-font-size",
7709
+ "value": "var(--font-size-xs)",
7710
+ "description": "Rule #24 \u2014 on touch devices (coarse pointer) interactive controls keep a \u226544px tap target * regardless of density; desktop (fine pointer) keeps the compact heights above. --control-height * resolves through these via var(), so inputs/buttons/selects/table rows all bump together."
7711
+ },
7712
+ {
7713
+ "name": "--progress-label-font-size",
7714
+ "value": "var(--font-size-xs)",
7715
+ "description": "Data-display component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7716
+ },
7717
+ {
7718
+ "name": "--tree-item-title-font-size",
7719
+ "value": "var(--font-size-xs)",
7720
+ "description": "Data-display component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7721
+ },
7722
+ {
7723
+ "name": "--tree-item-description-font-size",
7724
+ "value": "var(--font-size-xs)",
7725
+ "description": "Data-display component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7726
+ },
7727
+ {
7728
+ "name": "--timeline-note-font-size",
7729
+ "value": "var(--font-size-xs)",
7730
+ "description": "Data-display component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7731
+ },
7732
+ {
7733
+ "name": "--password-strength-score-font-size",
7734
+ "value": "var(--font-size-xs)",
7735
+ "description": "Data-entry component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7736
+ },
7737
+ {
7738
+ "name": "--password-strength-checklist-font-size",
7739
+ "value": "var(--font-size-xs)",
7740
+ "description": "Data-entry component tokens \u2014 small-by-design text knobs (rule #45/#46)."
7741
+ },
7742
+ {
7743
+ "name": "--dialog-space-x",
7744
+ "value": "var(--space-chrome-x)",
7745
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7746
+ },
7747
+ {
7748
+ "name": "--dialog-space-y",
7749
+ "value": "var(--space-chrome-y)",
7750
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7751
+ },
7752
+ {
7753
+ "name": "--dialog-space-inset",
7754
+ "value": "var(--dialog-space-y) var(--dialog-space-x)",
7755
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7756
+ },
7757
+ {
7758
+ "name": "--dialog-space-gap",
7759
+ "value": "var(--space-stack-md)",
7760
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7761
+ },
7762
+ {
7763
+ "name": "--dialog-close-space-offset",
7764
+ "value": "var(--space-4)",
7765
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7766
+ },
7767
+ {
7768
+ "name": "--alert-space-inset",
7769
+ "value": "var(--space-section-active)",
7770
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7771
+ },
7772
+ {
7773
+ "name": "--alert-space-gap",
7774
+ "value": "var(--space-inline-md)",
7775
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7776
+ },
7777
+ {
7778
+ "name": "--alert-inner-space-gap",
7779
+ "value": "var(--space-stack-sm)",
7780
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7781
+ },
7782
+ {
7783
+ "name": "--alert-dismiss-space-offset",
7784
+ "value": "var(--space-3)",
7785
+ "description": "Dialog inset defaults to the shared global chrome tokens (override --space-chrome-* once for the * whole system, or --dialog-space-x/-y for dialogs only)."
7786
+ },
7787
+ {
7788
+ "name": "--alert-bg-alpha",
7789
+ "value": "0.05",
7790
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7791
+ },
7792
+ {
7793
+ "name": "--alert-border-alpha",
7794
+ "value": "0.3",
7795
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7796
+ },
7797
+ {
7798
+ "name": "--empty-state-space-y",
7799
+ "value": "var(--space-10)",
7800
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7801
+ },
7802
+ {
7803
+ "name": "--empty-state-space-x",
7804
+ "value": "var(--space-6)",
7805
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7806
+ },
7807
+ {
7808
+ "name": "--skeleton-row-gap",
7809
+ "value": "var(--space-stack-sm)",
7810
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7811
+ },
7812
+ {
7813
+ "name": "--skeleton-cell-gap",
7814
+ "value": "var(--space-inline-lg)",
7815
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7816
+ },
7817
+ {
7818
+ "name": "--skeleton-card-inset",
7819
+ "value": "var(--space-section-active)",
7820
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7821
+ },
7822
+ {
7823
+ "name": "--skeleton-radius",
7824
+ "value": "var(--radius)",
7825
+ "description": "Soft (subtle) semantic tint ratios \u2014 themeable so a service can hit its exact spec * (a brand's success-bg/-border are often more present than the faint 5%/30% default)."
7826
+ },
7827
+ {
7828
+ "name": "--form-label-width",
7829
+ "value": "max-content",
7830
+ "description": "Width of the label column in horizontal/inline layout. A service theme sets * this once (e.g. 110px) to align every form to its design grid; the Form/ * FormField `labelWidth` prop overrides per form/field."
7831
+ },
7832
+ {
7833
+ "name": "--form-label-gap",
7834
+ "value": "var(--space-4)",
7835
+ "description": "Column gap between the label and its control in horizontal/inline layout."
7836
+ },
7837
+ {
7838
+ "name": "--pagination-gap",
7839
+ "value": "var(--space-inline-sm)",
7840
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7841
+ },
7842
+ {
7843
+ "name": "--pagination-item-gap",
7844
+ "value": "var(--space-inline-xs)",
7845
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7846
+ },
7847
+ {
7848
+ "name": "--pagination-size-width",
7849
+ "value": "5.5rem",
7850
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7851
+ },
7852
+ {
7853
+ "name": "--pagination-total-font-size",
7854
+ "value": "var(--font-size-sm)",
7855
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7856
+ },
7857
+ {
7858
+ "name": "--filter-bar-gap",
7859
+ "value": "var(--space-3)",
7860
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7861
+ },
7862
+ {
7863
+ "name": "--filter-bar-padding-y",
7864
+ "value": "var(--space-2)",
7865
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7866
+ },
7867
+ {
7868
+ "name": "--filter-label-font-size",
7869
+ "value": "var(--font-size-xs)",
7870
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7871
+ },
7872
+ {
7873
+ "name": "--filter-picker-width-sm",
7874
+ "value": "11rem",
7875
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7876
+ },
7877
+ {
7878
+ "name": "--filter-picker-width-md",
7879
+ "value": "14rem",
7880
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7881
+ },
7882
+ {
7883
+ "name": "--breadcrumb-font-size",
7884
+ "value": "var(--font-size-xs)",
7885
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7886
+ },
7887
+ {
7888
+ "name": "--menubar-shortcut-font-size",
7889
+ "value": "var(--font-size-xs)",
7890
+ "description": "Navigation primitive tokens: pagination, filters, compact pickers."
7891
+ },
7892
+ {
7893
+ "name": "--sidebar-section-label-font-size",
7894
+ "value": "var(--font-size-2xs)",
7895
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7896
+ },
7897
+ {
7898
+ "name": "--sidebar-product-tenant-font-size",
7899
+ "value": "var(--font-size-2xs)",
7900
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7901
+ },
7902
+ {
7903
+ "name": "--sidebar-badge-font-size",
7904
+ "value": "var(--font-size-2xs)",
7905
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7906
+ },
7907
+ {
7908
+ "name": "--sidebar-user-role-font-size",
7909
+ "value": "var(--font-size-2xs)",
7910
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7911
+ },
7912
+ {
7913
+ "name": "--sidebar-nav-sub-font-size",
7914
+ "value": "var(--font-size-xs)",
7915
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7916
+ },
7917
+ {
7918
+ "name": "--sidebar-flyout-title-font-size",
7919
+ "value": "var(--font-size-xs)",
7920
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7921
+ },
7922
+ {
7923
+ "name": "--topbar-chip-icon-font-size",
7924
+ "value": "var(--font-size-2xs)",
7925
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7926
+ },
7927
+ {
7928
+ "name": "--kbd-font-size",
7929
+ "value": "var(--font-size-2xs)",
7930
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7931
+ },
7932
+ {
7933
+ "name": "--sidebar-logo-mark-font-size",
7934
+ "value": "var(--font-size-xs)",
7935
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7936
+ },
7937
+ {
7938
+ "name": "--sidebar-avatar-font-size",
7939
+ "value": "var(--font-size-2xs)",
7940
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7941
+ },
7942
+ {
7943
+ "name": "--sidebar-user-name-font-size",
7944
+ "value": "var(--font-size-xs)",
7945
+ "description": "Shell (sidebar / topbar / kbd) component tokens \u2014 small-by-design text * knobs (rule #45/#46). A service re-tunes chrome text without moving the * global scale."
7946
+ },
7947
+ {
7948
+ "name": "--table-row-height-compact",
7949
+ "value": "1.75rem",
7950
+ "description": "Table component tokens: row height, cell padding."
7951
+ },
7952
+ {
7953
+ "name": "--table-row-height-default",
7954
+ "value": "2rem",
7955
+ "description": "Table component tokens: row height, cell padding."
7956
+ },
7957
+ {
7958
+ "name": "--table-row-height-comfortable",
7959
+ "value": "2.75rem",
7960
+ "description": "Table component tokens: row height, cell padding."
7961
+ },
7962
+ {
7963
+ "name": "--table-row-height",
7964
+ "value": "var(--table-row-height-default)",
7965
+ "description": "Table component tokens: row height, cell padding."
7966
+ },
7967
+ {
7968
+ "name": "--table-cell-padding-y",
7969
+ "value": "var(--space-2)",
7970
+ "description": "Table component tokens: row height, cell padding."
7971
+ },
7972
+ {
7973
+ "name": "--table-cell-space-x",
7974
+ "value": "var(--control-padding-x)",
7975
+ "description": "Table component tokens: row height, cell padding."
7976
+ },
7977
+ {
7978
+ "name": "--table-head-font-size",
7979
+ "value": "var(--font-size-xs)",
7980
+ "description": "Table component tokens: row height, cell padding."
7981
+ }
7982
+ ];
7983
+
6842
7984
  // src/data/rules.ts
6843
7985
  var CARDINAL_RULES = [
6844
7986
  {
@@ -7050,6 +8192,26 @@ var CARDINAL_RULES = [
7050
8192
  number: 42,
7051
8193
  title: "Props & Tokens Before Customization",
7052
8194
  body: "Before reaching for a Tailwind class, inline `style`, or extra CSS, you MUST first check whether the component already supports the need via a PROP, a design TOKEN, or a layout/typography PRIMITIVE. godx-ui is meant to be enough on its own (Ant-Design-style): `className` is for genuine one-offs only \u2014 never to redo what an API already does. Specifically: (1) NEVER hand-roll typography \u2014 no `text-[13px]`/`text-[11px]` arbitrary px (bypasses the golden type scale), no `font-medium`/`font-semibold`/`text-muted-foreground` on a raw `<span>`; use `<Text size tone weight tabular mono>` / `<Heading level>`. (2) NEVER hand-roll a trivial flex/grid wrapper; use `<Flex>` / `<ResponsiveGrid>` / `<PageContainer>`. (3) NEVER set a control's radius/height/colour with a utility when a `shape`/`size`/`tone`/token exists. If a real need has NO prop/token/primitive, that is a library GAP \u2014 file it (draft_bug_report), don't paper over it with ad-hoc Tailwind."
8195
+ },
8196
+ {
8197
+ number: 43,
8198
+ title: "Every form control goes through FormField",
8199
+ body: "Consumers MUST wrap every labelled form control (Input, Select, DatePicker, DateRangePicker, NumberInput, Radio.Group, Checkbox groups, range pairs, ...) in FormField \u2014 it owns the label (aria-labelledby, never a dangling <label for>), auto-generates/injects the control id, and wires aria-describedby/aria-errormessage/aria-invalid. Bare controls are the rare exception (e.g. a toolbar quick-filter with its own aria-label) and must carry id/name + aria-label themselves. Never hand-roll a label+control stack with Text/Label."
8200
+ },
8201
+ {
8202
+ number: 44,
8203
+ title: "Chrome is a token, default quiet",
8204
+ body: "Any decorative chrome a component draws \u2014 dividers, separator borders, and the padding that exists only to space that chrome \u2014 MUST read a token; never hard-code it in `src/styles/*.css` (a hard-coded `border-bottom: 1px solid hsl(var(--border))` leaves consumers no off-switch short of a variant fork). The DEFAULT is the quietest state (`none` / balanced rhythm); a service theme opts IN, e.g. `--page-header-divider: 1px solid hsl(var(--border))`. Born from real consumption: PageContainer's header divider was undisableable until tokenised."
8205
+ },
8206
+ {
8207
+ number: 45,
8208
+ title: "Every service-tunable constant gets a knob",
8209
+ body: 'When component CSS encodes a geometry choice that a service plausibly re-tunes to match its design handoff \u2014 form label column width, label\u2194control gap, header insets \u2014 it MUST be a documented component token (current value as the default). The theme sets it ONCE globally; props (`labelWidth`) override per instance; Form\u2192FormField priority stays intact. The test: "would a service theme.css want to change this to match its design grid?" If yes and the only route is forking CSS, that is a library gap \u2014 fix the library, don\'t patch the app. Born from real consumption: `--form-label-width` / `--form-label-gap` (design spec said 110px/8px; the values were prop-only and hard-coded `--space-4`).'
8210
+ },
8211
+ {
8212
+ number: 46,
8213
+ title: "Typography is tokens, default is base",
8214
+ body: "A UI framework gives consumers knobs: every font-size in `src/styles/*.css` MUST reference a token \u2014 the global modular scale `var(--font-size-{2xs|xs|sm|base|lg|xl|2xl})` or a per-component `var(--{component}-\u2026-font-size)` knob (rule #45) \u2014 never a hard-coded literal (`font-size: 12px` can't be re-themed). The DEFAULT body size is `--font-size-base`; components render body/UI text at `base`, not at the `sm` alias. Smaller-by-design text (badge, section label, caption) is a component token defaulting to a small step (`--badge-font-size: var(--font-size-xs)`), so a service re-tunes that part without moving the global scale. The `sm`/`xs` tokens stay for the explicit `<Text size>` API. Every component token is surfaced in the MCP `get_component` output (check:mcp-token-sync). Enforced by `check:typography`."
7053
8215
  }
7054
8216
  ];
7055
8217
  function findRule(num) {
@@ -9739,6 +10901,62 @@ ${section.body}
9739
10901
 
9740
10902
  _Source: ${skill.source}_`;
9741
10903
  }
10904
+ var TOKEN_PREFIXES = {
10905
+ Badge: ["badge"],
10906
+ Button: ["button"],
10907
+ Toggle: ["toggle"],
10908
+ TagInput: ["tag-input"],
10909
+ Card: ["card"],
10910
+ StatCard: ["stat-card"],
10911
+ Table: ["table"],
10912
+ DataTable: ["table"],
10913
+ Dialog: ["dialog"],
10914
+ AlertDialog: ["dialog"],
10915
+ Sheet: ["dialog"],
10916
+ Drawer: ["dialog"],
10917
+ Alert: ["alert"],
10918
+ EmptyState: ["empty-state"],
10919
+ Skeleton: ["skeleton"],
10920
+ Pagination: ["pagination"],
10921
+ Toolbar: ["filter"],
10922
+ Breadcrumb: ["breadcrumb"],
10923
+ Menubar: ["menubar"],
10924
+ Progress: ["progress"],
10925
+ TreeSelect: ["tree"],
10926
+ Timeline: ["timeline"],
10927
+ PasswordStrength: ["password-strength"],
10928
+ PasswordInput: ["password-strength"],
10929
+ Checkbox: ["checkbox"],
10930
+ Switch: ["switch"],
10931
+ Slider: ["slider"],
10932
+ ColorPicker: ["color-picker"],
10933
+ Command: ["command"],
10934
+ Radio: ["choice"],
10935
+ RadioGroup: ["choice"],
10936
+ Field: ["choice"],
10937
+ AppShell: ["sidebar", "topbar"],
10938
+ Sidebar: ["sidebar"],
10939
+ Topbar: ["topbar"],
10940
+ Form: ["form"],
10941
+ FormField: ["form"],
10942
+ // Shared control sizing/typography — every plain control reads these.
10943
+ Input: ["control"],
10944
+ Textarea: ["control"],
10945
+ NumberInput: ["control"],
10946
+ Select: ["control", "search-input"],
10947
+ Cascader: ["control"],
10948
+ DatePicker: ["control"],
10949
+ TimePicker: ["control"],
10950
+ InputOTP: ["control"]
10951
+ };
10952
+ function componentTokensFor(name) {
10953
+ const prefixes = TOKEN_PREFIXES[name] ?? [
10954
+ name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
10955
+ ];
10956
+ return COMPONENT_TOKENS.filter(
10957
+ (t) => prefixes.some((p) => t.name.startsWith(`--${p}-`))
10958
+ );
10959
+ }
9742
10960
  function getComponent(name) {
9743
10961
  const c = findComponent(name);
9744
10962
  if (!c) return `Component "${name}" not found. Use \`list_primitives\` to discover.`;
@@ -9764,6 +10982,20 @@ function getComponent(name) {
9764
10982
  `;
9765
10983
  for (const p of c.props) {
9766
10984
  out += `| \`${p.name}\` | \`${p.type}\` | ${p.required ? "\u2713" : ""} | ${p.defaultValue ? `\`${p.defaultValue}\`` : ""} | ${p.description} |
10985
+ `;
10986
+ }
10987
+ const tokens = componentTokensFor(c.name);
10988
+ if (tokens.length) {
10989
+ out += `
10990
+ ## Design tokens (theme knobs)
10991
+
10992
+ Override these in a service \`theme.css\` to re-tune ONLY this component (never hard-code or fork CSS \u2014 rules #44/#45/#46):
10993
+
10994
+ `;
10995
+ out += `| Token | Default | What it controls |
10996
+ |---|---|---|
10997
+ `;
10998
+ for (const t of tokens) out += `| \`${t.name}\` | \`${t.value}\` | ${t.description} |
9767
10999
  `;
9768
11000
  }
9769
11001
  if (c.usage && c.usage.length) {
@@ -10235,8 +11467,8 @@ ${c.example}
10235
11467
  // package.json
10236
11468
  var package_default = {
10237
11469
  name: "@godxjp/ui-mcp",
10238
- version: "0.21.2",
10239
- description: "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 34 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
11470
+ version: "0.21.5",
11471
+ description: "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 45 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
10240
11472
  type: "module",
10241
11473
  main: "./dist/index.js",
10242
11474
  module: "./dist/index.js",