# AdiaUI Catalog — LLM Rules (v0.9)

# Cross-component and contextual constraints that the JSON Schema validator
# cannot express. Concatenated into the agent system prompt.
# Generated from each component YAML's `a2ui.rules:` entries.

## Card
- The card's <header> grid activates only for DIRECT-child slotted elements. If you need an icon column, place the icon element (avatar-ui, icon-ui) directly in the header with slot="icon" — not inside a wrapper.
- Heading slot accepts inline badges/metadata: <span slot="heading"><text-ui strong>Title</text-ui><badge-ui text="New" variant="accent"></badge-ui></span> renders title + badge on one row.
- Description slot also accepts bare <p> or <small> elements as siblings of the heading — they participate in the grid's row 2 without needing slot="description".
- Multiple <section> siblings are allowed and stack vertically. [bleed] on a section removes its margin for edge-to-edge content (tables, charts); [padding] adds a canvas-scrim background for hero regions.
- Footer with a [slot="description"] + [slot="action"] pair triggers justify-content: space-between — useful for a "Last saved …" note on the left and a Save/Cancel button group on the right.

## ChatInput
- ChatInput is a self-contained composer — it stamps its own textarea + model picker + send button. DO NOT add a separate Button sibling for "send" inside the same parent. The user gets two send buttons (one built-in, one redundant).
- For chat interfaces emit ChatInput as the sole input component inside the chat shell. The submit event fires on Enter or send-button click; `detail` is `{ text, model }`.
- To customize models, set the `models` prop with an array of {value, label} option objects. Do not stamp a separate Select next to ChatInput for model selection — the built-in model picker handles it.

## Check
- Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. Wrapping breaks the consent-row layout (see field-ui anti_patterns).

## Field
- field-ui is for WIDE controls (input-ui, select-ui, textarea-ui, slider-ui, etc.) that need a separate label row. Small self-labeling widgets (check-ui, switch-ui, radio-ui, toggle-ui) carry their own [label] attribute and MUST NOT be wrapped in field-ui.
- field-ui[inline] is for inline WIDE-control rows (e.g. search field with trailing kbd hint), NOT for compact-widget rows. For settings rows or consent rows, use the widget's own [label] attribute directly without a field-ui wrapper.

## Grid
- For asymmetric column ratios, use [columns="N"] plus [span="M"] on the child that should be wider. Never set grid-template-columns via style — it bypasses the component API and reads as a local hack.
- Canonical ratios: 1:1 → columns="2"; 2:1 → columns="3" + span="2"; 3:1 → columns="4" + span="3"; 3:2 → columns="5" + span="3"+"2"; 4:1 → columns="5" + span="4". Round unusual ratios (e.g. 7:5) to 3:2.
- The default (no columns attribute) gives equal auto-columns flowing in a single row. Prefer the explicit numeric attribute for dashboard rows so the layout is predictable when items wrap.

## Link
- Use `<link-ui>` for navigation; use `<button-ui>` for actions. They are NOT interchangeable.
- When wrapping action affordances that visually mimic links (e.g. 'Forgot password?' that triggers a reset flow), prefer `<button-ui variant="ghost">` over a fake `<link-ui>` — the affordance is semantically a button, just visually understated.
- For inline-sentence affordances ('I agree to the [Terms] and [Privacy]'), nest `<link-ui>` directly inside `<text-ui>` so it inherits the paragraph's font / size / line-height.

## Radio
- Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For radio groups, the canonical pattern is a column of bare <radio-ui label='…'> elements sharing a [name=] — no field-ui wrapper around each radio.

## Switch
- Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For settings rows (label-left, switch-right), put the descriptive text in switch-ui's own [label] attribute; do not introduce a field-ui wrapper. For descriptive helper text below the switch, use <text-ui variant='caption'> as a sibling — not field-ui's hint slot.

