{"$schema":"aihio-design-system","version":"2.3.0","intents":["action","primary-action","secondary-action","destructive-action","toggle-state","navigation","selection","form-field","text-entry","status","informational","alert","error","container","surface","layout","header","footer","overlay","modal","menu","identity","labeling","metadata","tabs","tabular-data","dismissible"],"components":[{"$component":"aihio-alert","version":"1.1.0","intents":["alert","informational","status"],"attributes":{"variant":{"type":"enum","values":["default","success","warning","destructive"],"default":"default"}},"slots":{"default":{},"title":{},"description":{}},"composition":{"allowedSlots":["default","title","description"],"forbiddenChildren":["aihio-button","aihio-toggle","aihio-switch","aihio-input","aihio-combobox","aihio-dropdown","aihio-dialog"]},"a11yContract":{"handled":["role=\"alert\" (assertive) is set for variant=\"destructive\"; other variants get role=\"status\" (polite) so a confirmation does not interrupt the screen reader","An author-provided role is always left alone"],"required":[{"when":"variant=\"destructive\"","requirement":"Include slot=\"title\" or slot=\"description\" so assistive tech has content to announce. Colour alone is not a sufficient signal.","severity":"warn","rule":"alert-announced-content"},{"when":"role is set on the alert to anything but its variant's own (alert for destructive, status otherwise)","requirement":"Remove the role and let the variant set it: role=\"alert\" for destructive, which interrupts the screen reader, and role=\"status\" for the rest, which waits its turn.","severity":"error","rule":"alert-role"}]},"counterExamples":[{"markup":"<aihio-alert variant=\"error\">Oops</aihio-alert>","fix":"<aihio-alert variant=\"destructive\">\n  <div slot=\"title\">Could not save</div>\n</aihio-alert>","rule":"invalid-enum-attribute"},{"markup":"<aihio-alert><aihio-button>OK</aihio-button></aihio-alert>","fix":"<aihio-alert>\n  <div slot=\"title\">Your changes were saved</div>\n</aihio-alert>","rule":"forbidden-descendant"},{"markup":"<aihio-alert variant=\"success\" role=\"alert\">Saved</aihio-alert>","fix":"<aihio-alert variant=\"success\">Saved</aihio-alert>","rule":"alert-role"}]},{"$component":"aihio-avatar","version":"1.0.0","intents":["identity","labeling"],"attributes":{"src":{"type":"string"},"alt":{"type":"string"},"fallback":{"type":"string"},"size":{"type":"enum","values":["sm","md","lg"],"default":"md"}},"composition":{"allowedChildren":[]},"a11yContract":{"handled":["Image alt is forwarded from the alt attribute","Falls back to initials derived from alt when the image fails to load"],"required":[{"when":"src is set","requirement":"Provide alt describing the person or entity (e.g. alt=\"Jane Doe\"). Empty alt is only acceptable for purely decorative avatars.","severity":"error","rule":"avatar-alt"},{"when":"src is not set and fallback is empty","requirement":"Provide alt so initials can be derived, or set fallback explicitly.","severity":"error","rule":"avatar-fallback"}]},"counterExamples":[{"markup":"<aihio-avatar src=\"/u.jpg\"></aihio-avatar>","fix":"<aihio-avatar src=\"/u.jpg\" alt=\"Jane Doe\"></aihio-avatar>","rule":"avatar-alt"},{"markup":"<aihio-avatar>JD</aihio-avatar>","fix":"<aihio-avatar fallback=\"JD\" alt=\"Jane Doe\"></aihio-avatar>","rule":"invalid-child"}]},{"$component":"aihio-badge","version":"1.1.0","intents":["status","labeling","metadata"],"attributes":{"variant":{"type":"enum","values":["default","secondary","outline","success","warning","destructive"],"default":"default"}},"slots":{"default":{}},"composition":{"allowedChildren":["#text"],"forbiddenChildren":["aihio-button","aihio-input","aihio-dialog"]},"a11yContract":{"handled":["Badge stays semantically neutral: it does not add button, alert, status, or live-region roles automatically."],"required":[{"when":"badge conveys information not present elsewhere (e.g. unread count)","requirement":"Include the meaning in visible text or an aria-label on the surrounding context. Badges have no inherent role.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-badge><aihio-button>Delete</aihio-button></aihio-badge>","fix":"<aihio-cluster gap=\"tight\">\n  <aihio-badge>Draft</aihio-badge>\n  <aihio-button variant=\"ghost\" size=\"sm\">Delete</aihio-button>\n</aihio-cluster>","rule":"forbidden-descendant"},{"markup":"<aihio-badge variant=\"primary\">New</aihio-badge>","fix":"<aihio-badge variant=\"default\">New</aihio-badge>","rule":"invalid-enum-attribute"}]},{"$component":"aihio-button","version":"1.5.0","intents":["action","primary-action","secondary-action","destructive-action","navigation"],"attributes":{"variant":{"type":"enum","values":["default","secondary","outline","ghost","link","destructive"],"default":"default"},"size":{"type":"enum","values":["sm","md","lg","icon"],"default":"md"},"disabled":{"type":"boolean","default":false},"loading":{"type":"boolean","default":false},"type":{"type":"enum","values":["button","submit","reset"],"default":"button"},"name":{"type":"string"},"value":{"type":"string"},"form":{"type":"string"},"formaction":{"type":"string"},"formmethod":{"type":"enum","values":["get","post","dialog"]},"formenctype":{"type":"enum","values":["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},"formnovalidate":{"type":"boolean"},"formtarget":{"type":"string"},"command":{"type":"string"},"commandfor":{"type":"string"}},"slots":{"default":{}},"events":{"click":{}},"methods":{"click()":{"returns":"void"},"focus()":{"parameters":[{"name":"options","type":"FocusOptions","optional":true}],"returns":"void"},"blur()":{"returns":"void"}},"properties":{"control":{"type":"HTMLButtonElement | HTMLAnchorElement | null","readonly":true},"form":{"type":"HTMLFormElement | null","readonly":true},"type":{"type":"\"button\" | \"submit\" | \"reset\""}},"composition":{"forbiddenChildren":["aihio-button","aihio-input","aihio-dialog","aihio-dropdown"]},"a11yContract":{"handled":["The component renders a real <button> and forwards its attributes to it, so the role, Enter and Space activation, focus behaviour, disabled semantics, and the forced-colours mapping are the platform's rather than an emulation of it","An authored <a> child becomes the control instead of a <button>, so a call to action that goes to another page is a real link: it is announced as one, shows its URL, opens in a new tab, and works before the module loads","aria-* set on the host is mirrored onto that control","A disabled or loading link gets aria-disabled=\"true\", leaves the tab order, and its clicks are cancelled, since a link has no native disabled state","aria-busy reflects loading state, which also disables the control","Form participation is native: the button is a submitter of its owning form, so Enter in a field submits, SubmitEvent.submitter identifies it, and <fieldset disabled> disables it in both directions","type=\"submit\" runs native constraint validation before the form submits"],"required":[{"when":"size=\"icon\" or the button has no visible text","requirement":"Provide aria-label describing the action (e.g. aria-label=\"Close\").","severity":"error","rule":"button-accessible-name"},{"when":"the button submits a form","requirement":"Set type=\"submit\" and either place the button inside its <form> or reference that form with the form attribute.","severity":"warn","rule":"button-form-owner"},{"when":"the button goes to another page from a click handler (onclick sets location, or calls window.open or router.push)","requirement":"Put an <a href> inside aihio-button instead: <aihio-button><a href=\"/pricing\">See pricing</a></aihio-button>. A button that navigates is announced as a button, shows no URL, cannot be opened in a new tab, and does nothing without script.","severity":"error","rule":"button-link-navigation"},{"when":"the button wraps an <a>","requirement":"Give the <a> an href. Without one it is not a link: it has no role, takes no focus, and goes nowhere.","severity":"error","rule":"button-link-href"},{"when":"the button wraps an <a>","requirement":"Leave type, name, value, form*, command, and commandfor off the host. They configure a <button>, so on a link they do nothing.","severity":"warn","rule":"button-link-attributes"}]},"counterExamples":[{"markup":"<aihio-button variant=\"primary\">Save</aihio-button>","fix":"<aihio-button variant=\"default\">Save</aihio-button>","rule":"invalid-enum-attribute"},{"markup":"<aihio-button><aihio-button>Save</aihio-button></aihio-button>","fix":"<aihio-cluster>\n  <aihio-button variant=\"outline\">Cancel</aihio-button>\n  <aihio-button>Save</aihio-button>\n</aihio-cluster>","rule":"forbidden-descendant"},{"markup":"<aihio-button size=\"icon\">&#x2715;</aihio-button>","fix":"<aihio-button size=\"icon\" aria-label=\"Close\">&#x2715;</aihio-button>","rule":"button-accessible-name"},{"markup":"<form>\n  <aihio-input name=\"email\" aria-label=\"Email\"></aihio-input>\n</form>\n<aihio-button type=\"submit\">Sign in</aihio-button>","fix":"<form>\n  <aihio-input name=\"email\" aria-label=\"Email\"></aihio-input>\n  <aihio-button type=\"submit\">Sign in</aihio-button>\n</form>","rule":"button-form-owner"},{"markup":"<aihio-button onclick=\"location.href='/pricing'\">See pricing</aihio-button>","fix":"<aihio-button><a href=\"/pricing\">See pricing</a></aihio-button>","rule":"button-link-navigation"},{"markup":"<aihio-button href=\"/pricing\">See pricing</aihio-button>","fix":"<aihio-button><a href=\"/pricing\">See pricing</a></aihio-button>","rule":"unknown-attribute"}]},{"$component":"aihio-card","version":"1.0.0","intents":["container","surface"],"attributes":{"variant":{"type":"enum","values":["default","outline"],"default":"default"}},"slots":{"default":{}},"composition":{"allowedChildren":["aihio-card-header","aihio-card-content","aihio-card-footer","#flow"]},"a11yContract":{"handled":["Card only adds visual grouping; headings, paragraphs, links, and buttons inside keep their native semantics."],"required":[{"when":"card acts as a link or button (entire surface is clickable, or it has a click handler)","requirement":"Wrap the card in an <a> or attach role=\"button\" + keyboard handlers on the outer element. aihio-card itself has no interactive role.","severity":"warn","rule":"card-click-handler"}]},"counterExamples":[{"markup":"<aihio-card><aihio-card-title>Hi</aihio-card-title></aihio-card>","fix":"<aihio-card>\n  <aihio-card-header>\n    <aihio-card-title>Hi</aihio-card-title>\n  </aihio-card-header>\n</aihio-card>","rule":"invalid-child"},{"markup":"<aihio-card onclick=\"openDetails()\"><p>Open details</p></aihio-card>","fix":"<a href=\"/details\">\n  <aihio-card><p>Open details</p></aihio-card>\n</a>","rule":"card-click-handler"}],"related":[{"$component":"aihio-card-header"},{"$component":"aihio-card-title"},{"$component":"aihio-card-description"},{"$component":"aihio-card-content"},{"$component":"aihio-card-footer"}]},{"$component":"aihio-cluster","version":"1.0.0","intents":["layout","container"],"attributes":{"gap":{"type":"enum","values":["tight","sm","md","lg"],"default":"sm"},"align":{"type":"enum","values":["start","center","end","baseline"],"default":"center"},"justify":{"type":"enum","values":["start","center","end","between"],"default":"start"},"grow":{"type":"boolean","default":false}},"slots":{"default":{}},"composition":{"allowedChildren":["*"]},"a11yContract":{"handled":["Purely presentational: adds no role and no tab stop, so grouped controls keep their own semantics and focus order.","Items wrap rather than overflow, so grouped actions stay reachable at narrow widths."],"required":[{"when":"the cluster is a toolbar of related controls","requirement":"Add role=\"toolbar\" and an aria-label on the cluster; it is presentational by default.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-cluster justify=\"space-between\"><aihio-button>Save</aihio-button></aihio-cluster>","fix":"<aihio-cluster justify=\"between\"><aihio-button>Save</aihio-button></aihio-cluster>","rule":"invalid-enum-attribute"},{"markup":"<aihio-stack><aihio-button>Cancel</aihio-button><aihio-button>Save</aihio-button></aihio-stack>","fix":"<aihio-cluster><aihio-button>Cancel</aihio-button><aihio-button>Save</aihio-button></aihio-cluster>"},{"markup":"<aihio-card-footer>\n  <aihio-cluster justify=\"end\">\n    <aihio-button>Save</aihio-button>\n  </aihio-cluster>\n</aihio-card-footer>","fix":"<aihio-card-footer>\n  <aihio-cluster grow justify=\"end\">\n    <aihio-button>Save</aihio-button>\n  </aihio-cluster>\n</aihio-card-footer>","rule":"cluster-needs-grow"}]},{"$component":"aihio-combobox","version":"1.0.0","intents":["form-field","selection","text-entry"],"attributes":{"value":{"type":"string"},"name":{"type":"string"},"form":{"type":"string"},"placeholder":{"type":"string"},"disabled":{"type":"boolean","default":false},"readonly":{"type":"boolean","default":false},"required":{"type":"boolean","default":false},"error":{"type":"boolean","default":false},"size":{"type":"enum","values":["sm","md","lg"],"default":"md"},"filter":{"type":"enum","values":["contains","starts-with","none"],"default":"contains"},"allow-custom":{"type":"boolean","default":false},"loading":{"type":"boolean","default":false},"empty-text":{"type":"string","default":"No results"},"loading-text":{"type":"string","default":"Loading…"},"results-text":{"type":"string"},"open":{"type":"boolean","default":false}},"events":{"aihio-change":{"detail":{"value":"string","label":"string"},"bubbles":true,"composed":true},"aihio-search":{"detail":{"query":"string"},"bubbles":true,"composed":true},"aihio-open":{"bubbles":true,"composed":true},"aihio-close":{"detail":{"reason":"string"},"bubbles":true,"composed":true}},"methods":{"open()":{"returns":"void"},"close()":{"returns":"void"},"toggle()":{"returns":"void"},"focus()":{"parameters":[{"name":"options","type":"FocusOptions","optional":true}],"returns":"void"},"checkValidity()":{"returns":"boolean"},"reportValidity()":{"returns":"boolean"},"setCustomValidity()":{"parameters":[{"name":"message","type":"string"}],"returns":"void"}},"properties":{"value":{"type":"string"},"defaultValue":{"type":"string"},"selectedOption":{"type":"HTMLElement | null","readonly":true},"control":{"type":"HTMLInputElement | null","readonly":true},"form":{"type":"HTMLFormElement | null","readonly":true},"validity":{"type":"ValidityState | null","readonly":true},"validationMessage":{"type":"string","readonly":true},"willValidate":{"type":"boolean","readonly":true}},"composition":{"allowedChildren":["aihio-option"]},"a11yContract":{"handled":["A real <input role=\"combobox\"> with aria-autocomplete=\"list\", aria-expanded, and aria-controls pointing at the listbox","DOM focus stays in the input; aria-activedescendant follows the highlighted option, in the same tree as the options it names","Options render as role=\"option\" inside role=\"listbox\"; the chosen option has aria-selected=\"true\" and a check mark, disabled options have aria-disabled","ArrowDown/ArrowUp open the list and move through enabled options (wrapping); Alt+ArrowDown opens without moving; Alt+ArrowUp accepts and closes; PageDown/PageUp jump ten","Enter chooses the highlighted option; Escape closes and reverts unconfirmed text, and is left alone when the list is closed so a surrounding dialog still closes","Tab accepts the highlighted option and moves on; leaving the field any other way keeps the previous choice unless the text matches an option exactly","Typing filters case- and accent-insensitively (\"aland\" finds \"Åland\"), ranks prefix matches first, highlights the first match, and sets the matched text in bold","A polite status region announces the result count, the empty message, or the loading message once typing pauses","The toggle button is out of the tab order, carries the field's label, and exposes aria-expanded","Keys pressed while an IME is composing are left to the IME","The chosen value submits through a hidden input under name; required, form reset, <fieldset disabled>, and the form attribute behave natively","The list renders in the top layer, flips above the field when there is more room there, and fits the visual viewport above an on-screen keyboard","Forced colours restate the highlighted and disabled states with system colours; reduced motion removes the open animation and spinner"],"required":[{"when":"the combobox is not inside an aihio-field","requirement":"Label it with aria-labelledby pointing at visible text, or aria-label. Placeholder is not a label, and a wrapping <label> would fold the option text into the field's name.","severity":"error","rule":"combobox-label"},{"when":"two aihio-option children share a value","requirement":"Give every option a distinct value; the value is how the chosen option is found again.","severity":"error","rule":"combobox-option-values"},{"when":"the combobox is inside a <form> and its value should be submitted","requirement":"Set name. A combobox without a name is omitted from FormData.","severity":"warn","rule":"combobox-form-name"}]},"counterExamples":[{"markup":"<aihio-combobox placeholder=\"Country\">\n  <aihio-option value=\"fi\">Finland</aihio-option>\n</aihio-combobox>","fix":"<aihio-field>\n  <label slot=\"label\">Country</label>\n  <aihio-combobox name=\"country\">\n    <aihio-option value=\"fi\">Finland</aihio-option>\n  </aihio-combobox>\n</aihio-field>","rule":"combobox-label"},{"markup":"<label>\n  Country\n  <aihio-combobox>\n    <aihio-option value=\"fi\">Finland</aihio-option>\n  </aihio-combobox>\n</label>","fix":"<aihio-field>\n  <label slot=\"label\">Country</label>\n  <aihio-combobox name=\"country\">\n    <aihio-option value=\"fi\">Finland</aihio-option>\n  </aihio-combobox>\n</aihio-field>","rule":"combobox-label"},{"markup":"<aihio-combobox aria-label=\"Size\">\n  <option value=\"s\">Small</option>\n  <option value=\"m\">Medium</option>\n</aihio-combobox>","fix":"<aihio-combobox aria-label=\"Size\">\n  <aihio-option value=\"s\">Small</aihio-option>\n  <aihio-option value=\"m\">Medium</aihio-option>\n</aihio-combobox>","rule":"invalid-child"},{"markup":"<aihio-combobox aria-label=\"Plan\">\n  <aihio-option>Pro</aihio-option>\n  <aihio-option value=\"Pro\">Pro (annual)</aihio-option>\n</aihio-combobox>","fix":"<aihio-combobox aria-label=\"Plan\">\n  <aihio-option value=\"pro\">Pro</aihio-option>\n  <aihio-option value=\"pro-annual\">Pro (annual)</aihio-option>\n</aihio-combobox>","rule":"combobox-option-values"}],"related":[{"$component":"aihio-option","attributes":{"value":{"type":"string"},"label":{"type":"string"},"disabled":{"type":"boolean","default":false}}}]},{"$component":"aihio-data-grid","version":"1.0.0","intents":["tabular-data"],"attributes":{"row-count":{"type":"number"},"density":{"type":"enum","values":["default","compact"],"default":"default"},"loading":{"type":"boolean","default":false},"sort-ascending-text":{"type":"string","default":"Sorted by {column}, ascending"},"sort-descending-text":{"type":"string","default":"Sorted by {column}, descending"}},"events":{"aihio-range":{"detail":{"start":"number","end":"number"},"bubbles":true,"composed":true},"aihio-sort":{"detail":{"column":"string","direction":"'ascending' | 'descending'"},"bubbles":true,"composed":true}},"methods":{"scrollToRow()":{"parameters":[{"name":"index","type":"number"}],"returns":"void"}},"nativeElements":{"th":{"attributes":{"data-sortable":{"type":"string"},"aria-sort":{"type":"enum","values":["ascending","descending","none","other"]},"data-numeric":{"type":"boolean","default":false}}},"td":{"attributes":{"data-numeric":{"type":"boolean","default":false}}}},"properties":{"table":{"type":"HTMLTableElement | null","readonly":true},"body":{"type":"HTMLTableSectionElement | null","readonly":true},"start":{"type":"number","readonly":true},"end":{"type":"number","readonly":true},"rowCount":{"type":"number"}},"composition":{"requiredChildren":["table"],"allowedChildren":["table"]},"a11yContract":{"handled":["The native <table> becomes a grid with aria-rowcount for every row there is, and each rendered row carries aria-rowindex, so a screen reader says \"row 51,034 of 100,001\" while only the rows in view exist","The grid is one tab stop. Arrow keys move between cells, Page Up and Page Down by the rows in view, Home and End to the ends of a row, and Control+Home and Control+End to the first and last cell","Moving to a row that is not rendered scrolls it into view, waits for your code to render it, and then focuses it","A cell holding one link or button is focused on that control; a cell holding several is entered with Enter or F2 and left with Escape. Every other control in the grid is out of the tab order","Keys pressed in a text field or an open menu inside a cell are left to it","If the row holding focus scrolls away and your code removes it, focus moves to the header of the same column rather than to the page","The header stays in view, and sorts the way aihio-table's does: a button in the <th>, aria-sort on the <th>, and the new order announced in a polite status region","Columns take their widths from <col> elements or the header cells, so they do not change as rows come and go, and each row is one line high","Cells draw their focus ring inside their edge, where the scroll box cannot clip it","loading sets aria-busy on the table while the rows are stale"],"required":[{"when":"row-count is missing or not a whole number","requirement":"Set row-count to how many rows there are in all. The grid asks for rows by number, and sizes its scroll box from the count.","severity":"error","rule":"data-grid-row-count"},{"when":"row-count is above 350,000","requirement":"Narrow the rows with a filter, or show them a page at a time with aihio-table and aihio-pagination. Firefox stops a box's height at 17.9 million pixels, about 389,000 rows of the default height, and rows past it cannot be scrolled to.","severity":"warn","rule":"data-grid-row-limit"},{"when":"the table has no caption, aria-label, or aria-labelledby","requirement":"Name the table: a <caption>, aria-labelledby pointing at the visible heading above it, or aria-label.","severity":"error","rule":"table-accessible-name"},{"when":"the table has no <th> cells","requirement":"Name each column with a <th> in the <thead> row.","severity":"error","rule":"table-header-cells"},{"when":"a sortable header has no column name","requirement":"Give each sortable header a name, data-sortable=\"duration\", so aihio-sort says which column to sort by without depending on the header's wording.","severity":"warn","rule":"table-sort-column-name"},{"when":"people need to find text in the page, print every row, or read the rows in a screen reader's browse mode","requirement":"Use aihio-table with aihio-pagination instead. A grid has only the rows in view in the page, and those are all that find in page, printing, and browse mode reach.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-data-grid>\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","fix":"<aihio-data-grid row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","rule":"data-grid-row-count"},{"markup":"<aihio-data-grid row-count=\"100000\">\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","fix":"<aihio-data-grid row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","rule":"table-accessible-name"},{"markup":"<aihio-table row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-table>","fix":"<aihio-data-grid row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","rule":"unknown-attribute"},{"markup":"<aihio-data-grid row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable>Request</th>\n        <th scope=\"col\" data-sortable>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","fix":"<aihio-data-grid row-count=\"100000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"duration\">Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","rule":"table-sort-column-name"},{"markup":"<aihio-data-grid row-count=\"2000000\">\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody></tbody>\n  </table>\n</aihio-data-grid>","fix":"<aihio-table manual-sort>\n  <table aria-label=\"Requests\">\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"id\" aria-sort=\"ascending\">Request</th>\n        <th scope=\"col\" data-sortable=\"method\">Method</th>\n        <th scope=\"col\" data-sortable=\"path\">Path</th>\n        <th scope=\"col\" data-sortable=\"status\" data-numeric>Status</th>\n        <th scope=\"col\" data-sortable=\"duration\" data-numeric>Duration (ms)</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr><th scope=\"row\">#1</th><td>GET</td><td>/api/items/0</td><td data-numeric>200</td><td data-numeric>12</td></tr>\n    </tbody>\n  </table>\n</aihio-table>\n<aihio-pagination page=\"1\" pages=\"80000\" href=\"/requests?page={page}\" aria-label=\"Request pages\"></aihio-pagination>","rule":"data-grid-row-limit"}]},{"$component":"aihio-dialog","version":"1.2.0","intents":["overlay","modal","dismissible"],"attributes":{"open":{"type":"boolean","default":false}},"events":{"aihio-open":{"bubbles":true,"composed":true},"aihio-close":{"detail":{"reason":"string"},"bubbles":true,"composed":true},"aihio-before-close":{"detail":{"reason":"string"},"cancelable":true,"bubbles":true,"composed":true}},"methods":{"open()":{"returns":"void"},"close()":{"parameters":[{"name":"options","type":"{ restoreFocus?: boolean; reason?: string }","optional":true}],"returns":"void"}},"commands":["--open","--close","--toggle"],"composition":{"allowedChildren":["aihio-dialog-header","aihio-dialog-footer","#flow"]},"a11yContract":{"handled":["The native <dialog> panel supplies dialog semantics, and showModal() supplies modality","aria-labelledby is wired to aihio-dialog-title when present","aria-describedby is wired to aihio-dialog-description when present","Native modal dialog behavior makes the document outside the dialog inert and contains keyboard focus","A trigger and a Cancel button work from markup alone: commandfor plus command=\"--open\" or command=\"--close\" needs no script","Opening focuses an authored autofocus element, otherwise the first rendered tab stop — never a hidden field or an unselected tab — and Tab wraps between the first and last rendered tab stops","Escape closes the dialog","Focus is restored to the trigger on close","A shared overlay lock prevents concurrent dialogs from restoring page scroll too early"],"required":[{"when":"dialog has no aihio-dialog-title","requirement":"Provide aria-label on aihio-dialog describing the dialog's purpose; otherwise screen readers announce an unnamed dialog.","severity":"error","rule":"dialog-accessible-name"},{"when":"dialog confirms a destructive action","requirement":"Label the confirming button with variant=\"destructive\" and keep Cancel as the first focusable control.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-dialog open><p>Delete?</p></aihio-dialog>","fix":"<aihio-dialog id=\"delete-project-dialog\">\n  <aihio-dialog-header>\n    <aihio-dialog-title>Delete project?</aihio-dialog-title>\n  </aihio-dialog-header>\n  <aihio-dialog-footer>\n    <aihio-button commandfor=\"delete-project-dialog\" command=\"--close\" variant=\"outline\">Cancel</aihio-button>\n    <aihio-button variant=\"destructive\">Delete</aihio-button>\n  </aihio-dialog-footer>\n</aihio-dialog>","rule":"dialog-accessible-name"},{"markup":"<aihio-dialog><aihio-dialog><p>Nested</p></aihio-dialog></aihio-dialog>","fix":"<aihio-dialog id=\"first-step\" aria-label=\"Step 1\"><p>Step 1</p></aihio-dialog>\n<aihio-dialog id=\"second-step\" aria-label=\"Step 2\"><p>Step 2</p></aihio-dialog>","rule":"invalid-child"},{"markup":"<aihio-button commandfor=\"confirm\" command=\"show-modal\">Delete</aihio-button>\n<aihio-dialog id=\"confirm\" aria-label=\"Confirm delete\"></aihio-dialog>","fix":"<aihio-button commandfor=\"confirm\" command=\"--open\">Delete</aihio-button>\n<aihio-dialog id=\"confirm\" aria-label=\"Confirm delete\"><p>Delete this project?</p></aihio-dialog>","rule":"invalid-command"}],"related":[{"$component":"aihio-dialog-header"},{"$component":"aihio-dialog-title"},{"$component":"aihio-dialog-description"},{"$component":"aihio-dialog-footer"}]},{"$component":"aihio-dropdown","version":"1.2.0","intents":["menu","overlay","dismissible"],"attributes":{"open":{"type":"boolean","default":false},"align":{"type":"enum","values":["start","end"],"default":"start"}},"slots":{"trigger":{},"default":{}},"events":{"aihio-open":{"bubbles":true,"composed":true},"aihio-close":{"detail":{"reason":"string"},"bubbles":true,"composed":true},"aihio-before-close":{"detail":{"reason":"string"},"cancelable":true,"bubbles":true,"composed":true}},"methods":{"open()":{"parameters":[{"name":"options","type":"{ focus?: \"first\" | \"last\" | null }","optional":true}],"returns":"void"},"close()":{"parameters":[{"name":"options","type":"{ restoreFocus?: boolean; reason?: string }","optional":true}],"returns":"void"},"toggle()":{"parameters":[{"name":"options","type":"{ focus?: \"first\" | \"last\" | null; restoreFocus?: boolean }","optional":true}],"returns":"void"}},"composition":{"requiredSlots":["trigger"],"allowedChildren":["aihio-dropdown-item","aihio-dropdown-separator","aihio-button"]},"a11yContract":{"handled":["aria-haspopup=\"menu\" is set on the trigger","aria-expanded is toggled on the trigger","role=\"menu\" is set on the content panel","role=\"menuitem\" is set on each dropdown item — or, when the item's child is <a href>, on that link, which also takes the focus","Arrow keys move focus between items; Home/End jump to first/last","Typing a character moves to the next item whose label starts with it; a repeated letter cycles through the matches","A link item follows itself natively on Enter (so Ctrl/Cmd+Enter and middle-click open a new tab); Space is mapped to it","Escape closes and restores focus to the trigger","Tab and Shift+Tab close the menu and move on through the page","Click outside closes the menu","The Popover API provides top-layer rendering where supported; the component handles Escape and outside-pointer dismissal so close requests remain cancelable","The menu flips above the trigger and clamps to viewport edges when needed"],"required":[{"when":"the trigger is icon-only","requirement":"Provide aria-label on the trigger (e.g. aria-label=\"Open menu\").","severity":"error","rule":"dropdown-trigger-name"}]},"counterExamples":[{"markup":"<aihio-dropdown>\n  <aihio-dropdown-item>Profile</aihio-dropdown-item>\n</aihio-dropdown>","fix":"<aihio-dropdown>\n  <aihio-button slot=\"trigger\" variant=\"outline\">Account</aihio-button>\n  <aihio-dropdown-item>Profile</aihio-dropdown-item>\n</aihio-dropdown>","rule":"missing-required-slot"},{"markup":"<aihio-dropdown>\n  <aihio-button slot=\"trigger\">Menu</aihio-button>\n  <a href=\"/x\">Link</a>\n</aihio-dropdown>","fix":"<aihio-dropdown>\n  <aihio-button slot=\"trigger\">Menu</aihio-button>\n  <aihio-dropdown-item><a href=\"/x\">Link</a></aihio-dropdown-item>\n</aihio-dropdown>","rule":"invalid-child"}],"related":[{"$component":"aihio-dropdown-item","attributes":{"value":{"type":"string"},"disabled":{"type":"boolean","default":false}}},{"$component":"aihio-dropdown-separator"}]},{"$component":"aihio-field","version":"1.3.0","intents":["form-field","layout","labeling"],"attributes":{"error":{"type":"boolean","default":false}},"slots":{"label":{"accepts":["label","span","#text"]},"default":{"accepts":["aihio-input","aihio-combobox","input","select","textarea"]},"description":{"accepts":["#flow"]},"error":{"accepts":["#flow"]}},"composition":{"allowedSlots":["label","default","description","error"],"allowedChildren":["aihio-input","aihio-combobox","aihio-switch","label","span","p","#flow"],"forbiddenChildren":["aihio-card","aihio-dialog"]},"a11yContract":{"handled":["Generates ids for the label, description, and error, and wires aria-labelledby and aria-describedby on the control","Sets for on native label content and delegates label clicks to the wrapped native control","Reflects the error state onto the control, which sets aria-invalid","Points aria-describedby at the error message while erroring, and at the description otherwise, so only the relevant text is announced","A switch field lays out as a row, the switch beside its label; a native <textarea> or <select> is drawn to match aihio-input"],"required":[{"when":"the field has no slot=\"label\" content","requirement":"Provide slot=\"label\" content, or an aria-label on the control. aihio-field wires a label but cannot invent one.","severity":"error","rule":"field-label"},{"when":"the error attribute is written on the field","requirement":"Write the message in slot=\"error\" and leave the attribute to the field, which sets it from that content. An error attribute with no message marks the field invalid with nothing to announce.","severity":"error","rule":"field-error-message"}]},"counterExamples":[{"markup":"<aihio-field>\n  <aihio-input name=\"email\"></aihio-input>\n</aihio-field>","fix":"<aihio-field>\n  <label slot=\"label\">Email</label>\n  <aihio-input name=\"email\"></aihio-input>\n</aihio-field>","rule":"field-label"},{"markup":"<aihio-field error>\n  <span slot=\"label\">Email</span>\n  <aihio-input name=\"email\"></aihio-input>\n</aihio-field>","fix":"<aihio-field>\n  <span slot=\"label\">Email</span>\n  <aihio-input name=\"email\"></aihio-input>\n  <span slot=\"error\">Enter an email address.</span>\n</aihio-field>","rule":"field-error-message"}]},{"$component":"aihio-grid","version":"1.0.0","intents":["layout","container"],"attributes":{"columns":{"type":"enum","values":["2","3","4"]},"gap":{"type":"enum","values":["tight","sm","md","lg"],"default":"md"}},"slots":{"default":{}},"composition":{"allowedChildren":["*"]},"a11yContract":{"handled":["Purely presentational: adds no role, no landmark, and no tab stop. Items keep source order, so reading and tab order match the visual order."],"required":[{"when":"the grid is a list of like items, such as project cards","requirement":"Give it list semantics when the count matters to the reader: role=\"list\" on aihio-grid and role=\"listitem\" on each item.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-grid columns=\"6\"><aihio-card></aihio-card></aihio-grid>","fix":"<aihio-grid columns=\"3\"><aihio-card></aihio-card></aihio-grid>","rule":"invalid-enum-attribute"},{"markup":"<div style=\"display:grid;grid-template-columns:repeat(3,1fr);gap:16px\">…</div>","fix":"<aihio-grid columns=\"3\">…</aihio-grid>","rule":"hand-rolled-layout"}]},{"$component":"aihio-input","version":"1.2.0","intents":["form-field","text-entry"],"attributes":{"type":{"type":"string","default":"text"},"size":{"type":"enum","values":["sm","md","lg"],"default":"md"},"placeholder":{"type":"string"},"disabled":{"type":"boolean","default":false},"error":{"type":"boolean","default":false},"value":{"type":"string"},"name":{"type":"string"},"required":{"type":"boolean","default":false},"readonly":{"type":"boolean","default":false},"autocomplete":{"type":"string"},"min":{"type":"string"},"max":{"type":"string"},"minlength":{"type":"number"},"maxlength":{"type":"number"},"pattern":{"type":"string"},"step":{"type":"string"},"inputmode":{"type":"string"},"enterkeyhint":{"type":"string"},"autocapitalize":{"type":"string"},"spellcheck":{"type":"boolean"},"multiple":{"type":"boolean"},"accept":{"type":"string"},"capture":{"type":"string"},"list":{"type":"string"},"form":{"type":"string"}},"events":{"aihio-input":{"detail":{"value":"string"}},"aihio-change":{"detail":{"value":"string"}}},"methods":{"checkValidity()":{"returns":"boolean"},"reportValidity()":{"returns":"boolean"},"setCustomValidity()":{"parameters":[{"name":"message","type":"string"}],"returns":"void"},"select()":{"returns":"void"},"focus()":{"parameters":[{"name":"options","type":"FocusOptions","optional":true}],"returns":"void"}},"properties":{"value":{"type":"string"},"defaultValue":{"type":"string"},"control":{"type":"HTMLInputElement | null","readonly":true},"form":{"type":"HTMLFormElement | null","readonly":true},"validity":{"type":"ValidityState | null","readonly":true},"validationMessage":{"type":"string","readonly":true},"willValidate":{"type":"boolean","readonly":true}},"composition":{"allowedChildren":[]},"a11yContract":{"handled":["aria-invalid reflects the error attribute","The inner <input> inherits placeholder, type, disabled state, constraints, and the authored default value","The inner <input> is real light DOM, so name, required, readonly, autocomplete, and form ownership give native submission and validation"],"required":[{"when":"input has no visible <label> associated by for/id","requirement":"Provide aria-label on aihio-input, or wrap in a <label>. Placeholder is not a label.","severity":"error","rule":"input-label"},{"when":"error=true","requirement":"Describe the error via aria-describedby pointing to a visible message; the red border alone is not conveyed to screen readers.","severity":"error","rule":"input-error-description"},{"when":"the field is inside a <form> and its value should be submitted","requirement":"Set name. A field without a name is omitted from FormData entirely.","severity":"warn","rule":"input-form-name"}]},"counterExamples":[{"markup":"<aihio-input placeholder=\"Email\"></aihio-input>","fix":"<aihio-field>\n  <label slot=\"label\">Email</label>\n  <aihio-input type=\"email\" name=\"email\" placeholder=\"you@example.com\"></aihio-input>\n</aihio-field>","rule":"input-label"},{"markup":"<aihio-input error></aihio-input>","fix":"<aihio-field>\n  <label slot=\"label\">Email</label>\n  <aihio-input name=\"email\"></aihio-input>\n  <span slot=\"error\">Enter an email address.</span>\n</aihio-field>","rule":"input-error-description"},{"markup":"<form>\n  <aihio-input aria-label=\"Email\" type=\"email\"></aihio-input>\n</form>","fix":"<form>\n  <aihio-input aria-label=\"Email\" type=\"email\" name=\"email\"></aihio-input>\n</form>","rule":"input-form-name"}]},{"$component":"aihio-pagination","version":"1.0.0","intents":["navigation"],"attributes":{"page":{"type":"number","default":1},"pages":{"type":"number"},"href":{"type":"string"},"previous-text":{"type":"string","default":"Previous"},"next-text":{"type":"string","default":"Next"},"page-text":{"type":"string","default":"Page {page}"}},"events":{"aihio-page":{"detail":{"page":"number"},"cancelable":true,"bubbles":true,"composed":true}},"properties":{"page":{"type":"number"},"pages":{"type":"number"}},"composition":{"allowedChildren":[]},"a11yContract":{"handled":["A navigation landmark, named \"Pagination\" unless you name it with aria-label or aria-labelledby","The current page carries aria-current=\"page\", and is drawn with a border held to 3:1 and a heavier weight, not a colour alone","Each page is named \"Page 4\" (page-text) rather than \"4\", so a list of the page's links still says what each one is","Previous and next carry rel=\"prev\" and rel=\"next\". On the first and last page they stay in place, disabled and out of the tab order, so the row does not shift","Past seven pages the list keeps the first and last page and the pages beside the current one, with gaps hidden from screen readers, so its length never changes as the page does. Where seven do not fit on one line, in a narrow column or on a phone, it keeps the current page alone, five slots rather than seven","In a list with role=\"list\", which Safari otherwise drops once the bullets are removed","A button that changes the page announces the new page in a polite status region, and focus stays on the control that was used, or moves to the current page when that control is now disabled","A click that opens a link in a new tab or window is left to the browser","Drawn that compactly, previous and next show only their arrows; their names do not change","Forced colours: the current page is drawn in Highlight, and disabled controls in GrayText"],"required":[{"when":"pages is missing or not a whole number, or page is outside 1 to pages","requirement":"Set pages to how many pages there are, and page to the current one, counting from 1.","severity":"error","rule":"pagination-pages"},{"when":"href is set without {page}","requirement":"Put {page} where the page number goes in href. Without it every page links to the same address.","severity":"error","rule":"pagination-href"},{"when":"more than one aihio-pagination is on the page","requirement":"Name each one after the list it pages through, aria-label=\"Invoice pages\", so the landmarks can be told apart.","severity":"warn","rule":"pagination-label"}]},"counterExamples":[{"markup":"<aihio-pagination current=\"3\" total=\"12\" href=\"/invoices?page={page}\"></aihio-pagination>","fix":"<aihio-pagination page=\"3\" pages=\"12\" href=\"/invoices?page={page}\"></aihio-pagination>","rule":"unknown-attribute"},{"markup":"<aihio-pagination page=\"3\" pages=\"12\" href=\"/invoices?page=\"></aihio-pagination>","fix":"<aihio-pagination page=\"3\" pages=\"12\" href=\"/invoices?page={page}\"></aihio-pagination>","rule":"pagination-href"},{"markup":"<aihio-pagination page=\"0\" pages=\"12\" href=\"/invoices?page={page}\"></aihio-pagination>","fix":"<aihio-pagination page=\"1\" pages=\"12\" href=\"/invoices?page={page}\"></aihio-pagination>","rule":"pagination-pages"},{"markup":"<aihio-pagination page=\"1\" pages=\"4\" href=\"/invoices?page={page}\"></aihio-pagination>\n<aihio-pagination page=\"2\" pages=\"9\" href=\"/payments?page={page}\"></aihio-pagination>","fix":"<aihio-pagination page=\"1\" pages=\"4\" href=\"/invoices?page={page}\" aria-label=\"Invoice pages\"></aihio-pagination>\n<aihio-pagination page=\"2\" pages=\"9\" href=\"/payments?page={page}\" aria-label=\"Payment pages\"></aihio-pagination>","rule":"pagination-label"}]},{"$component":"aihio-stack","version":"1.0.0","intents":["layout","container"],"attributes":{"gap":{"type":"enum","values":["tight","sm","md","lg"],"default":"md"},"align":{"type":"enum","values":["start","center","end","stretch"],"default":"stretch"}},"slots":{"default":{}},"composition":{"allowedChildren":["*"]},"a11yContract":{"handled":["Purely presentational: adds no role, no landmark, and no tab stop, so the semantics of its children are untouched."],"required":[{"when":"the stack groups a titled region of the page","requirement":"Use a semantic sectioning element (<section>, <nav>, <main>) around or inside the stack. aihio-stack does not create a landmark.","severity":"warn"}]},"counterExamples":[{"markup":"<aihio-stack gap=\"medium\"><p>One</p></aihio-stack>","fix":"<aihio-stack gap=\"md\"><p>One</p></aihio-stack>","rule":"invalid-enum-attribute"},{"markup":"<div style=\"display:flex;flex-direction:column;gap:16px\"><p>One</p></div>","fix":"<aihio-stack gap=\"md\"><p>One</p></aihio-stack>","rule":"hand-rolled-layout"}]},{"$component":"aihio-switch","version":"1.0.0","intents":["form-field","toggle-state"],"attributes":{"checked":{"type":"boolean","default":false},"disabled":{"type":"boolean","default":false},"required":{"type":"boolean","default":false},"name":{"type":"string"},"value":{"type":"string","default":"on"},"form":{"type":"string"}},"events":{"aihio-change":{"detail":{"checked":"boolean"}}},"methods":{"click()":{"returns":"void"},"focus()":{"parameters":[{"name":"options","type":"FocusOptions","optional":true}],"returns":"void"},"checkValidity()":{"returns":"boolean"},"reportValidity()":{"returns":"boolean"},"setCustomValidity()":{"parameters":[{"name":"message","type":"string"}],"returns":"void"}},"properties":{"checked":{"type":"boolean"},"defaultChecked":{"type":"boolean"},"control":{"type":"HTMLInputElement | null","readonly":true},"form":{"type":"HTMLFormElement | null","readonly":true},"validity":{"type":"ValidityState | null","readonly":true},"validationMessage":{"type":"string","readonly":true},"willValidate":{"type":"boolean","readonly":true}},"composition":{"allowedChildren":[]},"a11yContract":{"handled":["The component renders a real <input type=\"checkbox\" role=\"switch\">, so Space toggles it, it is announced as a switch that is on or off, and focus and disabled semantics are native","Form participation is native: it submits name=value when on, resets with its form, and <fieldset disabled> disables it","aria-label, aria-labelledby, aria-describedby, and aria-invalid set on the host are forwarded to the checkbox","Inside aihio-field the label, description, and error are wired to the checkbox, and the field lays out as a row with the switch beside its label","On and off stay distinguishable under forced colours: the track keeps a border and the on state uses Highlight"],"required":[{"when":"the switch has no label","requirement":"Name the switch after the setting it controls: put it in aihio-field with a <label slot=\"label\">, wrap it in a <label>, or give it aria-label. Never label it with its state (\"On\", \"Enabled\").","severity":"error","rule":"switch-label"},{"when":"the switch is inside a <form> and its state should be submitted","requirement":"Set name. A switch without a name is omitted from FormData entirely.","severity":"warn","rule":"switch-form-name"},{"when":"the switch is named by a state word (On, Off, Enabled)","requirement":"Name the switch after the setting it controls. The switch announces its own state, so a switch named \"On\" is read as \"On, switch, on\" and never says what is on.","severity":"warn","rule":"switch-state-name"}]},"counterExamples":[{"markup":"<span>Email notifications</span>\n<aihio-switch name=\"email-notifications\"></aihio-switch>","fix":"<aihio-field>\n  <label slot=\"label\">Email notifications</label>\n  <aihio-switch name=\"email-notifications\"></aihio-switch>\n</aihio-field>","rule":"switch-label"},{"markup":"<aihio-switch aria-label=\"On\" checked></aihio-switch>","fix":"<aihio-switch aria-label=\"Email notifications\" checked></aihio-switch>","rule":"switch-state-name"},{"markup":"<aihio-switch checked=\"false\" aria-label=\"Weekly summary\"></aihio-switch>","fix":"<aihio-switch aria-label=\"Weekly summary\"></aihio-switch>","rule":"boolean-attribute-value"}]},{"$component":"aihio-table","version":"1.0.0","intents":["tabular-data"],"attributes":{"density":{"type":"enum","values":["default","compact"],"default":"default"},"sticky-header":{"type":"boolean","default":false},"manual-sort":{"type":"boolean","default":false},"loading":{"type":"boolean","default":false},"sort-ascending-text":{"type":"string","default":"Sorted by {column}, ascending"},"sort-descending-text":{"type":"string","default":"Sorted by {column}, descending"}},"events":{"aihio-sort":{"detail":{"column":"string","direction":"'ascending' | 'descending'"},"bubbles":true,"composed":true}},"methods":{"sort()":{"parameters":[{"name":"column","type":"string"},{"name":"direction","type":"'ascending' | 'descending'","optional":true}],"returns":"void"}},"nativeElements":{"th":{"attributes":{"data-sortable":{"type":"string"},"aria-sort":{"type":"enum","values":["ascending","descending","none","other"]},"data-numeric":{"type":"boolean","default":false},"data-sort-value":{"type":"string"}}},"td":{"attributes":{"data-sort-value":{"type":"string"},"data-numeric":{"type":"boolean","default":false}}}},"properties":{"table":{"type":"HTMLTableElement | null","readonly":true}},"composition":{"requiredChildren":["table"],"allowedChildren":["table"]},"a11yContract":{"handled":["It stays a native <table>: rows, columns, and header cells keep the platform's table semantics, so screen reader table navigation names the column and row of every cell, and nothing is re-created with ARIA roles","A sortable header's content moves into a <button> inside the <th>, which carries aria-sort, so the header keeps its role and the button sorts; the new order is announced in a polite status region","The first activation sorts ascending and the next descending; rows with no value go last either way, ties keep their order, a column of figures compares as numbers (read the way the page's language writes them), and text compares in the page's language with digits in numeric order (\"Item 2\" before \"Item 10\")","Rows are moved with moveBefore() where the browser has it, so focus and state inside a row survive a sort; elsewhere focus is put back after the move","A table wider than its box (or, with sticky-header, taller) scrolls inside it, and the box becomes a tab stop and a region named by the caption, so the keyboard can scroll it and a screen reader says what it is; when it fits again it is neither","The sort button's focus ring is drawn inside the header cell, so the scroll box never clips it","loading sets aria-busy on the table while the rows are stale","Sorting is by a button, not a click handler on the cell, and the sort indicator changes shape, not only colour; under forced colours the sort buttons are drawn as system buttons"],"required":[{"when":"the table has no caption, aria-label, or aria-labelledby","requirement":"Name the table: a <caption> as its first child, aria-labelledby pointing at the visible heading above it, or aria-label. An unnamed table is announced only as \"table\", and so is the scroll box it gets on a narrow screen.","severity":"error","rule":"table-accessible-name"},{"when":"the table has no <th> cells","requirement":"Name each column with a <th> in a <thead> row, and each row with <th scope=\"row\"> where one cell identifies it. Bold text in an ordinary cell is not a header.","severity":"error","rule":"table-header-cells"},{"when":"a row, header, or cell responds to clicks and holds no link or button","requirement":"Sort with data-sortable on the <th>, and put a link or button in the row for anything else, such as a link in the cell that names it. A click handler on a row or cell is reachable by pointer only.","severity":"error","rule":"table-click-handler"},{"when":"manual-sort is set and a sortable header has no column name","requirement":"Give each sortable header a name, data-sortable=\"amount\", so aihio-sort says which column to sort by without depending on the header's wording.","severity":"warn","rule":"table-sort-column-name"}]},"counterExamples":[{"markup":"<aihio-table>\n  <table>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n        <th scope=\"col\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n        <th scope=\"col\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"table-accessible-name"},{"markup":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <tr>\n      <td><strong>Invoice</strong></td>\n      <td><strong>Amount</strong></td>\n    </tr>\n    <tr>\n      <td>INV-1042</td>\n      <td>€1,250.00</td>\n    </tr>\n  </table>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n        <th scope=\"col\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"table-header-cells"},{"markup":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\" onclick=\"sortBy('invoice')\">Invoice</th>\n        <th scope=\"col\" onclick=\"sortBy('amount')\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable=\"invoice\">Invoice</th>\n        <th scope=\"col\" data-sortable=\"amount\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"table-click-handler"},{"markup":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n        <th scope=\"col\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr onclick=\"location.href = '/invoices/1042'\">\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n        <th scope=\"col\" data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\"><a href=\"/invoices/1042\">INV-1042</a></th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"table-click-handler"},{"markup":"<aihio-table>\n  <aihio-table-header>\n    <aihio-table-row>\n      <aihio-table-head>Invoice</aihio-table-head>\n    </aihio-table-row>\n  </aihio-table-header>\n  <aihio-table-body>\n    <aihio-table-row>\n      <aihio-table-cell>INV-1042</aihio-table-cell>\n    </aihio-table-row>\n  </aihio-table-body>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\">Invoice</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>INV-1042</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"unknown-component"},{"markup":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\" sortable>Invoice</th>\n        <th scope=\"col\" sortable data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","fix":"<aihio-table>\n  <table>\n    <caption>Invoices</caption>\n    <thead>\n      <tr>\n        <th scope=\"col\" data-sortable>Invoice</th>\n        <th scope=\"col\" data-sortable data-numeric>Amount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <th scope=\"row\">INV-1042</th>\n        <td data-numeric>€1,250.00</td>\n      </tr>\n    </tbody>\n  </table>\n</aihio-table>","rule":"table-sortable-header"}]},{"$component":"aihio-tabs","version":"1.1.0","intents":["tabs","navigation","layout"],"attributes":{"value":{"type":"string"}},"composition":{"requiredChildren":["aihio-tab-list","aihio-tab-panel"],"allowedChildren":["aihio-tab-list","aihio-tab-panel"]},"a11yContract":{"handled":["role=\"tablist\" on aihio-tab-list","role=\"tab\" on aihio-tab with aria-selected and tabindex roving","role=\"tabpanel\" on aihio-tab-panel with aria-labelledby wired to its tab","Arrow keys, Home, End move between tabs","Panels are hidden when inactive"],"required":[{"when":"every aihio-tab and aihio-tab-panel","requirement":"Match each tab's value to exactly one panel's value. Mismatched values leave panels orphaned.","severity":"error","rule":"tabs-value-pairs"}]},"counterExamples":[{"markup":"<aihio-tabs>\n  <aihio-tab value=\"a\">A</aihio-tab>\n  <aihio-tab-panel value=\"a\">Content</aihio-tab-panel>\n</aihio-tabs>","fix":"<aihio-tabs value=\"a\">\n  <aihio-tab-list>\n    <aihio-tab value=\"a\">A</aihio-tab>\n  </aihio-tab-list>\n  <aihio-tab-panel value=\"a\">Content</aihio-tab-panel>\n</aihio-tabs>","rule":"missing-required-child"},{"markup":"<aihio-tabs value=\"a\">\n  <aihio-tab-list>\n    <aihio-tab value=\"a\">A</aihio-tab>\n  </aihio-tab-list>\n  <aihio-tab-panel value=\"b\">Content</aihio-tab-panel>\n</aihio-tabs>","fix":"<aihio-tabs value=\"a\">\n  <aihio-tab-list>\n    <aihio-tab value=\"a\">A</aihio-tab>\n  </aihio-tab-list>\n  <aihio-tab-panel value=\"a\">Content</aihio-tab-panel>\n</aihio-tabs>","rule":"tabs-value-pairs"}],"related":[{"$component":"aihio-tab-list"},{"$component":"aihio-tab","attributes":{"value":{"type":"string"},"disabled":{"type":"boolean","default":false}}},{"$component":"aihio-tab-panel","attributes":{"value":{"type":"string"}}}]},{"$component":"aihio-toggle","version":"2.0.0","intents":["toggle-state","action"],"attributes":{"pressed":{"type":"boolean","default":false},"disabled":{"type":"boolean","default":false},"variant":{"type":"enum","values":["default","outline"],"default":"default"},"size":{"type":"enum","values":["sm","md","lg"],"default":"md"}},"slots":{"default":{}},"events":{"aihio-toggle":{"detail":{"pressed":"boolean"}}},"methods":{"click()":{"returns":"void"},"focus()":{"parameters":[{"name":"options","type":"FocusOptions","optional":true}],"returns":"void"},"blur()":{"returns":"void"}},"properties":{"control":{"type":"HTMLButtonElement | null","readonly":true},"pressed":{"type":"boolean"}},"composition":{"forbiddenChildren":["aihio-button","aihio-toggle","aihio-input"]},"a11yContract":{"handled":["The component renders a real <button> with aria-pressed, so the role, Enter and Space activation, focus behaviour, disabled semantics, and the forced-colours mapping are the platform's rather than an emulation of them","aria-pressed on that control reflects the pressed attribute","aria-* set on the host is mirrored onto the control","The control is always type=\"button\", so a toggle inside a form never submits it"],"required":[{"when":"toggle has no visible text (icon-only)","requirement":"Provide aria-label describing what the toggle controls (e.g. aria-label=\"Bold\").","severity":"error","rule":"toggle-accessible-name"},{"when":"the toggle turns a setting on or off, or is named by a state word (On, Enabled)","requirement":"Name the toggle after what it controls (e.g. \"Bold\"), never after its state: a toggle labelled \"Enabled\" is announced as \"Enabled, toggle button, pressed\" and never says what is enabled. For an on/off setting in a form, use aihio-switch.","severity":"warn","rule":"toggle-state-name"}]},"counterExamples":[{"markup":"<aihio-toggle><aihio-button>B</aihio-button></aihio-toggle>","fix":"<aihio-toggle aria-label=\"Bold\">B</aihio-toggle>","rule":"forbidden-descendant"},{"markup":"<aihio-toggle pressed=\"false\">Bold</aihio-toggle>","fix":"<aihio-toggle>Bold</aihio-toggle>","rule":"boolean-attribute-value"},{"markup":"<span>Email notifications</span>\n<aihio-toggle pressed>Enabled</aihio-toggle>","fix":"<aihio-field>\n  <label slot=\"label\">Email notifications</label>\n  <aihio-switch name=\"email-notifications\" checked></aihio-switch>\n</aihio-field>","rule":"toggle-state-name"}]}],"patterns":[{"id":"auth-form","name":"Auth Form","intents":["surface","layout","form-field","text-entry","primary-action","secondary-action"],"requiredComponents":["aihio-card","aihio-field","aihio-input","aihio-cluster","aihio-button"],"markup":"<aihio-card data-aihio-intent=\"surface\">\n  <form>\n    <aihio-card-header>\n      <aihio-card-title>Sign in</aihio-card-title>\n      <aihio-card-description>Use your work email and password to continue.</aihio-card-description>\n    </aihio-card-header>\n    <aihio-card-content>\n      <aihio-stack data-aihio-intent=\"layout\">\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Email</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            type=\"email\"\n            name=\"email\"\n            autocomplete=\"email\"\n            placeholder=\"you@example.com\"\n            required\n          ></aihio-input>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Password</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            type=\"password\"\n            name=\"password\"\n            autocomplete=\"current-password\"\n            required\n          ></aihio-input>\n        </aihio-field>\n      </aihio-stack>\n    </aihio-card-content>\n    <aihio-card-footer>\n      <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n        <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Forgot password</aihio-button>\n        <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Sign in</aihio-button>\n      </aihio-cluster>\n    </aihio-card-footer>\n  </form>\n</aihio-card>","variations":[{"id":"passwordless","name":"Passwordless","markup":"<aihio-card data-aihio-intent=\"surface\">\n  <form>\n    <aihio-card-header>\n      <aihio-card-title>Email me a sign-in link</aihio-card-title>\n      <aihio-card-description>We will send a one-time login link to your inbox.</aihio-card-description>\n    </aihio-card-header>\n    <aihio-card-content>\n      <aihio-field data-aihio-intent=\"form-field\">\n        <label slot=\"label\">Email</label>\n        <aihio-input\n          data-aihio-intent=\"text-entry\"\n          type=\"email\"\n          name=\"email\"\n          autocomplete=\"email\"\n          placeholder=\"you@example.com\"\n          required\n        ></aihio-input>\n        <span slot=\"description\">The link expires after 10 minutes.</span>\n      </aihio-field>\n    </aihio-card-content>\n    <aihio-card-footer>\n      <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n        <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Use password instead</aihio-button>\n        <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Send magic link</aihio-button>\n      </aihio-cluster>\n    </aihio-card-footer>\n  </form>\n</aihio-card>"},{"id":"signup","name":"Sign Up","markup":"<aihio-card data-aihio-intent=\"surface\">\n  <form>\n    <aihio-card-header>\n      <aihio-card-title>Create your account</aihio-card-title>\n      <aihio-card-description>Set up a new workspace login for your team.</aihio-card-description>\n    </aihio-card-header>\n    <aihio-card-content>\n      <aihio-stack data-aihio-intent=\"layout\">\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Full name</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            name=\"name\"\n            autocomplete=\"name\"\n            placeholder=\"Jane Doe\"\n            required\n          ></aihio-input>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Email</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            type=\"email\"\n            name=\"email\"\n            autocomplete=\"email\"\n            placeholder=\"jane@company.com\"\n            required\n          ></aihio-input>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Password</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            type=\"password\"\n            name=\"password\"\n            autocomplete=\"new-password\"\n            required\n          ></aihio-input>\n          <span slot=\"description\">At least 12 characters, including one symbol.</span>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Confirm password</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            type=\"password\"\n            name=\"password_confirmation\"\n            autocomplete=\"new-password\"\n            required\n          ></aihio-input>\n        </aihio-field>\n      </aihio-stack>\n    </aihio-card-content>\n    <aihio-card-footer>\n      <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n        <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Already have an account?</aihio-button>\n        <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Create account</aihio-button>\n      </aihio-cluster>\n    </aihio-card-footer>\n  </form>\n</aihio-card>"}]},{"id":"data-card-grid","name":"Data Card Grid","intents":["layout","surface","status","identity","secondary-action"],"requiredComponents":["aihio-grid","aihio-card","aihio-badge","aihio-avatar","aihio-button","aihio-stack","aihio-cluster"],"markup":"<section>\n  <aihio-stack data-aihio-intent=\"layout\" gap=\"lg\">\n    <header>\n      <h2>Active projects</h2>\n      <p>Track ownership, launch stage, and the next review for each project.</p>\n    </header>\n\n    <aihio-grid data-aihio-intent=\"layout\" columns=\"3\" role=\"list\">\n      <aihio-card data-aihio-intent=\"surface\" role=\"listitem\">\n        <aihio-card-header>\n          <aihio-card-title>Customer portal</aihio-card-title>\n          <aihio-card-description>Next review in 3 days.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-stack data-aihio-intent=\"layout\" gap=\"sm\" align=\"start\">\n            <aihio-badge data-aihio-intent=\"status\">Ready</aihio-badge>\n            <aihio-cluster data-aihio-intent=\"layout\">\n              <aihio-avatar data-aihio-intent=\"identity\" alt=\"Jane Doe\" size=\"sm\"></aihio-avatar>\n              <span>Jane Doe</span>\n            </aihio-cluster>\n          </aihio-stack>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Open</aihio-button>\n        </aihio-card-footer>\n      </aihio-card>\n\n      <aihio-card data-aihio-intent=\"surface\" role=\"listitem\">\n        <aihio-card-header>\n          <aihio-card-title>Billing sync</aihio-card-title>\n          <aihio-card-description>Audit notes pending from finance.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-stack data-aihio-intent=\"layout\" gap=\"sm\" align=\"start\">\n            <aihio-badge data-aihio-intent=\"status\" variant=\"warning\">Needs review</aihio-badge>\n            <aihio-cluster data-aihio-intent=\"layout\">\n              <aihio-avatar data-aihio-intent=\"identity\" alt=\"Raine Luntta\" size=\"sm\"></aihio-avatar>\n              <span>Raine Luntta</span>\n            </aihio-cluster>\n          </aihio-stack>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Review</aihio-button>\n        </aihio-card-footer>\n      </aihio-card>\n\n      <aihio-card data-aihio-intent=\"surface\" role=\"listitem\">\n        <aihio-card-header>\n          <aihio-card-title>Ops migration</aihio-card-title>\n          <aihio-card-description>Rollout staged for next Tuesday.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-stack data-aihio-intent=\"layout\" gap=\"sm\" align=\"start\">\n            <aihio-badge data-aihio-intent=\"status\" variant=\"secondary\">Scheduled</aihio-badge>\n            <aihio-cluster data-aihio-intent=\"layout\">\n              <aihio-avatar data-aihio-intent=\"identity\" alt=\"Ada King\" size=\"sm\"></aihio-avatar>\n              <span>Ada King</span>\n            </aihio-cluster>\n          </aihio-stack>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">View plan</aihio-button>\n        </aihio-card-footer>\n      </aihio-card>\n    </aihio-grid>\n  </aihio-stack>\n</section>","variations":[]},{"id":"data-table","name":"Data Table","intents":["tabular-data","status","menu","primary-action","navigation","form-field","layout"],"requiredComponents":["aihio-table","aihio-button","aihio-field","aihio-input","aihio-stack","aihio-cluster"],"markup":"<section>\n  <aihio-stack data-aihio-intent=\"layout\" gap=\"md\">\n    <aihio-cluster data-aihio-intent=\"layout\" justify=\"between\">\n      <h2 id=\"invoices-heading\">Invoices</h2>\n      <aihio-button data-aihio-intent=\"primary-action\"><a href=\"/invoices/new\">New invoice</a></aihio-button>\n    </aihio-cluster>\n\n    <form role=\"search\" action=\"/invoices\" method=\"get\">\n      <aihio-field data-aihio-intent=\"form-field\">\n        <label slot=\"label\">Search invoices</label>\n        <aihio-input type=\"search\" name=\"q\" placeholder=\"Customer or invoice number\"></aihio-input>\n      </aihio-field>\n    </form>\n\n    <aihio-table data-aihio-intent=\"tabular-data\">\n      <table aria-labelledby=\"invoices-heading\">\n        <thead>\n          <tr>\n            <th scope=\"col\" data-sortable=\"invoice\">Invoice</th>\n            <th scope=\"col\" data-sortable=\"customer\">Customer</th>\n            <th scope=\"col\" data-sortable=\"status\">Status</th>\n            <th scope=\"col\" data-sortable=\"issued\" aria-sort=\"descending\">Issued</th>\n            <th scope=\"col\" data-sortable=\"amount\" data-numeric>Amount</th>\n            <th scope=\"col\">Actions</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr>\n            <th scope=\"row\"><a href=\"/invoices/1046\">INV-1046</a></th>\n            <td>Wide World Importers</td>\n            <td data-sort-value=\"3\"><aihio-badge data-aihio-intent=\"status\" variant=\"secondary\">Draft</aihio-badge></td>\n            <td><time datetime=\"2026-10-02\">2 Oct 2026</time></td>\n            <td data-numeric>€3,200.00</td>\n            <td>\n              <aihio-dropdown data-aihio-intent=\"menu\" align=\"end\">\n                <aihio-button slot=\"trigger\" variant=\"ghost\" size=\"icon\" aria-label=\"Actions for INV-1046\">&#x22ef;</aihio-button>\n                <aihio-dropdown-item>Edit draft</aihio-dropdown-item>\n                <aihio-dropdown-item>Download PDF</aihio-dropdown-item>\n              </aihio-dropdown>\n            </td>\n          </tr>\n          <tr>\n            <th scope=\"row\"><a href=\"/invoices/1045\">INV-1045</a></th>\n            <td>Adventure Works</td>\n            <td data-sort-value=\"2\"><aihio-badge data-aihio-intent=\"status\" variant=\"warning\">Due</aihio-badge></td>\n            <td><time datetime=\"2026-09-30\">30 Sep 2026</time></td>\n            <td data-numeric>€640.00</td>\n            <td>\n              <aihio-dropdown data-aihio-intent=\"menu\" align=\"end\">\n                <aihio-button slot=\"trigger\" variant=\"ghost\" size=\"icon\" aria-label=\"Actions for INV-1045\">&#x22ef;</aihio-button>\n                <aihio-dropdown-item>Send reminder</aihio-dropdown-item>\n                <aihio-dropdown-item>Download PDF</aihio-dropdown-item>\n              </aihio-dropdown>\n            </td>\n          </tr>\n          <tr>\n            <th scope=\"row\"><a href=\"/invoices/1044\">INV-1044</a></th>\n            <td>Contoso</td>\n            <td data-sort-value=\"1\"><aihio-badge data-aihio-intent=\"status\" variant=\"destructive\">Overdue</aihio-badge></td>\n            <td><time datetime=\"2026-09-12\">12 Sep 2026</time></td>\n            <td data-numeric>€12,400.00</td>\n            <td>\n              <aihio-dropdown data-aihio-intent=\"menu\" align=\"end\">\n                <aihio-button slot=\"trigger\" variant=\"ghost\" size=\"icon\" aria-label=\"Actions for INV-1044\">&#x22ef;</aihio-button>\n                <aihio-dropdown-item>Send reminder</aihio-dropdown-item>\n                <aihio-dropdown-item>Download PDF</aihio-dropdown-item>\n              </aihio-dropdown>\n            </td>\n          </tr>\n          <tr>\n            <th scope=\"row\"><a href=\"/invoices/1043\">INV-1043</a></th>\n            <td>Fabrikam</td>\n            <td data-sort-value=\"4\"><aihio-badge data-aihio-intent=\"status\" variant=\"success\">Paid</aihio-badge></td>\n            <td><time datetime=\"2026-09-04\">4 Sep 2026</time></td>\n            <td data-numeric>€980.50</td>\n            <td>\n              <aihio-dropdown data-aihio-intent=\"menu\" align=\"end\">\n                <aihio-button slot=\"trigger\" variant=\"ghost\" size=\"icon\" aria-label=\"Actions for INV-1043\">&#x22ef;</aihio-button>\n                <aihio-dropdown-item>Download PDF</aihio-dropdown-item>\n                <aihio-dropdown-item>Issue refund</aihio-dropdown-item>\n              </aihio-dropdown>\n            </td>\n          </tr>\n          <tr>\n            <th scope=\"row\"><a href=\"/invoices/1042\">INV-1042</a></th>\n            <td>Northwind Traders</td>\n            <td data-sort-value=\"4\"><aihio-badge data-aihio-intent=\"status\" variant=\"success\">Paid</aihio-badge></td>\n            <td><time datetime=\"2026-08-29\">29 Aug 2026</time></td>\n            <td data-numeric>€1,250.00</td>\n            <td>\n              <aihio-dropdown data-aihio-intent=\"menu\" align=\"end\">\n                <aihio-button slot=\"trigger\" variant=\"ghost\" size=\"icon\" aria-label=\"Actions for INV-1042\">&#x22ef;</aihio-button>\n                <aihio-dropdown-item>Download PDF</aihio-dropdown-item>\n                <aihio-dropdown-item>Issue refund</aihio-dropdown-item>\n              </aihio-dropdown>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    </aihio-table>\n\n    <aihio-cluster data-aihio-intent=\"layout\" justify=\"between\">\n      <p>Showing 1–5 of 42 invoices</p>\n      <aihio-pagination data-aihio-intent=\"navigation\" page=\"1\" pages=\"9\" href=\"/invoices?page={page}\" aria-label=\"Invoice pages\"></aihio-pagination>\n    </aihio-cluster>\n  </aihio-stack>\n</section>","variations":[{"id":"no-results","name":"No Results","markup":"<section>\n  <aihio-stack data-aihio-intent=\"layout\" gap=\"md\">\n    <aihio-cluster data-aihio-intent=\"layout\" justify=\"between\">\n      <h2 id=\"invoice-search-heading\">Invoices</h2>\n      <aihio-button data-aihio-intent=\"primary-action\"><a href=\"/invoices/new\">New invoice</a></aihio-button>\n    </aihio-cluster>\n\n    <form role=\"search\" action=\"/invoices\" method=\"get\">\n      <aihio-field data-aihio-intent=\"form-field\">\n        <label slot=\"label\">Search invoices</label>\n        <aihio-input type=\"search\" name=\"q\" value=\"acme\" placeholder=\"Customer or invoice number\"></aihio-input>\n      </aihio-field>\n    </form>\n\n    <aihio-table data-aihio-intent=\"tabular-data\">\n      <table aria-labelledby=\"invoice-search-heading\">\n        <thead>\n          <tr>\n            <th scope=\"col\">Invoice</th>\n            <th scope=\"col\">Customer</th>\n            <th scope=\"col\">Status</th>\n            <th scope=\"col\">Issued</th>\n            <th scope=\"col\" data-numeric>Amount</th>\n            <th scope=\"col\">Actions</th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr>\n            <td colspan=\"6\">\n              <aihio-stack data-aihio-intent=\"layout\" gap=\"sm\" align=\"start\">\n                <p>No invoices match “acme”. Check the spelling, or search by invoice number.</p>\n                <aihio-button data-aihio-intent=\"navigation\" variant=\"outline\" size=\"sm\"><a href=\"/invoices\">Show all invoices</a></aihio-button>\n              </aihio-stack>\n            </td>\n          </tr>\n        </tbody>\n      </table>\n    </aihio-table>\n  </aihio-stack>\n</section>"}]},{"id":"destructive-confirmation","name":"Destructive Confirmation","intents":["overlay","modal","destructive-action","secondary-action","dismissible"],"requiredComponents":["aihio-dialog","aihio-button"],"markup":"<aihio-button\n  data-aihio-intent=\"destructive-action\"\n  variant=\"destructive\"\n  commandfor=\"delete-project\"\n  command=\"--open\"\n>Delete project</aihio-button>\n\n<aihio-dialog id=\"delete-project\" data-aihio-intent=\"overlay modal dismissible\">\n  <aihio-dialog-header>\n    <aihio-dialog-title>Delete project?</aihio-dialog-title>\n    <aihio-dialog-description>\n      This permanently removes project access, deploy history, and notification rules.\n    </aihio-dialog-description>\n  </aihio-dialog-header>\n  <form method=\"post\">\n    <aihio-dialog-footer>\n      <aihio-button\n        data-aihio-intent=\"secondary-action\"\n        variant=\"outline\"\n        commandfor=\"delete-project\"\n        command=\"--close\"\n      >Cancel</aihio-button>\n      <aihio-button data-aihio-intent=\"destructive-action\" variant=\"destructive\" type=\"submit\">Delete project</aihio-button>\n    </aihio-dialog-footer>\n  </form>\n</aihio-dialog>","variations":[]},{"id":"empty-state","name":"Empty State","intents":["surface","informational","primary-action","secondary-action"],"requiredComponents":["aihio-card","aihio-button"],"markup":"<aihio-card data-aihio-intent=\"surface\">\n  <aihio-card-header>\n    <aihio-card-title>No projects yet</aihio-card-title>\n    <aihio-card-description>Create your first project to start collecting feedback.</aihio-card-description>\n  </aihio-card-header>\n  <aihio-card-content>\n    <p>Projects organize deployments, environments, and release notes for your team.</p>\n  </aihio-card-content>\n  <aihio-card-footer>\n    <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Import from CSV</aihio-button>\n    <aihio-button data-aihio-intent=\"primary-action\">Create project</aihio-button>\n  </aihio-card-footer>\n</aihio-card>","variations":[{"id":"search-results","name":"Search Results","markup":"<aihio-card data-aihio-intent=\"surface\">\n  <aihio-card-header>\n    <aihio-card-title>No results for \"ops\"</aihio-card-title>\n    <aihio-card-description>Try a broader term or remove one of your filters.</aihio-card-description>\n  </aihio-card-header>\n  <aihio-card-content>\n    <p>Your current filters are hiding every matching workspace and archived project.</p>\n  </aihio-card-content>\n  <aihio-card-footer>\n    <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\">Clear filters</aihio-button>\n    <aihio-button data-aihio-intent=\"primary-action\">Create new project</aihio-button>\n  </aihio-card-footer>\n</aihio-card>"}]},{"id":"inline-form-validation","name":"Inline Form With Validation","intents":["layout","form-field","text-entry","alert","primary-action","secondary-action"],"requiredComponents":["aihio-stack","aihio-field","aihio-input","aihio-alert","aihio-cluster","aihio-button"],"markup":"<form data-aihio-intent=\"form-field\">\n  <aihio-stack data-aihio-intent=\"layout\">\n    <aihio-field data-aihio-intent=\"form-field\">\n      <label slot=\"label\">Password</label>\n      <aihio-input\n        data-aihio-intent=\"text-entry\"\n        type=\"password\"\n        name=\"password\"\n        autocomplete=\"new-password\"\n        required\n      ></aihio-input>\n      <span slot=\"description\">At least 12 characters, including one symbol.</span>\n      <span slot=\"error\">Password must include at least 12 characters and one symbol.</span>\n    </aihio-field>\n\n    <aihio-alert data-aihio-intent=\"alert\" variant=\"destructive\">\n      <div slot=\"title\">Password is too weak</div>\n      <div slot=\"description\">Update the field above, then resubmit the form.</div>\n    </aihio-alert>\n\n    <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n      <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\" type=\"reset\">Cancel</aihio-button>\n      <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save password</aihio-button>\n    </aihio-cluster>\n  </aihio-stack>\n</form>","variations":[{"id":"success","name":"Success","markup":"<form data-aihio-intent=\"form-field\">\n  <aihio-stack data-aihio-intent=\"layout\">\n    <aihio-field data-aihio-intent=\"form-field\">\n      <label slot=\"label\">Password</label>\n      <aihio-input\n        data-aihio-intent=\"text-entry\"\n        type=\"password\"\n        name=\"password\"\n        autocomplete=\"new-password\"\n        value=\"correct horse battery staple\"\n        required\n      ></aihio-input>\n      <span slot=\"description\">Password strength looks good.</span>\n    </aihio-field>\n\n    <aihio-alert data-aihio-intent=\"informational status\" variant=\"success\">\n      <div slot=\"title\">Password accepted</div>\n      <div slot=\"description\">You can submit the form without additional changes.</div>\n    </aihio-alert>\n\n    <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n      <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\" type=\"reset\">Cancel</aihio-button>\n      <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save password</aihio-button>\n    </aihio-cluster>\n  </aihio-stack>\n</form>"}]},{"id":"settings-section","name":"Settings Section","intents":["surface","form-field","text-entry","toggle-state","status","primary-action","secondary-action","layout"],"requiredComponents":["aihio-card","aihio-field","aihio-input","aihio-switch","aihio-button","aihio-badge","aihio-stack","aihio-cluster"],"markup":"<aihio-card data-aihio-intent=\"surface\">\n  <form>\n    <aihio-card-header>\n      <aihio-cluster data-aihio-intent=\"layout\" justify=\"between\">\n        <aihio-card-title>Workspace settings</aihio-card-title>\n        <aihio-badge data-aihio-intent=\"status\" variant=\"secondary\">Live</aihio-badge>\n      </aihio-cluster>\n      <aihio-card-description>Update the defaults your team sees on first login.</aihio-card-description>\n    </aihio-card-header>\n    <aihio-card-content>\n      <aihio-stack data-aihio-intent=\"layout\" gap=\"lg\">\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Workspace name</label>\n          <aihio-input\n            data-aihio-intent=\"text-entry\"\n            name=\"workspace-name\"\n            value=\"Northwind\"\n            required\n          ></aihio-input>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Default landing page</label>\n          <select name=\"landing-page\">\n            <option value=\"overview\">Overview</option>\n            <option value=\"projects\" selected>Projects</option>\n            <option value=\"settings\">Settings</option>\n          </select>\n          <span slot=\"description\">Where members arrive after they sign in.</span>\n        </aihio-field>\n\n        <aihio-field data-aihio-intent=\"form-field\">\n          <label slot=\"label\">Email notifications</label>\n          <aihio-switch data-aihio-intent=\"toggle-state\" name=\"email-notifications\" checked></aihio-switch>\n          <span slot=\"description\">A digest of workspace activity, sent weekly.</span>\n        </aihio-field>\n      </aihio-stack>\n    </aihio-card-content>\n    <aihio-card-footer>\n      <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n        <aihio-button data-aihio-intent=\"secondary-action\" variant=\"outline\" type=\"reset\">Reset</aihio-button>\n        <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save changes</aihio-button>\n      </aihio-cluster>\n    </aihio-card-footer>\n  </form>\n</aihio-card>","variations":[]},{"id":"tabbed-settings","name":"Tabbed Settings","intents":["tabs","navigation","layout","surface","form-field","text-entry","toggle-state","primary-action"],"requiredComponents":["aihio-tabs","aihio-card","aihio-field","aihio-input","aihio-switch","aihio-button","aihio-stack","aihio-cluster"],"markup":"<aihio-tabs data-aihio-intent=\"tabs navigation layout\" value=\"profile\">\n  <aihio-tab-list>\n    <aihio-tab value=\"profile\">Profile</aihio-tab>\n    <aihio-tab value=\"notifications\">Notifications</aihio-tab>\n    <aihio-tab value=\"security\">Security</aihio-tab>\n  </aihio-tab-list>\n\n  <aihio-tab-panel value=\"profile\">\n    <aihio-card data-aihio-intent=\"surface\">\n      <form>\n        <aihio-card-header>\n          <aihio-card-title>Profile</aihio-card-title>\n          <aihio-card-description>Update how your name and email appear to the team.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-stack data-aihio-intent=\"layout\">\n            <aihio-field data-aihio-intent=\"form-field\">\n              <label slot=\"label\">Full name</label>\n              <aihio-input data-aihio-intent=\"text-entry\" name=\"name\" autocomplete=\"name\" value=\"Jane Doe\" required></aihio-input>\n            </aihio-field>\n            <aihio-field data-aihio-intent=\"form-field\">\n              <label slot=\"label\">Email</label>\n              <aihio-input data-aihio-intent=\"text-entry\" type=\"email\" name=\"email\" autocomplete=\"email\" value=\"jane@company.com\" required></aihio-input>\n            </aihio-field>\n          </aihio-stack>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n            <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save profile</aihio-button>\n          </aihio-cluster>\n        </aihio-card-footer>\n      </form>\n    </aihio-card>\n  </aihio-tab-panel>\n\n  <aihio-tab-panel value=\"notifications\">\n    <aihio-card data-aihio-intent=\"surface\">\n      <form>\n        <aihio-card-header>\n          <aihio-card-title>Notifications</aihio-card-title>\n          <aihio-card-description>Choose which updates should interrupt your day.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-stack data-aihio-intent=\"layout\" gap=\"lg\">\n            <aihio-field data-aihio-intent=\"form-field\">\n              <label slot=\"label\">Incident digests</label>\n              <aihio-switch data-aihio-intent=\"toggle-state\" name=\"incident-digests\" checked></aihio-switch>\n              <span slot=\"description\">A summary of open incidents every morning.</span>\n            </aihio-field>\n            <aihio-field data-aihio-intent=\"form-field\">\n              <label slot=\"label\">Weekly summary</label>\n              <aihio-switch data-aihio-intent=\"toggle-state\" name=\"weekly-summary\"></aihio-switch>\n              <span slot=\"description\">Activity across every project, sent on Mondays.</span>\n            </aihio-field>\n          </aihio-stack>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n            <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save notifications</aihio-button>\n          </aihio-cluster>\n        </aihio-card-footer>\n      </form>\n    </aihio-card>\n  </aihio-tab-panel>\n\n  <aihio-tab-panel value=\"security\">\n    <aihio-card data-aihio-intent=\"surface\">\n      <form>\n        <aihio-card-header>\n          <aihio-card-title>Security</aihio-card-title>\n          <aihio-card-description>Rotate credentials and confirm recovery options.</aihio-card-description>\n        </aihio-card-header>\n        <aihio-card-content>\n          <aihio-field data-aihio-intent=\"form-field\">\n            <label slot=\"label\">Recovery email</label>\n            <aihio-input data-aihio-intent=\"text-entry\" type=\"email\" name=\"recovery-email\" autocomplete=\"email\" value=\"security@company.com\"></aihio-input>\n            <span slot=\"description\">Used only if you lose access to your account.</span>\n          </aihio-field>\n        </aihio-card-content>\n        <aihio-card-footer>\n          <aihio-cluster data-aihio-intent=\"layout\" grow justify=\"end\">\n            <aihio-button data-aihio-intent=\"primary-action\" type=\"submit\">Save security settings</aihio-button>\n          </aihio-cluster>\n        </aihio-card-footer>\n      </form>\n    </aihio-card>\n  </aihio-tab-panel>\n</aihio-tabs>","variations":[]},{"id":"toast-alert-stack","name":"Toast-Style Alert Stack","intents":["alert","informational","status","layout"],"requiredComponents":["aihio-alert","aihio-stack"],"markup":"<aihio-stack aria-live=\"polite\" data-aihio-intent=\"layout\" gap=\"sm\">\n  <aihio-alert data-aihio-intent=\"alert informational\">\n    <div slot=\"title\">Deployment finished</div>\n    <div slot=\"description\">Customer portal v18 is now live in production.</div>\n  </aihio-alert>\n\n  <aihio-alert data-aihio-intent=\"alert status\">\n    <div slot=\"title\">Usage report ready</div>\n    <div slot=\"description\">A new CSV export is available in Billing.</div>\n  </aihio-alert>\n\n  <aihio-alert data-aihio-intent=\"alert\" variant=\"destructive\">\n    <div slot=\"title\">Webhook delivery failed</div>\n    <div slot=\"description\">Three retries have failed for the Slack incident channel.</div>\n  </aihio-alert>\n</aihio-stack>","variations":[]}]}