@godxjp/ui-mcp 27.12.0 → 28.0.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.
Files changed (2) hide show
  1. package/dist/index.js +5 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -597,7 +597,7 @@ export default function InvoiceList({
597
597
  </DataTable.Toolbar>
598
598
  </DataTable>
599
599
  );
600
- }`,storyPath:"data-display/DataTable.stories.tsx",rules:[24,31,35,37]},{name:"Card",subParts:["CardAction","CardCover","CardDescription","CardFooter","CardHeader","CardTitle"],group:"data-display",tagline:"Surface container with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. \u26A0\uFE0F The bare <Card> has NO inner padding \u2014 body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props \u2014 the Card renders the strip and the children become the selected tab's body. For a non-tab toolbar/filter strip (list controls, filter chips) use <CardBar extra={\u2026}> \u2014 a positionable bar that auto-draws its separator from its position (top\u2192bottom border, bottom\u2192top border, middle\u2192both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card.",props:[{name:"accent",type:'"primary" | "success" | "warning" | "info" | "attention" | "destructive"',description:"Semantic accent TONE. Where it is drawn is `accentPlacement`'s job \u2014 by default a leading-edge stripe on `border-inline-start` at the `--card-accent-rail-width` measure (6px)."},{name:"accentPlacement",type:'"edge" | "perimeter"',defaultValue:'"edge"',description:'Where `accent` is drawn. "edge" is the classic leading rail. "perimeter" is the FULL attention border \u2014 the whole edge in the accent tone, at the same optical weight as `variant="featured"` but tone-owned, so a card can read as "action required" (accent="attention") or "failed" (accent="destructive") without borrowing the brand colour. Inert without `accent`.'},{name:"variant",type:'"default" | "muted" | "outline" | "borderless" | "featured"',defaultValue:'"default"',description:'Surface fill AND edge. `outline` is Ant Design\'s `outlined` \u2014 no fill, hairline kept. `borderless` is Ant Design\'s `variant="borderless"` (its deprecated `bordered={false}`) \u2014 no hairline, fill kept. The two are mirror images and neither substitutes for the other. `featured` is the BRAND perimeter; its colour is the `--card-featured-border-color` knob rather than a hard-coded `--primary`. For a perimeter in a semantic tone use `accent` + `accentPlacement="perimeter"` instead.'},{name:"hoverable",type:"boolean",description:'Ant Design `hoverable` \u2014 the card lifts to `--card-hover-shadow` on hover and takes a pointer cursor. PRESENTATION ONLY: it announces nothing and binds no handler, so pair it with a real control (a Link/Button inside, or the whole card rendered as one via `asChild`). Never with a bare onClick on the Card div \u2014 a keyboard or screen-reader user cannot reach that. Composes with `accent`/`accentPlacement="perimeter"`: the hover raises the shadow TOKEN, so the attention ring survives the hover.'},{name:"asChild",type:"boolean",defaultValue:"false",description:'Borrow the child\'s element for the card BOX instead of rendering a div \u2014 the standard Slot passthrough (Button, AspectRatio, ListRow). This is the other half of `hoverable`: `<Card asChild hoverable><a href="\u2026">` IS "the whole card rendered as one control" \u2014 one tab stop, announced as one link, with the focus ring on the card box (measured in Chromium: `<div>`, `<a>` and `<button>` identical on box, border, radius, fill, padding inset, shadow and cursor at 1440 and 390). For a click-only card hand it your router\'s Link component as the child. NESTING CAVEAT: the card is now ONE control, so it may not contain another \u2014 an <a>/<Button>/menu trigger inside a card-as-link or card-as-button is invalid HTML, and `tabList` (a strip of button triggers) is for the same reason not drawn under `asChild` and warns in development. A card that needs interactive children is not one control: drop `asChild` and put the Link/Button inside it, which is the other branch `hoverable` names. Exactly one child \u2014 two throws React.Children.only, the same error `Button asChild` throws.'},{name:"density",type:'"tight" | "cozy"',description:"Internal padding density (base 16 / tight 12 / cozy 20). This IS Ant Design's `size` axis; there is deliberately no `size` prop (removed 2026-08-24) \u2014 see docs/DESIGN-AUTHORITY.md, a capability this library already has keeps its own name."},{name:"tabList",type:"{ key: string; tab: ReactNode; disabled?: boolean }[]",description:"Ant Design `tabList` \u2014 the tab strip that lives IN THE CARD'S HEAD: under the title, on the same surface, inside the same border, so the card and its tabs read as ONE object. The entry keeps antd's own field names (`key`/`tab`/`disabled`), NOT the Tabs component's `value`/`label`/`content` \u2014 a card tab carries only the trigger, because the panel is the card body. The Card's children become the selected tab's body; wrap them in <CardContent> (or <CardContent flush> for an edge-to-edge DataTable, which still reaches the card edge inside a tab)."},{name:"activeTabKey",type:"string",description:"Ant Design `activeTabKey` \u2014 the CONTROLLED selection. With it set the card never moves itself; pair it with `onTabChange` and swap the children yourself, exactly as in antd."},{name:"defaultActiveTabKey",type:"string",description:"Ant Design `defaultActiveTabKey` \u2014 the uncontrolled initial selection. Without it the first selectable entry of `tabList` opens (antd's own fallback); a disabled tab is never the open one."},{name:"onTabChange",type:"(key: string) => void",description:"Ant Design `onTabChange` \u2014 fires with the newly selected `key`, however the selection moved (pointer or keyboard)."},{name:"extra",type:"ReactNode | { start?: ReactNode; end?: ReactNode }",description:"Ant Design `tabBarExtraContent`, RENAMED to `extra` and made logical \u2014 the same precedent `Tabs.extra` already set in this package, and antd's `left`/`right` keys are `start`/`end` here so an RTL locale gets the slot on the correct edge. It rides the TAB BAR beside the strip, so it is inert without `tabList`; a header-level action is <CardAction> inside <CardHeader>."},{name:"tabProps",type:"Omit<TabsProps, 'items' | 'value' | 'defaultValue' | 'onValueChange' | 'extra' | 'children'>",description:"Ant Design `tabProps` \u2014 passed straight to the Tabs that draws the strip, so `variant`, `size`, `centered`, `overflow`, `indicator` and the rest are reachable. The fields the CARD owns are omitted rather than silently overwritten: `items` comes from `tabList`, `value`/`defaultValue`/`onValueChange` from `activeTabKey`/`defaultActiveTabKey`/`onTabChange`, and `extra` is the Card's own slot. antd drops the same fields \u2014 it writes its own over `tabProps` \u2014 so this is that behaviour made visible in the type."}],usage:['DO always wrap body content in <CardContent> \u2014 the bare <Card> div has zero inner padding; content renders flush against card edges without it. Never add className="p-4" directly on <Card> as a substitute.',"DO put titles/descriptions in <CardHeader>/<CardTitle>/<CardDescription>. Use <CardHeader banded> for a visually separated muted-background header band (mirrors <CardFooter separated>). Pair with <CardAction> inside a flex-row CardHeader for header-level action buttons.",'DO set <CardTitle level={n}> to keep a valid document outline (h1 \u2192 h2 \u2192 h3, no skipped levels): CardTitle renders <h3> by default, so a section card directly under a page <h1> needs level={2}. Pick the level by OUTLINE position, NEVER for visual size \u2014 the title size is fixed by tokens and does not change with level. When the card title is a styled label rather than a section heading, use <CardTitle as="p"> so it is not announced as a heading.',"DO use <CardContent flush> for edge-to-edge children such as DataTable, Table, or a Tabs list \u2014 this removes horizontal padding. Combine with <CardContent tight> when there is no visual gap needed after the header, and <CardContent solo> when there is no CardHeader above (top padding matches the card shell).","DO use <CardFooter separated> to render a top-bordered action band (Save/Cancel buttons, table summary row). Use <CardFooter flush> for a full-bleed footer bar.","DO use <CardFooter actions> for Ant Design's `actions` row \u2014 N EQUAL-WIDTH cells split by vertical hairlines (\u8907\u88FD / \u5171\u6709 / \u524A\u9664 under a profile or entity card). It is a different band from `separated`, which packs children at the inline end at their natural widths: that is the right shape for a Save/Cancel pair and the wrong one for a divided strip. `actions` is self-sufficient \u2014 it draws its own top rule and full-bleed edges, so it needs neither `separated` nor `flush` beside it. The dividers are logical (border-inline-start), so the strip mirrors under RTL.","DO use <CardCover> as the first child for full-bleed cover media \u2014 the header below it uses card-section top spacing, not the card shell.","DO make the WHOLE card the control with `asChild` when the whole card is the click target: `<Card asChild hoverable><a href={href}><CardHeader><CardTitle level={2}>\u2026</CardTitle></CardHeader><CardContent>\u2026</CardContent></a></Card>` (or your router's Link as the child when there is no href, e.g. a nav.push handler). One tab stop, announced as one link, focus ring on the card box. DON'T wrap the card in a raw <button> (that is a `no-raw-button` error) and DON'T put a bare onClick on the Card div (unreachable by keyboard and screen reader \u2014 `hoverable`'s own docblock forbids it). NESTING CAVEAT: a card-as-link/button may not CONTAIN another control \u2014 an <a>, a <Button>, a DropdownMenu trigger or a `tabList` strip inside it is invalid HTML (`tabList` is dropped with a development warning). When the card needs interactive children it is not one control: drop `asChild` and use the other branch \u2014 `hoverable` plus a real Link/Button inside <CardHeader>/<CardAction>/<CardFooter>.",'ANT DESIGN PROPS THIS FAMILY ANSWERS BY COMPOSITION, not by a prop of the same name \u2014 do not ask for these to be added: `title` is <CardHeader> + <CardTitle> (and CardTitle.level emits a real heading, which antd does not); a header-level action is <CardAction> inside <CardHeader>. `cover` is <CardCover>. `actions` is <CardFooter actions>. `loading` is a Skeleton in the body \u2014 antd renders a Skeleton with paragraph rows and no title, so the equivalent is <CardContent solo><SkeletonRows rows={4} /></CardContent>. `type="inner"` is variant="muted" plus <CardHeader banded>. `size` is `density`. `Card.Grid` is <ResponsiveGrid>; `Card.Meta` is <ListRow leading title description trailing>.',"DO use `tabList` for a tab strip that belongs to the CARD \u2014 antd's card-head tabs, ported name for name (gh#570): `tabList={[{ key, tab, disabled? }]}` plus `activeTabKey`/`defaultActiveTabKey`/`onTabChange`, with `extra` for antd's `tabBarExtraContent` and `tabProps` for everything else on the Tabs underneath. The strip renders INSIDE the card head, under the title, on the same surface and inside the same border, and the Card's children become the selected tab's body (wrap them in <CardContent>, or <CardContent flush> for an edge-to-edge DataTable). DON'T hand-roll it as a <Tabs> parked on the page above the card (the strip floats off the card and the two read as two objects) or as a <Card> repeated inside each tab (the shell is copied per view). A <Tabs> INSIDE <CardContent tight flush> is still correct for a strip that belongs to the BODY rather than to the card head.",'DO reach for `accentPlacement="perimeter"` when the whole card needs attention, not one edge: `<Card accent="attention" accentPlacement="perimeter">` is the semantic-tone equivalent of `variant="featured"` (which is brand-toned by definition). Never hand-roll it with `className="border-2 border-[--attention]"` or a page-local `.card--attention` rule \u2014 the placement owns the border weight, the outer ring AND the slot-padding compensation, so text stays on the same column as an unaccented sibling.',"DON'T hand-roll a stat/KPI tile with <Card> + raw divs \u2014 use <StatCard> (label, value, hint, delta, layout, inverse props) which is already a Card internally with correct token-driven layout.","SPACING IS BORDER-AWARE & token-driven (theme via src/tokens/components/card.css, never hard-code padding on slots): `--card-space-inset` is the shared horizontal column every slot (header/content/footer) aligns to. A DIVIDED section \u2014 a `banded` header or a `separated` footer, i.e. one carrying a divider border \u2014 pads SYMMETRICALLY top+bottom from `--card-space-divided-y` (a band reads as its own region). A PLAIN header flows into the body instead: top `--card-space-shell-y`, no bottom, and the body supplies the gap via `--card-space-body-y`. THE TWO AXES ARE INDEPENDENT: `--card-space-inset` is inline-only, while `--card-space-shell-y` owns the BLOCK shell edges (plain-header top, `solo` body top, terminal slot bottom) and defaults to the inset \u2014 so a shell/theme can make a card SHORTER without narrowing its column by overriding `--card-space-shell-y` alone (this is how AuthShell's `--auth-shell-card-padding-block-compact` reaches CardContent). Never bridge it with a consumer selector on the card-content slot. Special case: `<CardContent flush>` zeroes BOTH of its block edges \u2014 for ANY full-bleed body, not only one containing a <Table>` gate left a flush file LIST floating 18px off its header while the flush table beside it sat at 0) \u2014 so the plain header above it supplies the gap from its own `--card-space-body-y` bottom padding instead. `tight` and `solo` still own that axis themselves. `--card-space-gap` is the in-slot stack gap (title\u2195description). Tune the band rhythm once at `--card-space-divided-y`; tune the accent stripe width at `--card-accent-rail-width` (default 6px)."],useCases:['Dashboard KPI summary row: wrap each metric in <StatCard> (or a plain <Card density="tight"> with <CardContent>) to render a uniform grid of labeled value tiles with optional trend deltas.','Invoice or order detail panel: <Card accent="primary"> with <CardHeader banded><CardTitle>, <CardContent> body rows (use <Descriptions> inside), and <CardFooter separated> holding approve/reject buttons.',"Section container on a settings or form page: a single <Card> wrapping a <CardHeader><CardTitle> plus <CardContent> containing <FormField> groups, with <CardFooter separated> for Save/Cancel.","Data table with toolbar: <Card> + <CardHeader> (title + filter controls in <CardAction>) + <CardContent flush> containing <DataTable> \u2014 <CardContent flush> removes horizontal padding so the table header spans full width.","Detail screen with views: <Card tabList={[{key,tab}]} activeTabKey onTabChange extra={<Button/>}> with <CardHeader><CardTitle> above the strip and <CardContent flush><DataTable/></CardContent> as the body \u2014 one card, tabs in its head, and a table that reaches the card edge inside the tab (docs/data-display/card/examples/tab-list.tsx).",'Featured announcement or alert card: <Card variant="featured"> with an accent stripe (<accent="warning">) to visually elevate a card above sibling cards on the page.',"Media/cover card (e.g. entity profile): <CardCover> first (full-bleed image), then <CardHeader> + <CardContent> below it for structured metadata."],related:["StatCard \u2014 use instead of a plain Card when rendering a KPI/metric tile (label + value + optional delta/hint). StatCard is a Card internally; do not re-wrap it in another Card.","CardContent \u2014 mandatory inner wrapper for all body content inside Card. Provides the correct padding and supports flush/tight/solo variants. The only correct way to put padded content inside Card.","Descriptions \u2014 use inside <CardContent> when body content is a label-value metadata list (e.g. entity details, invoice fields); do not hand-roll a dl/dt/dd grid.","DataState / InfiniteQueryState \u2014 use instead of Card when the content is a TanStack Query-driven list that needs automatic skeleton, empty, and error states; Card does not manage loading lifecycle."],example:`import { Card, CardHeader, CardTitle, CardContent } from "@godxjp/ui/data-display";
600
+ }`,storyPath:"data-display/DataTable.stories.tsx",rules:[24,31,35,37]},{name:"Card",subParts:["CardAction","CardCover","CardDescription","CardFooter","CardHeader","CardTitle"],group:"data-display",tagline:"Surface container with optional accent stripe, variant fill (including the Ant Design borderless edge), hoverable lift, and density. \u26A0\uFE0F The bare <Card> has NO inner padding \u2014 body content MUST be wrapped in <CardContent> (titles in <CardHeader>), or it sits FLUSH against the card edges. Never hand-roll padding with className=\"p-4\"; use <CardContent>. Compose with CardHeader/CardTitle/CardContent/CardFooter. For Ant Design's card-head TAB STRIP (tabs under the title, inside the card border) use the `tabList`/`activeTabKey`/`defaultActiveTabKey`/`onTabChange`/`extra`/`tabProps` props \u2014 the Card renders the strip and the children become the selected tab's body. For a non-tab toolbar/filter strip (list controls, filter chips) use <CardBar extra={\u2026}> \u2014 a positionable bar that auto-draws its separator from its position (top\u2192bottom border, bottom\u2192top border, middle\u2192both) and pins `extra` content to the inline-end edge; place it as first/last child of the Card.",props:[{name:"accent",type:'"primary" | "success" | "warning" | "info" | "attention" | "destructive"',description:"Semantic accent TONE. Where it is drawn is `accentPlacement`'s job \u2014 by default a leading-edge stripe on `border-inline-start` at the `--card-accent-rail-width` measure (6px)."},{name:"accentPlacement",type:'"edge" | "perimeter"',defaultValue:'"edge"',description:'Where `accent` is drawn. "edge" is the classic leading rail. "perimeter" is the FULL attention border \u2014 the whole edge in the accent tone, at the same optical weight as `variant="featured"` but tone-owned, so a card can read as "action required" (accent="attention") or "failed" (accent="destructive") without borrowing the brand colour. Inert without `accent`. LIMIT (gh#750): the perimeter ring is painted OUTSIDE the border box, and a scroll container clips it \u2014 a horizontal ScrollArea is `overflow: auto hidden`, and `overflow-clip-margin` is honoured only by `clip`, not by `auto`. A perimeter card flush against a scroller edge loses 1px of ring on that side; inside a scroller use the default "edge" placement (its rail is inside the box) or a tone fill.'},{name:"variant",type:'"default" | "muted" | "outline" | "borderless" | "featured"',defaultValue:'"default"',description:'Surface fill AND edge. `outline` is Ant Design\'s `outlined` \u2014 no fill, hairline kept. `borderless` is Ant Design\'s `variant="borderless"` (its deprecated `bordered={false}`) \u2014 no hairline, fill kept. The two are mirror images and neither substitutes for the other. `featured` is the BRAND perimeter; its colour is the `--card-featured-border-color` knob rather than a hard-coded `--primary`. For a perimeter in a semantic tone use `accent` + `accentPlacement="perimeter"` instead.'},{name:"hoverable",type:"boolean",description:'Ant Design `hoverable` \u2014 the card lifts to `--card-hover-shadow` on hover and takes a pointer cursor. PRESENTATION ONLY: it announces nothing and binds no handler, so pair it with a real control (a Link/Button inside, or the whole card rendered as one via `asChild`). Never with a bare onClick on the Card div \u2014 a keyboard or screen-reader user cannot reach that. Composes with `accent`/`accentPlacement="perimeter"`: the hover raises the shadow TOKEN, so the attention ring survives the hover.'},{name:"asChild",type:"boolean",defaultValue:"false",description:'Borrow the child\'s element for the card BOX instead of rendering a div \u2014 the standard Slot passthrough (Button, AspectRatio, ListRow). This is the other half of `hoverable`: `<Card asChild hoverable><a href="\u2026">` IS "the whole card rendered as one control" \u2014 one tab stop, announced as one link, with the focus ring on the card box (measured in Chromium: `<div>`, `<a>` and `<button>` identical on box, border, radius, fill, padding inset, shadow and cursor at 1440 and 390). For a click-only card hand it your router\'s Link component as the child. NESTING CAVEAT: the card is now ONE control, so it may not contain another \u2014 an <a>/<Button>/menu trigger inside a card-as-link or card-as-button is invalid HTML, and `tabList` (a strip of button triggers) is for the same reason not drawn under `asChild` and warns in development. A card that needs interactive children is not one control: drop `asChild` and put the Link/Button inside it, which is the other branch `hoverable` names. Exactly one child \u2014 two throws React.Children.only, the same error `Button asChild` throws.'},{name:"density",type:'"tight" | "cozy"',description:"Internal padding density (base 16 / tight 12 / cozy 20). This IS Ant Design's `size` axis; there is deliberately no `size` prop (removed 2026-08-24) \u2014 see docs/DESIGN-AUTHORITY.md, a capability this library already has keeps its own name."},{name:"tabList",type:"{ key: string; tab: ReactNode; disabled?: boolean }[]",description:"Ant Design `tabList` \u2014 the tab strip that lives IN THE CARD'S HEAD: under the title, on the same surface, inside the same border, so the card and its tabs read as ONE object. The entry keeps antd's own field names (`key`/`tab`/`disabled`), NOT the Tabs component's `value`/`label`/`content` \u2014 a card tab carries only the trigger, because the panel is the card body. The Card's children become the selected tab's body; wrap them in <CardContent> (or <CardContent flush> for an edge-to-edge DataTable, which still reaches the card edge inside a tab)."},{name:"activeTabKey",type:"string",description:"Ant Design `activeTabKey` \u2014 the CONTROLLED selection. With it set the card never moves itself; pair it with `onTabChange` and swap the children yourself, exactly as in antd."},{name:"defaultActiveTabKey",type:"string",description:"Ant Design `defaultActiveTabKey` \u2014 the uncontrolled initial selection. Without it the first selectable entry of `tabList` opens (antd's own fallback); a disabled tab is never the open one."},{name:"onTabChange",type:"(key: string) => void",description:"Ant Design `onTabChange` \u2014 fires with the newly selected `key`, however the selection moved (pointer or keyboard)."},{name:"extra",type:"ReactNode | { start?: ReactNode; end?: ReactNode }",description:"Ant Design `tabBarExtraContent`, RENAMED to `extra` and made logical \u2014 the same precedent `Tabs.extra` already set in this package, and antd's `left`/`right` keys are `start`/`end` here so an RTL locale gets the slot on the correct edge. It rides the TAB BAR beside the strip, so it is inert without `tabList`; a header-level action is <CardAction> inside <CardHeader>."},{name:"tabProps",type:"Omit<TabsProps, 'items' | 'value' | 'defaultValue' | 'onValueChange' | 'extra' | 'children'>",description:"Ant Design `tabProps` \u2014 passed straight to the Tabs that draws the strip, so `variant`, `size`, `centered`, `overflow`, `indicator` and the rest are reachable. The fields the CARD owns are omitted rather than silently overwritten: `items` comes from `tabList`, `value`/`defaultValue`/`onValueChange` from `activeTabKey`/`defaultActiveTabKey`/`onTabChange`, and `extra` is the Card's own slot. antd drops the same fields \u2014 it writes its own over `tabProps` \u2014 so this is that behaviour made visible in the type."}],usage:['DO always wrap body content in <CardContent> \u2014 the bare <Card> div has zero inner padding; content renders flush against card edges without it. Never add className="p-4" directly on <Card> as a substitute.',"DO put titles/descriptions in <CardHeader>/<CardTitle>/<CardDescription>. Use <CardHeader banded> for a visually separated muted-background header band (mirrors <CardFooter separated>). Pair with <CardAction> inside a flex-row CardHeader for header-level action buttons.",'DO set <CardTitle level={n}> to keep a valid document outline (h1 \u2192 h2 \u2192 h3, no skipped levels): CardTitle renders <h3> by default, so a section card directly under a page <h1> needs level={2}. Pick the level by OUTLINE position, NEVER for visual size \u2014 the title size is fixed by tokens and does not change with level. When the card title is a styled label rather than a section heading, use <CardTitle as="p"> so it is not announced as a heading.',"DO use <CardContent flush> for edge-to-edge children such as DataTable, Table, or a Tabs list \u2014 this removes horizontal padding. Combine with <CardContent tight> when there is no visual gap needed after the header, and <CardContent solo> when there is no CardHeader above (top padding matches the card shell).","DO use <CardFooter separated> to render a top-bordered action band (Save/Cancel buttons, table summary row). Use <CardFooter flush> for a full-bleed footer bar.","DO use <CardFooter actions> for Ant Design's `actions` row \u2014 N EQUAL-WIDTH cells split by vertical hairlines (\u8907\u88FD / \u5171\u6709 / \u524A\u9664 under a profile or entity card). It is a different band from `separated`, which packs children at the inline end at their natural widths: that is the right shape for a Save/Cancel pair and the wrong one for a divided strip. `actions` is self-sufficient \u2014 it draws its own top rule and full-bleed edges, so it needs neither `separated` nor `flush` beside it. The dividers are logical (border-inline-start), so the strip mirrors under RTL.","DO use <CardCover> as the first child for full-bleed cover media \u2014 the header below it uses card-section top spacing, not the card shell.","DO make the WHOLE card the control with `asChild` when the whole card is the click target: `<Card asChild hoverable><a href={href}><CardHeader><CardTitle level={2}>\u2026</CardTitle></CardHeader><CardContent>\u2026</CardContent></a></Card>` (or your router's Link as the child when there is no href, e.g. a nav.push handler). One tab stop, announced as one link, focus ring on the card box. DON'T wrap the card in a raw <button> (that is a `no-raw-button` error) and DON'T put a bare onClick on the Card div (unreachable by keyboard and screen reader \u2014 `hoverable`'s own docblock forbids it). NESTING CAVEAT: a card-as-link/button may not CONTAIN another control \u2014 an <a>, a <Button>, a DropdownMenu trigger or a `tabList` strip inside it is invalid HTML (`tabList` is dropped with a development warning). When the card needs interactive children it is not one control: drop `asChild` and use the other branch \u2014 `hoverable` plus a real Link/Button inside <CardHeader>/<CardAction>/<CardFooter>.",'ANT DESIGN PROPS THIS FAMILY ANSWERS BY COMPOSITION, not by a prop of the same name \u2014 do not ask for these to be added: `title` is <CardHeader> + <CardTitle> (and CardTitle.level emits a real heading, which antd does not); a header-level action is <CardAction> inside <CardHeader>. `cover` is <CardCover>. `actions` is <CardFooter actions>. `loading` is a Skeleton in the body \u2014 antd renders a Skeleton with paragraph rows and no title, so the equivalent is <CardContent solo><SkeletonRows rows={4} /></CardContent>. `type="inner"` is variant="muted" plus <CardHeader banded>. `size` is `density`. `Card.Grid` is <ResponsiveGrid>; `Card.Meta` is <ListRow leading title description trailing>.',"DO use `tabList` for a tab strip that belongs to the CARD \u2014 antd's card-head tabs, ported name for name (gh#570): `tabList={[{ key, tab, disabled? }]}` plus `activeTabKey`/`defaultActiveTabKey`/`onTabChange`, with `extra` for antd's `tabBarExtraContent` and `tabProps` for everything else on the Tabs underneath. The strip renders INSIDE the card head, under the title, on the same surface and inside the same border, and the Card's children become the selected tab's body (wrap them in <CardContent>, or <CardContent flush> for an edge-to-edge DataTable). DON'T hand-roll it as a <Tabs> parked on the page above the card (the strip floats off the card and the two read as two objects) or as a <Card> repeated inside each tab (the shell is copied per view). A <Tabs> INSIDE <CardContent tight flush> is still correct for a strip that belongs to the BODY rather than to the card head.",'DO reach for `accentPlacement="perimeter"` when the whole card needs attention, not one edge: `<Card accent="attention" accentPlacement="perimeter">` is the semantic-tone equivalent of `variant="featured"` (which is brand-toned by definition). Never hand-roll it with `className="border-2 border-[--attention]"` or a page-local `.card--attention` rule \u2014 the placement owns the border weight, the outer ring AND the slot-padding compensation, so text stays on the same column as an unaccented sibling.',"DON'T hand-roll a stat/KPI tile with <Card> + raw divs \u2014 use <StatCard> (label, value, hint, delta, layout, inverse props) which is already a Card internally with correct token-driven layout.","SPACING IS BORDER-AWARE & token-driven (theme via src/tokens/components/card.css, never hard-code padding on slots): `--card-space-inset` is the shared horizontal column every slot (header/content/footer) aligns to. A DIVIDED section \u2014 a `banded` header or a `separated` footer, i.e. one carrying a divider border \u2014 pads SYMMETRICALLY top+bottom from `--card-space-divided-y` (a band reads as its own region). A PLAIN header flows into the body instead: top `--card-space-shell-y`, no bottom, and the body supplies the gap via `--card-space-body-y`. THE TWO AXES ARE INDEPENDENT: `--card-space-inset` is inline-only, while `--card-space-shell-y` owns the BLOCK shell edges (plain-header top, `solo` body top, terminal slot bottom) and defaults to the inset \u2014 so a shell/theme can make a card SHORTER without narrowing its column by overriding `--card-space-shell-y` alone (this is how AuthShell's `--auth-shell-card-padding-block-compact` reaches CardContent). Never bridge it with a consumer selector on the card-content slot. Special case: `<CardContent flush>` zeroes BOTH of its block edges \u2014 for ANY full-bleed body, not only one containing a <Table>` gate left a flush file LIST floating 18px off its header while the flush table beside it sat at 0) \u2014 so the plain header above it supplies the gap from its own `--card-space-body-y` bottom padding instead. `tight` and `solo` still own that axis themselves. `--card-space-gap` is the in-slot stack gap (title\u2195description). Tune the band rhythm once at `--card-space-divided-y`; tune the accent stripe width at `--card-accent-rail-width` (default 6px)."],useCases:['Dashboard KPI summary row: wrap each metric in <StatCard> (or a plain <Card density="tight"> with <CardContent>) to render a uniform grid of labeled value tiles with optional trend deltas.','Invoice or order detail panel: <Card accent="primary"> with <CardHeader banded><CardTitle>, <CardContent> body rows (use <Descriptions> inside), and <CardFooter separated> holding approve/reject buttons.',"Section container on a settings or form page: a single <Card> wrapping a <CardHeader><CardTitle> plus <CardContent> containing <FormField> groups, with <CardFooter separated> for Save/Cancel.","Data table with toolbar: <Card> + <CardHeader> (title + filter controls in <CardAction>) + <CardContent flush> containing <DataTable> \u2014 <CardContent flush> removes horizontal padding so the table header spans full width.","Detail screen with views: <Card tabList={[{key,tab}]} activeTabKey onTabChange extra={<Button/>}> with <CardHeader><CardTitle> above the strip and <CardContent flush><DataTable/></CardContent> as the body \u2014 one card, tabs in its head, and a table that reaches the card edge inside the tab (docs/data-display/card/examples/tab-list.tsx).",'Featured announcement or alert card: <Card variant="featured"> with an accent stripe (<accent="warning">) to visually elevate a card above sibling cards on the page.',"Media/cover card (e.g. entity profile): <CardCover> first (full-bleed image), then <CardHeader> + <CardContent> below it for structured metadata."],related:["StatCard \u2014 use instead of a plain Card when rendering a KPI/metric tile (label + value + optional delta/hint). StatCard is a Card internally; do not re-wrap it in another Card.","CardContent \u2014 mandatory inner wrapper for all body content inside Card. Provides the correct padding and supports flush/tight/solo variants. The only correct way to put padded content inside Card.","Descriptions \u2014 use inside <CardContent> when body content is a label-value metadata list (e.g. entity details, invoice fields); do not hand-roll a dl/dt/dd grid.","DataState / InfiniteQueryState \u2014 use instead of Card when the content is a TanStack Query-driven list that needs automatic skeleton, empty, and error states; Card does not manage loading lifecycle."],example:`import { Card, CardHeader, CardTitle, CardContent } from "@godxjp/ui/data-display";
601
601
 