## ChatComposer
- chat-composer is the bespoke replacement for legacy <chat-input-ui data-chat-input> inside <chat-shell>. Place an inner <chat-input-ui submit-on-enter> as the primary input.
- The host listens for 'composer-submit' on the composer (not on the inner input). The event detail mirrors the inner submit event so existing handlers Just Work.

## ChatEmpty
- chat-empty is the bespoke replacement for legacy <empty-state-ui data-chat-empty>. Place as the first child of <chat-thread>; visibility is automatic via the [empty] reflected attribute.

## ChatHeader
- chat-header replaces the legacy <header> chrome bar inside <chat-shell>. Use named slots for canonical clusters; ad-hoc content goes in the default slot.

## ChatShell
- chat-shell is a behavior wrapper; don't nest col-ui/row-ui inside it directly — author the structural DOM using [data-chat-*] markers instead.

## ChatSidebar
- chat-sidebar is the bespoke replacement for legacy <aside data-sidebar>. Use slot="leading" or slot="trailing" to position. Add resizable + collapsible attributes to opt in to interactive behaviors.
- For chrome bars inside the sidebar, prefer <admin-topbar slot="header"> and <admin-statusbar slot="footer"> over raw <header-ui> / <footer-ui> when authoring shell-tier markup.

## ChatStatus
- chat-status replaces legacy <span data-chat-status> for the streaming/connection indicator. Place inside <chat-header slot="status">.

## ChatThread
- chat-thread is the bespoke replacement for legacy <section data-chat-messages> inside <chat-shell>. Use it for the message scroll surface; the host appends dynamic message divs as children.
- Place <chat-empty> as an optional first child for the empty state; the [empty] reflected attribute drives its visibility via CSS (no JS toggling).

## EditorCanvasEmpty
- editor-canvas-empty is the bespoke empty-state slot for <editor-canvas>. Place as the first child of <editor-canvas>; visibility is automatic via the [empty] reflected attribute.

## EditorCanvasToolbar
- editor-canvas-toolbar is the bespoke chrome strip for the <editor-canvas> top edge. Use it to hold view-mode tabs (preview / schema / DOM) or breadcrumb-style navigation scoped to the canvas region.
- Place as the first child of <editor-canvas>; the canvas's flex-column layout positions the toolbar above the content body automatically, and the toolbar is sticky to keep tabs visible during canvas scroll.
- Distinct from <editor-toolbar> (app-scope, top of <editor-shell>) — this is canvas-scope, holding view-mode affordances rather than document-wide actions.

## EditorCanvas
- editor-canvas is the bespoke replacement for legacy <div data-canvas> inside <editor-shell>. Use it as the central content region for editor work surfaces.
- Place <editor-canvas-empty> as an optional first child for the empty state; the [empty] reflected attribute drives its visibility via CSS (no JS toggling).
- Place <editor-canvas-toolbar> as the first child to mount view-mode tabs (preview / schema / DOM, etc.), breadcrumbs, or canvas-scoped actions above the content body. Replaces the ad-hoc <div data-view-strip> pattern. Distinct from <editor-toolbar> (app-scope) — this is canvas-scope.

## EditorShell
- editor-shell is a layout skeleton. Children must follow the documented structural DOM; the element wires behavior, not content.

## EditorSidebar
- editor-sidebar wraps <pane-ui resizable> rather than implementing drag itself. Place a <pane-ui resizable> as the only structural child; fill the pane with header / section / footer slots.
- The cluster-distinct localStorage prefix (adia-editor-sidebar-*) keeps editor sidebars from colliding with admin (adia-sidebar-*) and chat (adia-chat-sidebar-*) sidebars on the same domain.

## EditorStatusbar
- editor-statusbar replaces legacy <footer> chrome bar inside <editor-shell>. Use named slots for canonical clusters; ad-hoc content goes in the default slot.

