@godxjp/ui-mcp 32.11.0 → 32.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5123,7 +5123,7 @@ A block with no reason is IGNORED and the finding stands. An unclosed block runs
|
|
|
5123
5123
|
The class-shaped rules (gap-*/p-*/m-*, bg-<palette>-*, w-[\u2026], pr-*, dark:*) only read class
|
|
5124
5124
|
expressions \u2014 a className/class attribute, a class-named binding (\`baseClass\`, \`statusStyles\`,
|
|
5125
5125
|
\`badgeVariants\`) or a cn()/clsx()/cva() call \u2014 so prose that merely spells a utility is not a
|
|
5126
|
-
finding and needs no suppression.`,pe=[{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:"nested-card",severity:"warn",category:"composition",standard:null,fix:'A Card (or StatCard) inside a Card draws two borders for one boundary. Group inside the outer Card with <Heading> + <Flex direction="col"> or <Separator />.'},{id:"card-per-section",severity:"warn",category:"composition",standard:null,fix:'5+ Cards in one file is a Card per section. Mark a section with <Heading level={2}> and section spacing (PageContainer children / Flex gap="lg"); keep Card for a real boundary (a table surface, a selectable item).'},{id:"kpi-row-off-dashboard",severity:"warn",category:"composition",standard:null,fix:"3+ StatCards outside a file whose path names a dashboard. A row count is not a KPI: drop the row and show the total in <Pagination showTotal>. StatCard rows are for a dashboard with comparison or trend (delta)."},{id:"empty-state-icon-in-card",severity:"warn",category:"composition",standard:null,fix:'In an admin surface an empty state is one sentence and one action. Drop `icon` from an EmptyState inside a Card, or use variant="compact".'},{id:"badge-wall",severity:"warn",category:"composition",standard:null,fix:"5+ Badge/Tag in one file. Badge is for status only; render categories, tiers and scopes as plain text, one status column per table."},{id:"alert-per-page",severity:"warn",category:"composition",standard:null,fix:"4+ Alerts in one file. Keep one Alert for a state that blocks or changes the task; field errors go in FormField, results in a toast, standing info in plain text."},{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:"formfield-needs-form",severity:"error",category:"composition",standard:"@godxjp/ui Form (gh#998)",fix:'Wrap FormFields in <Form layout="horizontal" labelWidth controlWidth>; a row of fields is <Flex attached> or <Form columns>, never a hand-rolled <Flex>. A field component whose whole output is one FormField is exempt, and so is a field-group component that every caller renders inside a <Form> (checked across the project); when only some callers do, wrap the bare call sites the message names, not the fields. A call site inside a same-file render helper (`const legFields = (\u2026) => \u2026`) counts as under the <Form> its every call sits in (gh#1260).'},{id:"mixed-button-size",severity:"error",category:"composition",standard:null,fix:'Sibling <Button>s under one parent (through fragments, {cond && \u2026}, ternaries, Tooltip wrappers) share ONE size; a missing size is default; icon-sm pairs with sm, icon-xs with xs, icon with default. Never `size="sm"` beside a default Button in one action row.'},{id:"dialog-form-too-big",severity:"error",category:"composition",standard:"@godxjp/ui form placement (gh#998)",fix:"Three or more FormFields in a Dialog body is a page: give the form its own route. Dialogs hold a confirmation or one or two fields; a side Sheet (drawer) may hold a filter or edit form."},{id:"select-width-hint",severity:"warn",category:"composition",standard:"GOV.UK Design System \xB7 text input width",fix:"Size a Select for its content \u2014 `controlWidth` on the FormField, or once on the <Form>."},{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-hand-rolled-scrollport",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.1.1 (Keyboard) \xB7 WAI-ARIA 1.2 (group) \xB7 Deque axe-core scrollable-region-focusable",fix:'Replace className="overflow-auto / overflow-y-auto / overflow-x-auto / overflow-scroll" on your own element with <ScrollArea label={t("\u2026")} orientation>, which is the tab stop, the role and the localized name \u2014 and withholds all three while there is nothing to scroll. A browser audit only fails a scrollport whose content has NO focusable child, so the same markup is clean or broken depending on the data; this reads the markup instead (gh#825). overflow-hidden is a clipping box, not a scrollport, and is not flagged.'},{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.'},{id:"no-hand-rolled-break-anywhere",severity:"warn",category:"composition",standard:"CSS Text 3 \xA75.5 (overflow-wrap) \xB7 WCAG 2.2 SC 1.4.10 (Reflow)",fix:`Replace className="[overflow-wrap:anywhere] break-words whitespace-normal" (or wrap-anywhere) with <Text break="anywhere">, which emits overflow-wrap: anywhere AND releases a table cell's inherited nowrap, so an email, code or id shrinks its column to the viewport. Not whitespace="pre-wrap": its break-word does not lower min-content, so a table cell stays wide (gh#927).`}];function ge(e){return e?pe.filter(t=>t.category===e):pe}var fe="node node_modules/@godxjp/ui/scripts/visual-audit.mjs [--widths 390x844,1280x900] [--storage-state state.json] <baseUrl> [route \u2026] (--storage-state takes a Playwright storageState JSON so signed-in routes are audited signed in; every route at 320\xD7900, 390\xD7844, 768\xD71024, 1024\xD7900, 1280\xD7900, 844\xD7390 by default; findings carry viewport + element + threshold origin; JSON adds unmapped[] (reviewer required, never a pass), matrix[] (per-width rule status + reviewer checklist) and acceptance \u2014 at best 'probes passed; independent acceptance pending', never done. 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)",be=[{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 \u2014 86 KB instead of 367 KB gzip, since the bundled @font-face declarations are most of the CSS, gh#971); 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:"mixed-button-height",severity:"error",category:"layout",standard:"@godxjp/ui Button size (one size per row) \xB7 Nielsen consistency heuristic",fix:"Buttons in one flex row must render at one height (within 0.5px): one `size` per row; icon-sm pairs with sm, icon-xs with xs, icon with default. Runtime twin of the static mixed-button-size rule."},{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. The accent @godxjp/ui itself ships is exempt (gh#823) \u2014 this finding is always a colour someone chose."},{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."},{id:"c01-content-reachable",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 / 1.4.4 / 1.4.12; 1.5px tolerance repo-chosen threshold",kind:"probe",fix:"Nothing may be cut off: no horizontal scroll on the document or the app's main region, and no text/icon partly hidden by an overflow:hidden/clip ancestor. Let the container shrink its content (min-width:0 + wrap), or truncate with a recoverable full value (title / aria-label)."},{id:"c02-search-usable",severity:"warn",category:"layout",standard:"search text area \u2265 8ch after padding/icon, hint fits within 1.5px (repo-chosen threshold); WCAG 1.4.10 principle",kind:"probe",fix:"A search field narrower than 8ch is not a search field. Below the width where it fits, show a named search trigger (data-audit-role=search-trigger) that opens a full-width field; keep the scope hint visible somewhere when the placeholder no longer fits."},{id:"c03-single-brand",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist only",kind:"checklist",fix:"Reviewer counts brand presentations in the closed and open shell: one logo+wordmark, distinct from the service name and breadcrumb."},{id:"c04-compact-account-name",severity:"warn",category:"a11y",standard:"compact (<600px) bar shows no account name (repo-chosen threshold); trigger name per WCAG 2.2 SC 4.1.2",kind:"probe",fix:"Below 600px the bar shows the avatar only: hide the account name (it belongs in the opened account menu) and make the avatar the trigger \u2014 accessible name + aria-haspopup, opening identity and account controls."},{id:"c05-compact-editor-inset",severity:"warn",category:"layout",standard:"first unindented line \u2264 24px from the editor inner edge below 600px (repo-chosen threshold); WCAG 1.4.10 principle",kind:"probe",fix:"Reclaim the desktop drag-handle gutter on phones: the editor's base inset (padding included) is at most 24px below 600px; keep list indentation, and give tap/keyboard alternatives to the hover handle."},{id:"c06-compact-actions",severity:"warn",category:"layout",standard:"\u22642 direct actions + \u22641 overflow per task-action group below 600px (repo-chosen threshold); targets \u226524\xD724 per WCAG 2.2 SC 2.5.8",kind:"probe",fix:"On phones a task-action group shows at most two direct actions plus one overflow menu that really holds the rest; every target at least 24\xD724 CSS px (or a documented SC 2.5.8 exception)."},{id:"c07-width-control",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist (no library hook maps a width control)",kind:"checklist",fix:"A wide-page control must move content to an approved destination width (\xB11px), or be hidden on phones / labelled as a desktop-only preference that persists."},{id:"c08-sidebar-field-edges",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist (equivalent groups are a semantic call)",kind:"checklist",fix:"Equivalent sidebar fields share left and right edges within 2px; the reviewer inventories the fields before reading any mapping."},{id:"c09-progress-role",severity:"warn",category:"color",standard:"repo-chosen policy \u2014 human checklist (tone \u2194 meaning is a semantic call)",kind:"checklist",fix:"Ordinary branded progress uses the brand role; a status tone (success/warning/destructive) only where the bar reports that status. Thickness matches the approved variant \xB11px."},{id:"c10-rail-toggle-attached",severity:"warn",category:"layout",standard:"toggle centre within 8px of the declared rail edge at \u22651024px (repo-chosen threshold)",kind:"probe",fix:"An edge-mounted rail toggle sits on the rail edge it controls in both desktop states. Declare the pattern with data-audit-role=rail-toggle (+ data-audit-edge=start|end). A toggle inside `Sidebar action` is the supported placement and reports not-applicable-sidebar-action."},{id:"c11-search-keyline",severity:"warn",category:"layout",standard:"desktop search start within 2px of the content column start (repo-chosen threshold)",kind:"probe",fix:"A centred top-bar search starts on the content column's keyline, not the window's centre. Applies when the search sits in Topbar `center` (or carries data-audit-keyline)."},{id:"c12-tracked-uppercase-label",severity:"warn",category:"layout",standard:"section label uppercase AND \u226412px AND tracking \u22650.04em fails (repo-chosen threshold); no WCAG font floor implied",kind:"probe",fix:"Section labels read as sentence-case text at the body scale, not tiny tracked capitals. Literal domain codes are exempt when marked as such (<code>, <kbd>, data-audit-literal)."}];function ve(e){return e?be.filter(t=>t.category===e):be}var H='---\nversion: alpha\nname: godxjp-ui\ndescription: "Restrained Japanese-enterprise admin UI. Warm off-white canvas, near-black ink, one neutral accent, hairline borders, 14px/1.7 type, dense tables. Hierarchy comes from spacing steps and type, not from boxes, colour or shadow."\n\ncolors:\n canvas: "#fdfdfc" # --background (= --card, --popover)\n ink: "#24231e" # --foreground\n ink-muted: "#68665e" # --muted-foreground\n ink-tertiary: "#928f87" # --text-tertiary (captions, meta)\n primary: "#18181b" # --primary. Neutral placeholder: set your brand here\n on-primary: "#fdfdfc" # --primary-foreground\n ring: "{colors.primary}" # --ring follows --primary; no separate focus hue\n muted: "#f4f3f0" # --muted / --secondary (quiet fills, zebra, hover)\n accent: "#ebe9e5" # --accent (hover/selected background, NOT a brand colour)\n hairline: "#eeedec" # --border (dividers, card edges, table rules)\n control-edge: "#90877f" # --input (the edge of an input; 3:1 against canvas)\n success: "#68be8d" # --success \u82E5\u7AF9\n warning: "#f8b500" # --warning \u5C71\u5439\n info: "#4c6cb3" # --info \u7FA4\u9752\n attention: "#eb6101" # --attention \u6731 (non-destructive urgency: late, overdue)\n destructive: "#b7282e" # --destructive \u831C (delete, failed, irreversible)\n text-success: "#09673b" # --text-success (status as text, AA on canvas)\n text-warning: "#8f5600" # --text-warning\n text-info: "#1d48a5" # --text-info\n text-error: "#aa181f" # --text-error\n\ntypography:\n family: "system stack (--font-sans-base); Noto Sans JP via opt-in @godxjp/ui/styles/fonts"\n base: 14px # --font-size-base; every step derives from it (ratio --font-size-ratio 1.1227)\n page-title: { size: "~20px (--heading-h1)", weight: 500, lineHeight: 1.25 }\n section-title: { size: "~17.6px (--heading-h2)", weight: 500, lineHeight: 1.25 }\n card-title: { size: "14px (--card-title-font-size)", weight: 500, lineHeight: 1.25 }\n body: { size: 14px, weight: 400, lineHeight: 1.7 } # --line-height-body\n caption: { size: "~12.5px (--font-size-xs)", weight: 400, lineHeight: 1.5 }\n weights: [400, 500, 700] # --font-weight-semibold is 500; there is no 600\n numerals: tabular # Text tabular / Badge tabular on every number column\n\nrounded:\n base: 6px # --radius (rides --scaling)\n steps: "--radius-sm/md/lg/xl = radius / \u03C6\xB2, / \u03C6, \xD71, \xD7 \u03C6"\n card: "~9.7px (--card-radius = radius \xD7 \u03C6)"\n pill: 9999px # --radius-pill: switch, radio, pill badge only\n\nspacing:\n scale: "--space-1..24 = 4px steps (4, 8, 12, 16, 20, 24, 32, 40, 48, 80, 96)"\n stack: { xs: 4px, sm: 8px, md: 16px, lg: 24px, xl: 40px } # Flex direction="col" gap\n inline: { xs: 4px, sm: 8px, md: 12px, lg: 16px, xl: 24px } # Flex direction="row" gap\n page: { x: 24px, y: 24px } # --space-page-x / -y\n section: 16px # --space-section\n\ndensity:\n axis: "AppProvider density / PageContainer density / data-density"\n compact: "--scaling 0.92 (data-heavy tables)"\n default: "--scaling 1 (control and row height 32px, --band-height-md)"\n comfortable: "--scaling 1.08 (login, public, touch)"\n touch-floor: 44px # --band-height-xl\n\ncomponents:\n button-primary:\n {\n background: "{colors.primary}",\n text: "{colors.on-primary}",\n height: "--control-height",\n radius: "{rounded.base}",\n }\n button-secondary: { variant: outline, edge: "{colors.control-edge}" }\n input:\n {\n background: "{colors.canvas}",\n edge: "{colors.control-edge}",\n focus: "edge recolours to {colors.ring}",\n }\n card:\n {\n background: "{colors.canvas}",\n border: "1px {colors.hairline}",\n shadow: "--shadow-sm",\n radius: "{rounded.card}",\n padding: "--card-space-inset",\n }\n table:\n { rule: "1px {colors.hairline}", rowHeight: "--table-row-height", header: "{colors.ink-muted}" }\n badge: { variant: outline, tone: "status role only" }\n---\n\n## Overview\n\nA calm, dense tool for people who use it all day. The look is restrained Japanese enterprise: \u6E0B\u307F (low chroma), \u9593 (even breathing room), \u7C21\u7D20 (nothing that does not carry information). Distinctiveness comes from exact spacing, alignment and type, never from decoration.\n\nThe default `--primary` is a neutral ink (`240 6% 10%`). It is a placeholder, not a brand. Set the brand once with `--primary` in your theme, `tenantTheme(\'#RRGGBB\')` from `@godxjp/ui/app`, a `data-brand` preset (`brand`, `crm`, `logistics`, `partner`, `slate`) or `@godxjp/ui/themes/godx.css`. `--ring` follows `--primary`.\n\n## Colors\n\nColour is scarce. Most of the screen is canvas, ink and hairline.\n\n- **Accent (`--primary`)**: the one primary action per view, focus ring, links, the selected nav item. Nothing else: no tinted headers, avatars, icons or section backgrounds.\n- **Status (\u82E5\u7AF9 success, \u5C71\u5439 warning, \u7FA4\u9752 info, \u6731 attention, \u831C destructive)**: only to state a status. Use `Badge status=\u2026` (maps lifecycle keys to a fixed tone and icon) or `tone`. For status as text use the `--text-*` roles. Status colours are never re-themed per tenant.\n- \u6731 attention for urgency that is not destructive (late, overdue); \u831C only for loss (delete, failed, irreversible).\n- `--accent` is the hover/selected surface, not a brand colour. `--muted` is for quiet fills.\n- No gradients, glows or tinted page sections on app surfaces.\n\n## Typography\n\n- One family. Hierarchy from size steps, weight 400/500/700 and `ink` vs `ink-muted`. Page title ~20px; anything larger belongs on a marketing page. One exception: a long-form document title (Reader) is ~22px, two steps above its ~17.6px section headings, because one step reads flat there.\n- Body 14px / 1.7. Captions and meta 12.5px muted. `Text tabular` on every number, time and money column.\n- Japanese prose: `Text break="phrase"` so lines break between phrases. `Prose`/`Markdown` and `LegalDocumentShell` bodies do this themselves under `lang="ja"`.\n- Copy is quiet and factual: no praise, no alarm, no emoji, no em dash in UI strings. A success message states the fact (\u300C\u627F\u8A8D\u3057\u307E\u3057\u305F\u300D).\n\n## Layout & Density\n\n- Hierarchy is spacing first: related items `gap="xs"`/`"sm"`, groups `"md"`, sections `"lg"`/`"xl"`. If every gap is the same, nothing is grouped.\n- A section is a `Heading` plus spacing. Add a `Separator` when two sections must not read as one. Use a surface only when it is a real boundary (see Components).\n- Pick one density per page up front: `compact` for review grids, `default` everywhere else, `comfortable` for login and touch.\n- Content measure: forms and settings in `PageContainer measure="narrow"` or `"medium"`; tables use the full width.\n- Spacing comes from component props (`gap`, `pad`) and tokens, never `p-4`/`mt-6` literals.\n\n## Elevation\n\n- Flat. A card is canvas with a 1px `--border` hairline and a near-invisible `--shadow-sm`; nothing heavier at rest.\n- `--shadow-md`/`--shadow-lg` belong to things that float: popover, dropdown, dialog, sheet, toast.\n- No hover lift on data cards. Depth never signals importance.\n\n## Components\n\n- **PageContainer** is the page shell: `title`, optional `subtitle`, `breadcrumb`, `extra` (page actions, primary last), `toolbar`, `footer`. Omit `title` only when the content draws the page\'s one `<h1>` (a document reader); PageContainer then renders no heading.\n- **Card** only for a real boundary: a table with its own toolbar and pagination, a list of setting rows, a dashboard panel, a danger zone (`accent="destructive"`), a selectable item. Not around every section, never nested. `CardContent flush` for tables and row lists.\n- **Toolbar** (+ `ToolbarGroup`) is the filter strip: search, labelled filters, clear-all, result count. It sits above the table, never in the page header.\n- **DataTable** for lists: sort, selection with `DataTable.BulkActions`, density, empty/loading/error states. Row actions in one kebab `DropdownMenu`.\n- **Descriptions** for record metadata. **ListRow** for setting rows (`trailing` holds the control).\n- **Form + FormField** for every input; actions at the end of the form or in a sticky `PageContainer footer`. Create/edit is a page, not a dialog.\n- **StatCard** only on a dashboard, always with `delta` that compares (vs last week, vs target). **EmptyState**: one sentence and at most one action.\n- **Alert** only for something the user must act on now. **Badge** only for status; one per row at most.\n\n## Admin Screen Anatomy\n\n### List\n\nHeader: title, then `extra` with the primary create action last. Below it a `Toolbar` with search and filters. Then one Card holding the `DataTable` (`CardContent flush`) and `Pagination`. No KPI row, no subtitle that restates the title, no filter Card separate from the table.\n\n### Detail\n\nHeader: record name as title, status `Badge` beside it, actions in `extra` (one primary). Body: `Descriptions` for metadata, then sections (Heading + content) for related records and history (`Timeline`). Use `Tabs` only when sections are independent and long. No Card per section; no icon beside every heading.\n\n### Form\n\n`Form` in a narrow measure; fields grouped by Heading sections, not Cards. Labels via `FormField`; helper text only when it adds information. One primary submit and a secondary cancel at the end, or in a sticky footer for long forms. Errors inline on the field plus `FormErrors` summary on submit.\n\n### Editor\n\nEdit (or create) one document or record. Header: record name as title, status `Badge` beside it, a subtitle only for a fact such as when it was last saved, and ONE primary Save in `extra` (`Button type="submit" form=<form id>`), Cancel `outline` before it. Body: one `Form` in a `SplitPane`: the main column holds the title field and a large body editor (`BlockEditor`, or `Textarea autoSize`); the aside holds metadata fields and read-only facts in `Descriptions`. The change note goes either directly under the body (where the writer finishes) or last in the aside \u2014 pick one per app and keep it. Name each aside group with a `Heading level={2}` one step above its field labels. The main\u2194aside gap is `--split-pane-gap` (default 16px); set it once in the theme. A part that is its own controlled component with its own `Form` (forms cannot nest) stays a separate form; the header Save then submits by handler (`onClick`) instead of `form=<id>`, still one Save. No Card per field group, no second Save at the bottom.\n\n### Reader\n\nA long-form document to read. The document owns the page\'s one `<h1>`: `PageContainer` gets `breadcrumb` and `extra` (Edit) and no `title`. `LegalDocumentShell` draws the reading column: title two steps above the section headings, one metadata line (version, date with `dateKind="updated"` for a revision), the contents rail, and prose at a reading measure (Japanese breaks between phrases under `lang="ja"`; stacked, a contents entry hugs its label). No Card around the article, no subtitle, no icon per heading.\n\n### Settings\n\nEach section is a Heading (plus one muted line if it adds a fact) over its content: `Descriptions` for read-only values, `ListRow` rows in one `Card` with `CardContent flush` for controls (`trailing` holds the control). Separate sections with spacing or a `Separator`. Changes that apply instantly need no Save button; a page with a Save button applies nothing until saved. Destructive actions last, in one `Card accent="destructive"`, each behind `AlertDialog`.\n\n### Dashboard\n\nThe only archetype with KPI tiles: at most four `StatCard`s, each with a comparison `delta` (use `inverse` when up is bad). Below, panels in `ResponsiveGrid`, each a Card with a title and one "view all" link: a work queue, a short table, a chart. Every number answers "compared to what".\n\n### Approval queue\n\nA list whose rows are decisions. Show requester, target, reason, submitted time; approve and reject reachable from the row (kebab or row buttons); with `DataTable`, also in `BulkActions`. Escalated or overdue rows get one status Badge, not a coloured row.\n\n## Do\'s and Don\'ts\n\n### Do\n\n- Lead with the data. The first thing below the header is the table, form or record.\n- Use one `Button` default variant per view; the rest `outline` or `ghost`.\n- Group by spacing, then by heading, then by Separator, and only then by Card.\n- Write the subtitle only when it adds a fact the title does not (date, scope, count).\n- Right-align numbers, `tabular` digits, ISO/`Intl` dates.\n\n### Don\'t\n\n- A subtitle that restates or translates the title.\n- A StatCard row above a list or form.\n- A Card around every section, each with Header + Title + Description.\n- An EmptyState with a large icon inside a Card; an icon in every card header.\n- Alert banners as page decoration; several Alerts stacked.\n- Badge walls: a badge per cell, rainbow tones, badges for non-status labels.\n- Uniform `gap` everywhere; arbitrary px spacing.\n- Coloured avatars, tinted section backgrounds, gradients, hover lift.\n\n## Responsive\n\n- Check 390, 768 and 1280 wide. Nothing scrolls horizontally except a wide table inside its own container.\n- `DataTable` hides `hiddenOnMobile` columns and can stack rows (`collapseBelow`); `Table preset="action-collection"` keeps the actions column visible at 390.\n- `ResponsiveGrid` columns follow its container. `AppShell` turns the sidebar into a drawer on narrow screens.\n- Header `extra` wraps under the title; a toolbar wraps its filters. Touch targets 44px at `comfortable`.\n- Below 640px: at most 2 direct actions in `extra` plus one overflow `DropdownMenu` for the rest; it wraps but never prioritises for you.\n- Bar search is `TopbarSearch`, never a bare `SearchInput`: it folds to a named search cell below 640px. To share the content keyline, keep the default `topbarSpan="content"` and put it first in `Topbar start`.\n- Account cell: `TopbarItem icon={<Avatar/>} labelHideBelow="sm"` with the name repeated in the menu\'s `DropdownMenuLabel`, so a phone shows the avatar only.\n- A sidebar collapse toggle goes in `Sidebar action` (it receives `collapsed`), not in the bar beside the brand. The phone drawer has its own trigger.\n\n## Iteration Guide\n\n1. Pick the archetype, call `get_exemplar` and build from that screen, not from memory.\n2. `npx @godxjp/ui audit <files>` \u2014 zero errors.\n3. `npx @godxjp/ui visual-audit <url> <routes>` \u2014 runs every route at 320, 390, 768, 1024, 1280 and 844\xD7390 (landscape phone) and measures what a screenshot glance misses: clipped or squeezed controls (WCAG 1.4.10 has two halves \u2014 no two-way scrolling AND no lost information; page overflow alone checks only the first), compact search, avatar-only identity, editor inset, action count and target size, rail and search keylines, tiny tracked capitals. Zero machine failures at every width; an "unmapped" result is not a pass. Signed-in routes: `--storage-state state.json` (Playwright storageState).\n4. Inspect every screenshot at every width, region by region \u2014 top bar, page header, body, aside or rail, menus and drawers \u2014 against the checklist the report prints. A region you were told not to change is still inspected; a defect there becomes an issue, not a silent pass.\n5. No top-N and no round cap. Stop only when every width has zero machine failures and every checklist row is pass or a written N/A.\n6. The builder does not accept its own screen. Report "probes passed; independent acceptance pending" with the report JSON, the screenshots per width and the checklist; acceptance comes from someone who did not build it (a person, or a separate agent given only the build and the artifacts). Never call a screen done on self-review.\n\n## Known Gaps\n\n- The default primary is neutral by design; a product without a brand colour looks monochrome until one is set.\n- `ui-audit` checks primitives and tokens, not structure: a page can pass it and still show every Don\'t above.\n- No edge-mounted rail toggle (a control straddling the sidebar border); `Sidebar action` is the supported place. Under `topbarSpan="full"` the bar shares no keyline with the content column.\n- `card-per-section` counts Cards per file, so a screen split across components can pass with a Card around every section.\n- Exemplar previews deploy on release tags, so they can lag `main`. On GitHub Pages a showcase URL answers HTTP 404 and renders through `404.html`, so open it in a browser; a plain fetch sees the 404.\n- Dark theme (`data-theme="dark"`) is supported; the exemplars are reviewed in light.\n- From 640px up, `BlockEditor` text does not share the keyline of the `Input` above it: measured at 768/1024/1280, input text 13px inside its frame, editor text 57px inside (\u039444px), because the \u22EE\u22EE/+ handles (two 24px targets) sit in a start gutter inside the frame. Aligning the text would put the handles outside the field border, where a content column 24px from the viewport edge has no room. Below 640px the gutter collapses (C05). `--block-editor-gutter-inline-size` sets the gutter width; it cannot close the gap, because the handles live inside it. Accepted, not a defect to fix in the app.\n- On narrow screens the `PageContainer` title steps down (\u224817.6px) while body h2 stays \u224815.7px \u2014 one step apart. A document page not built on `LegalDocumentShell` keeps only that step on phones; there is no intermediate page-title size yet.\n';var ye=e=>e.toLowerCase().replace(/&/g," ").replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");function U(e=H){let t=[],a=e,n=/^---\n[\s\S]*?\n---\n/.exec(e);n&&(t.push({id:"tokens",title:"Tokens (YAML front matter)",body:n[0].trim()}),a=e.slice(n[0].length));for(let o of a.split(/^(?=## )/m)){let i=/^## (.+)$/m.exec(o);!i||!o.startsWith("## ")||t.push({id:ye(i[1]),title:i[1].trim(),body:o.trim()})}return t}function we(e){let t=ye(e);if(!t)return;let a=U();return a.find(n=>n.id===t)??a.find(n=>n.id.startsWith(t))??a.find(n=>n.id.split("-").includes(t))}function ke(e){let t=U().find(a=>a.id==="admin-screen-anatomy");if(t)for(let a of t.body.split(/^(?=### )/m)){let n=/^### (.+)$/m.exec(a);if(n&&n[1].trim().toLowerCase()===e.trim().toLowerCase())return a.trim()}}var We="https://godx-jp.github.io/godxjp-ui/",E=[{archetype:"list",aliases:["index","table","crud","collection","\u4E00\u89A7"],showcase:"table-crud-list",anatomy:["List"],focus:"Header actions in `extra`, Toolbar filters above the table, one Card with DataTable + Pagination."},{archetype:"detail",aliases:["record","show","master-detail","\u8A73\u7D30"],showcase:"table-master-detail",anatomy:["Detail"],focus:"Record header with status Badge and actions, Descriptions metadata, history as a headed section."},{archetype:"editor",aliases:["form","edit","create","document-editor","record-editor","\u7DE8\u96C6"],showcase:"page-editor",anatomy:["Editor","Form"],focus:"One primary Save in `extra` submitting the Form by id; title field + large body editor in the main column, metadata + change note in a SplitPane aside. The body uses @godxjp/block-editor (a second package); `Textarea autoSize` fits the same FormField."},{archetype:"reader",aliases:["document","article","wiki","view","read","\u95B2\u89A7"],showcase:"page-reader",anatomy:["Reader"],focus:"PageContainer with breadcrumb + `extra` and NO `title`; LegalDocumentShell (@godxjp/ui/lab) owns the h1, metadata line, contents rail and prose at reading width."},{archetype:"settings",aliases:["preferences","account","\u8A2D\u5B9A"],showcase:"settings-account-sections",anatomy:["Settings"],focus:"Heading + ListRow sections without Cards; one destructive Card at the end."},{archetype:"dashboard",aliases:["overview","home","kpi","\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9"],showcase:"case1-warehouse-dashboard",anatomy:["Dashboard"],focus:"Four StatCards with comparison deltas, then titled panels in a ResponsiveGrid."},{archetype:"approval-queue",aliases:["approval","queue","inbox","review","\u627F\u8A8D"],showcase:"table-approval-queue",anatomy:["Approval queue"],focus:'Decision rows in `Table preset="action-collection"`, row actions in one kebab.'}],xe=e=>e.trim().toLowerCase().replace(/[\s_]+/g,"-");function Te(e){let t=xe(e);if(t)return E.find(a=>a.archetype===t||a.showcase===t||a.aliases.some(n=>xe(n)===t))}function Se(e){return`${We}showcase/${e}`}var Ce={"case1-warehouse-dashboard":`/**
|
|
5126
|
+
finding and needs no suppression.`,pe=[{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:"nested-card",severity:"warn",category:"composition",standard:null,fix:'A Card (or StatCard) inside a Card draws two borders for one boundary. Group inside the outer Card with <Heading> + <Flex direction="col"> or <Separator />.'},{id:"card-per-section",severity:"warn",category:"composition",standard:null,fix:'5+ Cards in one file is a Card per section. Mark a section with <Heading level={2}> and section spacing (PageContainer children / Flex gap="lg"); keep Card for a real boundary (a table surface, a selectable item).'},{id:"kpi-row-off-dashboard",severity:"warn",category:"composition",standard:null,fix:"3+ StatCards outside a file whose path names a dashboard. A row count is not a KPI: drop the row and show the total in <Pagination showTotal>. StatCard rows are for a dashboard with comparison or trend (delta)."},{id:"empty-state-icon-in-card",severity:"warn",category:"composition",standard:null,fix:'In an admin surface an empty state is one sentence and one action. Drop `icon` from an EmptyState inside a Card, or use variant="compact".'},{id:"badge-wall",severity:"warn",category:"composition",standard:null,fix:"5+ Badge/Tag in one file. Badge is for status only; render categories, tiers and scopes as plain text, one status column per table."},{id:"alert-per-page",severity:"warn",category:"composition",standard:null,fix:"4+ Alerts in one file. Keep one Alert for a state that blocks or changes the task; field errors go in FormField, results in a toast, standing info in plain text."},{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:"formfield-needs-form",severity:"error",category:"composition",standard:"@godxjp/ui Form (gh#998)",fix:'Wrap FormFields in <Form layout="horizontal" labelWidth controlWidth>; a row of fields is <Flex attached> or <Form columns>, never a hand-rolled <Flex>. A field component whose whole output is one FormField is exempt, and so is a field-group component that every caller renders inside a <Form> (checked across the project); when only some callers do, wrap the bare call sites the message names, not the fields. A call site inside a same-file render helper (`const legFields = (\u2026) => \u2026`) counts as under the <Form> its every call sits in (gh#1260).'},{id:"mixed-button-size",severity:"error",category:"composition",standard:null,fix:'Sibling <Button>s under one parent (through fragments, {cond && \u2026}, ternaries, Tooltip wrappers) share ONE size; a missing size is default; icon-sm pairs with sm, icon-xs with xs, icon with default. Never `size="sm"` beside a default Button in one action row.'},{id:"dialog-form-too-big",severity:"error",category:"composition",standard:"@godxjp/ui form placement (gh#998)",fix:"Three or more FormFields in a Dialog body is a page: give the form its own route. Dialogs hold a confirmation or one or two fields; a side Sheet (drawer) may hold a filter or edit form."},{id:"select-width-hint",severity:"warn",category:"composition",standard:"GOV.UK Design System \xB7 text input width",fix:"Size a Select for its content \u2014 `controlWidth` on the FormField, or once on the <Form>."},{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-hand-rolled-scrollport",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.1.1 (Keyboard) \xB7 WAI-ARIA 1.2 (group) \xB7 Deque axe-core scrollable-region-focusable",fix:'Replace className="overflow-auto / overflow-y-auto / overflow-x-auto / overflow-scroll" on your own element with <ScrollArea label={t("\u2026")} orientation>, which is the tab stop, the role and the localized name \u2014 and withholds all three while there is nothing to scroll. A browser audit only fails a scrollport whose content has NO focusable child, so the same markup is clean or broken depending on the data; this reads the markup instead (gh#825). overflow-hidden is a clipping box, not a scrollport, and is not flagged.'},{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.'},{id:"no-hand-rolled-break-anywhere",severity:"warn",category:"composition",standard:"CSS Text 3 \xA75.5 (overflow-wrap) \xB7 WCAG 2.2 SC 1.4.10 (Reflow)",fix:`Replace className="[overflow-wrap:anywhere] break-words whitespace-normal" (or wrap-anywhere) with <Text break="anywhere">, which emits overflow-wrap: anywhere AND releases a table cell's inherited nowrap, so an email, code or id shrinks its column to the viewport. Not whitespace="pre-wrap": its break-word does not lower min-content, so a table cell stays wide (gh#927).`}];function ge(e){return e?pe.filter(t=>t.category===e):pe}var fe="node node_modules/@godxjp/ui/scripts/visual-audit.mjs [--widths 390x844,1280x900] [--storage-state state.json] [--text-spacing] [--text-zoom 200] [--screenshots DIR] <baseUrl> [route \u2026] (--storage-state takes a Playwright storageState JSON so signed-in routes are audited signed in; --text-spacing / --text-zoom 200 re-render each width with the WCAG 1.4.12 / 1.4.4 override and add evidence[] (geometry delta vs the unmodified render, C01 under the override, screenshot paths with --screenshots) \u2014 a zero delta is an infra error 'override not applied', never a pass; every route at 320\xD7900, 390\xD7844, 768\xD71024, 1024\xD7900, 1280\xD7900, 844\xD7390 by default; findings carry viewport + element + threshold origin; JSON adds unmapped[] (reviewer required, never a pass), matrix[] (per-width rule status + reviewer checklist) and acceptance \u2014 at best 'probes passed; independent acceptance pending', never done. 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)",be=[{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 \u2014 86 KB instead of 367 KB gzip, since the bundled @font-face declarations are most of the CSS, gh#971); 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:"mixed-button-height",severity:"error",category:"layout",standard:"@godxjp/ui Button size (one size per row) \xB7 Nielsen consistency heuristic",fix:"Buttons in one flex row must render at one height (within 0.5px): one `size` per row; icon-sm pairs with sm, icon-xs with xs, icon with default. Runtime twin of the static mixed-button-size rule."},{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. The accent @godxjp/ui itself ships is exempt (gh#823) \u2014 this finding is always a colour someone chose."},{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."},{id:"c01-content-reachable",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 / 1.4.4 / 1.4.12; 1.5px tolerance repo-chosen threshold",kind:"probe",fix:"Nothing may be cut off: no horizontal scroll on the document or the app's main region, and no text/icon partly hidden by an overflow:hidden/clip ancestor. Let the container shrink its content (min-width:0 + wrap), or truncate with a recoverable full value (title / aria-label)."},{id:"c02-search-usable",severity:"warn",category:"layout",standard:"search text area \u2265 8ch after padding/icon, hint fits within 1.5px (repo-chosen threshold); WCAG 1.4.10 principle",kind:"probe",fix:"A search field narrower than 8ch is not a search field. Below the width where it fits, show a named search trigger (data-audit-role=search-trigger) that opens a full-width field; keep the scope hint visible somewhere when the placeholder no longer fits."},{id:"c03-single-brand",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist only",kind:"checklist",fix:"Reviewer counts brand presentations in the closed and open shell: one logo+wordmark, distinct from the service name and breadcrumb."},{id:"c04-compact-account-name",severity:"warn",category:"a11y",standard:"compact (<600px) bar shows no account name (repo-chosen threshold); trigger name per WCAG 2.2 SC 4.1.2",kind:"probe",fix:"Below 600px the bar shows the avatar only: hide the account name (it belongs in the opened account menu) and make the avatar the trigger \u2014 accessible name + aria-haspopup, opening identity and account controls."},{id:"c05-compact-editor-inset",severity:"warn",category:"layout",standard:"first unindented line \u2264 24px from the editor inner edge below 600px (repo-chosen threshold); WCAG 1.4.10 principle",kind:"probe",fix:"Reclaim the desktop drag-handle gutter on phones: the editor's base inset (padding included) is at most 24px below 600px; keep list indentation, and give tap/keyboard alternatives to the hover handle."},{id:"c06-compact-actions",severity:"warn",category:"layout",standard:"\u22642 direct actions + \u22641 overflow per task-action group below 600px (repo-chosen threshold); targets \u226524\xD724 per WCAG 2.2 SC 2.5.8",kind:"probe",fix:"On phones a task-action group shows at most two direct actions plus one overflow menu that really holds the rest; every target at least 24\xD724 CSS px (or a documented SC 2.5.8 exception)."},{id:"c07-width-control",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist (no library hook maps a width control)",kind:"checklist",fix:"A wide-page control must move content to an approved destination width (\xB11px), or be hidden on phones / labelled as a desktop-only preference that persists."},{id:"c08-sidebar-field-edges",severity:"warn",category:"layout",standard:"repo-chosen policy \u2014 human checklist (equivalent groups are a semantic call)",kind:"checklist",fix:"Equivalent sidebar fields share left and right edges within 2px; the reviewer inventories the fields before reading any mapping."},{id:"c09-progress-role",severity:"warn",category:"color",standard:"repo-chosen policy \u2014 human checklist (tone \u2194 meaning is a semantic call)",kind:"checklist",fix:"Ordinary branded progress uses the brand role; a status tone (success/warning/destructive) only where the bar reports that status. Thickness matches the approved variant \xB11px."},{id:"c10-rail-toggle-attached",severity:"warn",category:"layout",standard:"toggle centre within 8px of the declared rail edge at \u22651024px (repo-chosen threshold)",kind:"probe",fix:"An edge-mounted rail toggle sits on the rail edge it controls in both desktop states. Declare the pattern with data-audit-role=rail-toggle (+ data-audit-edge=start|end). A toggle inside `Sidebar action` is the supported placement and reports not-applicable-sidebar-action."},{id:"c11-search-keyline",severity:"warn",category:"layout",standard:"desktop search start within 2px of the content column start (repo-chosen threshold)",kind:"probe",fix:"A centred top-bar search starts on the content column's keyline, not the window's centre. Applies when the search sits in Topbar `center` (or carries data-audit-keyline)."},{id:"c12-tracked-uppercase-label",severity:"warn",category:"layout",standard:"section label uppercase AND \u226412px AND tracking \u22650.04em fails (repo-chosen threshold); no WCAG font floor implied",kind:"probe",fix:"Section labels read as sentence-case text at the body scale, not tiny tracked capitals. Literal domain codes are exempt when marked as such (<code>, <kbd>, data-audit-literal)."}];function ve(e){return e?be.filter(t=>t.category===e):be}var H='---\nversion: alpha\nname: godxjp-ui\ndescription: "Restrained Japanese-enterprise admin UI. Warm off-white canvas, near-black ink, one neutral accent, hairline borders, 14px/1.7 type, dense tables. Hierarchy comes from spacing steps and type, not from boxes, colour or shadow."\n\ncolors:\n canvas: "#fdfdfc" # --background (= --card, --popover)\n ink: "#24231e" # --foreground\n ink-muted: "#68665e" # --muted-foreground\n ink-tertiary: "#928f87" # --text-tertiary (captions, meta)\n primary: "#18181b" # --primary. Neutral placeholder: set your brand here\n on-primary: "#fdfdfc" # --primary-foreground\n ring: "{colors.primary}" # --ring follows --primary; no separate focus hue\n muted: "#f4f3f0" # --muted / --secondary (quiet fills, zebra, hover)\n accent: "#ebe9e5" # --accent (hover/selected background, NOT a brand colour)\n hairline: "#eeedec" # --border (dividers, card edges, table rules)\n control-edge: "#90877f" # --input (the edge of an input; 3:1 against canvas)\n success: "#68be8d" # --success \u82E5\u7AF9\n warning: "#f8b500" # --warning \u5C71\u5439\n info: "#4c6cb3" # --info \u7FA4\u9752\n attention: "#eb6101" # --attention \u6731 (non-destructive urgency: late, overdue)\n destructive: "#b7282e" # --destructive \u831C (delete, failed, irreversible)\n text-success: "#09673b" # --text-success (status as text, AA on canvas)\n text-warning: "#8f5600" # --text-warning\n text-info: "#1d48a5" # --text-info\n text-error: "#aa181f" # --text-error\n\ntypography:\n family: "system stack (--font-sans-base); Noto Sans JP via opt-in @godxjp/ui/styles/fonts"\n base: 14px # --font-size-base; every step derives from it (ratio --font-size-ratio 1.1227)\n page-title: { size: "~20px (--heading-h1)", weight: 500, lineHeight: 1.25 }\n section-title: { size: "~17.6px (--heading-h2)", weight: 500, lineHeight: 1.25 }\n card-title: { size: "14px (--card-title-font-size)", weight: 500, lineHeight: 1.25 }\n body: { size: 14px, weight: 400, lineHeight: 1.7 } # --line-height-body\n caption: { size: "~12.5px (--font-size-xs)", weight: 400, lineHeight: 1.5 }\n weights: [400, 500, 700] # --font-weight-semibold is 500; there is no 600\n numerals: tabular # Text tabular / Badge tabular on every number column\n\nrounded:\n base: 6px # --radius (rides --scaling)\n steps: "--radius-sm/md/lg/xl = radius / \u03C6\xB2, / \u03C6, \xD71, \xD7 \u03C6"\n card: "~9.7px (--card-radius = radius \xD7 \u03C6)"\n pill: 9999px # --radius-pill: switch, radio, pill badge only\n\nspacing:\n scale: "--space-1..24 = 4px steps (4, 8, 12, 16, 20, 24, 32, 40, 48, 80, 96)"\n stack: { xs: 4px, sm: 8px, md: 16px, lg: 24px, xl: 40px } # Flex direction="col" gap\n inline: { xs: 4px, sm: 8px, md: 12px, lg: 16px, xl: 24px } # Flex direction="row" gap\n page: { x: 24px, y: 24px } # --space-page-x / -y\n section: 16px # --space-section\n\ndensity:\n axis: "AppProvider density / PageContainer density / data-density"\n compact: "--scaling 0.92 (data-heavy tables)"\n default: "--scaling 1 (control and row height 32px, --band-height-md)"\n comfortable: "--scaling 1.08 (login, public, touch)"\n touch-floor: 44px # --band-height-xl\n\ncomponents:\n button-primary:\n {\n background: "{colors.primary}",\n text: "{colors.on-primary}",\n height: "--control-height",\n radius: "{rounded.base}",\n }\n button-secondary: { variant: outline, edge: "{colors.control-edge}" }\n input:\n {\n background: "{colors.canvas}",\n edge: "{colors.control-edge}",\n focus: "edge recolours to {colors.ring}",\n }\n card:\n {\n background: "{colors.canvas}",\n border: "1px {colors.hairline}",\n shadow: "--shadow-sm",\n radius: "{rounded.card}",\n padding: "--card-space-inset",\n }\n table:\n { rule: "1px {colors.hairline}", rowHeight: "--table-row-height", header: "{colors.ink-muted}" }\n badge: { variant: outline, tone: "status role only" }\n---\n\n## Overview\n\nA calm, dense tool for people who use it all day. The look is restrained Japanese enterprise: \u6E0B\u307F (low chroma), \u9593 (even breathing room), \u7C21\u7D20 (nothing that does not carry information). Distinctiveness comes from exact spacing, alignment and type, never from decoration.\n\nThe default `--primary` is a neutral ink (`240 6% 10%`). It is a placeholder, not a brand. Set the brand once with `--primary` in your theme, `tenantTheme(\'#RRGGBB\')` from `@godxjp/ui/app`, a `data-brand` preset (`brand`, `crm`, `logistics`, `partner`, `slate`) or `@godxjp/ui/themes/godx.css`. `--ring` follows `--primary`.\n\n## Colors\n\nColour is scarce. Most of the screen is canvas, ink and hairline.\n\n- **Accent (`--primary`)**: the one primary action per view, focus ring, links, the selected nav item. Nothing else: no tinted headers, avatars, icons or section backgrounds.\n- **Status (\u82E5\u7AF9 success, \u5C71\u5439 warning, \u7FA4\u9752 info, \u6731 attention, \u831C destructive)**: only to state a status. Use `Badge status=\u2026` (maps lifecycle keys to a fixed tone and icon) or `tone`. For status as text use the `--text-*` roles. Status colours are never re-themed per tenant.\n- \u6731 attention for urgency that is not destructive (late, overdue); \u831C only for loss (delete, failed, irreversible).\n- `--accent` is the hover/selected surface, not a brand colour. `--muted` is for quiet fills.\n- No gradients, glows or tinted page sections on app surfaces.\n\n## Typography\n\n- One family. Hierarchy from size steps, weight 400/500/700 and `ink` vs `ink-muted`. Page title ~20px; anything larger belongs on a marketing page. One exception: a long-form document title (Reader) is ~22px, two steps above its ~17.6px section headings, because one step reads flat there.\n- Body 14px / 1.7. Captions and meta 12.5px muted. `Text tabular` on every number, time and money column.\n- Japanese prose: `Text break="phrase"` so lines break between phrases. `Prose`/`Markdown` and `LegalDocumentShell` bodies do this themselves under `lang="ja"`.\n- Copy is quiet and factual: no praise, no alarm, no emoji, no em dash in UI strings. A success message states the fact (\u300C\u627F\u8A8D\u3057\u307E\u3057\u305F\u300D).\n\n## Layout & Density\n\n- Hierarchy is spacing first: related items `gap="xs"`/`"sm"`, groups `"md"`, sections `"lg"`/`"xl"`. If every gap is the same, nothing is grouped.\n- A section is a `Heading` plus spacing. Add a `Separator` when two sections must not read as one. Use a surface only when it is a real boundary (see Components).\n- Pick one density per page up front: `compact` for review grids, `default` everywhere else, `comfortable` for login and touch.\n- Content measure: forms and settings in `PageContainer measure="narrow"` or `"medium"`; tables use the full width.\n- Spacing comes from component props (`gap`, `pad`) and tokens, never `p-4`/`mt-6` literals.\n\n## Elevation\n\n- Flat. A card is canvas with a 1px `--border` hairline and a near-invisible `--shadow-sm`; nothing heavier at rest.\n- `--shadow-md`/`--shadow-lg` belong to things that float: popover, dropdown, dialog, sheet, toast.\n- No hover lift on data cards. Depth never signals importance.\n\n## Components\n\n- **PageContainer** is the page shell: `title`, optional `subtitle`, `breadcrumb`, `extra` (page actions, primary last), `toolbar`, `footer`. Omit `title` only when the content draws the page\'s one `<h1>` (a document reader); PageContainer then renders no heading.\n- **Card** only for a real boundary: a table with its own toolbar and pagination, a list of setting rows, a dashboard panel, a danger zone (`accent="destructive"`), a selectable item. Not around every section, never nested. `CardContent flush` for tables and row lists.\n- **Toolbar** (+ `ToolbarGroup`) is the filter strip: search, labelled filters, clear-all, result count. It sits above the table, never in the page header.\n- **DataTable** for lists: sort, selection with `DataTable.BulkActions`, density, empty/loading/error states. Row actions in one kebab `DropdownMenu`.\n- **Descriptions** for record metadata. **ListRow** for setting rows (`trailing` holds the control).\n- **Form + FormField** for every input; actions at the end of the form or in a sticky `PageContainer footer`. Create/edit is a page, not a dialog.\n- **StatCard** only on a dashboard, always with `delta` that compares (vs last week, vs target). **EmptyState**: one sentence and at most one action.\n- **Alert** only for something the user must act on now. **Badge** only for status; one per row at most.\n\n## Admin Screen Anatomy\n\n### List\n\nHeader: title, then `extra` with the primary create action last. Below it a `Toolbar` with search and filters. Then one Card holding the `DataTable` (`CardContent flush`) and `Pagination`. No KPI row, no subtitle that restates the title, no filter Card separate from the table.\n\n### Detail\n\nHeader: record name as title, status `Badge` beside it, actions in `extra` (one primary). Body: `Descriptions` for metadata, then sections (Heading + content) for related records and history (`Timeline`). Use `Tabs` only when sections are independent and long. No Card per section; no icon beside every heading.\n\n### Form\n\n`Form` in a narrow measure; fields grouped by Heading sections, not Cards. Labels via `FormField`; helper text only when it adds information. One primary submit and a secondary cancel at the end, or in a sticky footer for long forms. Errors inline on the field plus `FormErrors` summary on submit.\n\n### Editor\n\nEdit (or create) one document or record. Header: record name as title, status `Badge` beside it, a subtitle only for a fact such as when it was last saved, and ONE primary Save in `extra` (`Button type="submit" form=<form id>`), Cancel `outline` before it. Body: one `Form` in a `SplitPane`: the main column holds the title field and a large body editor (`BlockEditor`, or `Textarea autoSize`); the aside holds metadata fields and read-only facts in `Descriptions`. The change note goes either directly under the body (where the writer finishes) or last in the aside \u2014 pick one per app and keep it. Name each aside group with a `Heading level={2}` one step above its field labels. The main\u2194aside gap is `--split-pane-gap` (default 16px); set it once in the theme. A part that is its own controlled component with its own `Form` (forms cannot nest) stays a separate form; the header Save then submits by handler (`onClick`) instead of `form=<id>`, still one Save. No Card per field group, no second Save at the bottom.\n\n### Reader\n\nA long-form document to read. The document owns the page\'s one `<h1>`: `PageContainer` gets `breadcrumb` and `extra` (Edit) and no `title`. `LegalDocumentShell` draws the reading column: title two steps above the section headings, one metadata line (version, date with `dateKind="updated"` for a revision), the contents rail, and prose at a reading measure (Japanese breaks between phrases under `lang="ja"`; stacked, a contents entry hugs its label). No Card around the article, no subtitle, no icon per heading.\n\n### Settings\n\nEach section is a Heading (plus one muted line if it adds a fact) over its content: `Descriptions` for read-only values, `ListRow` rows in one `Card` with `CardContent flush` for controls (`trailing` holds the control). Separate sections with spacing or a `Separator`. Changes that apply instantly need no Save button; a page with a Save button applies nothing until saved. Destructive actions last, in one `Card accent="destructive"`, each behind `AlertDialog`.\n\n### Dashboard\n\nThe only archetype with KPI tiles: at most four `StatCard`s, each with a comparison `delta` (use `inverse` when up is bad). Below, panels in `ResponsiveGrid`, each a Card with a title and one "view all" link: a work queue, a short table, a chart. Every number answers "compared to what".\n\n### Approval queue\n\nA list whose rows are decisions. Show requester, target, reason, submitted time; approve and reject reachable from the row (kebab or row buttons); with `DataTable`, also in `BulkActions`. Escalated or overdue rows get one status Badge, not a coloured row.\n\n## Do\'s and Don\'ts\n\n### Do\n\n- Lead with the data. The first thing below the header is the table, form or record.\n- Use one `Button` default variant per view; the rest `outline` or `ghost`.\n- Group by spacing, then by heading, then by Separator, and only then by Card.\n- Write the subtitle only when it adds a fact the title does not (date, scope, count).\n- Right-align numbers, `tabular` digits, ISO/`Intl` dates.\n\n### Don\'t\n\n- A subtitle that restates or translates the title.\n- A StatCard row above a list or form.\n- A Card around every section, each with Header + Title + Description.\n- An EmptyState with a large icon inside a Card; an icon in every card header.\n- Alert banners as page decoration; several Alerts stacked.\n- Badge walls: a badge per cell, rainbow tones, badges for non-status labels.\n- Uniform `gap` everywhere; arbitrary px spacing.\n- Coloured avatars, tinted section backgrounds, gradients, hover lift.\n\n## Responsive\n\n- Check 390, 768 and 1280 wide. Nothing scrolls horizontally except a wide table inside its own container.\n- `DataTable` hides `hiddenOnMobile` columns and can stack rows (`collapseBelow`); `Table preset="action-collection"` keeps the actions column visible at 390.\n- `ResponsiveGrid` columns follow its container. `AppShell` turns the sidebar into a drawer on narrow screens.\n- Header `extra` wraps under the title; a toolbar wraps its filters. Touch targets 44px at `comfortable`.\n- Below 640px: at most 2 direct actions in `extra` plus one overflow `DropdownMenu` for the rest; it wraps but never prioritises for you.\n- Bar search is `TopbarSearch`, never a bare `SearchInput`: it folds to a named search cell below 640px. To share the content keyline, keep the default `topbarSpan="content"` and put it first in `Topbar start`.\n- Account cell: `TopbarItem icon={<Avatar/>} labelHideBelow="sm"` with the name repeated in the menu\'s `DropdownMenuLabel`, so a phone shows the avatar only.\n- A sidebar collapse toggle goes in `Sidebar action` (it receives `collapsed`), not in the bar beside the brand. The phone drawer has its own trigger.\n\n## Iteration Guide\n\n1. Pick the archetype, call `get_exemplar` and build from that screen, not from memory.\n2. `npx @godxjp/ui audit <files>` \u2014 zero errors.\n3. `npx @godxjp/ui visual-audit <url> <routes>` \u2014 runs every route at 320, 390, 768, 1024, 1280 and 844\xD7390 (landscape phone) and measures what a screenshot glance misses: clipped or squeezed controls (WCAG 1.4.10 has two halves \u2014 no two-way scrolling AND no lost information; page overflow alone checks only the first), compact search, avatar-only identity, editor inset, action count and target size, rail and search keylines, tiny tracked capitals. Zero machine failures at every width; an "unmapped" result is not a pass. Signed-in routes: `--storage-state state.json` (Playwright storageState). WCAG 1.4.12 / 1.4.4 cells: `--text-spacing` / `--text-zoom 200` (add `--screenshots DIR`) \u2014 each reports its geometry delta against the base render, and a zero delta is "override not applied", not a pass.\n4. Inspect every screenshot at every width, region by region \u2014 top bar, page header, body, aside or rail, menus and drawers \u2014 against the checklist the report prints. A region you were told not to change is still inspected; a defect there becomes an issue, not a silent pass.\n5. No top-N and no round cap. Stop only when every width has zero machine failures and every checklist row is pass or a written N/A.\n6. The builder does not accept its own screen. Report "probes passed; independent acceptance pending" with the report JSON, the screenshots per width and the checklist; acceptance comes from someone who did not build it (a person, or a separate agent given only the build and the artifacts). Never call a screen done on self-review.\n\n## Known Gaps\n\n- The default primary is neutral by design; a product without a brand colour looks monochrome until one is set.\n- `ui-audit` checks primitives and tokens, not structure: a page can pass it and still show every Don\'t above.\n- No edge-mounted rail toggle (a control straddling the sidebar border); `Sidebar action` is the supported place. Under `topbarSpan="full"` the bar shares no keyline with the content column.\n- `card-per-section` counts Cards per file, so a screen split across components can pass with a Card around every section.\n- Exemplar previews deploy on release tags, so they can lag `main`. On GitHub Pages a showcase URL answers HTTP 404 and renders through `404.html`, so open it in a browser; a plain fetch sees the 404.\n- Dark theme (`data-theme="dark"`) is supported; the exemplars are reviewed in light.\n- From 640px up, `BlockEditor` text does not share the keyline of the `Input` above it: measured at 768/1024/1280, input text 13px inside its frame, editor text 57px inside (\u039444px), because the \u22EE\u22EE/+ handles (two 24px targets) sit in a start gutter inside the frame. Aligning the text would put the handles outside the field border, where a content column 24px from the viewport edge has no room. Below 640px the gutter collapses (C05). `--block-editor-gutter-inline-size` sets the gutter width; it cannot close the gap, because the handles live inside it. Accepted, not a defect to fix in the app.\n- On narrow screens the `PageContainer` title steps down (\u224817.6px) while body h2 stays \u224815.7px \u2014 one step apart. A document page not built on `LegalDocumentShell` keeps only that step on phones; there is no intermediate page-title size yet.\n';var ye=e=>e.toLowerCase().replace(/&/g," ").replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");function U(e=H){let t=[],a=e,n=/^---\n[\s\S]*?\n---\n/.exec(e);n&&(t.push({id:"tokens",title:"Tokens (YAML front matter)",body:n[0].trim()}),a=e.slice(n[0].length));for(let o of a.split(/^(?=## )/m)){let i=/^## (.+)$/m.exec(o);!i||!o.startsWith("## ")||t.push({id:ye(i[1]),title:i[1].trim(),body:o.trim()})}return t}function we(e){let t=ye(e);if(!t)return;let a=U();return a.find(n=>n.id===t)??a.find(n=>n.id.startsWith(t))??a.find(n=>n.id.split("-").includes(t))}function ke(e){let t=U().find(a=>a.id==="admin-screen-anatomy");if(t)for(let a of t.body.split(/^(?=### )/m)){let n=/^### (.+)$/m.exec(a);if(n&&n[1].trim().toLowerCase()===e.trim().toLowerCase())return a.trim()}}var We="https://godx-jp.github.io/godxjp-ui/",E=[{archetype:"list",aliases:["index","table","crud","collection","\u4E00\u89A7"],showcase:"table-crud-list",anatomy:["List"],focus:"Header actions in `extra`, Toolbar filters above the table, one Card with DataTable + Pagination."},{archetype:"detail",aliases:["record","show","master-detail","\u8A73\u7D30"],showcase:"table-master-detail",anatomy:["Detail"],focus:"Record header with status Badge and actions, Descriptions metadata, history as a headed section."},{archetype:"editor",aliases:["form","edit","create","document-editor","record-editor","\u7DE8\u96C6"],showcase:"page-editor",anatomy:["Editor","Form"],focus:"One primary Save in `extra` submitting the Form by id; title field + large body editor in the main column, metadata + change note in a SplitPane aside. The body uses @godxjp/block-editor (a second package); `Textarea autoSize` fits the same FormField."},{archetype:"reader",aliases:["document","article","wiki","view","read","\u95B2\u89A7"],showcase:"page-reader",anatomy:["Reader"],focus:"PageContainer with breadcrumb + `extra` and NO `title`; LegalDocumentShell (@godxjp/ui/lab) owns the h1, metadata line, contents rail and prose at reading width."},{archetype:"settings",aliases:["preferences","account","\u8A2D\u5B9A"],showcase:"settings-account-sections",anatomy:["Settings"],focus:"Heading + ListRow sections without Cards; one destructive Card at the end."},{archetype:"dashboard",aliases:["overview","home","kpi","\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9"],showcase:"case1-warehouse-dashboard",anatomy:["Dashboard"],focus:"Four StatCards with comparison deltas, then titled panels in a ResponsiveGrid."},{archetype:"approval-queue",aliases:["approval","queue","inbox","review","\u627F\u8A8D"],showcase:"table-approval-queue",anatomy:["Approval queue"],focus:'Decision rows in `Table preset="action-collection"`, row actions in one kebab.'}],xe=e=>e.trim().toLowerCase().replace(/[\s_]+/g,"-");function Te(e){let t=xe(e);if(t)return E.find(a=>a.archetype===t||a.showcase===t||a.aliases.some(n=>xe(n)===t))}function Se(e){return`${We}showcase/${e}`}var Ce={"case1-warehouse-dashboard":`/**
|
|
5127
5127
|
* Showcase \xB7 case1 \u2014 Warehouse Dashboard (T\u1ED5ng quan kho)
|
|
5128
5128
|
*
|
|
5129
5129
|
* A full standalone admin/\u52E4\u6020 dashboard page, served at \`/showcase/case1-warehouse-dashboard\`.
|
|
@@ -7311,7 +7311,7 @@ export default function Demo() {
|
|
|
7311
7311
|
</PageContainer>
|
|
7312
7312
|
);
|
|
7313
7313
|
}
|
|
7314
|
-
`};var p={name:"@godxjp/ui-mcp",version:"32.11.0",godxUiCompatibility:"32.11.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 _=[{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. Core only by default \u2014 pass tier="lab" for the opt-in @godxjp/ui/lab components.',inputSchema:{type:"object",properties:{tier:{type:"string",enum:["core","lab","all"],description:'Default "core". "lab" lists the opt-in `@godxjp/ui/lab` components (niche, kept and tested, not in the default context); "all" lists both.'},group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_utilities",description:'List every NON-component public export of @godxjp/ui \u2014 hooks, helper functions and constants (cn, formatDate, formatCurrency, toast, useDebouncedValue, buttonVariants, CHART_COLORS, SHOW_PARENT \u2026). Reach for this before hand-writing a className merger, a date/money formatter, a debounce hook or a chart palette: two products in this org each re-implemented `cn` because it could not be found. Optionally filter by kind. Then `get_component name="<name>"` for its signature, usage and example.',inputSchema:{type:"object",properties:{kind:{type:"string",enum:["hook","function","value"],description:"hook = only legal inside a component body; function = callable anywhere; value = a constant to read."}}}},{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. Core only by default; pass tier="lab" or "all" to include the opt-in @godxjp/ui/lab components.',inputSchema:{type:"object",properties:{query:{type:"string"},tier:{type:"string",enum:["core","lab","all"],description:'Default "core". "lab" lists the opt-in `@godxjp/ui/lab` components (niche, kept and tested, not in the default context); "all" lists both.'}},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:"get_design",description:"DESIGN.md \u2014 the visual spec for screens built with @godxjp/ui: real tokens (YAML front matter), colour scarcity rules, typography, layout & density, elevation, components, ADMIN SCREEN ANATOMY (list / detail / form / settings / dashboard / approval queue), do's and don'ts, responsive, iteration guide, known gaps. Read before composing a screen. Pass `section` for one section; omit it for the whole document (~12 KB).",inputSchema:{type:"object",properties:{section:{type:"string",description:"Section id or heading word, e.g. 'tokens', 'colors', 'admin-screen-anatomy', 'dos-and-donts', 'iteration-guide'. Unknown ids list the valid ones."}}}},{name:"get_exemplar",description:"A reference screen to match for an admin archetype: its anatomy notes from DESIGN.md, the full source of the showcase built from real @godxjp/ui primitives, and its live preview URL for screenshot comparison. Archetypes: "+E.map(e=>e.archetype).join(", ")+". Omit `archetype` to list them.",inputSchema:{type:"object",properties:{archetype:{type:"string",description:E.map(e=>e.archetype).join(" | ")+" (aliases: "+E.flatMap(e=>e.aliases??[]).join(", ")+")."}}}},{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"]}}],Je=new Set(_.map(e=>e.name));function Ze(e=$()){let t=p.godxUiCompatibility??p.version,a=`@godxjp/ui-mcp ${p.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let n=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} (${n})`}function et(e=$()){let t=e?j(e.version):null,a=j(p.version);return!e||!t||!a?null:lt(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${p.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 ${p.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 Ne(e,t){let a=await tt(e,t);if(!Je.has(e))return a;let n=$(),o=et(n);return`${Ze(n)}
|
|
7314
|
+
`};var p={name:"@godxjp/ui-mcp",version:"32.12.0",godxUiCompatibility:"32.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 _=[{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. Core only by default \u2014 pass tier="lab" for the opt-in @godxjp/ui/lab components.',inputSchema:{type:"object",properties:{tier:{type:"string",enum:["core","lab","all"],description:'Default "core". "lab" lists the opt-in `@godxjp/ui/lab` components (niche, kept and tested, not in the default context); "all" lists both.'},group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_utilities",description:'List every NON-component public export of @godxjp/ui \u2014 hooks, helper functions and constants (cn, formatDate, formatCurrency, toast, useDebouncedValue, buttonVariants, CHART_COLORS, SHOW_PARENT \u2026). Reach for this before hand-writing a className merger, a date/money formatter, a debounce hook or a chart palette: two products in this org each re-implemented `cn` because it could not be found. Optionally filter by kind. Then `get_component name="<name>"` for its signature, usage and example.',inputSchema:{type:"object",properties:{kind:{type:"string",enum:["hook","function","value"],description:"hook = only legal inside a component body; function = callable anywhere; value = a constant to read."}}}},{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. Core only by default; pass tier="lab" or "all" to include the opt-in @godxjp/ui/lab components.',inputSchema:{type:"object",properties:{query:{type:"string"},tier:{type:"string",enum:["core","lab","all"],description:'Default "core". "lab" lists the opt-in `@godxjp/ui/lab` components (niche, kept and tested, not in the default context); "all" lists both.'}},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:"get_design",description:"DESIGN.md \u2014 the visual spec for screens built with @godxjp/ui: real tokens (YAML front matter), colour scarcity rules, typography, layout & density, elevation, components, ADMIN SCREEN ANATOMY (list / detail / form / settings / dashboard / approval queue), do's and don'ts, responsive, iteration guide, known gaps. Read before composing a screen. Pass `section` for one section; omit it for the whole document (~12 KB).",inputSchema:{type:"object",properties:{section:{type:"string",description:"Section id or heading word, e.g. 'tokens', 'colors', 'admin-screen-anatomy', 'dos-and-donts', 'iteration-guide'. Unknown ids list the valid ones."}}}},{name:"get_exemplar",description:"A reference screen to match for an admin archetype: its anatomy notes from DESIGN.md, the full source of the showcase built from real @godxjp/ui primitives, and its live preview URL for screenshot comparison. Archetypes: "+E.map(e=>e.archetype).join(", ")+". Omit `archetype` to list them.",inputSchema:{type:"object",properties:{archetype:{type:"string",description:E.map(e=>e.archetype).join(" | ")+" (aliases: "+E.flatMap(e=>e.aliases??[]).join(", ")+")."}}}},{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"]}}],Je=new Set(_.map(e=>e.name));function Ze(e=$()){let t=p.godxUiCompatibility??p.version,a=`@godxjp/ui-mcp ${p.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let n=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} (${n})`}function et(e=$()){let t=e?j(e.version):null,a=j(p.version);return!e||!t||!a?null:lt(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${p.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 ${p.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 Ne(e,t){let a=await tt(e,t);if(!Je.has(e))return a;let n=$(),o=et(n);return`${Ze(n)}
|
|
7315
7315
|
${o?`${o}
|
|
7316
7316
|
`:""}
|
|
7317
7317
|
${a}`}async function tt(e,t){switch(e){case"list_skills":return ot();case"list_primitives":return Pe(t.group,t.tier);case"list_utilities":return xt(t.kind);case"list_patterns":return pt();case"list_anti_ai_tells":return gt(t.category);case"list_redesign_checks":return ft(t.category);case"list_audit_rules":return mt(t.category);case"list_visual_checks":return ut(t.category);case"get_anti_ai_tell":return bt(String(t.name??""));case"get_redesign_check":return vt(String(t.symptom??""));case"get_skill_section":return Le(String(t.skill??""),String(t.section??""));case"get_component":return Tt(String(t.name??""),t.verbose===!0);case"get_pattern":return At(String(t.name??""));case"get_rule":return Dt(typeof t.number=="number"?t.number:void 0);case"get_vocab":return Et(t.name==null?void 0:String(t.name));case"get_tokens":return Ot(t.category);case"list_consumer_skills":return it();case"get_consumer_skill":return rt(String(t.skill??""),String(t.section??""));case"route_consumer_task":return Ee(String(t.task??""),{consumerOnly:!0});case"draft_bug_report":return st(t);case"check_compatibility":return ze(t.version==null?void 0:String(t.version));case"route_task":return Ee(String(t.task??""));case"suggest_primitive":return Nt(String(t.use_case??""));case"search_components":return Rt(String(t.query??""),t.tier);case"get_frame_coverage":return Ct(t.name===void 0?void 0:String(t.name));case"get_design":return at(t.section==null?void 0:String(t.section));case"get_exemplar":return nt(t.archetype==null?"":String(t.archetype));case"lint_jsx":return It(String(t.jsx??""));default:return`Unknown tool: ${e}`}}function at(e){if(e===void 0||e.trim()==="")return H;let t=we(e);if(t)return t.body;let a=U().map(n=>`- \`${n.id}\` \u2014 ${n.title}`).join(`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui-mcp",
|
|
3
|
-
"version": "32.
|
|
4
|
-
"godxUiCompatibility": "32.
|
|
3
|
+
"version": "32.12.0",
|
|
4
|
+
"godxUiCompatibility": "32.12.x",
|
|
5
5
|
"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).",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|