{
  "note": "The i80 Design System as Web Components. Load components.css and i80.js, then write these tags. Your page needs *, *::before, *::after { box-sizing: border-box }. An element ignores an attribute it does not know, so use the ones listed here.",
  "css": "https://design.i80.dk/v/0.5.2/components.css",
  "script": "https://design.i80.dk/v/0.5.2/i80.js",
  "docs": "https://design.i80.dk/elements/",
  "components": [
    {
      "tag": "i80-button",
      "title": "Button",
      "description": "An action. Renders a real <button>, or an <a> when it has href. A <button> or <a> you write inside is kept, with its type, name, value and handlers.",
      "examples": [
        "<i80-button color=\"primary\">Save</i80-button>\n<i80-button color=\"secondary\">Cancel</i80-button>\n<i80-button color=\"tertiary\" icon=\"plus\">Add item</i80-button>\n<i80-button color=\"critical\" icon=\"trash\">Delete</i80-button>",
        "<i80-button size=\"small\">Small</i80-button>\n<i80-button size=\"medium\">Medium</i80-button>\n<i80-button size=\"large\">Large</i80-button>\n<i80-button icon=\"settings\" icon-only>Settings</i80-button>",
        "<i80-button href=\"/plans\" icon=\"arrow-right\" icon-position=\"trailing\">All plans</i80-button>\n<i80-button color=\"primary\"><button type=\"submit\" name=\"action\" value=\"publish\">Publish</button></i80-button>"
      ],
      "attributes": [
        {
          "name": "color",
          "values": "primary · secondary · tertiary · critical",
          "default": "primary",
          "notes": "Visual weight."
        },
        {
          "name": "size",
          "values": "small · medium · large",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "Flight icon, see the icon library."
        },
        {
          "name": "icon-position",
          "values": "leading · trailing",
          "default": "leading",
          "notes": ""
        },
        {
          "name": "icon-only",
          "values": "boolean",
          "default": "",
          "notes": "Shows only the icon; the text becomes the aria-label."
        },
        {
          "name": "full-width",
          "values": "boolean",
          "default": "",
          "notes": "Fills the parent's width."
        },
        {
          "name": "href",
          "values": "URL",
          "default": "",
          "notes": "Renders a link instead of a button."
        },
        {
          "name": "external",
          "values": "boolean",
          "default": "",
          "notes": "With href: opens in a new tab."
        },
        {
          "name": "type",
          "values": "button · submit · reset",
          "default": "button",
          "notes": "For the created <button>."
        },
        {
          "name": "disabled",
          "values": "boolean",
          "default": "",
          "notes": ""
        },
        {
          "name": "inline",
          "values": "boolean",
          "default": "",
          "notes": "Sits in a line of text instead of on its own."
        },
        {
          "name": "opens",
          "values": "id",
          "default": "",
          "notes": "Opens the <i80-modal> or <i80-flyout> with that id."
        },
        {
          "name": "close",
          "values": "boolean",
          "default": "",
          "notes": "Inside a modal or flyout: closes it."
        }
      ]
    },
    {
      "tag": "i80-dropdown",
      "title": "Dropdown",
      "description": "A button that opens a list of actions or links. Esc, click outside and the arrow keys work. Items are real links or buttons; an item's own <a>, <button> or <form> is kept, so server-side delete forms work as they are.",
      "examples": [
        "<i80-dropdown text=\"Actions\">\n  <i80-dropdown-title>Plan</i80-dropdown-title>\n  <i80-dropdown-item href=\"#edit\" icon=\"edit\">Edit</i80-dropdown-item>\n  <i80-dropdown-item icon=\"duplicate\">Duplicate</i80-dropdown-item>\n  <i80-dropdown-separator></i80-dropdown-separator>\n  <i80-dropdown-item color=\"critical\" icon=\"trash\">Delete</i80-dropdown-item>\n</i80-dropdown>",
        "<i80-dropdown text=\"More\" color=\"secondary\" size=\"small\" position=\"bottom-left\">\n  <i80-dropdown-item color=\"critical\" icon=\"trash\">\n    <form method=\"post\" action=\"/plans/1/delete\"><button>Delete plan</button></form>\n  </i80-dropdown-item>\n</i80-dropdown>"
      ],
      "attributes": [
        {
          "name": "text",
          "values": "text",
          "default": "",
          "notes": "The toggle's label."
        },
        {
          "name": "color",
          "values": "primary · secondary",
          "default": "primary",
          "notes": ""
        },
        {
          "name": "size",
          "values": "small · medium",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "Icon before the text."
        },
        {
          "name": "position",
          "values": "bottom-right · bottom-left · top-right · top-left",
          "default": "bottom-right",
          "notes": "Where the list opens."
        },
        {
          "name": "width / height",
          "values": "CSS length",
          "default": "",
          "notes": "Fixed width, maximum height."
        },
        {
          "name": "match-toggle-width",
          "values": "boolean",
          "default": "",
          "notes": ""
        },
        {
          "name": "inline",
          "values": "boolean",
          "default": "",
          "notes": "Sits in a line of text instead of on its own."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Open initially. Methods: show(), hide(). Events: i80-open, i80-close."
        },
        {
          "name": "i80-dropdown-item",
          "values": "href · icon · trailing-icon · color (action · critical) · disabled · keep-open",
          "default": "",
          "notes": "One entry."
        }
      ]
    },
    {
      "tag": "i80-text-input",
      "title": "Text Input",
      "description": "A labelled text field with helper text and error. Renders a real <input>; an <input> you write inside (e.g. Django's {{ form.email }}) is kept. type=\"password\" gets a show/hide button.",
      "examples": [
        "<i80-text-input label=\"Name\" name=\"name\" placeholder=\"Ada Lovelace\" required></i80-text-input>",
        "<i80-text-input label=\"Email\" type=\"email\" helper-text=\"We never share it\" error=\"Enter a valid email address.\"></i80-text-input>",
        "<i80-text-input label=\"Password\" type=\"password\" name=\"password\"></i80-text-input>",
        "<i80-text-input label=\"Email\" error=\"Enter a valid email address.\">\n  <input type=\"email\" name=\"email\" id=\"id_email\" value=\"ada@\">\n</i80-text-input>"
      ],
      "attributes": [
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "Linked to the input."
        },
        {
          "name": "helper-text",
          "values": "text",
          "default": "",
          "notes": "Linked with aria-describedby."
        },
        {
          "name": "error",
          "values": "text",
          "default": "",
          "notes": "Shows the error and marks the input invalid."
        },
        {
          "name": "required / optional",
          "values": "boolean",
          "default": "",
          "notes": "Adds the Required badge or (Optional)."
        },
        {
          "name": "type",
          "values": "text · email · password · search · tel · url · number · date …",
          "default": "text",
          "notes": ""
        },
        {
          "name": "name, value, placeholder, autocomplete, minlength, maxlength, pattern, min, max, step, inputmode, list, form",
          "values": "",
          "default": "",
          "notes": "Passed to the created <input>."
        },
        {
          "name": "width",
          "values": "CSS length",
          "default": "",
          "notes": ""
        },
        {
          "name": "no-visibility-toggle",
          "values": "boolean",
          "default": "",
          "notes": "Password without the show/hide button."
        },
        {
          "name": "aria-describedby",
          "values": "id list",
          "default": "",
          "notes": "Merged with the helper text and error ids the element links."
        }
      ]
    },
    {
      "tag": "i80-textarea",
      "title": "Textarea",
      "description": "A labelled multi-line field. Same field attributes as Text Input.",
      "examples": [
        "<i80-textarea label=\"Notes\" name=\"notes\" helper-text=\"Visible to your team\" rows=\"4\"></i80-textarea>"
      ],
      "attributes": [
        {
          "name": "label, helper-text, error, invalid, required, optional, disabled, readonly",
          "values": "",
          "default": "",
          "notes": "As Text Input."
        },
        {
          "name": "name, value, placeholder, autocomplete, minlength, maxlength",
          "values": "",
          "default": "",
          "notes": "Passed to the created <textarea>."
        },
        {
          "name": "rows",
          "values": "number",
          "default": "4",
          "notes": ""
        },
        {
          "name": "width / height",
          "values": "CSS length",
          "default": "",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-select",
      "title": "Select",
      "description": "A labelled native select. Put <option> elements inside, or a whole <select>.",
      "examples": [
        "<i80-select label=\"Status\" name=\"status\" value=\"active\">\n  <option value=\"draft\">Draft</option>\n  <option value=\"active\">Active</option>\n  <option value=\"done\">Done</option>\n</i80-select>"
      ],
      "attributes": [
        {
          "name": "label, helper-text, error, invalid, required, optional, disabled",
          "values": "",
          "default": "",
          "notes": "As Text Input."
        },
        {
          "name": "name, multiple, size, autocomplete",
          "values": "",
          "default": "",
          "notes": "Passed to the created <select>."
        },
        {
          "name": "value",
          "values": "option value",
          "default": "",
          "notes": "Initially selected option."
        },
        {
          "name": "width",
          "values": "CSS length",
          "default": "",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-checkbox",
      "title": "Checkbox, Radio, Toggle",
      "description": "Single choices with their label after the control. Group them in <i80-checkbox-group>, <i80-radio-group> or <i80-toggle-group> for a fieldset with a legend; a radio group passes its name to its radios.",
      "examples": [
        "<i80-checkbox label=\"Email me updates\" name=\"updates\" helper-text=\"About once a week\"></i80-checkbox>\n<i80-toggle label=\"Notifications\" name=\"notify\" checked></i80-toggle>",
        "<i80-radio-group legend=\"Frequency\" name=\"frequency\" layout=\"horizontal\">\n  <i80-radio label=\"Daily\" value=\"daily\"></i80-radio>\n  <i80-radio label=\"Weekly\" value=\"weekly\" checked></i80-radio>\n  <i80-radio label=\"Monthly\" value=\"monthly\"></i80-radio>\n</i80-radio-group>",
        "<i80-checkbox-group legend=\"Topics\" error=\"Pick at least one\" required>\n  <i80-checkbox label=\"Plans\" name=\"topics\" value=\"plans\"></i80-checkbox>\n  <i80-checkbox label=\"Billing\" name=\"topics\" value=\"billing\"></i80-checkbox>\n</i80-checkbox-group>"
      ],
      "attributes": [
        {
          "name": "label, helper-text, error, invalid, name, value, checked, required, optional, disabled, readonly",
          "values": "",
          "default": "",
          "notes": "On i80-checkbox, i80-radio and i80-toggle."
        },
        {
          "name": "legend, helper-text, error, required, optional",
          "values": "",
          "default": "",
          "notes": "On i80-checkbox-group, i80-radio-group and i80-toggle-group."
        },
        {
          "name": "layout",
          "values": "vertical · horizontal",
          "default": "vertical",
          "notes": "On i80-checkbox-group, i80-radio-group and i80-toggle-group."
        },
        {
          "name": "name",
          "values": "text",
          "default": "",
          "notes": "On i80-radio-group: given to radios without a name."
        }
      ]
    },
    {
      "tag": "i80-toggle-group",
      "title": "Toggle Group",
      "description": "Several toggles under one legend, in a fieldset. The same attributes as the checkbox and radio groups.",
      "examples": [
        "<i80-toggle-group legend=\"Notifications\" helper-text=\"Sent to your work address\">\n  <i80-toggle label=\"Plan updates\" name=\"notify_plans\" checked></i80-toggle>\n  <i80-toggle label=\"Mentions\" name=\"notify_mentions\"></i80-toggle>\n</i80-toggle-group>"
      ],
      "attributes": [
        {
          "name": "legend, helper-text, error, required, optional",
          "values": "",
          "default": "",
          "notes": ""
        },
        {
          "name": "layout",
          "values": "vertical · horizontal",
          "default": "vertical",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-alert",
      "title": "Alert",
      "description": "A message. Text becomes the description; buttons and links become the actions. Success, warning and critical alerts are announced to screen readers.",
      "examples": [
        "<i80-alert type=\"inline\" color=\"success\" heading=\"Plan published\">Everyone on the team can see it now.</i80-alert>",
        "<i80-alert type=\"inline\" color=\"critical\" heading=\"Payment failed\" dismissible>\n  Your card was declined.\n  <i80-button color=\"secondary\">Update card</i80-button>\n</i80-alert>",
        "<i80-alert type=\"compact\" color=\"warning\">The export takes a few minutes.</i80-alert>"
      ],
      "attributes": [
        {
          "name": "type",
          "values": "page · inline · compact",
          "default": "inline",
          "notes": ""
        },
        {
          "name": "color",
          "values": "neutral · highlight · success · warning · critical",
          "default": "neutral",
          "notes": ""
        },
        {
          "name": "heading",
          "values": "text",
          "default": "",
          "notes": "The title."
        },
        {
          "name": "icon",
          "values": "icon name · false",
          "default": "by color",
          "notes": ""
        },
        {
          "name": "dismissible",
          "values": "boolean",
          "default": "",
          "notes": "Adds a close button; fires a cancelable i80-dismiss event, then removes the alert."
        }
      ]
    },
    {
      "tag": "i80-modal",
      "title": "Modal",
      "description": "A dialog on top of the page, built on the native <dialog>: focus stays inside, Esc closes it, and the page behind can't be used. Open it with opens=\"id\" on any button. Children with slot=\"footer\" go in the footer; any element with a close attribute closes it, as does <form method=\"dialog\">.",
      "examples": [
        "<i80-button color=\"critical\" icon=\"trash\" opens=\"delete-plan\">Delete plan</i80-button>\n<i80-modal id=\"delete-plan\" heading=\"Delete plan?\" color=\"critical\" size=\"small\">\n  <p>The plan and its tasks are deleted for everyone. This can't be undone.</p>\n  <div class=\"i80-button-set\" slot=\"footer\">\n    <i80-button color=\"critical\" close>Delete</i80-button>\n    <i80-button color=\"secondary\" close>Cancel</i80-button>\n  </div>\n</i80-modal>"
      ],
      "attributes": [
        {
          "name": "heading",
          "values": "text",
          "default": "",
          "notes": "The title."
        },
        {
          "name": "tagline",
          "values": "text",
          "default": "",
          "notes": "Small text above the title."
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "24px icon before the title."
        },
        {
          "name": "size",
          "values": "small · medium · large",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "color",
          "values": "neutral · warning · critical",
          "default": "neutral",
          "notes": ""
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Open it. Methods: show(), close(). Events: i80-open, and a cancelable i80-close."
        },
        {
          "name": "dismiss-disabled",
          "values": "boolean",
          "default": "",
          "notes": "Esc and the close buttons do nothing (e.g. while saving)."
        }
      ]
    },
    {
      "tag": "i80-flyout",
      "title": "Flyout",
      "description": "Like Modal, but slides in from the side for details and secondary tasks. It also closes on a click outside.",
      "examples": [
        "<i80-button color=\"secondary\" opens=\"plan-details\">Show details</i80-button>\n<i80-flyout id=\"plan-details\" heading=\"Q3 launch\" tagline=\"Plan\" description=\"Owned by the marketing team\">\n  <p>Starts 1 July, ends 30 September. 14 tasks, 3 overdue.</p>\n  <div class=\"i80-button-set\" slot=\"footer\"><i80-button color=\"primary\" close>Done</i80-button></div>\n</i80-flyout>"
      ],
      "attributes": [
        {
          "name": "heading, tagline, icon, open, dismiss-disabled",
          "values": "",
          "default": "",
          "notes": "As Modal."
        },
        {
          "name": "description",
          "values": "text",
          "default": "",
          "notes": "Text under the header."
        },
        {
          "name": "size",
          "values": "medium · large",
          "default": "medium",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-tabs",
      "title": "Tabs",
      "description": "Switch between views on the same page. Tabs and panels pair up by order. The arrow keys, Home and End move between tabs; Enter, Space or a click selects one.",
      "examples": [
        "<i80-tabs>\n  <i80-tab icon=\"list\">Tasks</i80-tab>\n  <i80-tab count=\"3\">Comments</i80-tab>\n  <i80-tab>Settings</i80-tab>\n  <i80-tab-panel><p>14 tasks in this plan.</p></i80-tab-panel>\n  <i80-tab-panel><p>3 new comments.</p></i80-tab-panel>\n  <i80-tab-panel><p>Plan settings.</p></i80-tab-panel>\n</i80-tabs>"
      ],
      "attributes": [
        {
          "name": "size",
          "values": "medium · large",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "selected",
          "values": "index",
          "default": "0",
          "notes": "The selected tab, from 0. Or put selected on an <i80-tab>. Event: i80-change with detail.index."
        },
        {
          "name": "i80-tab-panel",
          "values": "",
          "default": "",
          "notes": "The panel for the tab in the same position; it can be empty while the tab is being built."
        },
        {
          "name": "i80-tab",
          "values": "icon · count · selected",
          "default": "",
          "notes": "One tab; its text is the label."
        },
        {
          "name": "i80-tab-panel",
          "values": "",
          "default": "",
          "notes": "The content for the tab in the same position."
        }
      ]
    },
    {
      "tag": "i80-accordion",
      "title": "Accordion",
      "description": "Sections that open and close, for content people need only some of the time.",
      "examples": [
        "<i80-accordion>\n  <i80-accordion-item heading=\"Who can see a plan?\" open>Everyone on the team. Guests only see plans they are invited to.</i80-accordion-item>\n  <i80-accordion-item heading=\"Can I move tasks between plans?\">Yes, drag a task onto another plan in the sidebar.</i80-accordion-item>\n</i80-accordion>"
      ],
      "attributes": [
        {
          "name": "size",
          "values": "small · medium · large",
          "default": "medium",
          "notes": "On the accordion or an item."
        },
        {
          "name": "type",
          "values": "card · flush",
          "default": "card",
          "notes": "On the accordion or an item."
        },
        {
          "name": "heading",
          "values": "text",
          "default": "",
          "notes": "On i80-accordion-item. Or put an element with slot=\"toggle\" inside."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "On i80-accordion-item. Method: toggle(). Event: i80-toggle with detail.open."
        },
        {
          "name": "title-tag",
          "values": "div · h1 … h6",
          "default": "div",
          "notes": "The heading level of the item's title."
        },
        {
          "name": "contains-interactive",
          "values": "boolean",
          "default": "",
          "notes": "Set when the title contains links or buttons, so only the chevron toggles."
        }
      ]
    },
    {
      "tag": "i80-tooltip",
      "title": "Tooltip",
      "description": "A short hint on hover and focus. Wrap a button or link, or plain text, which then becomes a focusable button.",
      "examples": [
        "<i80-tooltip text=\"Copies the plan with all its tasks\">\n  <i80-button icon=\"duplicate\" icon-only color=\"secondary\">Duplicate</i80-button>\n</i80-tooltip>\n<i80-tooltip text=\"Only people on the team can see it\" placement=\"bottom\">Visibility</i80-tooltip>"
      ],
      "attributes": [
        {
          "name": "text",
          "values": "text",
          "default": "",
          "notes": "The tooltip."
        },
        {
          "name": "placement",
          "values": "top · bottom · left · right (-start · -end)",
          "default": "top",
          "notes": "Flips when there is no room."
        }
      ]
    },
    {
      "tag": "i80-toast",
      "title": "Toast",
      "description": "A short-lived message, shown as an elevated inline alert. From JavaScript use showToast(); from HTMX send an HX-Trigger header with an i80-toast event.",
      "examples": [
        "<i80-toast color=\"success\" heading=\"Plan published\" dismissible>Everyone on the team can see it now.</i80-toast>"
      ],
      "attributes": [
        {
          "name": "color, heading, icon, dismissible",
          "values": "",
          "default": "",
          "notes": "As Alert."
        },
        {
          "name": "showToast({heading, text, color, timeout})",
          "values": "",
          "default": "timeout 6000",
          "notes": "Stacks toasts in the bottom-right corner. import { showToast } from \".../i80.js\"."
        },
        {
          "name": "HX-Trigger",
          "values": "{\"i80-toast\": {\"heading\": \"Saved\", \"color\": \"success\"}}",
          "default": "",
          "notes": "Response header for HTMX."
        }
      ]
    },
    {
      "tag": "i80-badge",
      "title": "Badge and Tag",
      "description": "Badges show a status; tags label or categorise, optionally as a link or dismissible.",
      "examples": [
        "<i80-badge>Draft</i80-badge>\n<i80-badge color=\"success\" icon=\"check\">Active</i80-badge>\n<i80-badge color=\"warning\" type=\"outlined\">Due soon</i80-badge>\n<i80-badge color=\"critical\" type=\"inverted\" size=\"small\">Overdue</i80-badge>",
        "<i80-tag>Marketing</i80-tag>\n<i80-tag href=\"#tags/q3\" color=\"secondary\">Q3</i80-tag>\n<i80-tag dismissible>Removable</i80-tag>"
      ],
      "attributes": [
        {
          "name": "color (badge)",
          "values": "neutral · neutral-dark-mode · highlight · success · warning · critical",
          "default": "neutral",
          "notes": ""
        },
        {
          "name": "type (badge)",
          "values": "filled · inverted · outlined",
          "default": "filled",
          "notes": ""
        },
        {
          "name": "size (badge)",
          "values": "small · medium · large",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "icon, icon-only (badge)",
          "values": "",
          "default": "",
          "notes": ""
        },
        {
          "name": "href, color, dismissible (tag)",
          "values": "",
          "default": "",
          "notes": "color only with href: primary · secondary. dismissible fires i80-dismiss."
        }
      ]
    },
    {
      "tag": "i80-card",
      "title": "Card",
      "description": "A container with elevation or a border. For a card in a list, write the <li> yourself around it.",
      "examples": [
        "<i80-card level=\"mid\" level-hover=\"high\"><div style=\"padding:16px\">An elevated card</div></i80-card>\n<i80-card border background=\"neutral-secondary\"><div style=\"padding:16px\">A bordered card</div></i80-card>"
      ],
      "attributes": [
        {
          "name": "level / level-hover / level-active",
          "values": "base · mid · high",
          "default": "base",
          "notes": ""
        },
        {
          "name": "border",
          "values": "boolean",
          "default": "",
          "notes": "Border instead of shadow."
        },
        {
          "name": "background",
          "values": "neutral-primary · neutral-secondary",
          "default": "neutral-primary",
          "notes": ""
        },
        {
          "name": "overflow",
          "values": "visible · hidden",
          "default": "visible",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-link-standalone",
      "title": "Links",
      "description": "Standalone links stand on their own line with an icon; inline links sit inside text.",
      "examples": [
        "<i80-link-standalone href=\"#\" icon=\"arrow-left\">All plans</i80-link-standalone>\n<p>Read the <i80-link-inline href=\"https://design.i80.dk\" icon=\"external-link\" external>design guidelines</i80-link-inline> first.</p>"
      ],
      "attributes": [
        {
          "name": "href, external",
          "values": "",
          "default": "",
          "notes": ""
        },
        {
          "name": "color",
          "values": "primary · secondary",
          "default": "primary",
          "notes": ""
        },
        {
          "name": "icon, icon-position",
          "values": "",
          "default": "standalone: arrow-right, leading · inline: trailing",
          "notes": ""
        },
        {
          "name": "size (standalone)",
          "values": "small · medium · large",
          "default": "medium",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-table",
      "title": "Table",
      "description": "Styles a table your server already rendered: you write plain <table> markup, the element adds the i80 classes. A <th sort> becomes a sort button that sorts the rows in the browser; with sort-mode=\"server\" it only fires i80-sort so the server can sort.",
      "examples": [
        "<i80-table caption=\"Plans\" striped>\n  <table>\n    <thead>\n      <tr><th sort>Plan</th><th>Owner</th><th align=\"right\" sort>Tasks</th></tr>\n    </thead>\n    <tbody>\n      <tr><td>Q3 launch</td><td>Ada</td><td align=\"right\">14</td></tr>\n      <tr><td>Billing rework</td><td>Grace</td><td align=\"right\">3</td></tr>\n      <tr><td>Onboarding</td><td>Linus</td><td align=\"right\">28</td></tr>\n    </tbody>\n  </table>\n</i80-table>"
      ],
      "attributes": [
        {
          "name": "density",
          "values": "short · medium · tall · default",
          "default": "medium",
          "notes": "Row height."
        },
        {
          "name": "valign",
          "values": "top · middle · baseline",
          "default": "top",
          "notes": ""
        },
        {
          "name": "striped",
          "values": "boolean",
          "default": "",
          "notes": "Alternating row background."
        },
        {
          "name": "layout",
          "values": "fixed",
          "default": "",
          "notes": "Equal column widths (table-layout: fixed)."
        },
        {
          "name": "caption",
          "values": "text",
          "default": "",
          "notes": "Read by screen readers, not shown."
        },
        {
          "name": "sort-mode",
          "values": "client · server",
          "default": "client",
          "notes": "server only fires i80-sort with {key, order, index}."
        },
        {
          "name": "th sort",
          "values": "empty · asc · desc",
          "default": "",
          "notes": "Makes the column sortable and sets the starting order."
        },
        {
          "name": "th sort-key / td sort-value",
          "values": "text",
          "default": "",
          "notes": "What the event reports, and what to sort by instead of the cell text."
        },
        {
          "name": "td align / th align",
          "values": "left · center · right",
          "default": "left",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-pagination",
      "title": "Pagination",
      "description": "Page links for a list. With href-template they are real links (the server renders the page); without it they are buttons that fire i80-page.",
      "examples": [
        "<i80-pagination total=\"248\" page=\"3\" per-page=\"25\" show-info show-size-selector></i80-pagination>",
        "<i80-pagination total=\"248\" page=\"3\" per-page=\"25\" href-template=\"?page={page}&amp;per={perPage}\"></i80-pagination>\n<i80-pagination type=\"compact\" has-next has-previous></i80-pagination>"
      ],
      "attributes": [
        {
          "name": "total",
          "values": "number",
          "default": "",
          "notes": "Number of items. Leave it out when you don't know it (use type=\"compact\")."
        },
        {
          "name": "page / per-page",
          "values": "number",
          "default": "1 / 25",
          "notes": "Event: i80-page with {page, perPage}."
        },
        {
          "name": "type",
          "values": "numbered · compact",
          "default": "numbered",
          "notes": "Compact has arrows only."
        },
        {
          "name": "has-next / has-previous",
          "values": "boolean",
          "default": "",
          "notes": "Override which arrows are active, e.g. for cursor paging."
        },
        {
          "name": "href-template",
          "values": "URL with {page} and {perPage}",
          "default": "",
          "notes": "Renders links instead of buttons."
        },
        {
          "name": "show-info",
          "values": "boolean",
          "default": "",
          "notes": "Shows \"51–75 of 248\"."
        },
        {
          "name": "show-size-selector, page-sizes, size-label",
          "values": "boolean, list, text",
          "default": "10,25,50,100",
          "notes": "Items-per-page select. Event: i80-page-size."
        },
        {
          "name": "aria-label",
          "values": "text",
          "default": "Pagination",
          "notes": "The name of the navigation landmark."
        }
      ]
    },
    {
      "tag": "i80-app-header",
      "title": "App Header",
      "description": "The bar across the top of an app. Below the breakpoint the actions collapse behind a Menu button. Put the main navigation in slot=\"global\" and the account side in slot=\"utility\".",
      "examples": [
        "<i80-app-header home-href=\"#\" home-text=\"zPlan\" home-icon=\"grid\">\n  <i80-button slot=\"global\" color=\"tertiary\" href=\"#plans\">Plans</i80-button>\n  <i80-button slot=\"global\" color=\"tertiary\" href=\"#people\">People</i80-button>\n  <i80-dropdown slot=\"utility\" text=\"Ada\" color=\"secondary\" size=\"small\" position=\"bottom-left\">\n    <i80-dropdown-item icon=\"user\">Profile</i80-dropdown-item>\n    <i80-dropdown-item icon=\"sign-out\">Sign out</i80-dropdown-item>\n  </i80-dropdown>\n</i80-app-header>"
      ],
      "attributes": [
        {
          "name": "home-href, home-text, home-icon",
          "values": "",
          "default": "",
          "notes": "The link back to the front page."
        },
        {
          "name": "home-icon-only",
          "values": "boolean",
          "default": "",
          "notes": "Shows the icon alone; the text becomes its accessible name."
        },
        {
          "name": "breakpoint",
          "values": "px",
          "default": "768",
          "notes": "Below this width the menu button appears."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Opens the collapsed menu. Method: toggle()."
        }
      ]
    },
    {
      "tag": "i80-app-side-nav",
      "title": "App Side Nav",
      "description": "The local navigation down the side of an app. On a wide screen the toggle button minimises it to icons; below the breakpoint it slides in over the page and closes on the overlay or Escape.",
      "examples": [
        "<i80-app-side-nav style=\"height: 320px\">\n  <i80-nav-title>Plans</i80-nav-title>\n  <i80-nav-link href=\"#plans\" icon=\"folder\" count=\"12\" active>All plans</i80-nav-link>\n  <i80-nav-link href=\"#archive\" icon=\"archive\">Archive</i80-nav-link>\n  <i80-nav-title>Team</i80-nav-title>\n  <i80-nav-link href=\"#people\" icon=\"users\">People</i80-nav-link>\n  <i80-nav-link href=\"https://design.i80.dk\" icon=\"guide\" external>Design system</i80-nav-link>\n</i80-app-side-nav>"
      ],
      "attributes": [
        {
          "name": "minimized",
          "values": "boolean",
          "default": "",
          "notes": "Minimised on a wide screen. Event: i80-toggle with {minimized}."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Open on a narrow screen. Events: i80-open, i80-close."
        },
        {
          "name": "responsive",
          "values": "boolean",
          "default": "true",
          "notes": "responsive=\"false\" gives a plain sidebar with no toggle."
        },
        {
          "name": "breakpoint",
          "values": "px",
          "default": "1088",
          "notes": "Below this width it behaves as a panel."
        },
        {
          "name": "label",
          "values": "text",
          "default": "Application local navigation",
          "notes": "The name screen readers announce."
        },
        {
          "name": "i80-nav-link",
          "values": "href · icon · count · badge · active · external · back",
          "default": "",
          "notes": "One link; its text is the label."
        },
        {
          "name": "i80-nav-title",
          "values": "",
          "default": "",
          "notes": "A heading over a group of links."
        }
      ]
    },
    {
      "tag": "i80-app-footer",
      "title": "App Footer",
      "description": "The line at the bottom of an app: links and a copyright. Links you write inside become footer links.",
      "examples": [
        "<i80-app-footer year=\"2026\" copyright=\"© 2026 i80\">\n  <a href=\"#support\">Support</a>\n  <a href=\"#terms\">Terms</a>\n  <a href=\"#privacy\">Privacy</a>\n</i80-app-footer>"
      ],
      "attributes": [
        {
          "name": "theme",
          "values": "light · dark",
          "default": "light",
          "notes": ""
        },
        {
          "name": "year",
          "values": "number",
          "default": "this year",
          "notes": "Used in the default copyright line."
        },
        {
          "name": "copyright",
          "values": "text",
          "default": "© <year> i80",
          "notes": "Replaces the whole line."
        },
        {
          "name": "aria-label",
          "values": "text",
          "default": "Footer",
          "notes": "The name of the list."
        }
      ]
    },
    {
      "tag": "i80-breadcrumb",
      "title": "Breadcrumb",
      "description": "Where the page sits in the structure. Real links in an ordered list; the last item, or one marked current, becomes the current page.",
      "examples": [
        "<i80-breadcrumb>\n  <i80-crumb href=\"#\" icon=\"home\">Home</i80-crumb>\n  <i80-crumb href=\"#plans\">Plans</i80-crumb>\n  <i80-crumb>Q3 launch</i80-crumb>\n</i80-breadcrumb>"
      ],
      "attributes": [
        {
          "name": "aria-label",
          "values": "text",
          "default": "Breadcrumb",
          "notes": "The name of the navigation landmark."
        },
        {
          "name": "items-can-wrap",
          "values": "boolean",
          "default": "",
          "notes": "Let the trail wrap onto several lines."
        },
        {
          "name": "i80-crumb",
          "values": "href · icon · current · external",
          "default": "",
          "notes": "One step; its text is the label."
        }
      ]
    },
    {
      "tag": "i80-application-state",
      "title": "Application State",
      "description": "The empty, error and loading screens: a title, an explanation and what to do next. Content goes in the body; slot=\"footer\" holds the actions and slot=\"media\" an illustration above the title.",
      "examples": [
        "<i80-application-state title=\"No plans yet\" icon=\"folder\" align=\"center\">\n  <p>Plans keep tasks, dates and owners in one place.</p>\n  <i80-button slot=\"footer\" color=\"primary\" icon=\"plus\">New plan</i80-button>\n</i80-application-state>",
        "<i80-application-state title=\"Page not found\" error-code=\"404\" title-tag=\"h1\">\n  <p>Check the address, or go back to your plans.</p>\n  <i80-link-standalone slot=\"footer\" href=\"#\" icon=\"arrow-left\">All plans</i80-link-standalone>\n</i80-application-state>"
      ],
      "attributes": [
        {
          "name": "title",
          "values": "text",
          "default": "",
          "notes": "The headline."
        },
        {
          "name": "title-tag",
          "values": "div · h1 … h6",
          "default": "div",
          "notes": "The heading level."
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "24px icon above the title."
        },
        {
          "name": "error-code",
          "values": "text",
          "default": "",
          "notes": "Shows \"ERROR 404\" above the title."
        },
        {
          "name": "align",
          "values": "left · center",
          "default": "left",
          "notes": ""
        },
        {
          "name": "auto-centered",
          "values": "boolean",
          "default": "",
          "notes": "Centres the block in the space it has."
        }
      ]
    },
    {
      "tag": "i80-icon",
      "title": "Icon",
      "description": "A Flight icon. Without a title it is decorative and hidden from screen readers; give it a title and it becomes an image with that name.",
      "examples": [
        "<i80-icon name=\"plus\"></i80-icon>\n<i80-icon name=\"folder\" size=\"24\"></i80-icon>\n<i80-icon name=\"alert-triangle\" color=\"warning\" title=\"Check this\"></i80-icon>\n<i80-icon name=\"check\" color=\"success\"></i80-icon>"
      ],
      "attributes": [
        {
          "name": "name",
          "values": "icon name",
          "default": "",
          "notes": "One of the Flight icons, e.g. plus, trash, external-link."
        },
        {
          "name": "size",
          "values": "16 · 24",
          "default": "16",
          "notes": ""
        },
        {
          "name": "color",
          "values": "token name or CSS colour",
          "default": "currentColor",
          "notes": "A foreground token (action, success, warning, critical, faint …) or any CSS colour."
        },
        {
          "name": "title",
          "values": "text",
          "default": "",
          "notes": "Makes the icon an image with this accessible name."
        },
        {
          "name": "inline",
          "values": "boolean",
          "default": "",
          "notes": "Aligns the icon with the surrounding text."
        },
        {
          "name": "stretched",
          "values": "boolean",
          "default": "",
          "notes": "Fills its container instead of using the fixed size."
        }
      ]
    },
    {
      "tag": "i80-icon-tile",
      "title": "Icon Tile",
      "description": "A rounded tile with an icon in it, to mark what a page or a row is about.",
      "examples": [
        "<i80-icon-tile icon=\"folder\"></i80-icon-tile>\n<i80-icon-tile icon=\"users\" color=\"brand\" size=\"large\"></i80-icon-tile>\n<i80-icon-tile icon=\"calendar\" color=\"green\" size=\"small\"></i80-icon-tile>"
      ],
      "attributes": [
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": ""
        },
        {
          "name": "size",
          "values": "small · medium · large",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "color",
          "values": "neutral · brand · coral · pink · green · sky · purple · blue · yellow · teal",
          "default": "neutral",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-separator",
      "title": "Separator",
      "description": "A line between groups of content.",
      "examples": [
        "<i80-separator></i80-separator>\n<i80-separator spacing=\"0\"></i80-separator>"
      ],
      "attributes": [
        {
          "name": "spacing",
          "values": "24 · 0",
          "default": "24",
          "notes": "The margin above and below the line."
        }
      ]
    },
    {
      "tag": "i80-badge-count",
      "title": "Badge Count",
      "description": "A number beside a label: unread items, results, tasks.",
      "examples": [
        "<i80-badge-count>12</i80-badge-count>\n<i80-badge-count type=\"inverted\">3</i80-badge-count>\n<i80-badge-count type=\"outlined\" size=\"small\">99+</i80-badge-count>"
      ],
      "attributes": [
        {
          "name": "text",
          "values": "text",
          "default": "",
          "notes": "Or write the number inside the tag."
        },
        {
          "name": "size",
          "values": "small · medium",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "type",
          "values": "filled · inverted · outlined",
          "default": "filled",
          "notes": ""
        },
        {
          "name": "color",
          "values": "neutral · neutral-dark-mode",
          "default": "neutral",
          "notes": ""
        }
      ]
    },
    {
      "tag": "i80-button-set",
      "title": "Button Set",
      "description": "Buttons in a row with the spacing they should have. A tertiary button is pushed to the end.",
      "examples": [
        "<i80-button-set>\n  <i80-button color=\"primary\">Save</i80-button>\n  <i80-button color=\"secondary\">Cancel</i80-button>\n  <i80-button color=\"tertiary\" icon=\"trash\">Delete</i80-button>\n</i80-button-set>"
      ],
      "attributes": []
    },
    {
      "tag": "i80-calendar",
      "title": "Calendar (draft)",
      "description": "Our own component: a month grid or a week agenda for zPlan, built only from i80 tokens. Events are <i80-event> elements inside, so the server (or an HTMX swap) renders them and the calendar redraws. First version: the API may still change.",
      "examples": [
        "<i80-calendar month=\"2026-09\" selected=\"2026-09-19\" locale=\"en-GB\">\n  <i80-event date=\"2026-09-07\" color=\"action\">Kick-off</i80-event>\n  <i80-event date=\"2026-09-21\" color=\"success\" href=\"#launch\">Launch</i80-event>\n  <i80-event start=\"2026-09-22\" end=\"2026-09-24\" color=\"warning\">Offsite</i80-event>\n  <i80-event date=\"2026-09-30\" color=\"critical\">Budget due</i80-event>\n</i80-calendar>",
        "<i80-calendar view=\"week\" selected=\"2026-09-23\" locale=\"en-GB\">\n  <i80-event date=\"2026-09-21\" color=\"success\">Launch</i80-event>\n  <i80-event start=\"2026-09-22\" end=\"2026-09-24\" color=\"warning\">Offsite</i80-event>\n  <i80-event date=\"2026-09-23\" time=\"09:00\" end-time=\"09:15\" color=\"action\">Standup</i80-event>\n</i80-calendar>",
        "<i80-calendar view=\"day\" selected=\"2026-09-22\" locale=\"da-DK\"\n  label-previous=\"Forrige dag\" label-today=\"I dag\" label-next=\"Næste dag\" label-all-day=\"Hele dagen\">\n  <i80-event start=\"2026-09-22\" end=\"2026-09-24\" color=\"warning\">Offsite</i80-event>\n  <i80-event date=\"2026-09-22T09:00\" end=\"2026-09-22T09:15\" color=\"action\">Stand-up</i80-event>\n  <i80-event date=\"2026-09-22\" time=\"13:30\" end-time=\"14:30\" href=\"#retro\">Retrospektiv</i80-event>\n</i80-calendar>"
      ],
      "attributes": [
        {
          "name": "month",
          "values": "YYYY-MM",
          "default": "the current month",
          "notes": "The month shown."
        },
        {
          "name": "selected",
          "values": "YYYY-MM-DD",
          "default": "",
          "notes": "The selected day. Event: i80-select with detail.date and detail.events."
        },
        {
          "name": "view",
          "values": "month · week · day",
          "default": "month",
          "notes": "Week and day show an agenda with times."
        },
        {
          "name": "week-start",
          "values": "0–6",
          "default": "1",
          "notes": "First day of the week, 0 = Sunday."
        },
        {
          "name": "locale",
          "values": "BCP 47 tag",
          "default": "the page's lang",
          "notes": "Month and weekday names, e.g. da-DK."
        },
        {
          "name": "today",
          "values": "YYYY-MM-DD",
          "default": "today",
          "notes": "Override today (tests, other time zones)."
        },
        {
          "name": "heading-level",
          "values": "1–6",
          "default": "2",
          "notes": "The level of the month title."
        },
        {
          "name": "label-previous, label-today, label-next",
          "values": "text",
          "default": "Previous month · Today · Next month",
          "notes": "Button texts, e.g. Forrige måned · I dag · Næste måned."
        },
        {
          "name": "label-all-day",
          "values": "text",
          "default": "All day",
          "notes": "Shown in the agenda for an event without a time."
        },
        {
          "name": "i80-event",
          "values": "date · start + end · time + end-time · color (neutral · action · success · warning · critical) · href",
          "default": "",
          "notes": "One event; its text is the label. Dates may carry a time (2026-09-22T09:00). Changing the month fires i80-navigate, so a server can load that month's events."
        }
      ]
    },
    {
      "tag": "i80-coverage-map",
      "title": "Coverage Map (draft)",
      "description": "Our own component for zPlan: days across, rows (day, evening, night …) down, and in each cell how many are planned against how many are needed. It counts nothing itself — the server sets need, have and status — and only problems carry colour, so the holes stand out. First version: the API may still change.",
      "examples": [
        "<i80-coverage-map start=\"2026-10-05\" days=\"14\" selected=\"2026-10-07\" locale=\"en-GB\" label=\"Coverage, two weeks\">\n  <i80-coverage-row key=\"day\" label=\"Day\" detail=\"06–14\"></i80-coverage-row>\n  <i80-coverage-row key=\"evening\" label=\"Evening\" detail=\"14–21\"></i80-coverage-row>\n  <i80-coverage-row key=\"night\" label=\"Night\" detail=\"21–06\"></i80-coverage-row>\n  <i80-coverage date=\"2026-10-05\" row=\"day\" need=\"3\" have=\"3\"></i80-coverage>\n  <i80-coverage date=\"2026-10-06\" row=\"day\" need=\"3\" have=\"2\" status=\"hard\" note=\"1 short 07–09\"></i80-coverage>\n  <i80-coverage date=\"2026-10-07\" row=\"evening\" need=\"3\" have=\"3\" status=\"soft\" note=\"11 h rest broken\"></i80-coverage>\n  <i80-coverage date=\"2026-10-08\" row=\"day\" need=\"2\" have=\"3\" status=\"over\"></i80-coverage>\n  <i80-coverage date=\"2026-10-10\" row=\"night\" need=\"1\" have=\"0\" status=\"hard\" note=\"Nobody on the night\"></i80-coverage>\n</i80-coverage-map>"
      ],
      "attributes": [
        {
          "name": "start",
          "values": "YYYY-MM-DD",
          "default": "today",
          "notes": "The first day shown."
        },
        {
          "name": "days",
          "values": "1–366",
          "default": "28",
          "notes": "How many days."
        },
        {
          "name": "selected, selected-row",
          "values": "YYYY-MM-DD · row key",
          "default": "",
          "notes": "The selected day, or one cell. Event: i80-coverage-select with detail.date, row (null for a whole day), need, have, status, note and el."
        },
        {
          "name": "today",
          "values": "YYYY-MM-DD",
          "default": "",
          "notes": "Marks that day."
        },
        {
          "name": "locale",
          "values": "BCP 47 tag",
          "default": "the page's lang",
          "notes": "Weekday and month names, e.g. da-DK."
        },
        {
          "name": "label, label-week",
          "values": "text",
          "default": "Coverage · Week",
          "notes": "The table's name, and the word in front of the week number (Uge)."
        },
        {
          "name": "i80-coverage-row",
          "values": "key · label · detail",
          "default": "",
          "notes": "One row, top to bottom in the order written."
        },
        {
          "name": "i80-coverage",
          "values": "date · row · need · have · status (ok · soft · hard · over · none) · note",
          "default": "",
          "notes": "One cell. Shows have/need; the note goes into the cell's name and tooltip."
        }
      ]
    },
    {
      "tag": "i80-timeline",
      "title": "Timeline (draft)",
      "description": "Our own component for zPlan: one day on a time axis. On top a coverage strip — the fill is what is planned, the line what is needed, short stretches are buttons — and under it lanes of bars that can be dragged along the day, onto another lane, or stretched at either end (also with the keyboard). It only draws and reports: a change fires a cancellable i80-timeline-change the server answers, and status comes from the server. First version: the API may still change.",
      "examples": [
        "<i80-timeline day-start=\"06:00\" step=\"15\" label=\"Wed 7 Oct\" label-coverage=\"Coverage\">\n  <i80-need from=\"06:00\" to=\"07:00\" count=\"2\"></i80-need>\n  <i80-need from=\"07:00\" to=\"09:00\" count=\"3\"></i80-need>\n  <i80-need from=\"09:00\" to=\"14:00\" count=\"2\"></i80-need>\n  <i80-need from=\"14:00\" to=\"21:00\" count=\"2\"></i80-need>\n  <i80-need from=\"21:00\" to=\"06:00\" count=\"1\"></i80-need>\n  <i80-lane key=\"meds\" label=\"Medication\" detail=\"07–21\" readonly>\n    <i80-bar from=\"07:00\" to=\"21:00\" color=\"green\">Covered</i80-bar>\n  </i80-lane>\n  <i80-lane key=\"open\" label=\"Open shifts\" no-coverage>\n    <i80-bar id=\"s5\" from=\"21:00\" to=\"06:00\" color=\"neutral\" pattern=\"outline\">NV</i80-bar>\n  </i80-lane>\n  <i80-lane key=\"ada\" label=\"Ada\" detail=\"Medication · 30 h\">\n    <i80-bar id=\"s1\" from=\"06:00\" to=\"14:00\" color=\"blue\">DA</i80-bar>\n  </i80-lane>\n  <i80-lane key=\"grace\" label=\"Grace\" detail=\"37 h\">\n    <i80-bar id=\"s2\" from=\"07:00\" to=\"15:00\" color=\"blue\" status=\"soft\" note=\"10 h rest after the evening\">DA</i80-bar>\n  </i80-lane>\n  <i80-lane key=\"linus\" label=\"Linus\" detail=\"Trainee\">\n    <i80-bar id=\"s3\" from=\"14:00\" to=\"21:00\" color=\"purple\">AF</i80-bar>\n  </i80-lane>\n  <i80-lane key=\"mary\" label=\"Mary\" detail=\"On call\">\n    <i80-bar id=\"s4\" from=\"14:00\" to=\"21:00\" color=\"purple\" status=\"hard\" note=\"Over the 48 h week\">AF</i80-bar>\n  </i80-lane>\n</i80-timeline>"
      ],
      "attributes": [
        {
          "name": "day-start",
          "values": "HH:MM",
          "default": "00:00",
          "notes": "Where the 24-hour axis begins; an end at or before its start runs past midnight."
        },
        {
          "name": "step",
          "values": "5 · 10 · 15 · 30 · 60",
          "default": "15",
          "notes": "Minutes a drag or an arrow key moves."
        },
        {
          "name": "label, label-coverage, label-short, label-instructions",
          "values": "text",
          "default": "· Coverage · Short · (how to move with keys)",
          "notes": "Texts, e.g. Dækning · Mangler."
        },
        {
          "name": "hour-labels",
          "values": "number",
          "default": "2",
          "notes": "An hour label every n hours."
        },
        {
          "name": "i80-need",
          "values": "from · to · count",
          "default": "",
          "notes": "Needed people in that span; overlapping needs add up."
        },
        {
          "name": "i80-lane",
          "values": "key · label · detail · readonly · no-coverage · marked",
          "default": "",
          "notes": "A row. readonly: its bars don't move or count. no-coverage: its bars move but don't count (open shifts). marked: highlighted."
        },
        {
          "name": "i80-bar",
          "values": "id · from · to · color (neutral · blue · purple · green · amber · red) · pattern (solid · striped · outline) · status (ok · soft · hard) · note · selected · locked",
          "default": "",
          "notes": "A bar; its text is the label. Status adds a ring and an icon; the note goes into its name."
        },
        {
          "name": "events",
          "values": "",
          "default": "",
          "notes": "i80-timeline-change (cancellable): {bar, id, kind: move · resize, from, to, lane, previous}. i80-timeline-select: {kind: bar · lane · gap, …}."
        }
      ]
    },
    {
      "tag": "i80-file-input",
      "title": "File Input",
      "description": "A labelled <input type=\"file\">: the native control, only styled, so the file picker and the multipart upload work as usual. An <input> you write inside (e.g. Django's {{ form.logo }}) is kept.",
      "examples": [
        "<i80-file-input label=\"Upload a file\" name=\"logo\" accept=\"image/png,image/svg+xml\" helper-text=\"File size should be a maximum of 2 MB.\"></i80-file-input>",
        "<i80-file-input label=\"Logo\" name=\"logo\" required></i80-file-input>\n<i80-file-input label=\"Screenshots\" name=\"shots\" multiple optional error=\"The provided file format is not valid.\"></i80-file-input>",
        "<i80-file-input label=\"Logo\" error=\"This field is required.\">\n  <input type=\"file\" name=\"logo\" id=\"id_logo\" accept=\".png,.svg\" required>\n</i80-file-input>"
      ],
      "attributes": [
        {
          "name": "label, helper-text, error, invalid, required, optional, disabled",
          "values": "",
          "default": "",
          "notes": "As Text Input."
        },
        {
          "name": "name, accept, multiple, capture, form",
          "values": "",
          "default": "",
          "notes": "Passed to the created <input type=\"file\">."
        },
        {
          "name": "width",
          "values": "CSS length",
          "default": "",
          "notes": ""
        },
        {
          "name": "aria-describedby",
          "values": "id list",
          "default": "",
          "notes": "Merged with the helper text and error ids the element links."
        },
        {
          "name": "files",
          "values": "property",
          "default": "",
          "notes": "The chosen FileList; value is the first file's path, as on a native input."
        }
      ]
    },
    {
      "tag": "i80-masked-input",
      "title": "Masked Input",
      "description": "A secret in a real <input> (or <textarea> with multiline): hidden until the user reveals it, with an optional copy button. The form submits the value as usual; an <input> or <textarea> you write inside is kept.",
      "examples": [
        "<i80-masked-input label=\"Terraform Cloud team token\" name=\"token\" value=\"036215df4996ca649928d8864b4df9e42cba0d6d\" copy-button></i80-masked-input>",
        "<i80-masked-input label=\"Team token\" name=\"token\" value=\"036215df4996ca649928d8864b4df9e42cba0d6d\" visible width=\"320px\" helper-text=\"The token must include permissions to manage workspaces.\"></i80-masked-input>",
        "<i80-masked-input label=\"Private key\" name=\"key\" multiline rows=\"4\" copy-button error=\"The provided key is not valid\"\n  value=\"-----BEGIN RSA PRIVATE KEY-----&#10;MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf&#10;-----END RSA PRIVATE KEY-----\"></i80-masked-input>"
      ],
      "attributes": [
        {
          "name": "label, helper-text, error, invalid, required, optional, disabled, readonly",
          "values": "",
          "default": "",
          "notes": "As Text Input."
        },
        {
          "name": "name, value, placeholder, minlength, maxlength, pattern, autocomplete, rows, form",
          "values": "",
          "default": "",
          "notes": "Passed to the created control."
        },
        {
          "name": "multiline",
          "values": "boolean",
          "default": "",
          "notes": "Renders a <textarea> instead of an <input>."
        },
        {
          "name": "visible",
          "values": "boolean",
          "default": "",
          "notes": "Starts revealed instead of hidden."
        },
        {
          "name": "copy-button",
          "values": "boolean",
          "default": "",
          "notes": "Adds a button that copies the value to the clipboard."
        },
        {
          "name": "width / height",
          "values": "CSS length",
          "default": "",
          "notes": "Width of the control, height of the textarea."
        },
        {
          "name": "aria-describedby",
          "values": "id list",
          "default": "",
          "notes": "Merged with the helper text and error ids the element links."
        },
        {
          "name": "masked",
          "values": "property",
          "default": "true",
          "notes": "Whether the content is hidden. Events: i80-visibility-change (detail.masked), i80-copy (detail.ok, detail.text)."
        }
      ]
    },
    {
      "tag": "i80-radio-card",
      "title": "Radio Card",
      "description": "A radio button as a card: a <label> around a real <input type=\"radio\">, so the whole card is clickable, the arrow keys move between cards and the form submits the chosen value. <i80-radio-card-group> gives them one name, a legend and the error.",
      "examples": [
        "<i80-radio-card-group legend=\"Create connection\" name=\"connection\" alignment=\"center\">\n  <i80-radio-card label=\"Quick peering\" description=\"The fastest way to connect to your provider's network.\" icon=\"aws-color\" badge=\"2-5 min\" value=\"quick\" checked></i80-radio-card>\n  <i80-radio-card label=\"Manual peering using the AWS CLI\" description=\"Apply the connection settings from a template.\" icon=\"aws-color\" badge=\"5-10 min\" value=\"cli\"></i80-radio-card>\n  <i80-radio-card label=\"Manual peering in the web console\" description=\"Follow the instructions at the provider's side.\" icon=\"cloud\" badge=\"30-60 min\" value=\"console\"></i80-radio-card>\n</i80-radio-card-group>",
        "<i80-radio-card-group legend=\"Team access\" name=\"access\" layout=\"vertical\" control-position=\"left\" helper-text=\"You can change this later\" required>\n  <i80-radio-card label=\"Admin\" description=\"Full admin capabilities for this project and every workspace in it.\" value=\"admin\"></i80-radio-card>\n  <i80-radio-card label=\"Write\" description=\"Can edit the project and move workspaces.\" value=\"write\"></i80-radio-card>\n  <i80-radio-card label=\"Read\" description=\"Can see the project and its workspaces.\" value=\"read\"></i80-radio-card>\n</i80-radio-card-group>",
        "<i80-radio-card-group legend=\"Cluster type\" name=\"cluster\">\n  <i80-radio-card label=\"Managed\" value=\"managed\" max-width=\"320px\">\n    <i80-badge>6 clusters left</i80-badge>\n    <ul><li>Connect workloads in your own network</li><li>Operations handled for you</li></ul>\n  </i80-radio-card>\n  <i80-radio-card label=\"Self-managed\" value=\"self\" max-width=\"320px\" disabled>\n    <i80-badge icon=\"kubernetes\">Kubernetes only</i80-badge>\n    <ul><li>Multi-cloud artifact registry</li><li>Golden images workflow</li></ul>\n  </i80-radio-card>\n</i80-radio-card-group>"
      ],
      "attributes": [
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "The card's heading."
        },
        {
          "name": "description",
          "values": "text",
          "default": "",
          "notes": "One line under the label."
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "A 24px icon above the label."
        },
        {
          "name": "badge",
          "values": "text",
          "default": "",
          "notes": "A badge under the label; write <i80-badge> inside for colours or an icon."
        },
        {
          "name": "value, name, checked, disabled",
          "values": "",
          "default": "",
          "notes": "Passed to the radio. The group supplies the name."
        },
        {
          "name": "alignment",
          "values": "left · center",
          "default": "left",
          "notes": "Also on the group, for every card."
        },
        {
          "name": "control-position",
          "values": "bottom · left",
          "default": "bottom",
          "notes": "Where the radio sits. Also on the group."
        },
        {
          "name": "max-width",
          "values": "CSS length",
          "default": "",
          "notes": "A fixed width instead of filling the row."
        },
        {
          "name": "i80-radio-card-group",
          "values": "legend · name · helper-text · error · layout (horizontal · vertical) · required · optional · alignment · control-position",
          "default": "",
          "notes": "The fieldset around the cards. Anything else written in a card is kept in its content."
        }
      ]
    },
    {
      "tag": "i80-page-header",
      "title": "Page Header",
      "description": "The block at the top of a page: where you are, what the page is, and what you can do here. Title, subtitle and description are attributes; the breadcrumb, the badges, the actions and your own metadata are children, with slot=\"breadcrumb\", slot=\"badges\" and slot=\"actions\". Anything without a slot sits under the title as metadata.",
      "examples": [
        "<i80-page-header title=\"Q3 launch\" subtitle=\"Created 4 March by Ada\" description=\"Everything the launch depends on, in one plan.\"\n  icon=\"folder\" icon-color=\"pink\">\n  <i80-breadcrumb slot=\"breadcrumb\" aria-label=\"Plan\">\n    <i80-crumb href=\"#\" icon=\"home\">Home</i80-crumb>\n    <i80-crumb href=\"#plans\">Plans</i80-crumb>\n    <i80-crumb>Q3 launch</i80-crumb>\n  </i80-breadcrumb>\n  <i80-badge slot=\"badges\" color=\"success\" icon=\"check\">On track</i80-badge>\n  <i80-button slot=\"actions\" color=\"primary\" icon=\"plus\">New task</i80-button>\n  <i80-dropdown slot=\"actions\" text=\"Manage\" color=\"secondary\">\n    <i80-dropdown-item icon=\"edit\">Rename</i80-dropdown-item>\n    <i80-dropdown-item color=\"critical\" icon=\"trash\">Delete plan</i80-dropdown-item>\n  </i80-dropdown>\n</i80-page-header>",
        "<i80-page-header title=\"People\" title-tag=\"h2\">\n  <div class=\"i80-text i80-typography-body-100 i80-font-weight-regular i80-foreground-faint\">12 of 25 seats used</div>\n</i80-page-header>"
      ],
      "attributes": [
        {
          "name": "title",
          "values": "text",
          "default": "",
          "notes": "The page's name, in an <h1>."
        },
        {
          "name": "title-tag",
          "values": "h1 … h6",
          "default": "h1",
          "notes": "The heading level."
        },
        {
          "name": "subtitle",
          "values": "text",
          "default": "",
          "notes": "A faint line under the title."
        },
        {
          "name": "description",
          "values": "text",
          "default": "",
          "notes": "A sentence about the page, under the subtitle."
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "Shows an icon tile beside the title."
        },
        {
          "name": "icon-color",
          "values": "neutral · brand · coral · pink · green · sky · purple · blue · yellow · teal",
          "default": "neutral",
          "notes": "The tile's colour."
        },
        {
          "name": "slot=\"breadcrumb\" · slot=\"badges\" · slot=\"actions\"",
          "values": "",
          "default": "",
          "notes": "Above the title, next to it, and at the end of the row."
        }
      ]
    },
    {
      "tag": "i80-segmented-group",
      "title": "Segmented Group",
      "description": "Controls joined into one field: a search box with a filter menu, an input with a button. The segments are the native <input>, <select>, <button> and <a> you write inside — the group adds the i80 classes and rounds only the outer corners, so a server-rendered form looks right before any JavaScript runs. An <i80-dropdown> child is a segment too; anything else is passed through unstyled.",
      "examples": [
        "<i80-segmented-group>\n  <input type=\"search\" placeholder=\"Search services\" aria-label=\"Search services\">\n  <i80-dropdown text=\"Across\" color=\"secondary\">\n    <i80-dropdown-item>Metadata</i80-dropdown-item>\n    <i80-dropdown-item>Tags</i80-dropdown-item>\n    <i80-dropdown-item>Service name</i80-dropdown-item>\n  </i80-dropdown>\n</i80-segmented-group>",
        "<i80-segmented-group label=\"New API key\" helper-text=\"At most five keys per organisation\">\n  <input type=\"text\" name=\"key\" size=\"32\">\n  <button type=\"submit\" name=\"generate\">Generate</button>\n</i80-segmented-group>",
        "<i80-segmented-group full-width max-width=\"480px\">\n  <select aria-label=\"Match\"><option>Any of</option><option>All of</option></select>\n  <input type=\"text\" placeholder=\"tag:production\" aria-label=\"Filter\">\n  <button type=\"button\" class=\"i80-button--color-primary\">Apply</button>\n</i80-segmented-group>"
      ],
      "attributes": [
        {
          "name": "full-width",
          "values": "boolean",
          "default": "",
          "notes": "Fills the width of its container instead of its content."
        },
        {
          "name": "max-width",
          "values": "CSS length",
          "default": "",
          "notes": "How wide the row may grow."
        },
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "Renders the group as a form field, labelling the first control in the row."
        },
        {
          "name": "helper-text",
          "values": "text",
          "default": "",
          "notes": "Linked with aria-describedby."
        },
        {
          "name": "error",
          "values": "text",
          "default": "",
          "notes": "Shows the error and marks the control invalid."
        },
        {
          "name": "required / optional",
          "values": "boolean",
          "default": "",
          "notes": "Adds the Required badge or (Optional) to the label."
        },
        {
          "name": "invalid",
          "values": "boolean",
          "default": "",
          "notes": "Marks the control invalid without a message."
        }
      ]
    },
    {
      "tag": "i80-copy-button",
      "title": "Copy Button",
      "description": "A button that copies a value to the clipboard: either the text you give it, or the text of the element it points at (an input's value, otherwise what the element shows). The icon turns into a check or a cross for a moment, and a live region says so out loud. Method: copy(); event: i80-copy with detail.text and detail.success.",
      "examples": [
        "<p id=\"token\">tok_9f8a-31bd-4c7e</p>\n<i80-copy-button target-to-copy=\"#token\">Copy</i80-copy-button>",
        "<i80-copy-button text-to-copy=\"someSecretThingGoesHere\">Copy your secret key</i80-copy-button>\n<i80-copy-button text-to-copy=\"someSecretThingGoesHere\" size=\"small\">Copy</i80-copy-button>\n<i80-copy-button text-to-copy=\"someSecretThingGoesHere\" icon-only>Copy your secret key</i80-copy-button>",
        "<i80-text-input label=\"Cluster name\">\n  <input type=\"text\" id=\"cluster-name\" value=\"aws-east-cluster-01\">\n</i80-text-input>\n<i80-copy-button target-to-copy=\"#cluster-name\" icon-only>Copy the cluster name</i80-copy-button>"
      ],
      "attributes": [
        {
          "name": "text-to-copy",
          "values": "text",
          "default": "",
          "notes": "The value to copy."
        },
        {
          "name": "target-to-copy",
          "values": "CSS selector",
          "default": "",
          "notes": "Copy what that element holds instead: an input's value, otherwise its visible text (sr-only text is left out)."
        },
        {
          "name": "size",
          "values": "small · medium",
          "default": "medium",
          "notes": ""
        },
        {
          "name": "icon-only",
          "values": "boolean",
          "default": "",
          "notes": "Shows only the icon; the text becomes the aria-label."
        },
        {
          "name": "full-width",
          "values": "boolean",
          "default": "",
          "notes": "Fills the parent's width."
        },
        {
          "name": "disabled",
          "values": "boolean",
          "default": "",
          "notes": ""
        },
        {
          "name": "copied-message",
          "values": "text",
          "default": "Copied to clipboard",
          "notes": "What the live region says after a copy."
        },
        {
          "name": "error-message",
          "values": "text",
          "default": "Failed to copy to clipboard",
          "notes": "And when it failed."
        }
      ]
    },
    {
      "tag": "i80-copy-snippet",
      "title": "Copy Snippet",
      "description": "A short value in code type that is itself the button copying it. Write the value inside the tag, or in text-to-copy. Method: copy(); event: i80-copy with detail.text and detail.success.",
      "examples": [
        "<i80-copy-snippet>e4rt-yg80-39kt</i80-copy-snippet>\n<i80-copy-snippet color=\"secondary\">e4rt-yg80-39kt</i80-copy-snippet>",
        "<i80-copy-snippet full-width truncated>fbrct1ed-fgr35h-tyng89-wed4r and a good deal more text than fits on one line</i80-copy-snippet>"
      ],
      "attributes": [
        {
          "name": "text-to-copy",
          "values": "text",
          "default": "the tag's own text",
          "notes": "The value shown and copied."
        },
        {
          "name": "color",
          "values": "primary · secondary",
          "default": "primary",
          "notes": "Secondary keeps the value in the normal text colour."
        },
        {
          "name": "full-width",
          "values": "boolean",
          "default": "",
          "notes": ""
        },
        {
          "name": "truncated",
          "values": "boolean",
          "default": "",
          "notes": "Cuts the value off with an ellipsis instead of wrapping."
        },
        {
          "name": "label",
          "values": "text",
          "default": "copy <value>",
          "notes": "The button's accessible name."
        },
        {
          "name": "copied-message / error-message",
          "values": "text",
          "default": "Copied to clipboard",
          "notes": "What the live region says."
        }
      ]
    },
    {
      "tag": "i80-reveal",
      "title": "Reveal",
      "description": "A small toggle that shows and hides the content written inside it, for detail a reader only sometimes needs.",
      "examples": [
        "<i80-reveal text=\"Toggle me\">Additional content</i80-reveal>",
        "<i80-reveal text=\"Show the request\" text-when-open=\"Hide the request\" open>\n  <i80-copy-snippet>curl https://api.i80.dk/v1/plans</i80-copy-snippet>\n</i80-reveal>"
      ],
      "attributes": [
        {
          "name": "text",
          "values": "text",
          "default": "Show more",
          "notes": "The button's label."
        },
        {
          "name": "text-when-open",
          "values": "text",
          "default": "the text value",
          "notes": "A different label while the content is open."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Open on page load. Property: open. Method: toggle(). Event: i80-toggle with detail.open."
        },
        {
          "name": "described-by",
          "values": "id",
          "default": "",
          "notes": "Points the button's aria-describedby at other text on the page."
        }
      ]
    },
    {
      "tag": "i80-stepper-indicator",
      "title": "Stepper Indicator",
      "description": "The status mark of a step on its own: the hexagon with a number, a check or a spinner, or with type=\"task\" just the icon. The Stepper List and Nav draw their own, so this is for a table row or a list of your own.",
      "examples": [
        "<i80-stepper-indicator text=\"1\" status=\"incomplete\"></i80-stepper-indicator>\n<i80-stepper-indicator text=\"2\" status=\"progress\"></i80-stepper-indicator>\n<i80-stepper-indicator text=\"3\" status=\"processing\"></i80-stepper-indicator>\n<i80-stepper-indicator text=\"4\" status=\"complete\"></i80-stepper-indicator>",
        "<i80-stepper-indicator type=\"task\" status=\"incomplete\"></i80-stepper-indicator>\n<i80-stepper-indicator type=\"task\" status=\"progress\"></i80-stepper-indicator>\n<i80-stepper-indicator type=\"task\" status=\"processing\"></i80-stepper-indicator>\n<i80-stepper-indicator type=\"task\" status=\"complete\"></i80-stepper-indicator>"
      ],
      "attributes": [
        {
          "name": "type",
          "values": "step · task",
          "default": "step",
          "notes": "The hexagon with a number, or the status icon on its own."
        },
        {
          "name": "status",
          "values": "incomplete · progress · processing · complete",
          "default": "incomplete",
          "notes": ""
        },
        {
          "name": "text",
          "values": "text",
          "default": "",
          "notes": "The step number in the hexagon."
        },
        {
          "name": "interactive",
          "values": "boolean",
          "default": "",
          "notes": "Adds the hover and active styles, for an indicator inside something clickable."
        }
      ]
    },
    {
      "tag": "i80-stepper-list",
      "title": "Stepper List",
      "description": "A vertical list of steps with their status, as a real <ol>. Each <i80-stepper-step> is numbered by its place in the list; its text is the title, and elements inside it become the step's content.",
      "examples": [
        "<i80-stepper-list label=\"Setting up your cluster\">\n  <i80-stepper-step status=\"complete\">Create a project</i80-stepper-step>\n  <i80-stepper-step status=\"progress\" title=\"Add a cluster\" description=\"Pick a region close to you\">\n    <i80-button size=\"small\">Add cluster</i80-button>\n  </i80-stepper-step>\n  <i80-stepper-step>Invite your team</i80-stepper-step>\n</i80-stepper-list>",
        "<i80-stepper-list label=\"Deploying\" title-tag=\"h3\">\n  <i80-stepper-step status=\"complete\">Build the image</i80-stepper-step>\n  <i80-stepper-step status=\"processing\">Roll out the pods</i80-stepper-step>\n  <i80-stepper-step>Switch the traffic over</i80-stepper-step>\n</i80-stepper-list>"
      ],
      "attributes": [
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "The list's accessible name."
        },
        {
          "name": "title-tag",
          "values": "div · h1 … h6",
          "default": "div",
          "notes": "The heading level of the step titles."
        },
        {
          "name": "i80-stepper-step",
          "values": "status (incomplete · progress · processing · complete) · title · description",
          "default": "",
          "notes": "One step. The title and description can also be children with slot=\"title\" and slot=\"description\"; anything else inside becomes the step's content."
        }
      ]
    },
    {
      "tag": "i80-stepper-nav",
      "title": "Stepper Nav",
      "description": "The step bar of a wizard, with a panel per step. current is the step in progress: the ones before it are complete, the ones after are not. Steps and panels are paired by order, and a completed step is a button that goes back to it (the arrow keys move between the steps you can reach).",
      "examples": [
        "<i80-stepper-nav label=\"Create a cluster\" current=\"1\">\n  <i80-stepper-step title=\"Details\"></i80-stepper-step>\n  <i80-stepper-step title=\"Network\" description=\"VPC and subnets\"></i80-stepper-step>\n  <i80-stepper-step title=\"Review\"></i80-stepper-step>\n  <i80-stepper-panel>Name your cluster.</i80-stepper-panel>\n  <i80-stepper-panel>Pick a VPC.</i80-stepper-panel>\n  <i80-stepper-panel>Check it over, then create.</i80-stepper-panel>\n</i80-stepper-nav>",
        "<i80-stepper-nav label=\"Where your order is\" current=\"1\" interactive=\"false\">\n  <i80-stepper-step title=\"Ordered\"></i80-stepper-step>\n  <i80-stepper-step title=\"Packed\"></i80-stepper-step>\n  <i80-stepper-step title=\"On its way\"></i80-stepper-step>\n</i80-stepper-nav>"
      ],
      "attributes": [
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "The step list's accessible name."
        },
        {
          "name": "current",
          "values": "index",
          "default": "0",
          "notes": "The step in progress, counting from 0. Property: current. Event: i80-change with detail.index when a completed step is clicked."
        },
        {
          "name": "interactive",
          "values": "boolean",
          "default": "true",
          "notes": "interactive=\"false\" makes it a progress display: plain steps, no tablist. Read when the steps are taken in."
        },
        {
          "name": "title-tag",
          "values": "div · h1 … h6",
          "default": "div",
          "notes": "The heading level of the step titles."
        },
        {
          "name": "i80-stepper-step",
          "values": "title · description",
          "default": "",
          "notes": "One step; its status comes from current. slot=\"title\" and slot=\"description\" take HTML."
        },
        {
          "name": "i80-stepper-panel",
          "values": "",
          "default": "",
          "notes": "The content of the step in the same position; the others are hidden."
        }
      ]
    },
    {
      "tag": "i80-text",
      "title": "Text",
      "description": "The i80 type styles on the tag you choose. One tag for all three styles — display, body and code — because they render the same element with the same classes and differ only in which sizes and weights they allow. For static text the classes alone do the job: <h1 class=\"i80-text i80-typography-display-500 i80-font-weight-bold\">.",
      "examples": [
        "<i80-text tag=\"h1\" variant=\"display\" size=\"500\">Page title</i80-text>\n<i80-text tag=\"p\">Paragraph text</i80-text>\n<i80-text tag=\"pre\" variant=\"code\">$ i80 deploy --plan q3</i80-text>",
        "<i80-text tag=\"p\" weight=\"semibold\">Semibold body text</i80-text>\n<i80-text tag=\"p\" color=\"critical\">This plan is over budget</i80-text>\n<i80-text tag=\"p\" color=\"var(--i80-color-palette-blue-400)\">A colour from the palette</i80-text>\n<i80-text tag=\"p\" align=\"center\">Centred</i80-text>",
        "<i80-text tag=\"div\" size=\"300\">\n  <p>A block of content, all in body-300.</p>\n  <ul><li>with a <strong>strong</strong> word</li><li>and a <i80-link-inline href=\"#\">link</i80-link-inline></li></ul>\n</i80-text>"
      ],
      "attributes": [
        {
          "name": "variant",
          "values": "display · body · code",
          "default": "body",
          "notes": "Which type style. Display for headings, body for text, code for monospace."
        },
        {
          "name": "size",
          "values": "100 · 200 · 300 (· 400 · 500 for display)",
          "default": "200",
          "notes": "The step in the type scale."
        },
        {
          "name": "weight",
          "values": "regular · medium · semibold · bold",
          "default": "per style and size",
          "notes": "Only some combinations exist: display 500 is bold, 200 semibold, 100 medium; body is regular, medium or semibold; code is regular or bold."
        },
        {
          "name": "align",
          "values": "left · center · right",
          "default": "",
          "notes": ""
        },
        {
          "name": "color",
          "values": "primary · strong · faint · disabled · high-contrast · action · highlight · success · warning · critical … or any CSS colour",
          "default": "",
          "notes": "A name from the foreground palette becomes a class; anything else is applied as an inline colour."
        },
        {
          "name": "tag",
          "values": "span · div · p · h1 … h6 · code · pre · li · strong · em …",
          "default": "span",
          "notes": "The element that is rendered. Choose the tag the document needs."
        },
        {
          "name": "class",
          "values": "class names",
          "default": "",
          "notes": "Put on the rendered element, so a contextual class such as i80-form__header-title works."
        }
      ]
    },
    {
      "tag": "i80-form-field",
      "title": "Form Primitives",
      "description": "The parts a form field is assembled from, for a control the ready-made fields don't cover. <i80-form-field> wires one control to its label, helper text and error; <i80-form-fieldset> does the same for a group; <i80-form-error> is an error block; <i80-form-character-count> counts what a control holds as you type. The label, helper text, legend and indicator are single elements with a single class, so you write those yourself.",
      "examples": [
        "<i80-form-field label=\"Cluster name\" helper-text=\"Letters, numbers and dashes\" required>\n  <input type=\"text\" name=\"cluster\" class=\"i80-form-text-input i80-typography-body-200 i80-font-weight-regular\">\n</i80-form-field>",
        "<i80-form-field label=\"Secret ID\">\n  <input type=\"text\" name=\"secret\" class=\"i80-form-text-input i80-typography-body-200 i80-font-weight-regular\">\n  <i80-form-error>\n    <p>The secret ID is not valid.</p>\n    <p>It must be 32 characters long.</p>\n  </i80-form-error>\n</i80-form-field>",
        "<i80-form-field label=\"Summary\">\n  <textarea name=\"summary\" maxlength=\"80\" class=\"i80-form-textarea i80-typography-body-200 i80-font-weight-regular\"></textarea>\n  <i80-form-character-count max-length=\"80\"></i80-form-character-count>\n</i80-form-field>",
        "<i80-form-fieldset legend=\"Billing address\" helper-text=\"Where the invoice goes\" required>\n  <i80-text-input label=\"Street\"></i80-text-input>\n  <i80-text-input label=\"City\"></i80-text-input>\n</i80-form-fieldset>",
        "<label class=\"i80-form-label\" for=\"plan-name\">Plan name\n  <span class=\"i80-form-indicator i80-form-indicator--optional\">(Optional)</span>\n</label>\n<div class=\"i80-form-helper-text\">Only your team can see it.</div>\n<input id=\"plan-name\" name=\"plan\" class=\"i80-form-text-input i80-typography-body-200 i80-font-weight-regular\">"
      ],
      "attributes": [
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "The field's label, linked to the control with for."
        },
        {
          "name": "helper-text",
          "values": "text",
          "default": "",
          "notes": "Linked to the control with aria-describedby."
        },
        {
          "name": "error",
          "values": "text",
          "default": "",
          "notes": "One error message; marks the control invalid."
        },
        {
          "name": "required / optional",
          "values": "boolean",
          "default": "",
          "notes": "Adds the Required badge or (Optional) after the label or legend."
        },
        {
          "name": "invalid",
          "values": "boolean",
          "default": "",
          "notes": "Marks the control invalid without a message."
        },
        {
          "name": "layout",
          "values": "vertical · flag",
          "default": "vertical",
          "notes": "flag puts the control before the label, as a checkbox does."
        },
        {
          "name": "i80-form-fieldset",
          "values": "legend · helper-text · error · required · optional · layout (vertical · horizontal) · name",
          "default": "",
          "notes": "A <fieldset> around several fields, with a legend."
        },
        {
          "name": "i80-form-error",
          "values": "for · message",
          "default": "",
          "notes": "An error block with its icon. Each <p> inside is one message; for=\"control-id\" gives it the id the control's aria-describedby expects."
        },
        {
          "name": "i80-form-character-count",
          "values": "for · min-length · max-length · message · value",
          "default": "",
          "notes": "Counts the control with that id as the user types. message takes {currentLength}, {minLength}, {maxLength}, {remaining} and {shortfall}."
        }
      ]
    },
    {
      "tag": "i80-code-block",
      "title": "Code Block",
      "description": "A block of code: line numbers, an optional title and description, a copy button, and a height toggle when it is taller than max-height. The code is the text inside the tag — its indentation is removed, so the example can sit where it belongs in your HTML — or the value attribute for a single line. There is no syntax highlighting: the code is shown exactly as you wrote it. Add a highlighter of your own where a page needs one.",
      "examples": [
        "<i80-code-block label=\"the peering command\" language=\"bash\" copy-button>aws ec2 --region us-west-1 accept-vpc-peering-connection</i80-code-block>",
        "<i80-code-block title=\"Hello world\" description=\"Save this as main.go\" language=\"go\">\n  package main\n  import fmt\n  func main() {\n    fmt.Println(helloWorld)\n  }\n</i80-code-block>",
        "<i80-code-block label=\"a long method\" language=\"ruby\" max-height=\"130px\">\n  def convert_object_to_array(obj)\n    arr = obj.keys\n             .map { |key| [key, obj[key]] }\n             .flatten\n             .sort\n    return arr\n  end\n</i80-code-block>"
      ],
      "attributes": [
        {
          "name": "value",
          "values": "text",
          "default": "",
          "notes": "The code, for a single line. Otherwise it is the text inside the tag."
        },
        {
          "name": "title",
          "values": "text",
          "default": "",
          "notes": "A title above the code."
        },
        {
          "name": "title-tag",
          "values": "p · h1 … h6",
          "default": "p",
          "notes": "The title's element. Pick the heading level the page structure asks for."
        },
        {
          "name": "description",
          "values": "text",
          "default": "",
          "notes": "A line under the title."
        },
        {
          "name": "language",
          "values": "bash · go · hcl · json · log · ruby · shell-session · yaml",
          "default": "",
          "notes": "Adds the language-<name> class a highlighter keys off. It does not highlight anything by itself."
        },
        {
          "name": "line-numbers",
          "values": "boolean",
          "default": "true",
          "notes": "line-numbers=\"false\" hides the gutter. The numbers count lines of code, so with line-wrapping they stop matching the rows on screen."
        },
        {
          "name": "line-number-start",
          "values": "number",
          "default": "1",
          "notes": "The number the first line gets."
        },
        {
          "name": "line-wrapping",
          "values": "boolean",
          "default": "",
          "notes": "Wraps long lines instead of scrolling sideways."
        },
        {
          "name": "standalone",
          "values": "boolean",
          "default": "true",
          "notes": "standalone=\"false\" drops the rounded corners, for a block flush inside another box."
        },
        {
          "name": "max-height",
          "values": "CSS length",
          "default": "",
          "notes": "Caps the height: the code scrolls, and a button expands it. Property: expanded. Method: toggle(). Event: i80-toggle with detail.expanded."
        },
        {
          "name": "copy-button",
          "values": "boolean",
          "default": "",
          "notes": "Adds the <i80-copy-button> that copies the code."
        },
        {
          "name": "copy-button-text",
          "values": "text",
          "default": "Copy",
          "notes": "Its accessible name — say what is being copied when there is more than one on the page."
        },
        {
          "name": "copied-message",
          "values": "text",
          "default": "Copied to clipboard",
          "notes": "What the copy button announces."
        },
        {
          "name": "label",
          "values": "text",
          "default": "",
          "notes": "The code's accessible name, when there is no title to name it."
        },
        {
          "name": "labelled-by",
          "values": "id",
          "default": "the title",
          "notes": "Names the code from other text on the page."
        },
        {
          "name": "described-by",
          "values": "id",
          "default": "the description",
          "notes": "Points the code's aria-describedby at other text on the page."
        }
      ]
    },
    {
      "tag": "i80-rich-tooltip",
      "title": "Rich Tooltip",
      "description": "A tooltip that holds real content — a heading, a paragraph, a link — instead of a string. The toggle is text with a dotted underline and an optional icon; everything else you write inside is the bubble. Hover and focus open it, or trigger=\"click\" asks for a click; Escape and a click outside close it, because the bubble is a native popover. For a plain string, use <i80-tooltip>.",
      "examples": [
        "<i80-rich-tooltip text=\"More info\" icon=\"info\">\n  <i80-text tag=\"h4\" variant=\"display\" size=\"200\">Who can see this plan?</i80-text>\n  <i80-text tag=\"p\" size=\"200\">Everyone on the team, and anyone you share the link with.</i80-text>\n</i80-rich-tooltip>",
        "<i80-text tag=\"p\" size=\"300\">Lorem <i80-rich-tooltip inline text=\"ipsum dolor\"><i80-text tag=\"h4\" variant=\"display\" size=\"200\">Some title</i80-text><i80-text tag=\"p\" size=\"200\">Some descriptive information</i80-text></i80-rich-tooltip> sit amet consectetur adipiscing elit.</i80-text>",
        "<i80-rich-tooltip trigger=\"click\" size=\"medium\" text=\"Billing\" icon=\"info\" placement=\"right\" width=\"320px\">\n  <i80-text tag=\"p\" size=\"200\">Invoices are sent on the first of the month.</i80-text>\n  <i80-link-inline href=\"#billing\">Read the billing docs</i80-link-inline>\n</i80-rich-tooltip>",
        "<i80-rich-tooltip>\n  <i80-tag slot=\"toggle\">My text tag</i80-tag>\n  <i80-text tag=\"p\" size=\"200\">A toggle of your own, when the standard one does not fit.</i80-text>\n</i80-rich-tooltip>"
      ],
      "attributes": [
        {
          "name": "text",
          "values": "text",
          "default": "",
          "notes": "The toggle's text, underlined with dots."
        },
        {
          "name": "icon",
          "values": "icon name",
          "default": "",
          "notes": "An icon next to the text, at the text's size."
        },
        {
          "name": "icon-position",
          "values": "leading · trailing",
          "default": "trailing",
          "notes": ""
        },
        {
          "name": "size",
          "values": "small · medium · large",
          "default": "",
          "notes": "A typographic style for the toggle; without it the toggle inherits the surrounding text. Only applies when there is text."
        },
        {
          "name": "inline",
          "values": "boolean",
          "default": "",
          "notes": "The toggle sits in a line of text instead of on its own."
        },
        {
          "name": "trigger",
          "values": "hover · click",
          "default": "hover",
          "notes": "hover opens it on hover and on focus; click asks for a click."
        },
        {
          "name": "open",
          "values": "boolean",
          "default": "",
          "notes": "Open on page load. It is also written back when the bubble opens or closes. Property: open. Methods: show(), hide(). Events: i80-open, i80-close."
        },
        {
          "name": "placement",
          "values": "top · bottom · left · right (each also -start and -end)",
          "default": "bottom",
          "notes": "Where the bubble goes when there is room."
        },
        {
          "name": "offset",
          "values": "number",
          "default": "12",
          "notes": "Pixels between the toggle and the bubble."
        },
        {
          "name": "collision-detection",
          "values": "true · false · flip · shift · auto",
          "default": "true",
          "notes": "How the bubble avoids the window edge: true is flip and shift together, auto picks the side with most room, false pins it."
        },
        {
          "name": "width",
          "values": "CSS length",
          "default": "",
          "notes": "A fixed width instead of fitting the content (which caps at 280px)."
        },
        {
          "name": "height",
          "values": "CSS length",
          "default": "",
          "notes": "A fixed height."
        },
        {
          "name": "slot=\"toggle\"",
          "values": "on a child",
          "default": "",
          "notes": "That child becomes the toggle instead of the text and icon, and its accessibility is then yours."
        }
      ]
    },
    {
      "tag": "i80-time",
      "title": "Time",
      "description": "A real <time datetime> so the date stays machine-readable, reading as a date, a date and time, or “3 days ago”. Without display it picks for you: relative inside the last week, the local date and time before that. The formatting is the browser's own Intl in the reader's locale, and a relative time re-reads itself every second. A <time datetime> you wrote inside is the date and the element we go on using, so a server-rendered page reads before i80.js loads.",
      "examples": [
        "<i80-text tag=\"p\">Created <i80-time date=\"2024-11-29T09:30:00Z\"></i80-time>, and it ran <i80-time date=\"2024-10-16T00:00:00Z\" display=\"relative\"></i80-time>.</i80-text>",
        "<i80-text tag=\"p\"><i80-time start=\"2024-10-14T00:00:00Z\" end=\"2024-10-16T00:00:00Z\"></i80-time></i80-text>",
        "<i80-text tag=\"p\">\n  <i80-time date=\"2024-10-16T00:00:00Z\" display=\"friendly-only\" tooltip=\"false\"></i80-time>\n  <i80-time date=\"2024-10-16T00:00:00Z\" display=\"friendly-relative\"></i80-time>\n  <i80-time display=\"utc\"><time datetime=\"2024-10-16T00:00:00Z\">16 October 2024</time></i80-time>\n</i80-text>"
      ],
      "attributes": [
        {
          "name": "date",
          "values": "date",
          "default": "",
          "notes": "Anything the browser parses: an ISO string is safest."
        },
        {
          "name": "start",
          "values": "date",
          "default": "",
          "notes": "With end: a range, said as one line with the year mentioned once."
        },
        {
          "name": "end",
          "values": "date",
          "default": "",
          "notes": ""
        },
        {
          "name": "display",
          "values": "friendly-relative · friendly-local · friendly-only · relative · utc",
          "default": "",
          "notes": "friendly-only is the date, friendly-local adds the time and zone, relative is “3 days ago”, friendly-relative is both, utc is the ISO string. Left out: relative inside a week, friendly-local past it."
        },
        {
          "name": "locale",
          "values": "BCP 47 tag",
          "default": "",
          "notes": "The language the date is written in, e.g. en-GB or da-DK. Without it, the reader's own."
        },
        {
          "name": "tooltip",
          "values": "boolean",
          "default": "true",
          "notes": "The precise date in a tooltip on the time. tooltip=\"false\" leaves the text on its own, with no button around it."
        }
      ]
    },
    {
      "tag": "i80-advanced-table",
      "title": "Advanced Table",
      "description": "The Table with expandable nested rows, row selection, a sticky header and resizable columns — still on the <table> your server rendered. The styles lay the table out as a CSS grid, which takes the table roles away, so they are written back onto the real elements — the markup still works without JavaScript.",
      "examples": [
        "<i80-advanced-table caption=\"Policy sets\">\n  <table>\n    <thead>\n      <tr><th expandable>Name</th><th>Status</th><th>Description</th></tr>\n    </thead>\n    <tbody>\n      <tr expanded><th>Policy set 1</th><td><i80-badge color=\"success\" icon=\"check\">Pass</i80-badge></td><td></td></tr>\n      <tr depth=\"1\"><th>test-advisory-pass.sentinel</th><td><i80-badge color=\"success\" icon=\"check\">Pass</i80-badge></td><td>Sample description.</td></tr>\n      <tr depth=\"1\"><th>test-hard-mandatory-pass.sentinel</th><td><i80-badge color=\"success\" icon=\"check\">Pass</i80-badge></td><td>Sample description.</td></tr>\n      <tr><th>Policy set 2</th><td><i80-badge color=\"critical\" icon=\"x\">Fail</i80-badge></td><td></td></tr>\n      <tr depth=\"1\"><th>test-soft-mandatory-fail.sentinel</th><td><i80-badge color=\"critical\" icon=\"x\">Fail</i80-badge></td><td>Sample description.</td></tr>\n    </tbody>\n  </table>\n</i80-advanced-table>",
        "<i80-advanced-table caption=\"Folk albums\" selectable select-name=\"album\" sort-selected>\n  <table>\n    <thead>\n      <tr><th sort>Artist</th><th sort>Album</th><th align=\"right\" sort>Year</th></tr>\n    </thead>\n    <tbody>\n      <tr value=\"1\" select-label=\"row Nick Drake / Pink Moon\"><td>Nick Drake</td><td>Pink Moon</td><td align=\"right\">1972</td></tr>\n      <tr value=\"2\" select-label=\"row The Beatles / Abbey Road\" selected><td>The Beatles</td><td>Abbey Road</td><td align=\"right\">1969</td></tr>\n      <tr value=\"3\" select-label=\"row Melanie / Candles in the Rain\"><td>Melanie</td><td>Candles in the Rain</td><td align=\"right\">1971</td></tr>\n    </tbody>\n  </table>\n</i80-advanced-table>",
        "<i80-advanced-table caption=\"Team\" max-height=\"180px\" resizable-columns>\n  <table>\n    <thead>\n      <tr><th sort>Name</th><th tooltip=\"The address invitations go to\">Email</th><th sort>Role</th></tr>\n    </thead>\n    <tbody>\n      <tr><td>Burnaby Kuscha</td><td>bkuscha@example.com</td><td>Owner</td></tr>\n      <tr><td>Barton Penley</td><td>bpenley@example.com</td><td>Admin</td></tr>\n      <tr><td>Norina Emanulsson</td><td>nemanulsson@example.com</td><td>Contributor</td></tr>\n      <tr><td>Orbadiah Smales</td><td>osmales@example.com</td><td>Contributor</td></tr>\n    </tbody>\n  </table>\n</i80-advanced-table>"
      ],
      "attributes": [
        {
          "name": "density",
          "values": "short · medium · tall · default",
          "default": "medium",
          "notes": "Row height."
        },
        {
          "name": "valign",
          "values": "top · middle · baseline",
          "default": "top",
          "notes": ""
        },
        {
          "name": "striped",
          "values": "boolean",
          "default": "",
          "notes": "Alternating row background. Ignored on a table with nested rows, where it would fight the indentation."
        },
        {
          "name": "caption",
          "values": "text",
          "default": "",
          "notes": "Read by screen readers, not shown. A <caption> you write yourself is kept."
        },
        {
          "name": "selectable",
          "values": "boolean",
          "default": "",
          "notes": "Adds a checkbox column and a select-all that goes indeterminate. Off on a table with nested rows."
        },
        {
          "name": "select-name",
          "values": "text",
          "default": "",
          "notes": "The name of the row checkboxes, so the selection posts with the surrounding form."
        },
        {
          "name": "select-all-label",
          "values": "text",
          "default": "Select all rows",
          "notes": "The select-all checkbox's aria-label."
        },
        {
          "name": "sort-selected",
          "values": "boolean",
          "default": "",
          "notes": "The selection column gets a sort button that brings the ticked rows together."
        },
        {
          "name": "max-height",
          "values": "CSS length",
          "default": "",
          "notes": "The body scrolls at that height, and the header sticks while it does."
        },
        {
          "name": "sticky-header",
          "values": "false",
          "default": "",
          "notes": "With max-height: keeps the header from sticking."
        },
        {
          "name": "resizable-columns",
          "values": "boolean",
          "default": "",
          "notes": "A drag handle per column, also driven with the left and right arrow keys."
        },
        {
          "name": "sort-mode",
          "values": "client · server",
          "default": "client",
          "notes": "server only fires i80-sort with {key, order, index}."
        },
        {
          "name": "th sort",
          "values": "empty · asc · desc",
          "default": "",
          "notes": "Makes the column sortable and sets the starting order."
        },
        {
          "name": "th expandable",
          "values": "boolean",
          "default": "",
          "notes": "The column that carries the expand button of a row with children, and the expand-all in the header."
        },
        {
          "name": "th tooltip",
          "values": "text",
          "default": "",
          "notes": "An info button next to the column label."
        },
        {
          "name": "th hidden-label",
          "values": "boolean",
          "default": "",
          "notes": "The label is read by screen readers only (a column of buttons)."
        },
        {
          "name": "th width",
          "values": "CSS length",
          "default": "auto",
          "notes": "The column's grid track. min-width and max-width bound a resize."
        },
        {
          "name": "tr depth",
          "values": "number",
          "default": "0",
          "notes": "A child of the row above it: hidden until that row is expanded."
        },
        {
          "name": "tr expanded",
          "values": "boolean",
          "default": "",
          "notes": "Starts with its children shown."
        },
        {
          "name": "tr value",
          "values": "text",
          "default": "",
          "notes": "What the row's checkbox submits, and what i80-select reports."
        },
        {
          "name": "tr select-label",
          "values": "text",
          "default": "row N",
          "notes": "Named in the row checkbox's aria-label: \"Select <this>\"."
        },
        {
          "name": "tr selected",
          "values": "boolean",
          "default": "",
          "notes": "Starts ticked."
        },
        {
          "name": "th sort-key / td sort-value",
          "values": "text",
          "default": "",
          "notes": "What i80-sort reports, and what to sort by instead of the cell text."
        },
        {
          "name": "td align / th align",
          "values": "left · center · right",
          "default": "left",
          "notes": ""
        },
        {
          "name": "slot=\"empty\"",
          "values": "element",
          "default": "",
          "notes": "Shown instead of an empty body; without it, a plain \"No data available\" state."
        }
      ]
    },
    {
      "tag": "i80-filter-bar",
      "title": "Filter Bar",
      "description": "The row above a table: a Filters dropdown, a search field, and the applied filters below it as dismissible tags. The state lives where HTML keeps state — in the controls: every filter is a real named <input> or <select>, so with action (or inside a <form>) \"Apply filters\" submits ?project=project-1&version=1.0 and the server renders the page back with those controls set. The tags are read off the controls, so the server never describes the same filters twice. i80-filter fires on every change and is cancelable, for an app that re-fetches by itself.",
      "examples": [
        "<i80-filter-bar search>\n  <i80-filter-group key=\"project\" text=\"Project\" type=\"multi-select\" search>\n    <i80-filter-option value=\"project-1\" label=\"Project 1\" checked></i80-filter-option>\n    <i80-filter-option value=\"project-2\" label=\"Project 2\"></i80-filter-option>\n    <i80-filter-option value=\"project-3\" label=\"Project 3\"></i80-filter-option>\n  </i80-filter-group>\n  <i80-filter-group key=\"version\" text=\"Version\" type=\"single-select\">\n    <i80-filter-option value=\"1.0\" label=\"1.0\" checked></i80-filter-option>\n    <i80-filter-option value=\"2.0\" label=\"2.0\"></i80-filter-option>\n  </i80-filter-group>\n</i80-filter-bar>",
        "<i80-filter-bar>\n  <i80-filter-group key=\"modules\" text=\"Modules\" type=\"numerical\" selector=\"greater-than\" value=\"50\"></i80-filter-group>\n  <i80-filter-group key=\"created\" text=\"Created\" type=\"date\" selector=\"between\" value-start=\"2024-01-01\" value-end=\"2025-01-01\"></i80-filter-group>\n  <i80-filter-group key=\"owner\" text=\"Owner\" type=\"generic\">\n    <i80-text-input label=\"Owner\" name=\"owner\" value=\"ada\"></i80-text-input>\n  </i80-filter-group>\n  <i80-dropdown slot=\"actions\" text=\"Actions\" color=\"secondary\" size=\"small\" position=\"bottom-right\">\n    <i80-dropdown-item icon=\"edit\">Edit items</i80-dropdown-item>\n    <i80-dropdown-item icon=\"trash\" color=\"critical\">Delete items</i80-dropdown-item>\n  </i80-dropdown>\n</i80-filter-bar>",
        "<i80-filter-bar live search search-placeholder=\"Search projects\" search-aria-label=\"Search by project name\">\n  <i80-filter-group key=\"region\" text=\"Region\" type=\"multi-select\">\n    <i80-filter-option value=\"eu-north\" label=\"eu-north\"></i80-filter-option>\n    <i80-filter-option value=\"eu-west\" label=\"eu-west\"></i80-filter-option>\n  </i80-filter-group>\n  <i80-applied-filter key=\"owner\" value=\"ada\" href=\"#owner-cleared\">Owner: Ada</i80-applied-filter>\n</i80-filter-bar>"
      ],
      "attributes": [
        {
          "name": "search",
          "values": "boolean",
          "default": "",
          "notes": "Adds the search field next to the dropdown."
        },
        {
          "name": "search-name",
          "values": "text",
          "default": "search",
          "notes": "The search field's name."
        },
        {
          "name": "search-value",
          "values": "text",
          "default": "",
          "notes": "What the server searched for."
        },
        {
          "name": "search-placeholder",
          "values": "text",
          "default": "Search",
          "notes": ""
        },
        {
          "name": "search-aria-label",
          "values": "text",
          "default": "Search",
          "notes": "Falls back to the placeholder."
        },
        {
          "name": "action",
          "values": "URL",
          "default": "",
          "notes": "Makes the bar a <form> with that action. Without it the bar is a <div> and an enclosing <form> submits it. A <form> written inside the tag is used as the bar, so CSRF fields and method=\"post\" are yours."
        },
        {
          "name": "method",
          "values": "get · post",
          "default": "get",
          "notes": "For the created <form>."
        },
        {
          "name": "live",
          "values": "boolean",
          "default": "",
          "notes": "Applies as soon as a filter changes: no Apply button, and a note for screen readers."
        },
        {
          "name": "expanded",
          "values": "boolean",
          "default": "",
          "notes": "Forces the applied filters open or closed; by default open when something is applied."
        },
        {
          "name": "filters-text",
          "values": "text",
          "default": "Filters",
          "notes": "The dropdown's label."
        },
        {
          "name": "apply-text / clear-text / no-filters-text",
          "values": "text",
          "default": "",
          "notes": "Apply filters · Clear all filters · No filters applied."
        },
        {
          "name": "dropdown-width / dropdown-height",
          "values": "CSS length",
          "default": "600px",
          "notes": ""
        },
        {
          "name": "i80-filter-group",
          "values": "key · text · type (multi-select · single-select · numerical · date · time · datetime · generic) · search",
          "default": "",
          "notes": "One tab in the dropdown. key names the controls, text labels the tab, search adds a search over the options. A generic group holds any controls you write in it."
        },
        {
          "name": "i80-filter-group selector / value / value-start / value-end",
          "values": "text",
          "default": "",
          "notes": "A numerical or date group: the operator and the values the server applied."
        },
        {
          "name": "i80-filter-option",
          "values": "value · label · checked · disabled · name",
          "default": "",
          "notes": "One checkbox (a radio in a single-select group), named after its group. An <input> written inside is used as it is."
        },
        {
          "name": "i80-applied-filter",
          "values": "key · value · text · href",
          "default": "",
          "notes": "A tag for a filter the bar has no control for. With href, dismissing it goes to that URL."
        },
        {
          "name": "slot=\"actions\"",
          "values": "element",
          "default": "",
          "notes": "Anything else for the bar's right side: a Dropdown of bulk actions, a button, your own markup."
        },
        {
          "name": "i80-filter",
          "values": "event",
          "default": "",
          "notes": "detail: {filters, applied, reason (apply · dismiss · clear · change · search), key, value}. Cancelable: preventDefault() and the form is not submitted. Methods: apply(), clear(), dismiss(filter); property: filters."
        }
      ]
    },
    {
      "tag": "i80-flex",
      "title": "Flex",
      "description": "A row or a column, with one of the standard gaps between the items. The tag is the flex container itself, so every child you write is a flex item where you wrote it; `tag` puts the layout on a list, a <nav> or a <span> instead. <i80-flex-item> is only for a child whose own sizing differs.",
      "examples": [
        "<i80-flex align=\"center\" gap=\"8\">\n  <i80-icon name=\"info\" size=\"24\"></i80-icon>\n  <i80-text tag=\"p\">Three tasks are overdue.</i80-text>\n</i80-flex>",
        "<i80-flex justify=\"space-between\" align=\"center\" gap=\"16\">\n  <i80-text tag=\"h2\" variant=\"display\" size=\"300\">Q3 plans</i80-text>\n  <i80-flex gap=\"8\">\n    <i80-button color=\"secondary\" icon=\"download\">Export</i80-button>\n    <i80-button color=\"primary\" icon=\"plus\">New plan</i80-button>\n  </i80-flex>\n</i80-flex>",
        "<i80-flex wrap gap=\"8 24\">\n  <i80-flex-item basis=\"0\" grow>\n    <i80-card border><div style=\"padding: 16px\">Grows to fill the row</div></i80-card>\n  </i80-flex-item>\n  <i80-flex-item shrink=\"0\">\n    <i80-button color=\"secondary\">Never shrinks</i80-button>\n  </i80-flex-item>\n</i80-flex>",
        "<i80-flex tag=\"ul\" direction=\"column\" gap=\"8\" style=\"margin: 0; padding: 0; list-style: none\">\n  <li>All plans</li>\n  <li>Archive</li>\n</i80-flex>"
      ],
      "attributes": [
        {
          "name": "direction",
          "values": "row · column",
          "default": "row",
          "notes": "Which way the items are laid out. The reverse directions are left out on purpose: they move the visual order away from the DOM order."
        },
        {
          "name": "justify",
          "values": "start · center · end · space-between · space-around · space-evenly",
          "default": "",
          "notes": "Along the direction (justify-content)."
        },
        {
          "name": "align",
          "values": "start · center · end · stretch",
          "default": "",
          "notes": "Across the direction (align-items)."
        },
        {
          "name": "gap",
          "values": "0 · 4 · 8 · 12 · 16 · 24 · 32 · 48",
          "default": "0",
          "notes": "One value for both gaps, or two — \"8 24\" is a row gap of 8 and a column gap of 24, as in CSS. For anything else set --i80-layout-flex-row-gap and --i80-layout-flex-column-gap in a style attribute."
        },
        {
          "name": "wrap",
          "values": "boolean",
          "default": "",
          "notes": "Lets the items run onto more rows when they don't fit."
        },
        {
          "name": "inline",
          "values": "boolean",
          "default": "",
          "notes": "inline-flex: the container sits in a line of text."
        },
        {
          "name": "tag",
          "values": "element name",
          "default": "",
          "notes": "Renders that element around the children and puts the layout on it, for when the box has to be a list, a <nav> or a <span>. Its items are then the elements you write yourself."
        },
        {
          "name": "i80-flex-item basis",
          "values": "CSS length · 0",
          "default": "",
          "notes": "flex-basis: the size to start from."
        },
        {
          "name": "i80-flex-item grow",
          "values": "boolean · number",
          "default": "",
          "notes": "Whether the item takes the free space (flex-grow). A number above 1 is a share of it."
        },
        {
          "name": "i80-flex-item shrink",
          "values": "boolean · number",
          "default": "",
          "notes": "Whether the item gives up space when there isn't enough (flex-shrink). shrink=\"0\" keeps a button at its size."
        },
        {
          "name": "i80-flex-item collapse",
          "values": "boolean",
          "default": "",
          "notes": "Lets the item shrink below its content's own width (min-width: 0) — for text that should ellipsize."
        },
        {
          "name": "i80-flex-item tag",
          "values": "element name",
          "default": "",
          "notes": "As above, for the item."
        }
      ]
    },
    {
      "tag": "i80-grid",
      "title": "Grid",
      "description": "Columns of equal width that fill the space, with one of the standard gaps. column-min-width lets a column stretch from that width up; column-width keeps it at it; one column-width per breakpoint makes the same grid responsive. The tag is the grid container itself, and <i80-grid-item> is only for an item that spans more than one cell.",
      "examples": [
        "<i80-grid column-min-width=\"240px\" gap=\"24\">\n  <i80-card border><div style=\"padding: 16px\">One</div></i80-card>\n  <i80-card border><div style=\"padding: 16px\">Two</div></i80-card>\n  <i80-card border><div style=\"padding: 16px\">Three</div></i80-card>\n</i80-grid>",
        "<i80-grid column-width-sm=\"100%\" column-width-md=\"33.33%\" gap=\"24\">\n  <i80-card border><div style=\"padding: 16px\">Stacks on a phone</div></i80-card>\n  <i80-card border><div style=\"padding: 16px\">Three across from 768px up</div></i80-card>\n  <i80-card border><div style=\"padding: 16px\">Three across from 768px up</div></i80-card>\n</i80-grid>",
        "<i80-grid column-min-width=\"25%\" gap=\"12\" style=\"height: 240px; grid-template-rows: min-content\">\n  <i80-grid-item colspan=\"4\"><i80-card border><div style=\"padding: 8px\">Header</div></i80-card></i80-grid-item>\n  <i80-grid-item rowspan=\"2\"><i80-card border><div style=\"padding: 8px\">Side</div></i80-card></i80-grid-item>\n  <i80-grid-item colspan=\"3\" rowspan=\"2\"><i80-card border><div style=\"padding: 8px\">Main</div></i80-card></i80-grid-item>\n</i80-grid>"
      ],
      "attributes": [
        {
          "name": "column-min-width",
          "values": "CSS length",
          "default": "",
          "notes": "The width a column starts from and stretches up from, so a short row still fills the space and a long one wraps."
        },
        {
          "name": "column-width",
          "values": "CSS length",
          "default": "",
          "notes": "The width a column keeps, however many items there are. Not to be combined with column-min-width."
        },
        {
          "name": "column-width-sm · column-width-md · column-width-lg · column-width-xl · column-width-xxl",
          "values": "CSS length",
          "default": "",
          "notes": "The same, per breakpoint (see Foundations/Breakpoints). Smaller views are inherited by larger ones, so only the ones that change need a value."
        },
        {
          "name": "align",
          "values": "start · center · end · stretch",
          "default": "stretch",
          "notes": "How the items sit in their row (align-items). Needs a height on the grid to be visible."
        },
        {
          "name": "gap",
          "values": "0 · 4 · 8 · 12 · 16 · 24 · 32 · 48",
          "default": "0",
          "notes": "One value for both gaps, or two — \"16 48\" is a row gap of 16 and a column gap of 48. The gap is taken off the column width for you. For anything else set --i80-layout-grid-row-gap and --i80-layout-grid-column-gap in a style attribute."
        },
        {
          "name": "tag",
          "values": "element name",
          "default": "",
          "notes": "Renders that element around the children and puts the layout on it, for when the box has to be a list or a <nav>."
        },
        {
          "name": "i80-grid-item colspan · rowspan",
          "values": "number",
          "default": "1",
          "notes": "How many columns and rows the item spans."
        },
        {
          "name": "i80-grid-item colspan-sm … colspan-xxl · rowspan-sm … rowspan-xxl",
          "values": "number",
          "default": "",
          "notes": "The same, per breakpoint."
        },
        {
          "name": "i80-grid-item tag",
          "values": "element name",
          "default": "",
          "notes": "As above, for the item."
        }
      ]
    },
    {
      "tag": "i80-app-frame",
      "title": "App Frame",
      "description": "The frame around a whole screen: a grid that holds the header across the top, the side nav down the left, the main area and the footer. In HTML the areas are the elements you already write, so the tag creates nothing and moves nothing — it styles the <header>, <aside>, <main> and <footer> inside it, and the grid closes up around anything you leave out. This example sits on a page that already has a <main> and so marks its areas with slot; a real screen writes the elements (see the whole screen at /elements/app.html).",
      "examples": [
        "<i80-app-frame style=\"min-height: 320px\">\n  <div slot=\"header\"><i80-app-header home-href=\"#\" home-text=\"zPlan\" home-icon=\"grid\">\n    <i80-button slot=\"global\" color=\"tertiary\" href=\"#plans\">Plans</i80-button>\n  </i80-app-header></div>\n  <nav slot=\"sidebar\" aria-label=\"Sections\" style=\"padding: 16px\">\n    <i80-flex direction=\"column\" gap=\"8\">\n      <i80-link-standalone href=\"#plans\" icon=\"folder\">All plans</i80-link-standalone>\n      <i80-link-standalone href=\"#archive\" icon=\"archive\">Archive</i80-link-standalone>\n    </i80-flex>\n  </nav>\n  <div slot=\"main\" style=\"padding: 24px\">\n    <i80-text tag=\"h2\" variant=\"display\" size=\"300\">Q3 plans</i80-text>\n  </div>\n  <div slot=\"footer\"><i80-app-footer><a href=\"#support\">Support</a></i80-app-footer></div>\n</i80-app-frame>"
      ],
      "attributes": [
        {
          "name": "header · aside · main · footer",
          "values": "element",
          "default": "",
          "notes": "The four areas. Write the elements; the frame places them. The header sits above both columns when there is a side nav, the <main> gets id=\"i80-main\" as the target of a skip link, and an area you leave out simply isn't there."
        },
        {
          "name": "slot",
          "values": "header · sidebar · main · footer · modals",
          "default": "",
          "notes": "Names an area on any element, for a page that cannot have a second <main> — a documentation page, a demo inside one."
        },
        {
          "name": "modals",
          "values": "element",
          "default": "",
          "notes": "A container fixed over the whole page, hidden while it is empty, for markup injected by JavaScript. <i80-modal> and <i80-flyout> use the browser's top layer and don't need it."
        }
      ]
    }
  ]
}