## EditorToolbar
- editor-toolbar replaces legacy <header> chrome bar inside <editor-shell>. Use named slots (title / status / action / action-leading) for canonical clusters; ad-hoc inline content goes in the default slot.
- Buttons that should trigger named actions get [data-toolbar-action="<name>"]. The toolbar bubbles a single 'toolbar-action' event up to the host with the name in detail.

## GenRoot
- gen-root is an integration shell. Prefer admin-shell for admin UIs; use gen-root only for chat+canvas tooling.

## AdminCommand
- admin-command wraps a native <dialog>; the inner <command-ui> is the actual palette. Keyboard shortcut defaults to both Cmd+K (mac) and Ctrl+K (other) — the AdiaUI convention.
- Place admin-command as a direct child of admin-shell, NOT inside a sidebar or main column. The host coordinates triggers ([data-command-trigger]) by reaching across siblings.

## AdminContent
- admin-content is the bespoke replacement for raw <main> inside admin-shell. CSS-only; the shell's css/main.css selectors target both shapes via :is(main, admin-content).

## AdminPageBody
- admin-page-body is the centered body band of <admin-page>. Wraps an inner <section-ui> for centered reading-column rhythm; can host full-bleed content directly to opt out.

## AdminPageHeader
- admin-page-header is the sticky top band of <admin-page>. Wraps an inner <header-ui> for centered reading-column rhythm.

## AdminPage
- admin-page is the bespoke replacement for <article data-content-root>. Provides the page-content named container query so descendants can use @container page-content (max-width: 720px) etc.

## AdminScroll
- admin-scroll is the bespoke replacement for the legacy <section> child of <main> inside admin-shell. Single child convention — typically wraps an <admin-page> for sticky-band layout.

## AppShell
- admin-shell is a behavior wrapper; its children are native HTML landmarks (aside, main, header). Don't wrap them in col-ui/row-ui — app-shell.css handles grid layout based on [data-sidebar] attributes.

## AdminSidebar
- admin-sidebar is the bespoke replacement for legacy <aside data-sidebar>. Use slot="leading" or slot="trailing" to position. Add resizable + collapsible attributes to opt in to interactive behaviors.
- For chrome bars inside the sidebar, prefer <admin-topbar slot="header"> and <admin-statusbar slot="footer"> over raw <header-ui> / <footer-ui> when authoring shell-tier markup.

## AdminStatusbar
- admin-statusbar replaces <footer-ui> at shell-tier. Same slot vocabulary as <admin-topbar>; visual treatment differs (the shell css applies a top-border instead of bottom).

## AdminTopbar
- admin-topbar replaces <header-ui> at shell-tier — use it for chrome bars inside admin-shell, admin-content, admin-sidebar. Use <header-ui> for primitive containers (Card / Drawer / Modal).

## SimpleContent
- simple-content is the article surface inside simple-shell. Use for primary page body. Sibling to <simple-hero> (optional).

## SimpleHero
- simple-hero is the optional top strip inside simple-shell. Use for marketing splashes, error-page reassurance text, or single- flow page intros. Always followed by <simple-content> for body.

## SimpleShell
- simple-shell is the bespoke shell for thin / minimal page surfaces. Use when the page has no nav rail, no chrome bars, no command palette. For full app surfaces use admin-shell; for chat surfaces use chat-shell; for design tools use editor-shell.
- Compose with <simple-hero> (optional top hero strip) and <simple-content> (main article body). Both are CSS-only structural children — no JS, no state.

## ThemePanel
- theme-panel is the canonical appearance-preferences popover. Compose inside a <popover-ui slot="content"> in the topbar of a shell (admin, chat, editor, simple) when the page exposes user theming. Avoid hardcoding it as a shell child — placement is the consumer's call.
- Use [persist] for the docs surface or a real product app; omit [persist] for playgrounds and embedded demos so state stays ephemeral. The default is ephemeral.
- Add boolean attributes [parametric], [presets], [scheme-toggle] to enable sections; the minimum panel is the theme button grid.