602
602
  <Card accent="success">
603
603
  <CardHeader><CardTitle>\u6CE8\u6587\u30B5\u30DE\u30EA\u30FC</CardTitle></CardHeader>
@@ -1715,7 +1715,7 @@ export function ControlledExample() {
1715
1715
  onValueChange={setSelected}
1716
1716
  />
1717
1717
  );
1718
- }`,storyPath:"data-entry/CheckboxGroup.stories.tsx",rules:[3,6,23,31]},{name:"Radio",subParts:["RadioItem"],group:"data-entry",tagline:'Radio group on react-aria-components, with an options-array shorthand \u2014 always use Radio.Group, never a bare radio input. `role="radio"` is the real `<input>`; the painted dot is the `<label>` around it and carries `data-state`.',props:[{name:"optionType",type:'"default" | "button"',defaultValue:'"default"',description:"antd `optionType` \u2014 how each choice is DRAWN. `default` is a radio dot beside its label; `button` welds them into one segmented bar. It is paint, never semantics: the roles stay radiogroup/radio, so arrow-key traversal and native submission keep working (which is why this is not a ToggleGroup \u2014 a row of aria-pressed buttons permits 'none chosen')."},{name:"buttonStyle",type:'"outline" | "solid"',defaultValue:'"outline"',description:"antd `buttonStyle` \u2014 fill of the selected choice while `optionType` is `button`. Ignored otherwise."},{name:"value",type:"string",description:"Controlled selected value. Must be paired with onValueChange to update state."},{name:"defaultValue",type:"string",description:"Uncontrolled initial value. Use when you do not need to track selection in state."},{name:"onValueChange",type:"(value: string) => void",description:"Callback fired when the user selects a different option. Required when value is controlled."},{name:"options",type:"ChoiceOptionProp[]",description:"Declarative option list: { label: ReactNode; value: string; disabled?: boolean; description?: ReactNode }[]. When provided, Radio.Group renders each option as a labelled Field automatically. Omit to compose children manually."},{name:"orientation",type:'"vertical" | "horizontal"',defaultValue:'"vertical"',description:"Layout direction for the option list. Vertical stacks options; horizontal lays them side by side."},{name:"disabled",type:"boolean",description:"Disables the entire group when true. Individual options can also be disabled via options[].disabled."},{name:"name",type:"string",description:'HTML form field name. Required for native form submission \u2014 each option is a real `<input type="radio">` under this name, so the browser serialises the selected value with no extra wiring.'},{name:"className",type:"string",description:"Additional CSS class applied to the group root."},{name:"children",type:"React.ReactNode",description:"Manual composition fallback \u2014 used only when options is not provided. Render Radio.Item (+ Field wrapper) children directly inside Radio.Group."}],usage:["DO use Radio.Group (not the bare Radio export) as the root \u2014 it wires up the group context, keyboard navigation, and the shared input name. A lone Radio.Item outside a Radio.Group has no context and will not function.","DO prefer the options array API for static/data-driven option lists: pass options={[{ label, value, description?, disabled? }]} and Radio.Group renders each as a correctly-labelled Field automatically \u2014 no manual id/label wiring needed.",'DO pass name to Radio.Group when the selection must be submitted via a native HTML form \u2014 the options ARE `<input type="radio" name={name}>`, so FormData/fetch pick the value up without extra wiring.',"DO use controlled mode (value + onValueChange) when the selection drives other UI (conditional fields, preview panels). Use defaultValue for fire-and-forget uncontrolled forms.","DON'T hand-roll a label-plus-radio row with raw <input type='radio'> \u2014 use Radio.Group with options or compose Radio.Item inside Field for custom markup. Every option must be wrapped in Field (or equivalent) for the label htmlFor/id linkage.","DON'T disable individual options inside the options array and ALSO set disabled on the group \u2014 group-level disabled wins and overrides all per-item disabled states."],useCases:["Payment method selection (Credit Card / Bank Transfer / Invoice) on a checkout or invoice-creation form \u2014 mutually exclusive, 2-4 options, use options array + name for form submission.","Account type picker (Asset / Liability / Equity / Revenue / Expense) on a chart-of-accounts create/edit page \u2014 use options with descriptions to explain each type.","Report frequency chooser (Daily / Weekly / Monthly / Quarterly) in a scheduled-report settings panel \u2014 horizontal orientation when options are short labels.","Tax regime selector on an entity or vendor profile form where exactly one option must always be active \u2014 controlled mode so adjacent fields can react to the selection.","Approval workflow step type (Automatic / Manual / Conditional) in a workflow builder \u2014 use descriptions inside options to explain each mode without extra tooltip markup.","Filter scope toggle (All entities / Current entity only) in an admin dashboard filter bar \u2014 horizontal orientation, no name needed (state managed in React, not submitted)."],related:["Checkbox.Group \u2014 use when users may select multiple options simultaneously; Radio.Group enforces single-selection only.","Switch / Field \u2014 use for a single boolean on/off toggle (e.g. enable notifications); Radio.Group is for choosing one among three or more named options.","Select \u2014 use when there are many options (5+) and vertical screen space is limited; Radio.Group is preferable for 2-4 short options where all choices should be visible at a glance."],example:`{\`import { Radio } from "@godxjp/ui/data-entry";
1718
+ }`,storyPath:"data-entry/CheckboxGroup.stories.tsx",rules:[3,6,23,31]},{name:"Radio",subParts:["RadioItem"],group:"data-entry",tagline:'Radio group on react-aria-components, with an options-array shorthand \u2014 always use Radio.Group, never a bare radio input. `role="radio"` is the real `<input>`; the painted dot is the `<label>` around it and carries `data-state`.',props:[{name:"optionType",type:'"default" | "button"',defaultValue:'"default"',description:"antd `optionType` \u2014 how each choice is DRAWN. `default` is a radio dot beside its label; `button` welds them into one segmented bar. It is paint, never semantics: the roles stay radiogroup/radio, so arrow-key traversal and native submission keep working. That is also why this is not a ToggleGroup: a ToggleGroup's single mode is a row of aria-pressed buttons that permits 'none chosen' unless it is given `disallowEmptySelection` (gh#744), and Radio is a form control with a `name` that submits."},{name:"buttonStyle",type:'"outline" | "solid"',defaultValue:'"outline"',description:"antd `buttonStyle` \u2014 fill of the selected choice while `optionType` is `button`. Ignored otherwise."},{name:"value",type:"string",description:"Controlled selected value. Must be paired with onValueChange to update state."},{name:"defaultValue",type:"string",description:"Uncontrolled initial value. Use when you do not need to track selection in state."},{name:"onValueChange",type:"(value: string) => void",description:"Callback fired when the user selects a different option. Required when value is controlled."},{name:"options",type:"ChoiceOptionProp[]",description:"Declarative option list: { label: ReactNode; value: string; disabled?: boolean; description?: ReactNode }[]. When provided, Radio.Group renders each option as a labelled Field automatically. Omit to compose children manually."},{name:"orientation",type:'"vertical" | "horizontal"',defaultValue:'"vertical"',description:"Layout direction for the option list. Vertical stacks options; horizontal lays them side by side."},{name:"disabled",type:"boolean",description:"Disables the entire group when true. Individual options can also be disabled via options[].disabled."},{name:"name",type:"string",description:'HTML form field name. Required for native form submission \u2014 each option is a real `<input type="radio">` under this name, so the browser serialises the selected value with no extra wiring.'},{name:"className",type:"string",description:"Additional CSS class applied to the group root."},{name:"children",type:"React.ReactNode",description:"Manual composition fallback \u2014 used only when options is not provided. Render Radio.Item (+ Field wrapper) children directly inside Radio.Group."}],usage:["DO use Radio.Group (not the bare Radio export) as the root \u2014 it wires up the group context, keyboard navigation, and the shared input name. A lone Radio.Item outside a Radio.Group has no context and will not function.","DO prefer the options array API for static/data-driven option lists: pass options={[{ label, value, description?, disabled? }]} and Radio.Group renders each as a correctly-labelled Field automatically \u2014 no manual id/label wiring needed.",'DO pass name to Radio.Group when the selection must be submitted via a native HTML form \u2014 the options ARE `<input type="radio" name={name}>`, so FormData/fetch pick the value up without extra wiring.',"DO use controlled mode (value + onValueChange) when the selection drives other UI (conditional fields, preview panels). Use defaultValue for fire-and-forget uncontrolled forms.","DON'T hand-roll a label-plus-radio row with raw <input type='radio'> \u2014 use Radio.Group with options or compose Radio.Item inside Field for custom markup. Every option must be wrapped in Field (or equivalent) for the label htmlFor/id linkage.","DON'T disable individual options inside the options array and ALSO set disabled on the group \u2014 group-level disabled wins and overrides all per-item disabled states."],useCases:["Payment method selection (Credit Card / Bank Transfer / Invoice) on a checkout or invoice-creation form \u2014 mutually exclusive, 2-4 options, use options array + name for form submission.","Account type picker (Asset / Liability / Equity / Revenue / Expense) on a chart-of-accounts create/edit page \u2014 use options with descriptions to explain each type.","Report frequency chooser (Daily / Weekly / Monthly / Quarterly) in a scheduled-report settings panel \u2014 horizontal orientation when options are short labels.","Tax regime selector on an entity or vendor profile form where exactly one option must always be active \u2014 controlled mode so adjacent fields can react to the selection.","Approval workflow step type (Automatic / Manual / Conditional) in a workflow builder \u2014 use descriptions inside options to explain each mode without extra tooltip markup.","Filter scope toggle (All entities / Current entity only) in an admin dashboard filter bar \u2014 horizontal orientation, no name needed (state managed in React, not submitted)."],related:["Checkbox.Group \u2014 use when users may select multiple options simultaneously; Radio.Group enforces single-selection only.","Switch / Field \u2014 use for a single boolean on/off toggle (e.g. enable notifications); Radio.Group is for choosing one among three or more named options.","Select \u2014 use when there are many options (5+) and vertical screen space is limited; Radio.Group is preferable for 2-4 short options where all choices should be visible at a glance."],example:`{\`import { Radio } from "@godxjp/ui/data-entry";
1719
1719
 
1720
1720
  // --- Options-array API (recommended for most cases) ---
1721
1721
  const PAYMENT_METHODS = [
@@ -2072,7 +2072,7 @@ import { Separator } from "@godxjp/ui/layout";
2072
2072
  countLabel={t("common.items")}
2073
2073
  >
2074
2074
  {t("inbox.unread")}
2075
- </Toggle>`,storyPath:"data-entry/Toggle.stories.tsx",rules:[3,13,45]},{name:"ToggleGroup",subParts:["ToggleGroupItem"],group:"data-entry",tagline:"Radix ToggleGroup wrapper for single or multiple toggle selection.",props:[{name:"type",type:'"single" | "multiple"',required:!0,description:"Selection mode."},{name:"value",type:"string | string[]",description:"Controlled selected value(s)."},{name:"defaultValue",type:"string | string[]",description:"Uncontrolled initial value(s)."},{name:"onValueChange",type:"(value: string | string[]) => void",description:"Selection callback."},{name:"variant",type:'"default" | "outline" | "soft"',defaultValue:'"default"',description:"Visual style, PROVIDED TO EVERY ITEM via context \u2014 set it once on the group, not on each ToggleGroupItem. An explicit `variant` on an item still wins. The default is applied per item by toggleVariants, so an unset group emits no `data-variant` at all. `soft` (gh#734) is the chip fill: use it for a tag-filter panel, where `default`/`outline` leave the unselected chips reading as transparent."},{name:"shape",type:'"default" | "pill" | "sharp"',defaultValue:'"default"',description:"Corner shape, PROVIDED TO EVERY ITEM via context exactly as `variant`/`size` are \u2014 a chip row is a row of pills, so the decision belongs to the row (gh#734). An explicit `shape` on an item still wins. Same three values and same radius tokens as Button and Badge."},{name:"size",type:'"xs" | "sm" | "md" | "lg"',defaultValue:'"md"',description:"Control size, PROVIDED TO EVERY ITEM via context \u2014 set it once on the group. An explicit `size` on an item still wins. Heights come from the --control-height tier (xs 24px \xB7 sm 28px \xB7 md 32px \xB7 lg 36px). Pick the step the ROW already has: xs is the one that fits a 24px-dense row (gh#716)."},{name:"disabled",type:"boolean",description:"Disables the whole group; individual items also accept `disabled`."},{name:"wrap",type:"boolean",defaultValue:"false",description:'Let the row break onto further lines instead of running past its rail (gh#741) \u2014 the SAME name, boolean shape and `data-wrap` attribute `Flex` carries. OPT-IN, and the same default for every `variant`, both decided by measurement: a twelve-chip soft/pill xs row in a 320px rail is scrollWidth 644 > clientWidth 320 and 171.5px tall without it (chips squeezed to min-content, labels broken over up to seven lines) and scrollWidth 320 = clientWidth 320, 108px, 4 lines with it \u2014 while forcing wrap on all 18 groups of the docs page changed NONE of the 17 that already fit, at a 320px and a 1358px rail. So a default of true could only reflow rows that overflow today, silently, on an upgrade; and `variant="soft"` gets no different default because forcing wrap changed nothing across default/outline/soft either \u2014 paint is not the axis that decides, content width against the rail is. The wrapped lines keep the group\'s single `gap`, so both axes measure the same 4px, and `flex-wrap` reorders nothing: arrow keys still walk the items in DOM order across lines, in LTR and in RTL.'},{name:"ToggleGroupItem count / overflowCount / showZero / countLabel",type:"number | number | boolean | string",description:"The counter-pill vocabulary is available PER ITEM, because variant/size are a group decision but the number is per-item data. Same vocabulary and same rendering as Toggle and Button: Intl.NumberFormat on the active locale, `{overflowCount}+` above the cap, and the count folded into that item's own accessible name. This is the faceted filter chip row (Open 42 / Closed 118) and the reaction row."}],usage:["DO choose type='single' for mutually exclusive toolbar modes.","DO choose type='multiple' for independent formatting toggles.","DO set `variant`/`size`/`shape` ONCE on the ToggleGroup \u2014 they propagate to every ToggleGroupItem through context. Repeating them on each item is redundant (it still works, and an explicit item prop overrides the group).",'DO build a tag-filter panel as `<ToggleGroup type="multiple" variant="soft" shape="pill" size="xs">` with one counted `ToggleGroupItem` per tag \u2014 that is the whole antd `Tag.CheckableTag` row, one tab stop per chip, no `Tag` component needed (gh#734).',"DO set `size`/`variant` on an individual ToggleGroupItem only when that ONE item must differ from the group.",'DO add `wrap` to a TAG FILTER ROW \u2014 `<ToggleGroup type="multiple" variant="soft" shape="pill" size="xs" wrap>` is the whole folder-tag panel however many tags the folder has. This is what replaces the hand-built `<ul>` of individual `<Toggle>`s a row wider than its rail used to force (gh#741): that list loses exactly what the group owns \u2014 the shared `variant`/`size`/`shape` context, ONE `value`/`onValueChange`, and the group\'s arrow-key traversal, which keeps walking the chips in DOM order across the wrapped lines.',"DON'T set `wrap` on a 3\u20134 item segmented group. Measured at a 320px rail it is one 32px line either way, so the prop buys nothing and only adds a way for a toolbar to reflow.","DON'T pass size='default' \u2014 it is not a member of the `xs | sm | md | lg` union. Omit `size` for the md default.","DO give the group an accessible name (`aria-label`) \u2014 it renders a radiogroup (single) or a group of toggle buttons (multiple)."],useCases:["Text alignment selector","Formatting toolbar","View density switcher","Tag filter row (wrap)"],related:["Segmented \u2014 the single-select sibling with a shared connected track. ToggleGroup is the generic multi/single toggle set; Segmented is the one-of-N control.","Toggle","RadioGroup"],example:`import { ToggleGroup, ToggleGroupItem } from "@godxjp/ui/data-entry";
2075
+ </Toggle>`,storyPath:"data-entry/Toggle.stories.tsx",rules:[3,13,45]},{name:"ToggleGroup",subParts:["ToggleGroupItem"],group:"data-entry",tagline:"Single or multiple toggle selection on react-aria-components. On type=single the ARIA role follows `disallowEmptySelection`: omitted it is a `group` of `aria-pressed` buttons that MAY be all-off, set it is a `radiogroup` of `radio`s (gh#744).",props:[{name:"type",type:'"single" | "multiple"',required:!0,description:"Selection mode."},{name:"value",type:"string | string[]",description:"Controlled selected value(s)."},{name:"defaultValue",type:"string | string[]",description:"Uncontrolled initial value(s)."},{name:"onValueChange",type:"(value: string | string[]) => void",description:"Selection callback."},{name:"variant",type:'"default" | "outline" | "soft"',defaultValue:'"default"',description:"Visual style, PROVIDED TO EVERY ITEM via context \u2014 set it once on the group, not on each ToggleGroupItem. An explicit `variant` on an item still wins. The default is applied per item by toggleVariants, so an unset group emits no `data-variant` at all. `soft` (gh#734) is the chip fill: use it for a tag-filter panel, where `default`/`outline` leave the unselected chips reading as transparent."},{name:"shape",type:'"default" | "pill" | "sharp"',defaultValue:'"default"',description:"Corner shape, PROVIDED TO EVERY ITEM via context exactly as `variant`/`size` are \u2014 a chip row is a row of pills, so the decision belongs to the row (gh#734). An explicit `shape` on an item still wins. Same three values and same radius tokens as Button and Badge."},{name:"size",type:'"xs" | "sm" | "md" | "lg"',defaultValue:'"md"',description:"Control size, PROVIDED TO EVERY ITEM via context \u2014 set it once on the group. An explicit `size` on an item still wins. Heights come from the --control-height tier (xs 24px \xB7 sm 28px \xB7 md 32px \xB7 lg 36px). Pick the step the ROW already has: xs is the one that fits a 24px-dense row (gh#716)."},{name:"disabled",type:"boolean",description:"Disables the whole group; individual items also accept `disabled`."},{name:"disallowEmptySelection",type:"boolean",defaultValue:"false",description:'May the group end up with NOTHING selected? Omitted it may \u2014 pressing the selected item again clears it and reports `""`, which is what a tag-filter row wants. THIS PROP DECIDES THE ARIA ROLE of a type=single group (gh#744), because emptiness is the one thing the two candidate roles disagree about: ARIA has no press-again-to-deselect for a radio, so a radiogroup the user just emptied is a state a screen reader cannot read out. Omitted \u2192 `role="group"` + `aria-pressed` per item (no `aria-orientation`, which `group` does not take), arrow keys move FOCUS and Space/Enter presses. Set \u2192 `role="radiogroup"` + `role="radio"` / `aria-checked`, the selected item is the single tab stop, arrow keys move the SELECTION as APG requires, and pressing the selected item again keeps it. The name is React Aria\'s own (`useToggleGroupState`); neither antd nor Radix names the capability, and Radix\'s own single group emits radio roles while still allowing empty \u2014 the divergence is recorded in docs/DESIGN-AUTHORITY.md. On type=multiple it only keeps the last item selected; the roles do not move.'},{name:"wrap",type:"boolean",defaultValue:"false",description:'Let the row break onto further lines instead of running past its rail (gh#741) \u2014 the SAME name, boolean shape and `data-wrap` attribute `Flex` carries. OPT-IN, and the same default for every `variant`, both decided by measurement: a twelve-chip soft/pill xs row in a 320px rail is scrollWidth 644 > clientWidth 320 and 171.5px tall without it (chips squeezed to min-content, labels broken over up to seven lines) and scrollWidth 320 = clientWidth 320, 108px, 4 lines with it \u2014 while forcing wrap on all 18 groups of the docs page changed NONE of the 17 that already fit, at a 320px and a 1358px rail. So a default of true could only reflow rows that overflow today, silently, on an upgrade; and `variant="soft"` gets no different default because forcing wrap changed nothing across default/outline/soft either \u2014 paint is not the axis that decides, content width against the rail is. The wrapped lines keep the group\'s single `gap`, so both axes measure the same 4px, and `flex-wrap` reorders nothing: arrow keys still walk the items in DOM order across lines, in LTR and in RTL.'},{name:"ToggleGroupItem count / overflowCount / showZero / countLabel",type:"number | number | boolean | string",description:"The counter-pill vocabulary is available PER ITEM, because variant/size are a group decision but the number is per-item data. Same vocabulary and same rendering as Toggle and Button: Intl.NumberFormat on the active locale, `{overflowCount}+` above the cap, and the count folded into that item's own accessible name. This is the faceted filter chip row (Open 42 / Closed 118) and the reaction row."}],usage:["DO choose type='single' for mutually exclusive toolbar modes.","DO add `disallowEmptySelection` to a type='single' group that is a SETTING \u2014 a view density, a sort order, a fiscal period. It is what makes the group a real radiogroup (role, aria-checked, one tab stop, arrow keys that move the selection) and it stops the second press from clearing the value, so you no longer need the `onValueChange={(v) => { if (v) setX(v) }}` guard that used to paper over it (gh#744).",'DON\'T add `disallowEmptySelection` to a FILTER row. Clearing a chip by pressing it again is what the user expects there, and without the prop the group says so honestly: `role="group"` + `aria-pressed`, a set of buttons that may all be off.',"DO choose type='multiple' for independent formatting toggles.","DO set `variant`/`size`/`shape` ONCE on the ToggleGroup \u2014 they propagate to every ToggleGroupItem through context. Repeating them on each item is redundant (it still works, and an explicit item prop overrides the group).",'DO build a tag-filter panel as `<ToggleGroup type="multiple" variant="soft" shape="pill" size="xs">` with one counted `ToggleGroupItem` per tag \u2014 that is the whole antd `Tag.CheckableTag` row, one tab stop per chip, no `Tag` component needed (gh#734).',"DO set `size`/`variant` on an individual ToggleGroupItem only when that ONE item must differ from the group.",'DO add `wrap` to a TAG FILTER ROW \u2014 `<ToggleGroup type="multiple" variant="soft" shape="pill" size="xs" wrap>` is the whole folder-tag panel however many tags the folder has. This is what replaces the hand-built `<ul>` of individual `<Toggle>`s a row wider than its rail used to force (gh#741): that list loses exactly what the group owns \u2014 the shared `variant`/`size`/`shape` context, ONE `value`/`onValueChange`, and the group\'s arrow-key traversal, which keeps walking the chips in DOM order across the wrapped lines.',"DON'T set `wrap` on a 3\u20134 item segmented group. Measured at a 320px rail it is one 32px line either way, so the prop buys nothing and only adds a way for a toolbar to reflow.","DON'T pass size='default' \u2014 it is not a member of the `xs | sm | md | lg` union. Omit `size` for the md default.","DO give the group an accessible name (`aria-label`) \u2014 it renders a `group` of toggle buttons (single, the default), a `radiogroup` (single + `disallowEmptySelection`) or a `toolbar` of toggle buttons (multiple), and all three need a name."],useCases:["Text alignment selector","Formatting toolbar","View density switcher","Tag filter row (wrap)"],related:["Segmented \u2014 the single-select sibling with a shared connected track. ToggleGroup is the generic multi/single toggle set; Segmented is the one-of-N control.","Toggle","RadioGroup"],example:`import { ToggleGroup, ToggleGroupItem } from "@godxjp/ui/data-entry";
2076
2076
 
2077
2077
  // size/variant are set ONCE on the group and reach every item.
2078
2078
  <ToggleGroup type="single" size="lg" variant="outline" defaultValue="left" aria-label="Alignment">
@@ -2115,7 +2115,7 @@ export default function PasswordBlock() {
2115
2115
  </InputOTPGroup>
2116
2116
  </InputOTP>`,storyPath:"data-entry/InputOTP.stories.tsx",rules:[3,6]},{name:"Rating",group:"data-entry",tagline:"Star-rating input (radiogroup) \u2014 controlled via value/onValueChange, form-submittable via name, supports readOnly display.",props:[{name:"count",type:"number",defaultValue:"5",description:"antd `count` \u2014 number of symbols. This library's older `max` still works; `count` wins when both are given."},{name:"allowHalf",type:"boolean",defaultValue:"false",description:"antd `allowHalf` \u2014 a symbol can be half-filled, so the scale steps by 0.5 (keyboard included). It adds a hit area per symbol WITHOUT doubling the radios: a half is a position inside a step, and ten radios announced for a five-star scale would misstate the scale."},{name:"allowClear",type:"boolean",defaultValue:"false",description:"antd `allowClear` \u2014 choosing the chosen value clears it to 0. antd defaults this ON; it is OFF here, because a rating in a business form is usually required and a silent reset on a second click reads as a lost answer."},{name:"character",type:"React.ReactNode | ((index: number) => React.ReactNode)",description:"antd `character` \u2014 what a symbol IS. A node for every symbol, or a function of the 1-based index for a scale whose symbols differ (A/B/C, \u677E\u7AF9\u6885)."},{name:"tooltips",type:"readonly string[]",description:"antd `tooltips` \u2014 a label per step, in order. Folded into each symbol's ACCESSIBLE NAME rather than shown only on hover: a `title` is invisible to a keyboard and to touch, and saying what '3 of 5' means is the whole point of the prop."},{name:"value",type:"number",description:"Controlled rating (1..max)."},{name:"defaultValue",type:"number",defaultValue:"0",description:"Uncontrolled initial rating."},{name:"onValueChange",type:"(value: number) => void",description:"Rating callback."},{name:"max",type:"number",defaultValue:"5",description:"Number of stars."},{name:"readOnly",type:"boolean",description:"Display-only (e.g. an average score)."},{name:"name",type:"string",description:"Hidden input name for native form submission."}],usage:["DO theme the stars with `--rating-star-filled-color` (default `var(--warning)`) and `--rating-star-empty-color` (default `var(--muted-foreground)`, at `--rating-star-empty-alpha` 0.45) \u2014 HSL components, set on :root or a scoped [data-tenant] (gh#694). NEVER override `.ui-rating-star-filled`: it is an internal class. Unset, the stars paint exactly as before.","DO use readOnly to DISPLAY a score (e.g. product average); interactive (default) for collecting a rating.","DO pass `name` to submit the value in a plain form.","DON'T render raw star icons for input \u2014 this handles keyboard (radiogroup), hover preview, and a11y.","NOTE a long scale WRAPS rather than overflowing. At `max={10}` the row needs ~276px of hit area and a 320px viewport offers ~212 inside a card, so the stars fall onto a second line instead of being painted where no one can reach them. Don't add `flex-nowrap` or a fixed width to force one line; use a smaller `max` if a single row matters."],useCases:["Product / vendor review input","Display an average score (readOnly)","Feedback / CSAT survey","Priority or quality scoring in admin"],related:["RadioGroup (non-star single choice)","Slider (continuous 0-100 value)"],example:`import { Rating } from "@godxjp/ui/data-entry";
2117
2117
 
2118
- <Rating name="score" defaultValue={4} onValueChange={(v) => console.log(v)} />`,storyPath:"data-entry/Rating.stories.tsx",rules:[3,6,23]},{name:"Segmented",group:"data-entry",tagline:"One-of-N from a small, closed, always-visible set \u2014 the enterprise Segmented / filter bar drawn on react-aria-components' RadioGroup. A track with the chosen item as a lifted slab. Reach for it INSTEAD OF a Select when there are 2-4 options and all of them fit on screen, and instead of ToggleGroup when exactly one must always be chosen.",props:[{name:"block",type:"boolean",defaultValue:"false",description:"antd `block` \u2014 stretch the bar to its container and share the width EQUALLY between the choices, so the selected pill does not resize as the label under it changes length."},{name:"vertical",type:"boolean",defaultValue:"false",description:"antd `vertical` \u2014 stack the choices in a column. It changes the ARROW KEYS as well as the layout: the primitive reads `orientation` to decide which arrows move the roving focus."},{name:"size",type:'"xs" | "sm" | "md" | "lg"',defaultValue:'"md"',description:"Control height tier \u2014 reads the shared `--control-height` ladder: xs 24px \xB7 sm 28px \xB7 md 32px \xB7 lg 36px. The TRACK measures exactly one control height, so the bar sits level with an Input, a Button or a ToggleGroup of the same step on the same row. Pick the step the ROW already has: xs (gh#719) is the one that fits a 24px-dense toolbar or audit-log row \u2014 before it, that row could only get a hand-rolled set of Buttons, which loses the radiogroup semantics and the arrow keys. xs is the DENSE step, not a smaller default: the track spends its 2px inset at every step, so the individual SEGMENT measures 20px and clears WCAG 2.2 SC 2.5.8 through the Spacing exception (adjacent segments 26.47px apart for a one-glyph label) rather than the 24px minimum. Keep labels at a glyph or more, and stay on sm or md wherever the row height is yours to choose."},{name:"options",type:"{ value: string; label: ReactNode; icon?: ReactNode; disabled?: boolean; count?: number | string; overflowCount?: number; showZero?: boolean; countLabel?: string }[]",description:"The closed set of choices, in reading order. `label` is the visible content AND the item's accessible name. Use `count` for a filter total \u2014 do not nest `Badge` in `label` (gh#602)."},{name:"value",type:"string",description:"Controlled selection."},{name:"defaultValue",type:"string",description:"Uncontrolled initial selection."},{name:"onValueChange",type:"(value: string) => void",description:"Selection callback."},{name:"disabled",type:"boolean",description:"Disable the whole group."},{name:"name",type:"string",description:"Form field name \u2014 submits the selected value with a native form."},{name:"id",type:"string",description:"Root id, for a label that points at it."},{name:"className",type:"string",description:"Root class override."}],usage:["DO use it for a closed set of 2-4 peer choices that are cheap to show \u2014 theme, view mode, a date range preset.","DO give it an aria-label (or aria-labelledby) \u2014 the group needs a name, and each item takes its own from its label.","DON'T use ToggleGroup for a one-of-N choice: its items are aria-pressed toggle buttons and even at type=single the group can end up with nothing selected, which a setting can never be.","DON'T use it past ~4 options \u2014 that is a Select. Up to four, a horizontal track WRAPS to a second row when its options cannot share one (a phone-width status filter with counts), so no label or count is truncated while its item fits on a row. `block` is the exception: it promises EQUAL widths, so it still truncates \u2014 don't use `block` for four labelled options at phone width.","DO show a short mark and speak a long name by putting BOTH in `label`: an aria-hidden span for the glyph and a VisuallyHidden for the words. `label` is a ReactNode, the item takes its accessible name from its content, and the glyph drops out of that name once it is aria-hidden \u2014 so a bar of circle/triangle/cross marks still announces the state in words. There is no separate accessible-name prop and there does not need to be.","DO pass per-option totals via `count` \u2014 the DS paints an opaque pill that reads on both the recessed track and the selected slab. Do not put `Badge` in `label` for counts: `secondary` is `--muted`, which is the track fill (1.00:1, gh#602).","DO stack with `vertical` when the labels are too long to sit side by side: a stacked row is a WHOLE `--control-height` tall, where a horizontal bar spends part of that height on the track padding so the bar as a whole lines up with an Input beside it. Inside a MobileShell, which scopes the control tier to the touch step, that is what makes each row a 44px target.","DO remember that `size` and any scoped `--control-height` both reach the track: the item height is composed on the Segmented root, not frozen at :root.",'DO set `size="xs"` for a 24px-dense row \u2014 an audit-log toolbar, a table header strip, a row that already carries `<Button size="xs">` or an `xs` ToggleGroup. All three measure 24px off the same `--control-height-xs` step, so the row stays level, and the label type and the item inline padding step down with the band (gh#719). DON\'T hand-roll that row out of Buttons to get the height: a segmented control is a radiogroup, and a row of buttons loses the arrow keys and the "1 of 3, selected" announcement.'],useCases:["Theme switch (light / dark / system)","List vs board vs calendar view mode","Chart range: day / week / month","Status filter above a list, each option carrying its count (All 128 / Active 96 / Pending 12 / Archived 0)"],related:["ToggleGroup (independently pressed buttons, or a multi-select toolbar)","RadioGroup (the same semantics with a vertical, described list)","Select (the same choice when the set is long or hidden by default)","Tabs (switches PANELS, not a value)"],example:`import { Segmented } from "@godxjp/ui/data-entry";
2118
+ <Rating name="score" defaultValue={4} onValueChange={(v) => console.log(v)} />`,storyPath:"data-entry/Rating.stories.tsx",rules:[3,6,23]},{name:"Segmented",group:"data-entry",tagline:"One-of-N from a small, closed, always-visible set \u2014 the enterprise Segmented / filter bar drawn on react-aria-components' RadioGroup. A track with the chosen item as a lifted slab. Reach for it INSTEAD OF a Select when there are 2-4 options and all of them fit on screen, and instead of ToggleGroup when exactly one must always be chosen.",props:[{name:"block",type:"boolean",defaultValue:"false",description:"antd `block` \u2014 stretch the bar to its container and share the width EQUALLY between the choices, so the selected pill does not resize as the label under it changes length."},{name:"vertical",type:"boolean",defaultValue:"false",description:"antd `vertical` \u2014 stack the choices in a column. It changes the ARROW KEYS as well as the layout: the primitive reads `orientation` to decide which arrows move the roving focus."},{name:"size",type:'"xs" | "sm" | "md" | "lg"',defaultValue:'"md"',description:"Control height tier \u2014 reads the shared `--control-height` ladder: xs 24px \xB7 sm 28px \xB7 md 32px \xB7 lg 36px. The TRACK measures exactly one control height, so the bar sits level with an Input, a Button or a ToggleGroup of the same step on the same row. Pick the step the ROW already has: xs (gh#719) is the one that fits a 24px-dense toolbar or audit-log row \u2014 before it, that row could only get a hand-rolled set of Buttons, which loses the radiogroup semantics and the arrow keys. xs is the DENSE step, not a smaller default: the track spends its 2px inset at every step, so the individual SEGMENT measures 20px and clears WCAG 2.2 SC 2.5.8 through the Spacing exception (adjacent segments 26.47px apart for a one-glyph label) rather than the 24px minimum. Keep labels at a glyph or more, and stay on sm or md wherever the row height is yours to choose."},{name:"options",type:"{ value: string; label: ReactNode; icon?: ReactNode; disabled?: boolean; count?: number | string; overflowCount?: number; showZero?: boolean; countLabel?: string }[]",description:"The closed set of choices, in reading order. `label` is the visible content AND the item's accessible name. Use `count` for a filter total \u2014 do not nest `Badge` in `label` (gh#602)."},{name:"value",type:"string",description:"Controlled selection."},{name:"defaultValue",type:"string",description:"Uncontrolled initial selection."},{name:"onValueChange",type:"(value: string) => void",description:"Selection callback."},{name:"disabled",type:"boolean",description:"Disable the whole group."},{name:"name",type:"string",description:"Form field name \u2014 submits the selected value with a native form."},{name:"id",type:"string",description:"Root id, for a label that points at it."},{name:"className",type:"string",description:"Root class override."}],usage:["DO use it for a closed set of 2-4 peer choices that are cheap to show \u2014 theme, view mode, a date range preset.","DO give it an aria-label (or aria-labelledby) \u2014 the group needs a name, and each item takes its own from its label.","DON'T use ToggleGroup for a one-of-N choice unless you also pass `disallowEmptySelection`: without it, type=single is a row of aria-pressed toggle buttons that can end up with nothing selected, which a setting can never be (gh#744). Segmented is the one-of-N control and needs no such switch.","DON'T use it past ~4 options \u2014 that is a Select. Up to four, a horizontal track WRAPS to a second row when its options cannot share one (a phone-width status filter with counts), so no label or count is truncated while its item fits on a row. `block` is the exception: it promises EQUAL widths, so it still truncates \u2014 don't use `block` for four labelled options at phone width.","DO show a short mark and speak a long name by putting BOTH in `label`: an aria-hidden span for the glyph and a VisuallyHidden for the words. `label` is a ReactNode, the item takes its accessible name from its content, and the glyph drops out of that name once it is aria-hidden \u2014 so a bar of circle/triangle/cross marks still announces the state in words. There is no separate accessible-name prop and there does not need to be.","DO pass per-option totals via `count` \u2014 the DS paints an opaque pill that reads on both the recessed track and the selected slab. Do not put `Badge` in `label` for counts: `secondary` is `--muted`, which is the track fill (1.00:1, gh#602).","DO stack with `vertical` when the labels are too long to sit side by side: a stacked row is a WHOLE `--control-height` tall, where a horizontal bar spends part of that height on the track padding so the bar as a whole lines up with an Input beside it. Inside a MobileShell, which scopes the control tier to the touch step, that is what makes each row a 44px target.","DO remember that `size` and any scoped `--control-height` both reach the track: the item height is composed on the Segmented root, not frozen at :root.",'DO set `size="xs"` for a 24px-dense row \u2014 an audit-log toolbar, a table header strip, a row that already carries `<Button size="xs">` or an `xs` ToggleGroup. All three measure 24px off the same `--control-height-xs` step, so the row stays level, and the label type and the item inline padding step down with the band (gh#719). DON\'T hand-roll that row out of Buttons to get the height: a segmented control is a radiogroup, and a row of buttons loses the arrow keys and the "1 of 3, selected" announcement.'],useCases:["Theme switch (light / dark / system)","List vs board vs calendar view mode","Chart range: day / week / month","Status filter above a list, each option carrying its count (All 128 / Active 96 / Pending 12 / Archived 0)"],related:["ToggleGroup (independently pressed buttons, or a multi-select toolbar)","RadioGroup (the same semantics with a vertical, described list)","Select (the same choice when the set is long or hidden by default)","Tabs (switches PANELS, not a value)"],example:`import { Segmented } from "@godxjp/ui/data-entry";
2119
2119
 
2120
2120
  <Segmented
2121
2121
  aria-label="Theme"
@@ -4473,7 +4473,7 @@ A block with no reason is IGNORED and the finding stands. An unclosed block runs
4473
4473
  The class-shaped rules (gap-*/p-*/m-*, bg-<palette>-*, w-[\u2026], pr-*, dark:*) only read class
4474
4474
  expressions \u2014 a className/class attribute, a class-named binding (\`baseClass\`, \`statusStyles\`,
4475
4475
  \`badgeVariants\`) or a cn()/clsx()/cva() call \u2014 so prose that merely spells a utility is not a
4476
- finding and needs no suppression.`,Z=[{id:"dialog-needs-body",severity:"error",category:"composition",standard:null,fix:"Wrap a Dialog/Sheet's middle in <DialogBody>/<SheetBody>. The scroll lives on the body \u2014 the content box is `overflow: hidden` with no max-height \u2014 so an overlay without one clips long content at BOTH ends and takes the footer's buttons with it, leaving Escape as the only way out. It only shows on real data, never on demo data (gh#617)."},{id:"no-utility-spacing",severity:"error",category:"composition",standard:null,fix:"Remove gap-*/p-*/m-* from your own markup; space siblings with <Flex gap> / <ResponsiveGrid>. Page sections are spaced by <PageContainer> (docs/CONSUMER-RULES.md \xA73)."},{id:"no-utility-layout",severity:"error",category:"composition",standard:null,fix:'Replace className="flex \u2026" / "grid \u2026" with <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid columns>.'},{id:"no-hand-rolled-surface",severity:"warn",category:"composition",standard:null,fix:"A rounded+border/bg div is a fake surface \u2014 use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from tokens. A read-only sample of a colour a USER chose is Swatch, which takes that value as a prop (gh#527)."},{id:"sibling-cards-need-flex",severity:"warn",category:"composition",standard:null,fix:'Wrap adjacent <Card>s in <Flex direction="col" gap="lg"> or <ResponsiveGrid>; direct children of PageContainer are spaced by the page already.'},{id:"no-raw-palette-color",severity:"error",category:"tokens",standard:null,fix:"Use semantic tokens (bg-primary, text-muted-foreground), never raw palette (bg-blue-500)."},{id:"no-arbitrary-hex",severity:"error",category:"tokens",standard:null,fix:"No hardcoded hex in className; read design-system color tokens."},{id:"no-arbitrary-spacing",severity:"error",category:"tokens",standard:null,fix:"No p-[13px]/gap-[7px]; use the token scale / <Flex gap> / <PageContainer>."},{id:"no-arbitrary-size",severity:"error",category:"tokens",standard:null,fix:"No w-[37px]/h-[260px]; use token sizes or a sizing prop (min-w-[\u2026] allowed)."},{id:"no-arbitrary-typography",severity:"error",category:"tokens",standard:null,fix:"No text-[20px]/leading-[1.7]; use the golden-ratio type-scale tokens."},{id:"no-arbitrary-radius",severity:"error",category:"tokens",standard:null,fix:"No rounded-[6px]; use rounded-sm/md/lg radius tokens."},{id:"no-off-scale-token-value",severity:"warn",category:"tokens",standard:null,fix:"A design-system knob you override takes a step (style={{ '--card-space-inset': 'var(--space-4)' }}) or a calc() from one (calc(var(--space-4) + 2px)), not a raw 13px. Only axes that HAVE a scale count: space/padding/gap/margin, font-size, radius, icon-size (width/height/size/offset have none yet, so a number there is fine). A value that is genuinely off the grid keeps its literal and says why in place, with a /* scale-exempt: 6px status dot, below --space-1 */ comment on that line or the one above."},{id:"no-dark-color-override",severity:"warn",category:"tokens",standard:null,fix:"Drop dark: color overrides \u2014 semantic tokens already adapt."},{id:"raw-white-black",severity:"warn",category:"tokens",standard:null,fix:"Prefer semantic tokens (text-primary-foreground, bg-background) over raw white/black."},{id:"no-domain-tracking-token",severity:"error",category:"tokens",standard:null,fix:"No package-tracking/domain tokens; use semantic tokens or app theme overrides."},{id:"no-space-xy",severity:"error",category:"tokens",standard:null,fix:"Use <Flex gap> instead of space-x/y-*."},{id:"no-raw-select",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Select> from @godxjp/ui, not a raw <select>."},{id:"no-raw-table",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use the <Table>/<DataTable> family, not a raw <table>."},{id:"no-raw-input",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Input> from @godxjp/ui, not a raw <input>."},{id:"no-raw-textarea",severity:"warn",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Textarea> from @godxjp/ui, not a raw <textarea>."},{id:"no-raw-button",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Button> from @godxjp/ui, not a raw <button>."},{id:"card-manual-padding",severity:"error",category:"composition",standard:null,fix:"Wrap the body in <CardContent>; don't hand-roll padding on <Card>."},{id:"card-needs-content",severity:"error",category:"composition",standard:null,fix:"<Card> body must be in <CardContent> (no padding otherwise); flush only for a full-bleed table."},{id:"bare-control-needs-formfield",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.3.1 \xB7 3.3.2 \xB7 @godxjp/ui FormField (cardinal rule 227)",fix:"Wrap a labelled control in <FormField label=\u2026> \u2014 it owns label\u2194control id wiring, aria/error, AND the field rhythm; never pair a bare <Label> with an <Input>."},{id:"manual-field-error",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 3.3.1",fix:"Use <FormField error=\u2026>, not a hand-rolled <p class='text-destructive'>."},{id:"manual-field-helper",severity:"warn",category:"composition",standard:null,fix:"Use <FormField helper=\u2026>, not a hand-rolled helper <p>."},{id:"status-tone-not-variant",severity:"error",category:"api",standard:null,fix:"Badge/Tag/StatCard status uses tone, not variant (variant is structural)."},{id:"value-callback-on-value-change",severity:"error",category:"api",standard:null,fix:"Abstract value components use onValueChange, not onChange."},{id:"icon-button-needs-name",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 4.1.2 \xB7 1.1.1 \xB7 WAI-ARIA 1.2 \xB7 Accessible Name Computation 1.2",fix:"Name <Button size='icon'> with aria-label={t('\u2026')} OR from content \u2014 a <VisuallyHidden>/sr-only child beside the aria-hidden glyph. Text inside an aria-hidden subtree names nothing."},{id:"img-needs-alt",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 1.1.1 \xB7 HTML Living Standard",fix:"Add alt to every <img> (alt='' if decorative); prefer <Avatar>/<AspectRatio>."},{id:"no-positive-tabindex",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.4.3 \xB7 WAI-ARIA APG",fix:"Use tabIndex 0 or -1 only; never positive \u2014 it breaks focus order."},{id:"hand-rolled-close-glyph",severity:"warn",category:"a11y",standard:"WAI-ARIA 1.2 (dialog) \xB7 WCAG 2.2 SC 4.1.2",fix:"Pass onDismiss to <Alert>, or use <Dialog>/<Sheet>'s built-in labelled close \u2014 not a bare \u2715."},{id:"no-emoji-in-ui",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"No emoji in product UI; quiet i18n copy + Lucide icon + Badge tone."},{id:"no-emoji-flag",severity:"warn",category:"i18n",standard:"ISO 3166-1 \xB7 ECMA-402 Intl.DisplayNames \xB7 Unicode UTS #51",fix:"Derive country names from Intl.DisplayNames; no emoji flags."},{id:"hardcoded-currency",severity:"warn",category:"i18n",standard:"ISO 4217 \xB7 ECMA-402 Intl.NumberFormat",fix:"Format money with Intl.NumberFormat({ style: 'currency', currency }), not \xA5{amount}."},{id:"raw-intl-date",severity:"warn",category:"i18n",standard:"ISO 8601 \xB7 IANA tz \xB7 ECMA-402 Intl.DateTimeFormat",fix:"Use formatDate from @godxjp/ui/datetime, not hand-built or locale-default dates."},{id:"no-physical-direction",severity:"warn",category:"rtl",standard:"W3C CSS Logical Properties L1 \xB7 WCAG 2.2 (1.3.2)",fix:"Use logical utilities (ms-/me-/ps-/pe-, start-/end-, text-start/end, border-s/e, rounded-s/e)."},{id:"no-em-dash-in-copy",severity:"warn",category:"copy",standard:"@godxjp/ui reference-design typography",fix:"No em-dash (\u2014) in copy; use a middot \xB7 or two calm sentences."},{id:"lucide-icon-needs-size",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.4.4 \xB7 @godxjp/ui icon scale (--icon-size-*)",fix:'A lucide glyph outside a sizing context draws at its intrinsic 24px. Render it as <Icon as={Lock} size="sm" tone="muted" /> \u2014 the primitive puts it on the --icon-size-* scale and is aria-hidden unless you pass a label.'},{id:"no-hand-rolled-list",severity:"warn",category:"composition",standard:"WAI-ARIA 1.2 (list / listitem) \xB7 HTML Living Standard (ul/ol/li) \xB7 WCAG 2.2 SC 1.3.1",fix:'Build the list as <Flex as="ul" marker="none" direction="col" gap="none"> with <ListRow as="li"> rows \u2014 not a raw <ul>/<ol> (no gap token), not <div role="list">/<div role="listitem">, and never a wrapper around each row: the divider is :not(:last-child) among SIBLINGS, so a row alone in its own wrapper loses it silently (a consumer lost every divider in a settings menu and a dashboard this way). marker="none" keeps the element, the <li> semantics and the gap, and drops the bullet and the --space-5 indent (gh#714). A deliberate exception \u2014 a drag-and-drop Kanban column, an evidence list inside a TableCell \u2014 takes an ui-audit-disable-line that says so.'}];function ae(e){return e?Z.filter(t=>t.category===e):Z}var ne="node node_modules/@godxjp/ui/scripts/visual-audit.mjs <baseUrl> [route \u2026] (optional peers, TESTED range: playwright >=1.55 <2 [1.61.1] + @axe-core/playwright >=4.10 <5 [4.12.1] + axe-core >=4.10 <5 [4.12.1] + a chromium via `playwright install chromium`; --strict for a CI gate, --format json ALWAYS emits valid JSON with a status of ok|partial|error separating infra errors[] from product findings[], --rules to print this catalog)",oe=[{id:"css-layers-missing",severity:"error",category:"layout",standard:"@godxjp/ui styles contract (styles / styles/core are the only entries)",fix:"Import `@godxjp/ui/styles` (or `styles/core` without fonts); never cherry-pick *-layout.css \u2014 a missing layer renders naked menus and unsized Select rows."},{id:"control-height-mismatch",severity:"error",category:"layout",standard:"@godxjp/ui control tier (--control-height) \xB7 Nielsen consistency heuristic",fix:"Every control in one row must share --control-height; replace hand-rolled pills with Avatar/Button/Badge, never restyle a control's height."},{id:"sibling-card-gap",severity:"error",category:"layout",standard:"@godxjp/ui spacing scale (docs/SPACING.md)",fix:'Adjacent Cards need one space step between them \u2014 <Flex direction="col" gap>, <ResponsiveGrid>, or direct children of PageContainer.'},{id:"row-content-starved",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 reflow",fix:`A sibling (a w-full SelectTrigger) takes the row's width and truncates its neighbours \u2014 give the Select width="auto" or move it out of the row.`},{id:"axe-violations",severity:"warn",category:"a11y",standard:"WCAG 2.2 A/AA \xB7 WAI-ARIA 1.2 (axe-core engine)",fix:"Fix each axe node \u2014 contrast (1.4.3), name/role/value (4.1.2), ARIA, landmarks. Runs on the REAL DOM, catching what static analysis cannot."},{id:"target-size-min",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.5.8 (24\xD724 AA) \xB7 2.5.5 (44\xD744 AAA)",fix:"Interactive targets must be \u226524\xD724 CSS px; size from the --control-height tier."},{id:"oversaturated-accent",severity:"warn",category:"color",standard:"@godxjp/ui reference-design \u6E0B\u307F (OKLCH chroma \u2264 0.18)",fix:"Desaturate brand/primary surfaces (OKLCH chroma \u2264 0.18); read --primary tokens, no raw vivid bars."},{id:"emoji-rendered",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"Remove emoji from rendered product text; quiet i18n copy + Lucide icon + Badge tone."},{id:"alert-controls-misplaced",severity:"warn",category:"layout",standard:"@godxjp/ui Alert anatomy \xB7 WAI-ARIA 1.2 \xB7 WCAG 2.2 SC 4.1.2",fix:"Use <Alert>: one leading tone icon, <Alert.Actions> trailing-right normal width, onDismiss \xD7 top-right, one horizontal row."}];function ie(e){return e?oe.filter(t=>t.category===e):oe}var l={name:"@godxjp/ui-mcp",version:"27.12.0",godxUiCompatibility:"27.12.x",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).",type:"module",main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",bin:{"godx-ui-mcp":"./dist/index.js"},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org/",access:"public"},repository:{type:"git",url:"git+https://github.com/godx-jp/godxjp-ui.git",directory:"mcp"},homepage:"https://github.com/godx-jp/godxjp-ui/tree/main/mcp#readme",license:"Apache-2.0",scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js",inspect:"npx @modelcontextprotocol/inspector node dist/index.js","type-check":"tsc --noEmit",test:"vitest run",prepublishOnly:"npm run build"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.4.3"},devDependencies:{"@types/node":"^22.10.0",tsup:"^8.5.1",typescript:"^6.0.3",vitest:"^4.1.6"},keywords:["mcp","model-context-protocol","godxjp","ui","design-system","react","claude","cursor"],author:"GoDX (https://godx.jp)",bugs:{url:"https://github.com/godx-jp/godxjp-ui/issues"}};var B=[{name:"list_skills",description:"List every design/taste skill bundled by this MCP (id + name + whenToUse + section ids). Use FIRST to discover skills; then `get_skill_section` to drill in.",inputSchema:{type:"object",properties:{}}},{name:"list_primitives",description:"List every @godxjp/ui primitive/composite/shell (group + tagline per entry). Optionally filter by group. Then `get_component` for one's full API.",inputSchema:{type:"object",properties:{group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_patterns",description:"List every canonical copy-paste code pattern (signup-form, settings-page, data-table-page, async-data-state, confirm-destructive, \u2026); common aliases resolve too. Use before `get_pattern`.",inputSchema:{type:"object",properties:{}}},{name:"list_anti_ai_tells",description:"List every AI-tell pattern to AVOID (optionally by category). Use to self-audit a design before shipping; then `get_anti_ai_tell` for the fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["visual","layout","copy","interaction","imagery","structure"]}}}},{name:"list_redesign_checks",description:"List the redesign audit checklist (50+ checks; optionally by category). Use when auditing an existing project; then `get_redesign_check` for a symptom's fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["typography","color-surface","layout","interactivity","content","components","iconography","code-quality","omissions"]}}}},{name:"list_audit_rules",description:"List the LOCAL static ui-audit rules (scripts/ui-audit.mjs) to run BEFORE any visual review \u2014 each cites the standard it enforces (WCAG/WAI-ARIA/Intl/ISO/IANA/CSS-Logical) + a fix + the run command. Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["tokens","composition","api","a11y","i18n","rtl","copy"]}}}},{name:"list_visual_checks",description:"List the RUNTIME visual-audit checks (scripts/visual-audit.mjs \u2014 Playwright + axe-core) to run against the RUNNING app: contrast/ARIA (axe), target size, rendered-accent chroma, DOM emoji, banner layout. Needs a browser (vs list_audit_rules, static). Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["a11y","color","i18n","layout"]}}}},{name:"get_anti_ai_tell",description:"Fetch ONE anti-AI-tell \u2014 full body + concrete fix. Use after `list_anti_ai_tells`.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Exact tell name from list_anti_ai_tells."}},required:["name"]}},{name:"get_redesign_check",description:"Fetch redesign check(s) matching a symptom snippet. Returns full fix + UI note. Use after `list_redesign_checks`.",inputSchema:{type:"object",properties:{symptom:{type:"string",description:"Fragment of the symptom text (e.g. 'Inter everywhere' / '100vh')."}},required:["symptom"]}},{name:"get_skill_section",description:"Fetch ONE section of ONE skill \u2014 token-efficient. E.g. `skill='soft', section='double-bezel'`. Use after `list_skills` narrowed the relevant skill + section.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Skill id (e.g. 'soft', 'minimalist', 'taste')."},section:{type:"string",description:"Section id within that skill."}},required:["skill","section"]}},{name:"get_component",description:"Full guide for one @godxjp/ui component \u2014 import path, props/types/defaults, HOW to use it (DO/DON'T), WHEN to reach for it (use cases), related components (don't reinvent/confuse), a copy-paste example, story path, and cardinal rules. Use this before hand-rolling anything. Design-token knobs are listed compactly (name+default); pass `verbose:true` for what each token controls.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Component name (e.g. 'Button', 'DataTable')."},verbose:{type:"boolean",description:"Include the full design-token table with a 'what it controls' description per token. Default false (compact token+default only) to save context."}},required:["name"]}},{name:"get_pattern",description:"Full code snippet for one canonical pattern \u2014 copy-paste-ready.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Pattern slug (use list_patterns first)."}},required:["name"]}},{name:"get_rule",description:"Read one cardinal rule from CLAUDE.md (by number) OR all if no number.",inputSchema:{type:"object",properties:{number:{type:"number",description:"Rule number (1-N)."}}}},{name:"get_vocab",description:"Read shared prop-vocabulary type (`SizeProp`, `StatusProp`, `ColorProp`, `LoadingProp`, etc.) OR all if no name.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Vocab type name."}}}},{name:"get_tokens",description:"Read design tokens, optionally filtered by tier category (primitive / semantic / component).",inputSchema:{type:"object",properties:{category:{type:"string",enum:["primitive","semantic","component"]}}}},{name:"list_consumer_skills",description:"List the design skills relevant to an app-dev BUILDING WITH @godxjp/ui (audience consumer/both). Hides core library-maintenance skills. START HERE if you import @godxjp/ui and want guidance (design-to-page, compose-a-screen, taste, \u2026). Returns id + name + whenToUse + section ids.",inputSchema:{type:"object",properties:{}}},{name:"get_consumer_skill",description:"Fetch ONE section of ONE consumer-facing skill. Same as get_skill_section but refuses core-only skills (steers app-devs away from library-maintenance material). Use after list_consumer_skills / route_consumer_task.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Consumer skill id (e.g. 'design-to-page', 'compose-a-screen')."},section:{type:"string",description:"Section id within that skill."}},required:["skill"]}},{name:"route_consumer_task",description:"Natural-language task \u2192 consumer skill+section pointer. Like route_task but only points to consumer-facing skills (never core library-maintenance). Use FIRST when you're building an app with @godxjp/ui.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"draft_bug_report",description:"When @godxjp/ui ITSELF is at fault (missing token, a primitive lacking the controlled-vocabulary prop, a real a11y/behaviour bug, a wrong catalog example) and you cannot follow a rule \u2014 DON'T fake a workaround. This drafts a detailed GitHub issue body + a copy-paste `gh issue create` command so you can report it. Prints the command only; never runs gh.",inputSchema:{type:"object",properties:{summary:{type:"string",description:"One-line title of the bug / blocked rule."},repro:{type:"string",description:"Minimal steps or code to reproduce."},expected:{type:"string",description:"What SHOULD happen (per the rule/spec)."},actual:{type:"string",description:"What actually happens."},component:{type:"string",description:"Affected component name, if any (links to get_component)."},rule:{type:"number",description:"Cardinal rule number that can't be followed, if any."},version:{type:"string",description:"Installed @godxjp/ui version (e.g. '12.1.0')."},env:{type:"string",description:"Environment (browser/OS/framework), if relevant."}},required:["summary"]}},{name:"check_compatibility",description:"Report whether the @godxjp/ui version installed in the target project matches THIS catalog (which describes one release train). A mismatched minor means the props/tokens/patterns may describe a build they never installed (#140). Pass the installed version (`npm ls @godxjp/ui`); call it at the START of a consumer session.",inputSchema:{type:"object",properties:{version:{type:"string",description:"Installed @godxjp/ui version in the target project, e.g. '16.10.0'. Omit to just read the catalog's own version + compatible range."}}}},{name:"route_task",description:"Natural-language task \u2192 skill+section pointer (e.g. 'design a premium agency hero' \u2192 soft/vibe-archetypes). Use FIRST when you don't know which skill applies.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"suggest_primitive",description:"Use case \u2192 primitive recommendation. E.g. 'confirm a destructive delete' \u2192 DangerZone pattern + Dialog suggestion.",inputSchema:{type:"object",properties:{use_case:{type:"string"}},required:["use_case"]}},{name:"search_components",description:"Fuzzy-search primitives by name / tagline / prop. Returns ranked matches.",inputSchema:{type:"object",properties:{query:{type:"string"}},required:["query"]}},{name:"get_frame_coverage",description:"Verified preview-contract coverage for a component (issue #163). Answers 'is this state actually PROVEN?' \u2014 returns, per contract dimension (variants, tones, sizes, shapes, density, controlled/uncontrolled ownership, disabled/read-only/loading/empty/error/success, async retry/cancel/offline, responsive viewport matrix, RTL, long/localized content, keyboard/focus, accessible name/description/error, reduced motion / coarse touch), whether an EXECUTED case proves it (covered), whether nothing proves it (UNTESTED), or whether it cannot exist (not-applicable, with a reason). UNTESTED IS NOT A PASS: never infer that a component supports a state because an example renders. Call this before telling a user a component 'supports' anything. Omit `name` for the repo-wide summary and the tracked known gaps.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Public export name (e.g. 'Button', 'DataTable', 'CardFooter'). Omit for the repo-wide coverage summary."}}}},{name:"lint_jsx",description:"Heuristic check of a JSX snippet for common violations \u2014 raw `<button>` / `<input>`, `color='error'` on Tag/Badge, missing aria-label, missing source.code override on stories with cell renderers (rule 34), etc.",inputSchema:{type:"object",properties:{jsx:{type:"string"}},required:["jsx"]}}],De=new Set(B.map(e=>e.name));function Oe(e=H()){let t=l.godxUiCompatibility??l.version,a=`@godxjp/ui-mcp ${l.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let o=e.source==="node_modules"?"read from node_modules at answer time":"from GODX_UI_VERSION at launch \u2014 node_modules/@godxjp/ui not resolved";return`${a} \u2014 installed @godxjp/ui ${e.version} (${o})`}function Ee(e=H()){let t=e?P(e.version):null,a=P(l.version);return!e||!t||!a?null:Le(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${l.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog is BEHIND the package and may be missing props and components that exist. Do not conclude from this answer that a prop is unavailable. Restart the session so the server relaunches on the installed version (run \`npx @godxjp/ui sync-rules\` first if the project's .mcp.json still pins an older @godxjp/ui-mcp).`:t.major===a.major?null:`\u26A0\uFE0F MAJOR MISMATCH: this server is @godxjp/ui-mcp ${l.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog may describe components that do not exist in the installed package. Pin the MCP to @godxjp/ui-mcp@${e.version} (\`npx @godxjp/ui sync-rules\` updates the project's .mcp.json; a registration outside the project needs \`claude mcp remove <key>\`), then restart the agent.`}async function ce(e,t){let a=await Ne(e,t);if(!De.has(e))return a;let o=H(),n=Ee(o);return`${Oe(o)}
4476
+ finding and needs no suppression.`,Z=[{id:"dialog-needs-body",severity:"error",category:"composition",standard:null,fix:"Wrap a Dialog/Sheet's middle in <DialogBody>/<SheetBody>. The scroll lives on the body \u2014 the content box is `overflow: hidden` with no max-height \u2014 so an overlay without one clips long content at BOTH ends and takes the footer's buttons with it, leaving Escape as the only way out. It only shows on real data, never on demo data (gh#617)."},{id:"no-utility-spacing",severity:"error",category:"composition",standard:null,fix:"Remove gap-*/p-*/m-* from your own markup; space siblings with <Flex gap> / <ResponsiveGrid>. Page sections are spaced by <PageContainer> (docs/CONSUMER-RULES.md \xA73)."},{id:"no-utility-layout",severity:"error",category:"composition",standard:null,fix:'Replace className="flex \u2026" / "grid \u2026" with <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid columns>.'},{id:"no-hand-rolled-surface",severity:"warn",category:"composition",standard:null,fix:"A rounded+border/bg div is a fake surface \u2014 use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from tokens. A read-only sample of a colour a USER chose is Swatch, which takes that value as a prop (gh#527)."},{id:"sibling-cards-need-flex",severity:"warn",category:"composition",standard:null,fix:'Wrap adjacent <Card>s in <Flex direction="col" gap="lg"> or <ResponsiveGrid>; direct children of PageContainer are spaced by the page already.'},{id:"no-raw-palette-color",severity:"error",category:"tokens",standard:null,fix:"Use semantic tokens (bg-primary, text-muted-foreground), never raw palette (bg-blue-500)."},{id:"no-arbitrary-hex",severity:"error",category:"tokens",standard:null,fix:"No hardcoded hex in className; read design-system color tokens."},{id:"no-arbitrary-spacing",severity:"error",category:"tokens",standard:null,fix:"No p-[13px]/gap-[7px]; use the token scale / <Flex gap> / <PageContainer>."},{id:"no-arbitrary-size",severity:"error",category:"tokens",standard:null,fix:"No w-[37px]/h-[260px]; use token sizes or a sizing prop (min-w-[\u2026] allowed)."},{id:"no-arbitrary-typography",severity:"error",category:"tokens",standard:null,fix:"No text-[20px]/leading-[1.7]; use the golden-ratio type-scale tokens."},{id:"no-arbitrary-radius",severity:"error",category:"tokens",standard:null,fix:"No rounded-[6px]; use rounded-sm/md/lg radius tokens."},{id:"no-off-scale-token-value",severity:"warn",category:"tokens",standard:null,fix:"A design-system knob you override takes a step (style={{ '--card-space-inset': 'var(--space-4)' }}) or a calc() from one (calc(var(--space-4) + 2px)), not a raw 13px. Only axes that HAVE a scale count: space/padding/gap/margin, font-size, radius, icon-size (width/height/size/offset have none yet, so a number there is fine). A value that is genuinely off the grid keeps its literal and says why in place, with a /* scale-exempt: 6px status dot, below --space-1 */ comment on that line or the one above."},{id:"no-dark-color-override",severity:"warn",category:"tokens",standard:null,fix:"Drop dark: color overrides \u2014 semantic tokens already adapt."},{id:"raw-white-black",severity:"warn",category:"tokens",standard:null,fix:"Prefer semantic tokens (text-primary-foreground, bg-background) over raw white/black."},{id:"no-domain-tracking-token",severity:"error",category:"tokens",standard:null,fix:"No package-tracking/domain tokens; use semantic tokens or app theme overrides."},{id:"no-space-xy",severity:"error",category:"tokens",standard:null,fix:"Use <Flex gap> instead of space-x/y-*."},{id:"no-raw-select",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Select> from @godxjp/ui, not a raw <select>."},{id:"no-raw-table",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use the <Table>/<DataTable> family, not a raw <table>."},{id:"no-raw-input",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Input> from @godxjp/ui, not a raw <input>."},{id:"no-raw-textarea",severity:"warn",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Textarea> from @godxjp/ui, not a raw <textarea>."},{id:"no-raw-button",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Button> from @godxjp/ui, not a raw <button>."},{id:"card-manual-padding",severity:"error",category:"composition",standard:null,fix:"Wrap the body in <CardContent>; don't hand-roll padding on <Card>."},{id:"card-needs-content",severity:"error",category:"composition",standard:null,fix:"<Card> body must be in <CardContent> (no padding otherwise); flush only for a full-bleed table."},{id:"bare-control-needs-formfield",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.3.1 \xB7 3.3.2 \xB7 @godxjp/ui FormField (cardinal rule 227)",fix:"Wrap a labelled control in <FormField label=\u2026> \u2014 it owns label\u2194control id wiring, aria/error, AND the field rhythm; never pair a bare <Label> with an <Input>."},{id:"manual-field-error",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 3.3.1",fix:"Use <FormField error=\u2026>, not a hand-rolled <p class='text-destructive'>."},{id:"manual-field-helper",severity:"warn",category:"composition",standard:null,fix:"Use <FormField helper=\u2026>, not a hand-rolled helper <p>."},{id:"status-tone-not-variant",severity:"error",category:"api",standard:null,fix:"Badge/Tag/StatCard status uses tone, not variant (variant is structural)."},{id:"value-callback-on-value-change",severity:"error",category:"api",standard:null,fix:"Abstract value components use onValueChange, not onChange."},{id:"icon-button-needs-name",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 4.1.2 \xB7 1.1.1 \xB7 WAI-ARIA 1.2 \xB7 Accessible Name Computation 1.2",fix:"Name <Button size='icon'> with aria-label={t('\u2026')} OR from content \u2014 a <VisuallyHidden>/sr-only child beside the aria-hidden glyph. Text inside an aria-hidden subtree names nothing."},{id:"img-needs-alt",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 1.1.1 \xB7 HTML Living Standard",fix:"Add alt to every <img> (alt='' if decorative); prefer <Avatar>/<AspectRatio>."},{id:"no-positive-tabindex",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.4.3 \xB7 WAI-ARIA APG",fix:"Use tabIndex 0 or -1 only; never positive \u2014 it breaks focus order."},{id:"hand-rolled-close-glyph",severity:"warn",category:"a11y",standard:"WAI-ARIA 1.2 (dialog) \xB7 WCAG 2.2 SC 4.1.2",fix:"Pass onDismiss to <Alert>, or use <Dialog>/<Sheet>'s built-in labelled close \u2014 not a bare \u2715."},{id:"no-emoji-in-ui",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"No emoji in product UI; quiet i18n copy + Lucide icon + Badge tone."},{id:"no-emoji-flag",severity:"warn",category:"i18n",standard:"ISO 3166-1 \xB7 ECMA-402 Intl.DisplayNames \xB7 Unicode UTS #51",fix:"Derive country names from Intl.DisplayNames; no emoji flags."},{id:"hardcoded-currency",severity:"warn",category:"i18n",standard:"ISO 4217 \xB7 ECMA-402 Intl.NumberFormat",fix:"Format money with Intl.NumberFormat({ style: 'currency', currency }), not \xA5{amount}."},{id:"raw-intl-date",severity:"warn",category:"i18n",standard:"ISO 8601 \xB7 IANA tz \xB7 ECMA-402 Intl.DateTimeFormat",fix:"Use formatDate from @godxjp/ui/datetime, not hand-built or locale-default dates."},{id:"no-physical-direction",severity:"warn",category:"rtl",standard:"W3C CSS Logical Properties L1 \xB7 WCAG 2.2 (1.3.2)",fix:"Use logical utilities (ms-/me-/ps-/pe-, start-/end-, text-start/end, border-s/e, rounded-s/e)."},{id:"no-em-dash-in-copy",severity:"warn",category:"copy",standard:"@godxjp/ui reference-design typography",fix:"No em-dash (\u2014) in copy; use a middot \xB7 or two calm sentences."},{id:"lucide-icon-needs-size",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.4.4 \xB7 @godxjp/ui icon scale (--icon-size-*)",fix:'A lucide glyph outside a sizing context draws at its intrinsic 24px. Render it as <Icon as={Lock} size="sm" tone="muted" /> \u2014 the primitive puts it on the --icon-size-* scale and is aria-hidden unless you pass a label.'},{id:"no-hand-rolled-list",severity:"warn",category:"composition",standard:"WAI-ARIA 1.2 (list / listitem) \xB7 HTML Living Standard (ul/ol/li) \xB7 WCAG 2.2 SC 1.3.1",fix:'Build the list as <Flex as="ul" marker="none" direction="col" gap="none"> with <ListRow as="li"> rows \u2014 not a raw <ul>/<ol> (no gap token), not <div role="list">/<div role="listitem">, and never a wrapper around each row: the divider is :not(:last-child) among SIBLINGS, so a row alone in its own wrapper loses it silently (a consumer lost every divider in a settings menu and a dashboard this way). marker="none" keeps the element, the <li> semantics and the gap, and drops the bullet and the --space-5 indent (gh#714). A deliberate exception \u2014 a drag-and-drop Kanban column, an evidence list inside a TableCell \u2014 takes an ui-audit-disable-line that says so.'}];function ae(e){return e?Z.filter(t=>t.category===e):Z}var ne="node node_modules/@godxjp/ui/scripts/visual-audit.mjs <baseUrl> [route \u2026] (optional peers, TESTED range: playwright >=1.55 <2 [1.61.1] + @axe-core/playwright >=4.10 <5 [4.12.1] + axe-core >=4.10 <5 [4.12.1] + a chromium via `playwright install chromium`; --strict for a CI gate, --format json ALWAYS emits valid JSON with a status of ok|partial|error separating infra errors[] from product findings[], --rules to print this catalog)",oe=[{id:"css-layers-missing",severity:"error",category:"layout",standard:"@godxjp/ui styles contract (styles / styles/core are the only entries)",fix:"Import `@godxjp/ui/styles` (or `styles/core` without fonts); never cherry-pick *-layout.css \u2014 a missing layer renders naked menus and unsized Select rows."},{id:"control-height-mismatch",severity:"error",category:"layout",standard:"@godxjp/ui control tier (--control-height) \xB7 Nielsen consistency heuristic",fix:"Every control in one row must share --control-height; replace hand-rolled pills with Avatar/Button/Badge, never restyle a control's height."},{id:"sibling-card-gap",severity:"error",category:"layout",standard:"@godxjp/ui spacing scale (docs/SPACING.md)",fix:'Adjacent Cards need one space step between them \u2014 <Flex direction="col" gap>, <ResponsiveGrid>, or direct children of PageContainer.'},{id:"row-content-starved",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 reflow",fix:`A sibling (a w-full SelectTrigger) takes the row's width and truncates its neighbours \u2014 give the Select width="auto" or move it out of the row.`},{id:"axe-violations",severity:"warn",category:"a11y",standard:"WCAG 2.2 A/AA \xB7 WAI-ARIA 1.2 (axe-core engine)",fix:"Fix each axe node \u2014 contrast (1.4.3), name/role/value (4.1.2), ARIA, landmarks. Runs on the REAL DOM, catching what static analysis cannot."},{id:"target-size-min",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.5.8 (24\xD724 AA) \xB7 2.5.5 (44\xD744 AAA)",fix:"Interactive targets must be \u226524\xD724 CSS px; size from the --control-height tier."},{id:"oversaturated-accent",severity:"warn",category:"color",standard:"@godxjp/ui reference-design \u6E0B\u307F (OKLCH chroma \u2264 0.18)",fix:"Desaturate brand/primary surfaces (OKLCH chroma \u2264 0.18); read --primary tokens, no raw vivid bars."},{id:"emoji-rendered",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"Remove emoji from rendered product text; quiet i18n copy + Lucide icon + Badge tone."},{id:"alert-controls-misplaced",severity:"warn",category:"layout",standard:"@godxjp/ui Alert anatomy \xB7 WAI-ARIA 1.2 \xB7 WCAG 2.2 SC 4.1.2",fix:"Use <Alert>: one leading tone icon, <Alert.Actions> trailing-right normal width, onDismiss \xD7 top-right, one horizontal row."}];function ie(e){return e?oe.filter(t=>t.category===e):oe}var l={name:"@godxjp/ui-mcp",version:"28.0.0",godxUiCompatibility:"28.0.x",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).",type:"module",main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",bin:{"godx-ui-mcp":"./dist/index.js"},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org/",access:"public"},repository:{type:"git",url:"git+https://github.com/godx-jp/godxjp-ui.git",directory:"mcp"},homepage:"https://github.com/godx-jp/godxjp-ui/tree/main/mcp#readme",license:"Apache-2.0",scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js",inspect:"npx @modelcontextprotocol/inspector node dist/index.js","type-check":"tsc --noEmit",test:"vitest run",prepublishOnly:"npm run build"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.4.3"},devDependencies:{"@types/node":"^22.10.0",tsup:"^8.5.1",typescript:"^6.0.3",vitest:"^4.1.6"},keywords:["mcp","model-context-protocol","godxjp","ui","design-system","react","claude","cursor"],author:"GoDX (https://godx.jp)",bugs:{url:"https://github.com/godx-jp/godxjp-ui/issues"}};var B=[{name:"list_skills",description:"List every design/taste skill bundled by this MCP (id + name + whenToUse + section ids). Use FIRST to discover skills; then `get_skill_section` to drill in.",inputSchema:{type:"object",properties:{}}},{name:"list_primitives",description:"List every @godxjp/ui primitive/composite/shell (group + tagline per entry). Optionally filter by group. Then `get_component` for one's full API.",inputSchema:{type:"object",properties:{group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_patterns",description:"List every canonical copy-paste code pattern (signup-form, settings-page, data-table-page, async-data-state, confirm-destructive, \u2026); common aliases resolve too. Use before `get_pattern`.",inputSchema:{type:"object",properties:{}}},{name:"list_anti_ai_tells",description:"List every AI-tell pattern to AVOID (optionally by category). Use to self-audit a design before shipping; then `get_anti_ai_tell` for the fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["visual","layout","copy","interaction","imagery","structure"]}}}},{name:"list_redesign_checks",description:"List the redesign audit checklist (50+ checks; optionally by category). Use when auditing an existing project; then `get_redesign_check` for a symptom's fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["typography","color-surface","layout","interactivity","content","components","iconography","code-quality","omissions"]}}}},{name:"list_audit_rules",description:"List the LOCAL static ui-audit rules (scripts/ui-audit.mjs) to run BEFORE any visual review \u2014 each cites the standard it enforces (WCAG/WAI-ARIA/Intl/ISO/IANA/CSS-Logical) + a fix + the run command. Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["tokens","composition","api","a11y","i18n","rtl","copy"]}}}},{name:"list_visual_checks",description:"List the RUNTIME visual-audit checks (scripts/visual-audit.mjs \u2014 Playwright + axe-core) to run against the RUNNING app: contrast/ARIA (axe), target size, rendered-accent chroma, DOM emoji, banner layout. Needs a browser (vs list_audit_rules, static). Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["a11y","color","i18n","layout"]}}}},{name:"get_anti_ai_tell",description:"Fetch ONE anti-AI-tell \u2014 full body + concrete fix. Use after `list_anti_ai_tells`.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Exact tell name from list_anti_ai_tells."}},required:["name"]}},{name:"get_redesign_check",description:"Fetch redesign check(s) matching a symptom snippet. Returns full fix + UI note. Use after `list_redesign_checks`.",inputSchema:{type:"object",properties:{symptom:{type:"string",description:"Fragment of the symptom text (e.g. 'Inter everywhere' / '100vh')."}},required:["symptom"]}},{name:"get_skill_section",description:"Fetch ONE section of ONE skill \u2014 token-efficient. E.g. `skill='soft', section='double-bezel'`. Use after `list_skills` narrowed the relevant skill + section.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Skill id (e.g. 'soft', 'minimalist', 'taste')."},section:{type:"string",description:"Section id within that skill."}},required:["skill","section"]}},{name:"get_component",description:"Full guide for one @godxjp/ui component \u2014 import path, props/types/defaults, HOW to use it (DO/DON'T), WHEN to reach for it (use cases), related components (don't reinvent/confuse), a copy-paste example, story path, and cardinal rules. Use this before hand-rolling anything. Design-token knobs are listed compactly (name+default); pass `verbose:true` for what each token controls.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Component name (e.g. 'Button', 'DataTable')."},verbose:{type:"boolean",description:"Include the full design-token table with a 'what it controls' description per token. Default false (compact token+default only) to save context."}},required:["name"]}},{name:"get_pattern",description:"Full code snippet for one canonical pattern \u2014 copy-paste-ready.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Pattern slug (use list_patterns first)."}},required:["name"]}},{name:"get_rule",description:"Read one cardinal rule from CLAUDE.md (by number) OR all if no number.",inputSchema:{type:"object",properties:{number:{type:"number",description:"Rule number (1-N)."}}}},{name:"get_vocab",description:"Read shared prop-vocabulary type (`SizeProp`, `StatusProp`, `ColorProp`, `LoadingProp`, etc.) OR all if no name.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Vocab type name."}}}},{name:"get_tokens",description:"Read design tokens, optionally filtered by tier category (primitive / semantic / component).",inputSchema:{type:"object",properties:{category:{type:"string",enum:["primitive","semantic","component"]}}}},{name:"list_consumer_skills",description:"List the design skills relevant to an app-dev BUILDING WITH @godxjp/ui (audience consumer/both). Hides core library-maintenance skills. START HERE if you import @godxjp/ui and want guidance (design-to-page, compose-a-screen, taste, \u2026). Returns id + name + whenToUse + section ids.",inputSchema:{type:"object",properties:{}}},{name:"get_consumer_skill",description:"Fetch ONE section of ONE consumer-facing skill. Same as get_skill_section but refuses core-only skills (steers app-devs away from library-maintenance material). Use after list_consumer_skills / route_consumer_task.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Consumer skill id (e.g. 'design-to-page', 'compose-a-screen')."},section:{type:"string",description:"Section id within that skill."}},required:["skill"]}},{name:"route_consumer_task",description:"Natural-language task \u2192 consumer skill+section pointer. Like route_task but only points to consumer-facing skills (never core library-maintenance). Use FIRST when you're building an app with @godxjp/ui.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"draft_bug_report",description:"When @godxjp/ui ITSELF is at fault (missing token, a primitive lacking the controlled-vocabulary prop, a real a11y/behaviour bug, a wrong catalog example) and you cannot follow a rule \u2014 DON'T fake a workaround. This drafts a detailed GitHub issue body + a copy-paste `gh issue create` command so you can report it. Prints the command only; never runs gh.",inputSchema:{type:"object",properties:{summary:{type:"string",description:"One-line title of the bug / blocked rule."},repro:{type:"string",description:"Minimal steps or code to reproduce."},expected:{type:"string",description:"What SHOULD happen (per the rule/spec)."},actual:{type:"string",description:"What actually happens."},component:{type:"string",description:"Affected component name, if any (links to get_component)."},rule:{type:"number",description:"Cardinal rule number that can't be followed, if any."},version:{type:"string",description:"Installed @godxjp/ui version (e.g. '12.1.0')."},env:{type:"string",description:"Environment (browser/OS/framework), if relevant."}},required:["summary"]}},{name:"check_compatibility",description:"Report whether the @godxjp/ui version installed in the target project matches THIS catalog (which describes one release train). A mismatched minor means the props/tokens/patterns may describe a build they never installed (#140). Pass the installed version (`npm ls @godxjp/ui`); call it at the START of a consumer session.",inputSchema:{type:"object",properties:{version:{type:"string",description:"Installed @godxjp/ui version in the target project, e.g. '16.10.0'. Omit to just read the catalog's own version + compatible range."}}}},{name:"route_task",description:"Natural-language task \u2192 skill+section pointer (e.g. 'design a premium agency hero' \u2192 soft/vibe-archetypes). Use FIRST when you don't know which skill applies.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"suggest_primitive",description:"Use case \u2192 primitive recommendation. E.g. 'confirm a destructive delete' \u2192 DangerZone pattern + Dialog suggestion.",inputSchema:{type:"object",properties:{use_case:{type:"string"}},required:["use_case"]}},{name:"search_components",description:"Fuzzy-search primitives by name / tagline / prop. Returns ranked matches.",inputSchema:{type:"object",properties:{query:{type:"string"}},required:["query"]}},{name:"get_frame_coverage",description:"Verified preview-contract coverage for a component (issue #163). Answers 'is this state actually PROVEN?' \u2014 returns, per contract dimension (variants, tones, sizes, shapes, density, controlled/uncontrolled ownership, disabled/read-only/loading/empty/error/success, async retry/cancel/offline, responsive viewport matrix, RTL, long/localized content, keyboard/focus, accessible name/description/error, reduced motion / coarse touch), whether an EXECUTED case proves it (covered), whether nothing proves it (UNTESTED), or whether it cannot exist (not-applicable, with a reason). UNTESTED IS NOT A PASS: never infer that a component supports a state because an example renders. Call this before telling a user a component 'supports' anything. Omit `name` for the repo-wide summary and the tracked known gaps.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Public export name (e.g. 'Button', 'DataTable', 'CardFooter'). Omit for the repo-wide coverage summary."}}}},{name:"lint_jsx",description:"Heuristic check of a JSX snippet for common violations \u2014 raw `<button>` / `<input>`, `color='error'` on Tag/Badge, missing aria-label, missing source.code override on stories with cell renderers (rule 34), etc.",inputSchema:{type:"object",properties:{jsx:{type:"string"}},required:["jsx"]}}],De=new Set(B.map(e=>e.name));function Oe(e=H()){let t=l.godxUiCompatibility??l.version,a=`@godxjp/ui-mcp ${l.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let o=e.source==="node_modules"?"read from node_modules at answer time":"from GODX_UI_VERSION at launch \u2014 node_modules/@godxjp/ui not resolved";return`${a} \u2014 installed @godxjp/ui ${e.version} (${o})`}function Ee(e=H()){let t=e?P(e.version):null,a=P(l.version);return!e||!t||!a?null:Le(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${l.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog is BEHIND the package and may be missing props and components that exist. Do not conclude from this answer that a prop is unavailable. Restart the session so the server relaunches on the installed version (run \`npx @godxjp/ui sync-rules\` first if the project's .mcp.json still pins an older @godxjp/ui-mcp).`:t.major===a.major?null:`\u26A0\uFE0F MAJOR MISMATCH: this server is @godxjp/ui-mcp ${l.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog may describe components that do not exist in the installed package. Pin the MCP to @godxjp/ui-mcp@${e.version} (\`npx @godxjp/ui sync-rules\` updates the project's .mcp.json; a registration outside the project needs \`claude mcp remove <key>\`), then restart the agent.`}async function ce(e,t){let a=await Ne(e,t);if(!De.has(e))return a;let o=H(),n=Ee(o);return`${Oe(o)}
4477
4477
  ${n?`${n}
4478
4478
  `:""}
4479
4479
  ${a}`}async function Ne(e,t){switch(e){case"list_skills":return Re();case"list_primitives":return me(t.group);case"list_patterns":return Ue();case"list_anti_ai_tells":return je(t.category);case"list_redesign_checks":return qe(t.category);case"list_audit_rules":return He(t.category);case"list_visual_checks":return Ve(t.category);case"get_anti_ai_tell":return Ge(String(t.name??""));case"get_redesign_check":return We(String(t.symptom??""));case"get_skill_section":return ge(String(t.skill??""),String(t.section??""));case"get_component":return Ye(String(t.name??""),t.verbose===!0);case"get_pattern":return Qe(String(t.name??""));case"get_rule":return Je(typeof t.number=="number"?t.number:void 0);case"get_vocab":return Ze(t.name==null?void 0:String(t.name));case"get_tokens":return et(t.category);case"list_consumer_skills":return Ie();case"get_consumer_skill":return ze(String(t.skill??""),String(t.section??""));case"route_consumer_task":return le(String(t.task??""),{consumerOnly:!0});case"draft_bug_report":return Pe(t);case"check_compatibility":return ue(t.version==null?void 0:String(t.version));case"route_task":return le(String(t.task??""));case"suggest_primitive":return tt(String(t.use_case??""));case"search_components":return at(String(t.query??""));case"get_frame_coverage":return Xe(t.name===void 0?void 0:String(t.name));case"lint_jsx":return ot(String(t.jsx??""));default:return`Unknown tool: ${e}`}}function Re(){let e=`# Available skills (${S.length})
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui-mcp",
3
- "version": "27.12.0",
4
- "godxUiCompatibility": "27.12.x",
3
+ "version": "28.0.0",
4
+ "godxUiCompatibility": "28.0.x",
5
5
  "description": "Model Context Protocol server for @godxjp/ui — 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 — token-efficient (list → drill-down).",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",