{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "name": "smartwebUI Agent Catalog",
  "version": "1.5.0",
  "protocol": "smartwebui.surface/1.0",
  "iconCatalog": "./smartwebui-icons.json",
  "description": "A machine-readable contract for composing safe, accessible smartwebUI surfaces.",
  "license": "smartwebUI Source License v1.0 — (c) 2026 Elonovo. See LICENSE.md. Free to use and customize for your own projects; redistributing modified or rebranded versions requires written permission.",
  "security": {
    "executionModel": "declarative-only",
    "allowsArbitraryHtml": false,
    "allowsArbitraryJavaScript": false,
    "allowsInlineStyles": false,
    "textIsEscaped": true,
    "actions": "Action identifiers are emitted as smart:action events and are never executed by the renderer.",
    "hrefAndSrc": "Only http(s), mailto, same-origin relative paths, and in-page hashes are written to href or src. javascript:, data:, and protocol-relative URLs are rejected.",
    "unknownProperties": "Rejected at runtime. Each node is validated against its catalog property map."
  },
  "limits": {
    "maximumDepth": 20,
    "maximumNodes": 500,
    "maximumTextLength": 10000
  },
  "surfaceSchema": {
    "type": "object",
    "required": [
      "surface",
      "root"
    ],
    "additionalProperties": false,
    "properties": {
      "surface": {
        "type": "string",
        "pattern": "^[A-Za-z][A-Za-z0-9._:-]{0,127}$"
      },
      "root": {
        "$ref": "#/$defs/node"
      }
    }
  },
  "patchOperations": [
    {
      "type": "data.patch",
      "required": [
        "type",
        "id",
        "changes"
      ],
      "description": "Merge trusted property changes into an existing node and render it again."
    },
    {
      "type": "node.upsert",
      "required": [
        "type",
        "node"
      ],
      "optional": [
        "parent"
      ],
      "description": "Replace a node with the same stable ID or append it to a parent."
    },
    {
      "type": "node.remove",
      "required": [
        "type",
        "id"
      ],
      "description": "Remove a node and its descendants from the surface."
    },
    {
      "type": "node.append",
      "required": [
        "type",
        "parent"
      ],
      "oneOf": [
        "node",
        "nodes"
      ],
      "description": "Append one validated node or a batch of nodes to a child container for streaming growth."
    }
  ],
  "layoutPrimitives": [
    {
      "name": "Stack",
      "intent": "Arrange children vertically with consistent spacing.",
      "properties": {
        "gap": {
          "type": "string",
          "enum": [
            "small",
            "medium",
            "large"
          ]
        }
      },
      "children": "any catalog component or layout primitive"
    },
    {
      "name": "Cluster",
      "intent": "Arrange related controls or content inline with wrapping.",
      "properties": {
        "gap": {
          "type": "string",
          "enum": [
            "small",
            "medium",
            "large"
          ]
        }
      },
      "children": "any catalog component or layout primitive"
    },
    {
      "name": "Grid",
      "intent": "Arrange peer content in a responsive grid.",
      "properties": {
        "minimum": {
          "type": "string",
          "enum": [
            "compact",
            "standard",
            "wide"
          ]
        }
      },
      "children": "peer catalog components"
    },
    {
      "name": "Text",
      "intent": "Render trusted text with semantic emphasis.",
      "properties": {
        "text": {
          "type": "string",
          "required": true
        },
        "as": {
          "type": "string",
          "enum": [
            "p",
            "strong",
            "small",
            "h2",
            "h3"
          ]
        },
        "tone": {
          "type": "string",
          "enum": [
            "default",
            "secondary",
            "success",
            "warning",
            "danger"
          ]
        }
      },
      "children": "none"
    }
  ],
  "components": [
    {
      "name": "Button",
      "intent": "Initiate an immediate user action.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "variant": {
          "type": "string",
          "enum": [
            "primary",
            "secondary",
            "quiet",
            "success",
            "warning",
            "info",
            "danger",
            "link"
          ]
        },
        "size": {
          "type": "string",
          "enum": [
            "small",
            "medium",
            "large"
          ]
        },
        "icon": {
          "type": "string",
          "description": "Optional name from smartwebui-icons.json. Decorative because the button label remains visible."
        },
        "iconPosition": {
          "type": "string",
          "enum": ["start", "end"]
        },
        "iconSize": {
          "type": "number"
        },
        "iconStroke": {
          "type": "number"
        },
        "fullWidth": {
          "type": "boolean"
        },
        "pressed": {
          "type": "boolean"
        },
        "action": {
          "type": "string"
        },
        "payload": {
          "type": "object"
        },
        "disabled": {
          "type": "boolean"
        },
        "busy": {
          "type": "boolean"
        },
        "splitItems": {
          "type": "array",
          "description": "Optional related alternatives. When present, Button renders as a split button."
        },
        "menuLabel": {
          "type": "string"
        }
      },
      "events": [
        "smart:action"
      ],
      "children": "none",
      "constraints": [
        "Use one primary action per local decision area.",
        "Use danger only for destructive consequences."
      ],
      "accessibility": [
        "Requires a visible label or accessibleName.",
        "Disabled state is exposed natively."
      ]
    },
    {
      "name": "Icon",
      "intent": "Render one symbol from the curated smartwebUI icon catalog.",
      "properties": {
        "name": {
          "type": "string",
          "required": true
        },
        "label": {
          "type": "string",
          "description": "Accessible name for a meaningful standalone icon. Omit for decorative icons."
        },
        "size": {
          "type": "number",
          "description": "Rendered CSS pixel size. Hand-authored HTML may use any CSS length through --smart-icon-size."
        },
        "stroke": {
          "type": "number"
        },
        "tone": {
          "type": "string",
          "enum": ["default", "secondary", "success", "warning", "danger"]
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Use a catalog name; arbitrary SVG or markup is never accepted.",
        "Prefer a visible text label for actions instead of an icon alone."
      ],
      "accessibility": [
        "Supply label when the icon carries meaning by itself.",
        "Omit label when adjacent text already communicates the same meaning."
      ]
    },
    {
      "name": "Field",
      "intent": "Collect one labeled value from the user.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "control": {
          "type": "string",
          "enum": [
            "input",
            "textarea",
            "select",
            "combobox",
            "search",
            "file",
            "range",
            "date",
            "time",
            "tags",
            "daterange",
            "otp",
            "quantity",
            "multiselect",
            "numericrange",
            "rating",
            "color"
          ]
        },
        "inputType": {
          "type": "string",
          "enum": [
            "text",
            "email",
            "number",
            "search",
            "tel",
            "url",
            "password"
          ]
        },
        "name": {
          "type": "string"
        },
        "value": {
          "type": [
            "string",
            "number"
          ]
        },
        "values": {
          "type": "array"
        },
        "placeholder": {
          "type": "string"
        },
        "mask": {
          "type": "string",
          "description": "Optional # digit / A letter display mask applied without changing the declarative security boundary."
        },
        "format": {
          "type": "string",
          "enum": ["number", "currency", "phone"],
          "description": "Formats number and currency values with Intl; phone preserves a canonical E.164-style value."
        },
        "locale": {
          "type": "string",
          "description": "Optional BCP 47 locale for number and currency display."
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Za-z]{3}$",
          "description": "ISO 4217 currency code, required with format: currency."
        },
        "hint": {
          "type": "string"
        },
        "error": {
          "type": "string"
        },
        "options": {
          "type": "array"
        },
        "accept": {
          "type": "string"
        },
        "buttonLabel": {
          "type": "string"
        },
        "pickerTitle": {
          "type": "string"
        },
        "minimum": {
          "type": "number"
        },
        "maximum": {
          "type": "number"
        },
        "lower": {
          "type": "number"
        },
        "upper": {
          "type": "number"
        },
        "step": {
          "type": "number"
        },
        "disabled": {
          "type": "boolean"
        },
        "prefix": {
          "type": "string"
        },
        "suffix": {
          "type": "string"
        },
        "prefixIcon": {
          "type": "string",
          "description": "Decorative smartwebUI icon name shown before an input."
        },
        "suffixIcon": {
          "type": "string",
          "description": "Decorative smartwebUI icon name shown after an input."
        },
        "reveal": {
          "type": "boolean"
        },
        "copy": {
          "type": "boolean"
        },
        "start": {
          "type": "string"
        },
        "end": {
          "type": "string"
        },
        "endName": {
          "type": "string"
        },
        "strength": {
          "type": "boolean",
          "description": "Show a password strength meter under the control (password inputs)."
        },
        "dropzone": {
          "type": "boolean",
          "description": "Enable drag-and-drop on file controls."
        },
        "length": {
          "type": "number",
          "description": "Digit count for OTP controls (4-8, default 6)."
        },
        "maxLength": {
          "type": "number",
          "description": "Maximum textarea length; pair with counter for visible feedback."
        },
        "counter": {
          "type": "boolean",
          "description": "Show an automatically updated character counter for a textarea with maxLength."
        }
      },
      "events": [
        "smart:change",
        "smart:select",
        "smart:combobox",
        "smart:multiselect",
        "smart:date",
        "smart:time",
        "smart:range",
        "smart:rating",
        "smart:tags",
        "smart:copy",
        "smart:otp",
        "smart:quantity",
        "smart:strength",
        "smart:file"
      ],
      "children": "none",
      "constraints": [
        "Use select or combobox only when the option set is known and bounded.",
        "Use date, daterange, and time controls instead of native input types for consistent styling.",
        "Date values use ISO format YYYY-MM-DD. Time values use HH:MM (24-hour). Tag values are comma-separated.",
        "Use prefix, suffix, prefixIcon, suffixIcon, reveal, and copy only with control input.",
        "Use counter only with a textarea and a positive maxLength.",
        "Do not use placeholder text as the only label."
      ],
      "accessibility": [
        "A programmatic label is always generated.",
        "Custom select, date, and time controls expose dialog or listbox semantics.",
        "Search fields include a dedicated clear control."
      ]
    },
    {
      "name": "Choice",
      "intent": "Select one or more explicit options.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "kind": {
          "type": "string",
          "enum": [
            "checkbox",
            "radio"
          ]
        },
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "checked": {
          "type": "boolean"
        },
        "disabled": {
          "type": "boolean"
        },
        "detail": {
          "type": "string"
        }
      },
      "events": [
        "smart:change"
      ],
      "children": "none",
      "constraints": [
        "Radio choices sharing a decision must use the same name."
      ],
      "accessibility": [
        "The complete visible row is the input label."
      ]
    },
    {
      "name": "Switch",
      "intent": "Change an immediately applied binary setting.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "name": {
          "type": "string"
        },
        "checked": {
          "type": "boolean"
        },
        "disabled": {
          "type": "boolean"
        }
      },
      "events": [
        "smart:change"
      ],
      "children": "none",
      "constraints": [
        "Do not use when changes require a separate Save action."
      ],
      "accessibility": [
        "Uses a native checkbox with the complete row as its label."
      ]
    },
    {
      "name": "SegmentedControl",
      "intent": "Choose one compact setting from two to five options.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "options": {
          "type": "array",
          "required": true
        }
      },
      "events": [
        "smart:change"
      ],
      "children": "none",
      "constraints": [
        "Use Tabs for peer content views.",
        "Keep labels short and options mutually exclusive."
      ],
      "accessibility": [
        "Uses a native radio group with arrow, Home and End behavior."
      ]
    },
    {
      "name": "Card",
      "intent": "Group a coherent piece of content or one metric.",
      "properties": {
        "title": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "change": {
          "type": "string"
        },
        "interactive": {
          "type": "boolean"
        },
        "variant": {
          "type": "string",
          "enum": ["default", "media", "list"]
        },
        "selected": {
          "type": "boolean"
        },
        "mediaSrc": {
          "type": "string"
        },
        "mediaAlt": {
          "type": "string"
        }
      },
      "events": [],
      "children": "any catalog component or layout primitive",
      "constraints": [
        "Do not nest cards without a strong information hierarchy reason."
      ],
      "accessibility": [
        "A title is rendered as a heading when supplied."
      ]
    },
    {
      "name": "Badge",
      "intent": "Communicate a compact status or classification.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "tone": {
          "type": "string",
          "enum": [
            "neutral",
            "accent",
            "success",
            "warning",
            "danger"
          ]
        },
        "dotOnly": {
          "type": "boolean"
        },
        "plain": {
          "type": "boolean",
          "description": "Hide the leading status marker and keep label text only."
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Do not use as an action.",
        "Status meaning must be present in text, not color alone."
      ],
      "accessibility": [
        "Visible text always carries the status meaning."
      ]
    },
    {
      "name": "Tabs",
      "intent": "Switch between peer content views without leaving the current context, with optional count badges.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "selected": {
          "type": "string",
          "required": true
        },
        "items": {
          "type": "array",
          "required": true
        },
        "orientation": {
          "type": "string",
          "enum": [
            "horizontal",
            "vertical"
          ],
          "description": "Tab list direction; vertical renders a settings-style side list."
        }
      },
      "events": [
        "smart:tab"
      ],
      "children": "none; content is supplied through items",
      "constraints": [
        "Use SegmentedControl for compact settings.",
        "Each item requires value, label and content; count adds an optional badge to its label."
      ],
      "accessibility": [
        "Generates tablist, tab and tabpanel relationships.",
        "Supports arrows, Home and End."
      ]
    },
    {
      "name": "Accordion",
      "intent": "Progressively disclose secondary information.",
      "properties": {
        "items": {
          "type": "array",
          "required": true
        },
        "exclusive": {
          "type": "boolean"
        }
      },
      "events": ["smart:accordion"],
      "children": "none; content is supplied through items (text or nested catalog nodes)",
      "constraints": [
        "Do not hide information required to complete the primary task."
      ],
      "accessibility": [
        "Uses native details and summary elements."
      ]
    },
    {
      "name": "Dialog",
      "intent": "Request focused input or confirmation that temporarily blocks the page.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "text": {
          "type": "string"
        },
        "open": {
          "type": "boolean"
        },
        "variant": {
          "type": "string",
          "enum": [
            "default",
            "confirm"
          ]
        },
        "wide": {
          "type": "boolean"
        },
        "scroll": {
          "type": "boolean"
        }
      },
      "events": [
        "close"
      ],
      "children": "Button or Field components",
      "constraints": [
        "Open only because of a user action.",
        "Keep the task focused and short.",
        "Use variant confirm for destructive confirmations — the HTML class is .smart-dialog--confirm."
      ],
      "accessibility": [
        "Uses the native dialog element with modal focus behavior.",
        "open: true opens the dialog modally once the surface is mounted."
      ]
    },
    {
      "name": "Drawer",
      "intent": "Present a focused secondary task while preserving page context.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "text": {
          "type": "string"
        },
        "open": {
          "type": "boolean"
        }
      },
      "events": [
        "close"
      ],
      "children": "Button or Field components",
      "constraints": [
        "Use Dialog when the action must block the page completely."
      ],
      "accessibility": [
        "Uses native dialog semantics with drawer presentation.",
        "open: true opens the drawer modally once the surface is mounted."
      ]
    },
    {
      "name": "PopoverMenu",
      "intent": "Expose actions, labeled sections, separators, and checkbox items anchored to one trigger.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "items": {
          "type": "array",
          "required": true
        }
      },
      "events": [
        "smart:action"
      ],
      "children": "none; actions are supplied through items",
      "constraints": [
        "Use checkbox items only for menu-scoped toggles; use selection controls for form values.",
        "Keep the list short and task-related."
      ],
      "accessibility": [
        "Generates a menu trigger, menu items, arrow navigation, Escape and focus return."
      ]
    },
    {
      "name": "Tooltip",
      "intent": "Provide a brief label or clarification for a compact control.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "tooltip": {
          "type": "string",
          "required": true
        },
        "icon": {
          "type": "string"
        },
        "action": {
          "type": "string"
        },
        "payload": {
          "type": "object",
          "description": "Structured data forwarded with the smart:action event; never functions or HTML."
        }
      },
      "events": [
        "smart:action"
      ],
      "children": "none",
      "constraints": [
        "Never place essential instructions only in a tooltip."
      ],
      "accessibility": [
        "The trigger receives an accessible name and keyboard tooltip behavior."
      ]
    },
    {
      "name": "Toast",
      "intent": "Announce non-blocking completion or status feedback.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "message": {
          "type": "string"
        },
        "tone": {
          "type": "string",
          "enum": [
            "success",
            "info",
            "warning",
            "danger"
          ]
        },
        "placement": {
          "type": "string",
          "enum": ["top-start", "top-center", "top-end", "bottom-start", "bottom-center", "bottom-end"]
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Do not use for decisions or critical errors requiring action."
      ],
      "accessibility": [
        "Announced through the polite toast region with dismissal and automatic cleanup."
      ]
    },
    {
      "name": "Progress",
      "intent": "Communicate determinate or indeterminate work in progress.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "variant": {
          "type": "string",
          "enum": ["bar", "ring", "spinner", "skeleton"]
        },
        "value": {
          "type": "number"
        },
        "maximum": {
          "type": "number"
        },
        "indeterminate": {
          "type": "boolean"
        },
        "skeletons": {
          "type": "number",
          "minimum": 0,
          "maximum": 8
        },
        "size": {
          "type": "number"
        },
        "showValue": {
          "type": "boolean"
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Use determinate progress when completion can be measured."
      ],
      "accessibility": [
        "Uses native progress semantics or an explicitly labeled status spinner."
      ]
    },
    {
      "name": "Alert",
      "intent": "Show persistent inline status, policy, or error messaging.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "message": {
          "type": "string"
        },
        "tone": {
          "type": "string",
          "enum": [
            "info",
            "success",
            "warning",
            "danger"
          ]
        },
        "dismissible": {
          "type": "boolean"
        },
        "placement": {
          "type": "string",
          "enum": ["inline", "viewport-bottom"]
        },
        "actions": {
          "type": "array"
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Do not use for transient feedback — use Toast instead.",
        "Critical errors that require action should remain visible until resolved."
      ],
      "accessibility": [
        "Uses alert or status role depending on tone.",
        "Dismiss control is keyboard operable when present."
      ]
    },
    {
      "name": "Breadcrumbs",
      "intent": "Show the current location within a hierarchy.",
      "properties": {
        "label": {
          "type": "string"
        },
        "items": {
          "type": "array",
          "required": true
        },
        "collapse": {
          "type": "boolean"
        },
        "collapseAfter": {
          "type": "number"
        }
      },
      "events": ["smart:breadcrumbs"],
      "children": "none",
      "constraints": [
        "Keep labels concise.",
        "The current page must not be a link."
      ],
      "accessibility": [
        "Uses a nav element with an ordered list.",
        "Current page is marked with aria-current=\"page\"."
      ]
    },
    {
      "name": "Pagination",
      "intent": "Move between result pages without losing context.",
      "properties": {
        "label": {
          "type": "string"
        },
        "pages": {
          "type": "array",
          "required": true
        },
        "summary": {
          "type": "string"
        },
        "pageSize": {
          "type": "number"
        },
        "pageSizes": {
          "type": "array"
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Prefer real links for server-rendered pagination.",
        "Keep the current page obvious."
      ],
      "accessibility": [
        "Uses nav semantics and aria-current=\"page\" on the active page link."
      ]
    },
    {
      "name": "Table",
      "intent": "Present comparable rows with optional sorting, selection, sticky headers, fixed column widths, expandable details, and totals.",
      "properties": {
        "caption": {
          "type": "string"
        },
        "columns": {
          "type": "array",
          "required": true
        },
        "rows": {
          "type": "array"
        },
        "totals": {
          "type": "object"
        },
        "emptyLabel": {
          "type": "string"
        },
        "compact": {
          "type": "boolean"
        },
        "stickyHeader": {
          "type": "boolean"
        },
        "selectable": {
          "type": "boolean"
        },
        "expandable": {
          "type": "boolean"
        },
        "actions": {
          "type": "array"
        }
      },
      "events": [
        "smart:sort",
        "smart:table-select",
        "smart:table-action"
      ],
      "children": "none",
      "constraints": [
        "Every column needs a readable header.",
        "Use Empty when there are no rows.",
        "Column width accepts a trusted CSS width; expandable row detail accepts text or catalog content."
      ],
      "accessibility": [
        "Uses semantic table, thead, tbody, th scope=\"col\", and caption when provided.",
        "Sort buttons expose aria-sort state."
      ]
    },
    {
      "name": "Empty",
      "intent": "Explain why a region is blank and what to do next.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "message": {
          "type": "string"
        },
        "icon": {
          "type": "string"
        },
        "actionLabel": {
          "type": "string"
        },
        "action": {
          "type": "string"
        },
        "actionVariant": {
          "type": "string",
          "enum": [
            "primary",
            "secondary",
            "quiet",
            "danger"
          ]
        }
      },
      "events": [
        "smart:action"
      ],
      "children": "none",
      "constraints": [
        "Always offer a clear next step when creation is possible."
      ],
      "accessibility": [
        "Heading communicates the empty state in plain language."
      ]
    },
    {
      "name": "Avatar",
      "intent": "Identify a person or entity compactly.",
      "properties": {
        "initials": {
          "type": "string"
        },
        "src": {
          "type": "string"
        },
        "alt": {
          "type": "string"
        },
        "size": {
          "type": "string",
          "enum": [
            "sm",
            "md",
            "lg"
          ]
        },
        "status": {
          "type": "string",
          "enum": ["offline", "online", "away", "busy"]
        },
        "items": {
          "type": "array",
          "description": "When present, Avatar renders a stacked group."
        },
        "maximum": {
          "type": "number"
        },
        "label": {
          "type": "string"
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Image avatars require meaningful alt text.",
        "Use initials when no image is available."
      ],
      "accessibility": [
        "Image avatars use alt text.",
        "Initials-only avatars should be decorative unless paired with visible text nearby."
      ]
    },
    {
      "name": "List",
      "intent": "Show structured rows such as activity or settings entries.",
      "properties": {
        "items": {
          "type": "array",
          "required": true
        },
        "variant": {
          "type": "string",
          "enum": ["standard", "sortable", "timeline", "key-value", "notifications", "attachments"]
        },
        "label": {
          "type": "string"
        },
        "emptyLabel": {
          "type": "string"
        },
        "orderName": {
          "type": "string"
        }
      },
      "events": [
        "smart:attachment-remove",
        "smart:reorder"
      ],
      "children": "none",
      "constraints": [
        "Each item should have a primary title.",
        "Use meta text for timestamps or secondary facts.",
        "Sortable items require stable id or value properties and use a dedicated left-side handle."
      ],
      "accessibility": [
        "Uses an unordered list with readable row content; sortable lists use an ordered list.",
        "Sortable handles support pointer input plus Space or Enter to lift/drop, arrow keys to move, and Escape to cancel."
      ]
    },
    {
      "name": "Chip",
      "intent": "Represent a compact filter or removable token.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "tone": {
          "type": "string",
          "enum": [
            "default",
            "accent"
          ]
        },
        "removable": {
          "type": "boolean"
        }
      },
      "events": [
        "smart:chip-remove"
      ],
      "children": "none",
      "constraints": [
        "Do not use chips as primary actions.",
        "Removal must not be the only way to understand the filter state."
      ],
      "accessibility": [
        "Remove buttons include an accessible label."
      ]
    },
    {
      "name": "PageHeader",
      "intent": "Introduce a page with title, description, and actions.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "description": {
          "type": "string"
        },
        "actions": {
          "type": "array"
        }
      },
      "events": [
        "smart:action"
      ],
      "children": "Button components through actions",
      "constraints": [
        "Use one primary action in the header action cluster."
      ],
      "accessibility": [
        "Page title is rendered as an h1."
      ]
    },
    {
      "name": "AppShell",
      "intent": "Provide flat or nested, collapsible sidebar navigation and a main content region.",
      "properties": {
        "brand": {
          "type": "string"
        },
        "navigationLabel": {
          "type": "string"
        },
        "items": {
          "type": "array",
          "required": true
        },
        "collapsed": {
          "type": "boolean"
        }
      },
      "events": [],
      "children": "any catalog component or layout primitive",
      "constraints": [
        "Keep navigation labels short and distinct.",
        "Mark the current section with aria-current=\"page\".",
        "Give leaf items an icon when the desktop shell can collapse to its icon rail.",
        "An item with an items array renders as a native disclosure group and may set open."
      ],
      "accessibility": [
        "Sidebar uses nav semantics.",
        "Current page link exposes aria-current=\"page\"."
      ]
    },
    {
      "name": "FormErrors",
      "intent": "Summarize form-level validation issues with jump links.",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "items": {
          "type": "array",
          "required": true
        }
      },
      "events": [],
      "children": "none",
      "constraints": [
        "Use alongside field-level errors.",
        "Each summary item should point to a fixable control when possible."
      ],
      "accessibility": [
        "Uses role=\"alert\".",
        "Summary links target associated fields."
      ]
    },
    {
      "name": "Stepper",
      "intent": "Guide a user through a short, ordered sequence of steps.",
      "properties": {
        "steps": {
          "type": "array",
          "required": true
        },
        "step": {
          "type": "number"
        },
        "finishLabel": {
          "type": "string"
        },
        "continueLabel": {
          "type": "string"
        },
        "orientation": {
          "type": "string",
          "enum": ["horizontal", "vertical"]
        }
      },
      "events": [
        "smart:step"
      ],
      "children": "none; content is supplied through steps",
      "constraints": [
        "Keep the sequence short.",
        "Each step requires a label.",
        "Do not hide information required to complete the current step."
      ],
      "accessibility": [
        "The current step is marked with aria-current=\"step\".",
        "Completed steps are indicated in text, not color alone."
      ]
    },
    {
      "name": "AppHeader",
      "intent": "Provide horizontal application or site navigation with brand, links, and actions.",
      "properties": { "brand": { "type": "string", "required": true }, "brandHref": { "type": "string" }, "navigationLabel": { "type": "string" }, "items": { "type": "array", "required": true }, "actions": { "type": "array" }, "sticky": { "type": "boolean" } },
      "events": ["smart:action"], "children": "none", "constraints": ["Keep top-level navigation concise."], "accessibility": ["Uses header and labeled nav semantics with aria-current."]
    },
    {
      "name": "Footer",
      "intent": "Close a page or application with supporting navigation and legal text.",
      "properties": { "text": { "type": "string" }, "links": { "type": "array" }, "label": { "type": "string" } },
      "events": [], "children": "none", "constraints": ["Keep primary actions out of the footer."], "accessibility": ["Uses footer and labeled nav semantics."]
    },
    {
      "name": "CommandPalette",
      "intent": "Find and invoke application commands through a query-driven modal surface.",
      "properties": { "label": { "type": "string", "required": true }, "placeholder": { "type": "string" }, "items": { "type": "array", "required": true }, "open": { "type": "boolean" }, "shortcut": { "type": "string" } },
      "events": ["smart:action"], "children": "none", "constraints": ["Commands emit action identifiers and never execute supplied code."], "accessibility": ["Uses a modal dialog, search input, and listbox command results."]
    },
    {
      "name": "Payment",
      "intent": "Reserve a checkout slot the host application mounts a real payment element into (for example via smartwebui-stripe.js).",
      "properties": {
        "label": {
          "type": "string"
        },
        "amount": {
          "type": "number"
        },
        "currency": {
          "type": "string",
          "pattern": "^[A-Za-z]{3}$"
        },
        "description": {
          "type": "string"
        }
      },
      "events": [
        "smart:payment-ready (dispatched by the host-mounted element)"
      ],
      "children": "none",
      "constraints": [
        "Never include publishable keys, client secrets, card data, or prices requiring server trust — the host owns those.",
        "Amount and currency are display-only; the charge itself is defined server-side."
      ],
      "accessibility": [
        "The mount slot has a stable id derived from the node id.",
        "The container reports aria-busy while the payment element loads."
      ]
    },
    {
      "name": "Chart",
      "intent": "Plot bounded numeric series as sparklines, bars, stacked bars, lines, areas, dual-axis combinations, scatter plots, donuts, or operational gauges using design tokens.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "mark": {
          "type": "string",
          "enum": ["sparkline", "bar", "stacked-bar", "line", "area", "combo", "scatter", "donut", "gauge"]
        },
        "hole": {
          "type": "number",
          "minimum": 0,
          "maximum": 0.85
        },
        "x": {
          "type": "string"
        },
        "y": {
          "type": "string"
        },
        "y2": {
          "type": "string"
        },
        "unit": {
          "type": "string"
        },
        "unit2": {
          "type": "string"
        },
        "summary": {
          "type": "string"
        },
        "tone": {
          "type": "string",
          "enum": ["accent", "success", "warning", "danger", "secondary"]
        },
        "valueLabel": {
          "type": "string"
        },
        "selected": {
          "type": "number"
        },
        "labels": {
          "type": "array"
        },
        "values": {
          "type": "array"
        },
        "points": {
          "type": "array"
        },
        "series": {
          "type": "array"
        },
        "min": {
          "type": "number"
        },
        "max": {
          "type": "number"
        },
        "target": {
          "type": "number"
        },
        "thresholds": {
          "type": "array"
        }
      },
      "events": [
        "smart:chart-select"
      ],
      "children": "none",
      "constraints": [
        "Do not supply SVG, CSS, hex colors, or path data.",
        "Series tones are accent, success, warning, danger, or secondary only.",
        "At most 8 series and 48 finite numeric points per series.",
        "Combo series declare bar, line, or area marks and left or right axes; only combo uses the right axis.",
        "Scatter points use finite numeric x and y values. Gauge thresholds are ordered and clamped between min and max.",
        "Gauge communicates an operational range, thresholds, and an optional target; Progress ring remains the completion indicator.",
        "Donut accepts an optional numeric hole between 0 and 0.85 of the radius; hole 0 renders a filled pie without center labels.",
        "Sparkline composes inside Card."
      ],
      "accessibility": [
        "Renders as a figure with a caption, an SVG plot, an optional explicit summary, machine-readable axis metadata, and a visually hidden data table.",
        "Keyboard selection on Cartesian charts announces the chosen point; donut segments announce their selected series.",
        "Axis names and units remain available in table headings, including the secondary axis."
      ]
    },
    {
      "name": "Carousel",
      "intent": "Browse media or composed slides in a snap-scrolling strip, a gallery grid, or an opened zoomable lightbox viewer.",
      "properties": {
        "label": {
          "type": "string",
          "required": true
        },
        "items": {
          "type": "array",
          "required": true
        },
        "variant": {
          "type": "string",
          "enum": ["carousel", "gallery"]
        },
        "ratio": {
          "type": "string",
          "enum": ["1x1", "4x3", "16x9", "21x9"]
        },
        "selected": {
          "type": "number"
        },
        "autoplay": {
          "type": "boolean"
        },
        "interval": {
          "type": "number",
          "description": "Autoplay delay in milliseconds (4000-30000, default 5000)."
        },
        "loop": {
          "type": "boolean"
        },
        "lightbox": {
          "type": "boolean"
        }
      },
      "events": [
        "smart:slide"
      ],
      "children": "none; slides are supplied through items",
      "constraints": [
        "Each item accepts src and alt for media slides, caption for a visible caption, and content for nested catalog nodes rendered over the media.",
        "Image URLs follow the surface src policy: http, https, or same-origin relative paths only.",
        "At most 24 items.",
        "Autoplay pauses on hover, focus, and while offscreen; it never starts under prefers-reduced-motion.",
        "Use lightbox to let any slide open the fullscreen viewer with zoom; keep essential actions outside the viewer."
      ],
      "accessibility": [
        "The region exposes aria-roledescription=\"carousel\" with the supplied label; slides announce \"N of M\".",
        "Arrow keys, Home and End move between slides; dot buttons jump directly.",
        "Slide changes are announced through a polite live region.",
        "The lightbox is a native dialog with a visible close control; zoom state is exposed on the toggle and reset on navigation."
      ]
    },
    {
      "name": "RichText",
      "intent": "Collect formatted text through a bounded, sanitized editor.",
      "properties": {
        "label": { "type": "string", "required": true },
        "name": { "type": "string" },
        "value": { "type": "string", "description": "Plain initial text only; declarative surfaces never accept HTML." },
        "placeholder": { "type": "string" },
        "toolbar": { "type": "array", "description": "Commands are limited to bold, italic, underline, bulletList, numberedList, and link." },
        "required": { "type": "boolean" },
        "requiredMessage": { "type": "string" },
        "disabled": { "type": "boolean" }
      },
      "events": ["smart:change"],
      "children": "none",
      "constraints": ["HTML, markup, arbitrary commands, and allowlists are rejected.", "Initial value is inserted as text, never interpreted as markup."],
      "accessibility": ["The editor is labeled and exposes multiline textbox semantics.", "Toolbar controls have names and disabled state is propagated."]
    },
    {
      "name": "Board",
      "intent": "Arrange bounded work items in labeled workflow columns.",
      "properties": {
        "label": { "type": "string", "required": true },
        "columns": { "type": "array", "required": true, "description": "Columns accept id, label, and items; items accept id, title, meta, and description." },
        "compact": { "type": "boolean" },
        "disabled": { "type": "boolean" }
      },
      "events": ["smart:board", "smart:move"],
      "children": "none; columns and cards are supplied through columns",
      "constraints": ["At most 12 columns and 100 cards total.", "Identifiers are reduced to safe data-attribute identifiers; all visible values render as text."],
      "accessibility": ["Columns and cards expose list semantics.", "Keyboard movement and announcements are provided by the core runtime."]
    },
    {
      "name": "Conversation",
      "intent": "Present a bounded message history with optional user-authored reply composer.",
      "properties": {
        "label": { "type": "string", "required": true },
        "title": { "type": "string" },
        "presence": { "type": "string", "enum": ["offline", "online", "away", "busy"] },
        "messages": { "type": "array", "required": true, "description": "Messages accept author, text, time, and own." },
        "live": { "type": "string", "enum": ["off", "polite"] },
        "composer": { "type": "object", "description": "Optional safe composer configuration: label, name, placeholder, sendLabel, attachments, and disabled." }
      },
      "events": ["smart:send"],
      "children": "none",
      "constraints": ["Message content is plain text only.", "The composer never accepts a pre-authored value from an agent."],
      "accessibility": ["Messages use log semantics and configurable polite announcements.", "Own-message alignment is not the sole author indicator."]
    },
    {
      "name": "Composer",
      "intent": "Collect a user-authored message and emit it explicitly.",
      "properties": {
        "label": { "type": "string", "required": true },
        "name": { "type": "string" },
        "placeholder": { "type": "string" },
        "sendLabel": { "type": "string" },
        "attachments": { "type": "boolean" },
        "disabled": { "type": "boolean" }
      },
      "events": ["smart:send"],
      "children": "none",
      "constraints": ["Declarative agents cannot prefill or submit user-authored content.", "Attachment selection remains an explicit user action."],
      "accessibility": ["The textarea has a programmatic label.", "Enter sends and Shift+Enter inserts a new line after hydration."]
    },
    {
      "name": "Map",
      "intent": "Show a bounded map viewport and declarative locations without exposing credentials.",
      "properties": {
        "label": { "type": "string", "required": true },
        "latitude": { "type": "number" },
        "longitude": { "type": "number" },
        "latitudeDelta": { "type": "number" },
        "longitudeDelta": { "type": "number" },
        "mapType": { "type": "string", "enum": ["standard", "mutedStandard", "satellite", "hybrid"] },
        "locations": { "type": "array" },
        "state": { "type": "string", "enum": ["default", "loading", "error"] },
        "status": { "type": "string" },
        "compact": { "type": "boolean" }
      },
      "events": ["smart:map-ready", "smart:map-error"],
      "children": "none",
      "constraints": ["Tokens, credentials, callbacks, arbitrary scripts, and map providers are never accepted.", "At most 100 locations; titles and subtitles render as text."],
      "accessibility": ["The figure is named and locations remain available as text outside the canvas.", "Loading and error status are announced."]
    },
    {
      "name": "DataGrid",
      "intent": "Present a bounded, semantic data view that an application may progressively enhance.",
      "properties": {
        "label": { "type": "string", "required": true },
        "columns": { "type": "array", "required": true, "description": "Columns accept key, label, and sortable." },
        "rows": { "type": "array", "description": "Rows accept id, cells, and selected; cells map column keys to text." },
        "state": { "type": "string", "enum": ["default", "loading", "empty", "error"] },
        "message": { "type": "string" },
        "page": { "type": "number" },
        "pageSize": { "type": "number" },
        "total": { "type": "number" },
        "selectable": { "type": "boolean" },
        "sortable": { "type": "boolean" },
        "compact": { "type": "boolean" }
      },
      "events": ["smart:grid-query", "smart:grid-column", "smart:grid-edit", "smart:grid-selection", "smart:grid-export"],
      "children": "none",
      "constraints": ["Network endpoints, callbacks, export commands, and persistence keys are application-owned and never accepted.", "At most 24 columns and 200 rows; cell content renders as text."],
      "accessibility": ["Server-rendered fallback is a captioned native table.", "Loading, empty, and error rows remain announced table content."]
    },
    {
      "name": "MediaPlayer",
      "intent": "Play trusted URL-based audio or video with a semantic native fallback.",
      "properties": {
        "label": { "type": "string", "required": true },
        "mediaType": { "type": "string", "enum": ["video", "audio", "playlist"] },
        "title": { "type": "string" },
        "artist": { "type": "string" },
        "src": { "type": "string" },
        "poster": { "type": "string" },
        "captions": { "type": "string" },
        "tracks": { "type": "array" },
        "state": { "type": "string", "enum": ["default", "loading", "playing", "paused", "error"] },
        "status": { "type": "string" }
      },
      "events": ["smart:media-state", "smart:media-track", "smart:media-error"],
      "children": "none",
      "constraints": ["Media, poster, and caption URLs must be http, https, or relative paths.", "Credentials, DRM configuration, inline media data, autoplay, and callbacks are never accepted."],
      "accessibility": ["Native controls remain available before companion hydration.", "Video captions may be supplied as a safe URL and playlist tracks are named buttons."]
    },
    {
      "name": "SignaturePad",
      "intent": "Collect explicit user signing intent without allowing an agent to manufacture a signature.",
      "properties": {
        "label": { "type": "string", "required": true },
        "description": { "type": "string" },
        "name": { "type": "string" },
        "mode": { "type": "string", "enum": ["draw", "type"] },
        "required": { "type": "boolean" },
        "requiredMessage": { "type": "string" },
        "hint": { "type": "string" },
        "disabled": { "type": "boolean" },
        "state": { "type": "string", "enum": ["empty", "loading", "error"] }
      },
      "events": ["smart:signature-change", "smart:signature-invalid", "smart:signature-clear"],
      "children": "none",
      "constraints": ["No signature value, strokes, image, typed name, completed state, or submission action is accepted from an agent.", "The hidden signature value always begins empty and can change only through user interaction."],
      "accessibility": ["The signature region is labeled and offers draw and type modes.", "Required and error states are exposed without treating a generated mark as consent."]
    },
    {
      "name": "Tree",
      "intent": "Navigate hierarchical application content with disclosure and selection semantics.",
      "properties": {
        "label": { "type": "string", "required": true },
        "items": { "type": "array", "required": true, "description": "Items accept label, selected, expanded, and nested items." }
      },
      "events": ["smart:tree-select", "smart:tree-toggle"],
      "children": "none; hierarchy is supplied through items",
      "accessibility": ["Renders a WAI-ARIA tree with arrow-key, Home, and End navigation.", "Expanded branches expose aria-expanded and retain their tree level."]
    },
    {
      "name": "ContextMenu",
      "intent": "Offer a contextual action menu from a visible target using pointer or keyboard invocation.",
      "properties": {
        "label": { "type": "string" },
        "targetLabel": { "type": "string", "required": true },
        "items": { "type": "array", "required": true }
      },
      "children": "none; actions are supplied through items",
      "accessibility": ["Supports the Context Menu key and Shift+F10 as well as a pointer context menu.", "Escape closes the popover and restores focus to the invoking target."]
    },
    {
      "name": "SplitPane",
      "intent": "Divide a professional application workspace into two independently sized panes.",
      "properties": {
        "direction": { "type": "string", "enum": ["horizontal", "vertical"] },
        "value": { "type": "number", "description": "Initial first-pane percentage, constrained to 20–80." },
        "collapseAt": { "type": "number", "description": "Optional container width at which panes stack." }
      },
      "events": ["smart:split-resize"],
      "children": "First child renders in the first pane; remaining children render in the second pane.",
      "accessibility": ["The draggable separator is a keyboard-operable ARIA separator.", "Arrow keys resize by 2%; Shift+Arrow keys resize by 10%; Home and End choose the bounds."]
    },
    {
      "name": "FilterBar",
      "intent": "Compose search, saved views, and removable active filters for data-heavy screens.",
      "properties": {
        "label": { "type": "string" },
        "searchLabel": { "type": "string" },
        "placeholder": { "type": "string" },
        "query": { "type": "string" },
        "views": { "type": "array", "description": "Saved views with label, value, and optional selected flag." },
        "view": { "type": "string" },
        "viewLabel": { "type": "string" },
        "filters": { "type": "array", "description": "Active filters with key, label, and optional value." },
        "addFilterLabel": { "type": "string" },
        "clearLabel": { "type": "string" }
      },
      "events": ["smart:filter-change", "smart:filter-remove", "smart:view-change"],
      "children": "none; filters and views are supplied through properties",
      "accessibility": ["Search uses type=search with a clear affordance.", "Active filters expose removable chips with explicit labels."]
    },
    {
      "name": "NotificationCenter",
      "intent": "Present grouped inbox notifications from a bell trigger with read-state affordances.",
      "properties": {
        "label": { "type": "string" },
        "unreadCount": { "type": "number" },
        "groups": { "type": "array", "description": "Grouped sections with title and notification items." },
        "items": { "type": "array", "description": "Flat notification list when groups are omitted." },
        "footerLabel": { "type": "string" },
        "footerAction": { "type": "string" }
      },
      "events": ["smart:notification-read"],
      "children": "none; notifications are supplied through groups or items",
      "accessibility": ["Trigger exposes unread count.", "Mark-all-read is available in the panel header.", "Grouped sections use headings."]
    }
  ],
  "$defs": {
    "node": {
      "type": "object",
      "required": [
        "type",
        "id"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "Stack",
            "Cluster",
            "Grid",
            "Text",
            "Icon",
            "Button",
            "Field",
            "Choice",
            "Switch",
            "SegmentedControl",
            "Card",
            "Badge",
            "Tabs",
            "Accordion",
            "Dialog",
            "Drawer",
            "PopoverMenu",
            "Tooltip",
            "Toast",
            "Progress",
            "Alert",
            "Breadcrumbs",
            "Pagination",
            "Table",
            "Empty",
            "Avatar",
            "List",
            "Chip",
            "PageHeader",
            "AppShell",
            "AppHeader",
            "Footer",
            "CommandPalette",
            "FormErrors",
            "Stepper",
            "Payment",
            "Chart",
            "Carousel",
            "Tree",
            "ContextMenu",
            "SplitPane",
            "FilterBar",
            "NotificationCenter"
          ]
        },
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z][A-Za-z0-9._:-]{0,127}$"
        },
        "hidden": {
          "type": "boolean",
          "description": "Universal property. When true, the rendered node carries the hidden attribute."
        },
        "children": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/node"
          }
        }
      },
      "additionalProperties": true
    }
  }
}
