{
  "aliases": [],
  "category": "actions",
  "documentation": "https://fusion.dev/helix/components/button",
  "elements": [
    {
      "attributes": [
        {
          "description": "How much of the available width the button takes.\n\n- `auto` is the default and is never written: the button is as wide as its\n  label, which is what a control in a toolbar or beside a field should be.\n- `full` fills the container, for a single action at the foot of a form or\n  a card, and on a phone where a control is a target before it is a label.\n\nFor a row that should line up rather than follow the length of each word,\nset `--button-inline-size` on the container. It inherits, and it reaches a\nplain anchor wearing `data-button` as well as this element.\n\nA consumer's own anchor takes this attribute as `data-button-width`:\n\n```html\n<a data-button=\"solid\" data-button-width=\"full\" data-button-tone=\"primary\" href=\"/save\">Save</a>\n```\n\n**Not `fill`, and not `size`.** `fill` means a surface's own color\neverywhere else in the library, so it cannot also mean geometry beside an\n`emphasis` documented as \"How filled it is drawn\". `size` fails the rule's\nown test, which is whether two of them sitting together could legitimately\ndiffer: two buttons can, because this is a width *policy* rather than a\nscale.",
          "name": "width",
          "type": "'auto' | 'full'"
        },
        {
          "description": "How much fill the control is drawn with.\n\nNot `separation`: a Card's `outlined` says where a surface ends, and this\none says how much ink a control claims. One word cannot say both.\n\nReflected because the stylesheet selects on it, and undefaulted because\nit reflects: a reflected property with a default writes its attribute\nonto every host anybody renders. The resting treatment is the attribute's\nabsence, which each control names for itself, `outlined` on `Button` and\n`ghost` on `IconButton`.",
          "name": "emphasis",
          "type": "'ghost' | 'outlined' | 'solid' | 'subtle'"
        },
        {
          "description": "Which color the control is drawn in, by what it means.\n\nThree and an accent, not the full status set. A tag reports a state and a\nbutton performs an action: a success button and a warning button are not\nthings anybody writes, and offering them invites a page where the color\nof a control means nothing in particular. `accent` is the second\nemphasis: the second color a theme names, and the brand itself until one\nis named. It used to be a rotation off the brand seed whether or not\nanybody asked for a second color, which handed every theme a complement\nit had not chosen, 3 degrees off `warning` and sometimes too pale to\nread.",
          "name": "tone",
          "type": "'accent' | 'danger' | 'neutral' | 'primary'"
        },
        {
          "description": "What to send when the button is activated, and what to send it to.\n\nThe platform's invoker attributes, forwarded onto the inner `<button>`\nrather than left on the host: only a real button is recognized as an\ninvoker, so `commandfor` written on a custom element resolves nothing and\nthe trigger silently does nothing at all. A wrapper has to hand them down,\nthe same way `label` becomes `aria-label` in there.\n\n```html\n<helix-headless-button commandfor=\"confirm\" command=\"--show\">Delete…</helix-headless-button>\n```\n\n`commandFor` spells its attribute out, because Lit lowercases a property\nname to derive one rather than dash-casing it, and what a consumer writes\nis the platform's `commandfor`.",
          "name": "command",
          "type": "string"
        },
        {
          "description": "The id of the element the command is sent to.",
          "name": "commandfor",
          "type": "string"
        },
        {
          "description": "Whether a disabled button stays in the tab order, so a tooltip around it\ncan say why it is disabled.\n\nSet it on a button whose reason a Tooltip gives, such as a Save with no\nchanges to save. While the button is disabled, it keeps focus and takes\nhover, and it refuses every press: a click, Enter and Space, and Enter in\na field of its form. The click does not reach your listeners, and a submit\nbutton submits nothing. The inner control carries `aria-disabled` in place\nof the native `disabled`, so a screen reader still says it is unavailable.\nThe styled button keeps the disabled look.\n\nLeave it unset on a button with no reason to give. A plain disabled button\nleaves the tab order, as a native one does, and a keyboard reader skips a\nstop that does nothing. Set `pending` for the wait after a press.",
          "name": "focusable-when-disabled",
          "type": "boolean"
        },
        {
          "description": "Where this button's submission goes, overriding the form's own.\n\n**The five `form*` overrides ride on a native submit button that stands in\nfor this one**, because `requestSubmit(submitter)` refuses the inner\n`<button>` twice over: it is `type=\"button\"`, and it sits in this shadow\nroot, so its form owner is not the consumer's form. The stand-in is a\nhidden `<button type=\"submit\">` in the form for the one synchronous call,\nso the platform applies the overrides to that submission alone and leaves\nthe form as it was. See `submitAs`.",
          "name": "formaction",
          "type": "string"
        },
        {
          "description": "How the submission is encoded, overriding the form's own.",
          "name": "formenctype",
          "type": "string"
        },
        {
          "description": "Which HTTP method the submission uses, overriding the form's own.",
          "name": "formmethod",
          "type": "string"
        },
        {
          "description": "Whether this submission skips the form's validation.",
          "name": "formnovalidate",
          "type": "boolean"
        },
        {
          "description": "Which browsing context the response lands in, overriding the form's own.",
          "name": "formtarget",
          "type": "string"
        },
        {
          "description": "The button's name, read by screen readers, for a button whose content is\nnot text.\n\nLives here rather than only on `IconButton` so the two share one render.\nAn icon-only button without it is a control a screen reader announces as\n\"button\" and nothing else.\n\nUndefaulted, so a subclass's own default, such as Copy Button's `Copy`, is\nthe first value its element receives and so the English its words fall\nback to.",
          "name": "label",
          "type": "string"
        },
        {
          "description": "Whether the action the button started is still running.\n\nA pending button keeps focus and stays in the tab order, and refuses every\npress: a click, Enter and Space, and Enter in a field of its form. The\nclick does not reach your listeners. The inner control carries\n`aria-busy` and `aria-disabled`, and when the button becomes pending while\nit has focus, a reader hears `pending-label`. The styled button draws a\nturning ring over its words.\n\n`disabled` takes the button out of reach. Use `pending` for the wait after\na press, so focus stays where the reader left it.",
          "name": "pending",
          "type": "boolean"
        },
        {
          "description": "What a reader hears when the button becomes pending while it has focus.",
          "name": "pending-label",
          "type": "string"
        },
        {
          "description": "What activating the button does to its form.",
          "name": "type",
          "type": "'button' | 'submit' | 'reset'"
        },
        {
          "description": "The value this button submits under its own `name`, as a native submit\nbutton does.\n\n**Sent with exactly the submission this button starts and not otherwise.**\nA native submit button contributes `name=value` only when it is the one\nthat submitted, and every other button on the form contributes nothing,\nwhich is what the native button standing in for this one at submit does.\n\n`CopyButton` has a `value` of its own meaning the text to put on the\nclipboard, which is not this and is why it is declared there rather than\ninherited.",
          "name": "value",
          "type": "string"
        },
        {
          "description": "Whether the control is disabled and cannot be used.\n\nReflected, because the styled tier selects on it, and mirrored to\n`aria-disabled` rather than removing the element from the accessibility\ntree, so a screen reader can still announce that the control exists.\n\nThe mirror itself is in `willUpdate` below, here rather than in each\ncontrol, because here is where `disabled` is.",
          "name": "disabled",
          "type": "boolean"
        },
        {
          "description": "The name the control's value is submitted under.\n\n**Reflected, and it is the exception to \"reflect only what CSS selects\non\".** The platform builds a form's entry list from the `name` *content\nattribute*, not from the property, so a control named through the property\nsubmitted under no name at all and was silently dropped. Measured: two\ninputs in one form, one named by attribute and one by property, and the\nform carried a single entry.\n\nSafe for a React consumer, which is what the rule is protecting: React\nrenders a custom element's props as attributes on the server, so the name\nis already in the markup being hydrated and reflecting it writes what is\nthere. It is only a change to the markup when a consumer assigns it\nimperatively later, and nothing is hydrating by then.\n\nHere rather than on each control.",
          "name": "name",
          "type": "string"
        }
      ],
      "cssParts": [
        {
          "description": "The inner control.",
          "name": "button",
          "type": ""
        },
        {
          "description": "The live region that says `pending-label` when the button becomes pending under focus. It is clipped out of view and still read.",
          "name": "live",
          "type": ""
        },
        {
          "description": "The ring a pending button turns over its words, sized and colored by the `--button-spinner-*` hooks.",
          "name": "spinner",
          "type": ""
        },
        {
          "description": "The button's words, which the styled button trims to their capitals and baseline so the letters sit at the middle in any font.",
          "name": "text",
          "type": ""
        }
      ],
      "cssProperties": [
        {
          "description": "Fill behind the button, in every state.",
          "name": "--button-background-color",
          "type": ""
        },
        {
          "description": "Fill behind the button under the pointer. Without it, `--button-background-color` also applies on hover.",
          "name": "--button-background-color-hover",
          "type": ""
        },
        {
          "description": "Edge color of an outlined button. The other emphases draw no edge.",
          "name": "--button-border-color",
          "type": ""
        },
        {
          "description": "Label and icon color.",
          "name": "--button-foreground-color",
          "type": ""
        },
        {
          "description": "Space between the label and the content on either side of it.",
          "name": "--button-gap",
          "type": ""
        },
        {
          "description": "Minimum width. It inherits, so you can set it once on a section for every button inside it.",
          "name": "--button-inline-size",
          "type": ""
        },
        {
          "description": "Inline padding, for a container with spacing of its own.",
          "name": "--button-padding-inline",
          "type": ""
        },
        {
          "description": "Corner radius of the control.",
          "name": "--button-radius",
          "type": ""
        },
        {
          "description": "Trailing corners, which a group flattens.",
          "name": "--button-radius-end",
          "type": ""
        },
        {
          "description": "The bottom trailing corner, for a group stacking down.",
          "name": "--button-radius-end-end",
          "type": ""
        },
        {
          "description": "The bottom leading corner, for a group stacking down.",
          "name": "--button-radius-end-start",
          "type": ""
        },
        {
          "description": "Leading corners, which a group flattens.",
          "name": "--button-radius-start",
          "type": ""
        },
        {
          "description": "The top trailing corner, for a group stacking down.",
          "name": "--button-radius-start-end",
          "type": ""
        },
        {
          "description": "The top leading corner, for a group stacking down.",
          "name": "--button-radius-start-start",
          "type": ""
        },
        {
          "description": "Height of the control, for a row height outside the density steps.",
          "name": "--button-size",
          "type": ""
        },
        {
          "description": "Color of the ring a pending button turns. It follows the label's color.",
          "name": "--button-spinner-color",
          "type": ""
        },
        {
          "description": "How long the ring takes to turn once.",
          "name": "--button-spinner-duration",
          "type": ""
        },
        {
          "description": "Size of the ring a pending button turns. Defaults to the size of its icons.",
          "name": "--button-spinner-size",
          "type": ""
        }
      ],
      "cssStates": [
        {
          "description": "The button is pending: `pending` is set. A ring turns over its hidden words.",
          "name": "pending",
          "type": ""
        },
        {
          "description": "A Button Group in a selection mode has this button pressed.",
          "name": "pressed",
          "type": ""
        },
        {
          "description": "Set one frame after the first render. Transitions wait for it, so an element does not animate its own arrival.",
          "name": "settled",
          "type": ""
        }
      ],
      "description": "Four appearances, all drawn on the same control, so a link styled as a button\nlooks the same as a real button.\n\nThere are no sizes. A control takes its height from the `data-density` of the\nregion it is in, so a row of controls lines up. `data-density` is the only\nsize setting in the library.\n\nThe focus ring is declared in the component's styles, because the base\nlayer's `:focus-visible` rule applies to the light DOM and stops at the\nshadow boundary.",
      "entry": "button",
      "events": [
        {
          "bubbles": true,
          "description": "Fired when a press acts, with `detail.pointerType` saying what pressed the button.",
          "name": "helix-press",
          "reactProp": "onhelix-press",
          "type": "HelixPressEvent"
        }
      ],
      "label": "Button",
      "methods": [
        {
          "description": "Presses the button, as `click()` presses a native one: the command is sent,\nthe form submits or resets, and `helix-press` says `virtual`.\n\n**Passed to the inner control, because a click on the host reaches nothing\nthat acts.** The control in the shadow root is what answers a click, so\n`click()` from a script, an automation or a test, dispatched on the host,\nsubmitted nothing and sent no `helix-press`. The control's own click reaches\nthe host's listeners once, retargeted, as one from a native button does. A\ndisabled button does nothing, and a pending one refuses the press.",
          "name": "click",
          "type": "() => void"
        },
        {
          "description": "Returns whether the control is valid, and fires `invalid` on it when it is\nnot. The control shows no message for it: `reportValidity()` does that.",
          "name": "checkValidity",
          "type": "() => boolean"
        },
        {
          "description": "Returns whether the control is valid, and when it is not, fires `invalid`\nand shows the browser's message at the control.",
          "name": "reportValidity",
          "type": "() => boolean"
        },
        {
          "description": "Marks the control invalid with `message`, or clears that with an empty\nstring, as on a native control. The form does not submit while it is set.",
          "name": "setCustomValidity",
          "type": "(message: string) => void"
        },
        {
          "description": "Moves focus to the control, as focusing a native control does.",
          "name": "focus",
          "type": "(options: FocusOptions) => void"
        }
      ],
      "package": "@fusion.dev/helix",
      "part": "Root",
      "properties": [
        {
          "default": "",
          "description": "How much of the available width the button takes.\n\n- `auto` is the default and is never written: the button is as wide as its\n  label, which is what a control in a toolbar or beside a field should be.\n- `full` fills the container, for a single action at the foot of a form or\n  a card, and on a phone where a control is a target before it is a label.\n\nFor a row that should line up rather than follow the length of each word,\nset `--button-inline-size` on the container. It inherits, and it reaches a\nplain anchor wearing `data-button` as well as this element.\n\nA consumer's own anchor takes this attribute as `data-button-width`:\n\n```html\n<a data-button=\"solid\" data-button-width=\"full\" data-button-tone=\"primary\" href=\"/save\">Save</a>\n```\n\n**Not `fill`, and not `size`.** `fill` means a surface's own color\neverywhere else in the library, so it cannot also mean geometry beside an\n`emphasis` documented as \"How filled it is drawn\". `size` fails the rule's\nown test, which is whether two of them sitting together could legitimately\ndiffer: two buttons can, because this is a width *policy* rather than a\nscale.",
          "name": "width",
          "readonly": false,
          "references": [],
          "type": "'auto' | 'full'"
        },
        {
          "default": "",
          "description": "How much fill the control is drawn with.\n\nNot `separation`: a Card's `outlined` says where a surface ends, and this\none says how much ink a control claims. One word cannot say both.\n\nReflected because the stylesheet selects on it, and undefaulted because\nit reflects: a reflected property with a default writes its attribute\nonto every host anybody renders. The resting treatment is the attribute's\nabsence, which each control names for itself, `outlined` on `Button` and\n`ghost` on `IconButton`.",
          "name": "emphasis",
          "readonly": false,
          "references": [],
          "type": "'ghost' | 'outlined' | 'solid' | 'subtle'"
        },
        {
          "default": "",
          "description": "Which color the control is drawn in, by what it means.\n\nThree and an accent, not the full status set. A tag reports a state and a\nbutton performs an action: a success button and a warning button are not\nthings anybody writes, and offering them invites a page where the color\nof a control means nothing in particular. `accent` is the second\nemphasis: the second color a theme names, and the brand itself until one\nis named. It used to be a rotation off the brand seed whether or not\nanybody asked for a second color, which handed every theme a complement\nit had not chosen, 3 degrees off `warning` and sometimes too pale to\nread.",
          "name": "tone",
          "readonly": false,
          "references": [],
          "type": "'accent' | 'danger' | 'neutral' | 'primary'"
        },
        {
          "default": "''",
          "description": "What to send when the button is activated, and what to send it to.\n\nThe platform's invoker attributes, forwarded onto the inner `<button>`\nrather than left on the host: only a real button is recognized as an\ninvoker, so `commandfor` written on a custom element resolves nothing and\nthe trigger silently does nothing at all. A wrapper has to hand them down,\nthe same way `label` becomes `aria-label` in there.\n\n```html\n<helix-headless-button commandfor=\"confirm\" command=\"--show\">Delete…</helix-headless-button>\n```\n\n`commandFor` spells its attribute out, because Lit lowercases a property\nname to derive one rather than dash-casing it, and what a consumer writes\nis the platform's `commandfor`.",
          "name": "command",
          "readonly": false,
          "references": [
            {
              "label": "Invoker Commands",
              "url": "https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command"
            }
          ],
          "type": "string"
        },
        {
          "default": "''",
          "description": "The id of the element the command is sent to.",
          "name": "commandFor",
          "readonly": false,
          "references": [
            {
              "label": "Invoker Commands",
              "url": "https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-commandfor"
            }
          ],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether this button is a disclosure trigger, and whether its panel is open.\n\n**Set by whatever owns the panel, and a property rather than an attribute\nfor the reason `Menu` already states about its items**: an attribute\nwritten onto a consumer's element after it upgrades is a hydration mismatch\nwaiting to happen. Measured 28 Aug 2026 against React 19.2.8, server markup\nhydrated with the definitions loaded either side of `hydrateRoot`:\n`Megamenu` and `SpeedDial` wrote `aria-expanded` onto the slotted trigger\nand React reported \"some attributes of the server rendered HTML didn't\nmatch the client properties. This won't be patched up\" in both orders;\n`Popover` and `Menu`, which write nothing, were clean in both.\n\n**A property crosses that gap because React never looks at one.** It diffs\nthe attributes it rendered, and this one is `attribute: false`, so the state\ntravels to the inner `<button>` and lands inside a shadow root React does\nnot own and cannot compare. The consumer's own element is left exactly as\nthey served it and the reader is still told what the control does.\n\n`undefined` renders nothing at all, so a button that is not a trigger\ncarries no `aria-expanded`: the resting state is the attribute's absence,\nwhich is the rule every reflected property here follows.\n\nA consumer who wants to say this statically writes `aria-expanded` on the\nhost instead, which `AriaForwardController` carries to the same place.",
          "name": "expanded",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "true",
          "description": "Whether the button holds its group's tab stop.\n\nA toolbar keeps one tab stop across its children, and the obvious way to do\nthat is to write `tabindex` onto each of them. That is a change to markup\nthe consumer rendered, so a React consumer's hydration reports it and does\nnot patch it up. A parent sets this property instead, which writes nothing,\nand the stop lands on the inner control inside the shadow root.\n\nNo attribute, because it is coordination between elements rather than\nanything a consumer sets.",
          "name": "focusable",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "false",
          "description": "Whether a disabled button stays in the tab order, so a tooltip around it\ncan say why it is disabled.\n\nSet it on a button whose reason a Tooltip gives, such as a Save with no\nchanges to save. While the button is disabled, it keeps focus and takes\nhover, and it refuses every press: a click, Enter and Space, and Enter in\na field of its form. The click does not reach your listeners, and a submit\nbutton submits nothing. The inner control carries `aria-disabled` in place\nof the native `disabled`, so a screen reader still says it is unavailable.\nThe styled button keeps the disabled look.\n\nLeave it unset on a button with no reason to give. A plain disabled button\nleaves the tab order, as a native one does, and a keyboard reader skips a\nstop that does nothing. Set `pending` for the wait after a press.",
          "name": "focusableWhenDisabled",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "Where this button's submission goes, overriding the form's own.\n\n**The five `form*` overrides ride on a native submit button that stands in\nfor this one**, because `requestSubmit(submitter)` refuses the inner\n`<button>` twice over: it is `type=\"button\"`, and it sits in this shadow\nroot, so its form owner is not the consumer's form. The stand-in is a\nhidden `<button type=\"submit\">` in the form for the one synchronous call,\nso the platform applies the overrides to that submission alone and leaves\nthe form as it was. See `submitAs`.",
          "name": "formAction",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "''",
          "description": "How the submission is encoded, overriding the form's own.",
          "name": "formEnctype",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "''",
          "description": "Which HTTP method the submission uses, overriding the form's own.",
          "name": "formMethod",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "false",
          "description": "Whether this submission skips the form's validation.",
          "name": "formNoValidate",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "Which browsing context the response lands in, overriding the form's own.",
          "name": "formTarget",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "The button's name, read by screen readers, for a button whose content is\nnot text.\n\nLives here rather than only on `IconButton` so the two share one render.\nAn icon-only button without it is a control a screen reader announces as\n\"button\" and nothing else.\n\nUndefaulted, so a subclass's own default, such as Copy Button's `Copy`, is\nthe first value its element receives and so the English its words fall\nback to.",
          "name": "label",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "false",
          "description": "Whether the action the button started is still running.\n\nA pending button keeps focus and stays in the tab order, and refuses every\npress: a click, Enter and Space, and Enter in a field of its form. The\nclick does not reach your listeners. The inner control carries\n`aria-busy` and `aria-disabled`, and when the button becomes pending while\nit has focus, a reader hears `pending-label`. The styled button draws a\nturning ring over its words.\n\n`disabled` takes the button out of reach. Use `pending` for the wait after\na press, so focus stays where the reader left it.",
          "name": "pending",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "'Loading'",
          "description": "What a reader hears when the button becomes pending while it has focus.",
          "name": "pendingLabel",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "What kind of panel this button opens, where it opens one: `menu` for a\n`Menu`.\n\nSet by the component that owns the panel, as a property for the reason\n`expanded` gives above. It is what makes a reader say the button opens a\nmenu. Read from Chromium's tree, a Menu's trigger was a plain button that\nreported collapsed and expanded and no popup.",
          "name": "popup",
          "readonly": false,
          "references": [],
          "type": "'dialog' | 'grid' | 'listbox' | 'menu' | 'tree'"
        },
        {
          "default": "'button'",
          "description": "What activating the button does to its form.",
          "name": "type",
          "readonly": false,
          "references": [],
          "type": "'button' | 'submit' | 'reset'"
        },
        {
          "default": "''",
          "description": "The value this button submits under its own `name`, as a native submit\nbutton does.\n\n**Sent with exactly the submission this button starts and not otherwise.**\nA native submit button contributes `name=value` only when it is the one\nthat submitted, and every other button on the form contributes nothing,\nwhich is what the native button standing in for this one at submit does.\n\n`CopyButton` has a `value` of its own meaning the text to put on the\nclipboard, which is not this and is why it is declared there rather than\ninherited.",
          "name": "value",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "The element this button's command is sent to, resolved.\n\nThe platform's own accessor, because the resolution is otherwise kept\ninside: `linkCommand` writes the target onto the inner `<button>`, where a\nconsumer cannot reach it, and `commandFor` answers with an id rather than\nan element. Writing assigns the inner control directly, which is what an\ninvoker without an id in the same tree needs.",
          "name": "commandForElement",
          "readonly": false,
          "references": [],
          "type": "Element | null"
        },
        {
          "default": "false",
          "description": "Whether the control is disabled and cannot be used.\n\nReflected, because the styled tier selects on it, and mirrored to\n`aria-disabled` rather than removing the element from the accessibility\ntree, so a screen reader can still announce that the control exists.\n\nThe mirror itself is in `willUpdate` below, here rather than in each\ncontrol, because here is where `disabled` is.",
          "name": "disabled",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "The name the control's value is submitted under.\n\n**Reflected, and it is the exception to \"reflect only what CSS selects\non\".** The platform builds a form's entry list from the `name` *content\nattribute*, not from the property, so a control named through the property\nsubmitted under no name at all and was silently dropped. Measured: two\ninputs in one form, one named by attribute and one by property, and the\nform carried a single entry.\n\nSafe for a React consumer, which is what the rule is protecting: React\nrenders a custom element's props as attributes on the server, so the name\nis already in the markup being hydrated and reflecting it writes what is\nthere. It is only a change to the markup when a consumer assigns it\nimperatively later, and nothing is hydrating by then.\n\nHere rather than on each control.",
          "name": "name",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether the control meets its constraints, and why not.",
          "name": "validity",
          "readonly": true,
          "references": [
            {
              "label": "ElementInternals.validity",
              "url": "https://developer.mozilla.org/docs/Web/API/ElementInternals/validity"
            }
          ],
          "type": "ValidityState"
        },
        {
          "default": "",
          "description": "The message the browser would show for the control, or an empty string.",
          "name": "validationMessage",
          "readonly": true,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether the control is checked when its form is submitted.",
          "name": "willValidate",
          "readonly": true,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "",
          "description": "The form the control belongs to, or `null`.",
          "name": "form",
          "readonly": true,
          "references": [
            {
              "label": "form-associated custom elements",
              "url": "https://html.spec.whatwg.org/multipage/custom-elements.html#form-associated-custom-elements"
            }
          ],
          "type": "HTMLFormElement | null"
        },
        {
          "default": "",
          "description": "The labels associated with the control.",
          "name": "labels",
          "readonly": true,
          "references": [
            {
              "label": "form-associated custom elements",
              "url": "https://html.spec.whatwg.org/multipage/custom-elements.html#form-associated-custom-elements"
            }
          ],
          "type": "NodeList"
        },
        {
          "default": "new Promise<void>((resolve) => { this.resolveSettled = resolve })",
          "description": "Resolves a frame after the first update, when transitions are switched on.\n\n**Named `whenSettled` because `settled` is taken four times over**, by\nImage, Splitter and both color controls, none of them about transitions. A\nbase class takes the name every subclass then cannot use.\n\n**The state it reports cannot be watched, which is why this exists.** A\ncustom state is deliberately not in the DOM, so there is no\n`MutationObserver` equivalent for `ElementInternals.states`. Await this\ninstead.\n\nSeparate from `updateComplete`, which resolves in the microtask checkpoint\nthe suppression is still active in. That is the distinction `readyToMove`\nin `@fusion.dev/helix-testing` is written around.\n\nIt never resolves where `requestAnimationFrame` never fires, which is a\nserver, a hidden tab, and happy-dom. Callers that must not hang there race\nit against a timeout, as the happy-dom harness's `readyToMove` does. The\nbrowser harness's awaits it alone: a browser always runs the frame, and a\ntimer beside it was a bet on how long one takes, which a busy run lost.",
          "name": "whenSettled",
          "readonly": true,
          "references": [],
          "type": "Promise<void>"
        }
      ],
      "slots": [
        {
          "description": "The button's label.",
          "name": "",
          "type": ""
        },
        {
          "description": "Content after the label.",
          "name": "end",
          "type": ""
        },
        {
          "description": "Content before the label, usually an icon.",
          "name": "start",
          "type": ""
        }
      ],
      "specifier": "@fusion.dev/helix/button",
      "status": "stable",
      "summary": "Triggers an action, submits a form, or navigates.",
      "tagName": "helix-button",
      "tier": "styled",
      "words": [
        "pendingLabel"
      ]
    },
    {
      "attributes": [
        {
          "description": "What to send when the button is activated, and what to send it to.\n\nThe platform's invoker attributes, forwarded onto the inner `<button>`\nrather than left on the host: only a real button is recognized as an\ninvoker, so `commandfor` written on a custom element resolves nothing and\nthe trigger silently does nothing at all. A wrapper has to hand them down,\nthe same way `label` becomes `aria-label` in there.\n\n```html\n<helix-headless-button commandfor=\"confirm\" command=\"--show\">Delete…</helix-headless-button>\n```\n\n`commandFor` spells its attribute out, because Lit lowercases a property\nname to derive one rather than dash-casing it, and what a consumer writes\nis the platform's `commandfor`.",
          "name": "command",
          "type": "string"
        },
        {
          "description": "The id of the element the command is sent to.",
          "name": "commandfor",
          "type": "string"
        },
        {
          "description": "Whether a disabled button stays in the tab order, so a tooltip around it\ncan say why it is disabled.\n\nSet it on a button whose reason a Tooltip gives, such as a Save with no\nchanges to save. While the button is disabled, it keeps focus and takes\nhover, and it refuses every press: a click, Enter and Space, and Enter in\na field of its form. The click does not reach your listeners, and a submit\nbutton submits nothing. The inner control carries `aria-disabled` in place\nof the native `disabled`, so a screen reader still says it is unavailable.\nThe styled button keeps the disabled look.\n\nLeave it unset on a button with no reason to give. A plain disabled button\nleaves the tab order, as a native one does, and a keyboard reader skips a\nstop that does nothing. Set `pending` for the wait after a press.",
          "name": "focusable-when-disabled",
          "type": "boolean"
        },
        {
          "description": "Where this button's submission goes, overriding the form's own.\n\n**The five `form*` overrides ride on a native submit button that stands in\nfor this one**, because `requestSubmit(submitter)` refuses the inner\n`<button>` twice over: it is `type=\"button\"`, and it sits in this shadow\nroot, so its form owner is not the consumer's form. The stand-in is a\nhidden `<button type=\"submit\">` in the form for the one synchronous call,\nso the platform applies the overrides to that submission alone and leaves\nthe form as it was. See `submitAs`.",
          "name": "formaction",
          "type": "string"
        },
        {
          "description": "How the submission is encoded, overriding the form's own.",
          "name": "formenctype",
          "type": "string"
        },
        {
          "description": "Which HTTP method the submission uses, overriding the form's own.",
          "name": "formmethod",
          "type": "string"
        },
        {
          "description": "Whether this submission skips the form's validation.",
          "name": "formnovalidate",
          "type": "boolean"
        },
        {
          "description": "Which browsing context the response lands in, overriding the form's own.",
          "name": "formtarget",
          "type": "string"
        },
        {
          "description": "The button's name, read by screen readers, for a button whose content is\nnot text.\n\nLives here rather than only on `IconButton` so the two share one render.\nAn icon-only button without it is a control a screen reader announces as\n\"button\" and nothing else.\n\nUndefaulted, so a subclass's own default, such as Copy Button's `Copy`, is\nthe first value its element receives and so the English its words fall\nback to.",
          "name": "label",
          "type": "string"
        },
        {
          "description": "Whether the action the button started is still running.\n\nA pending button keeps focus and stays in the tab order, and refuses every\npress: a click, Enter and Space, and Enter in a field of its form. The\nclick does not reach your listeners. The inner control carries\n`aria-busy` and `aria-disabled`, and when the button becomes pending while\nit has focus, a reader hears `pending-label`. The styled button draws a\nturning ring over its words.\n\n`disabled` takes the button out of reach. Use `pending` for the wait after\na press, so focus stays where the reader left it.",
          "name": "pending",
          "type": "boolean"
        },
        {
          "description": "What a reader hears when the button becomes pending while it has focus.",
          "name": "pending-label",
          "type": "string"
        },
        {
          "description": "What activating the button does to its form.",
          "name": "type",
          "type": "'button' | 'submit' | 'reset'"
        },
        {
          "description": "The value this button submits under its own `name`, as a native submit\nbutton does.\n\n**Sent with exactly the submission this button starts and not otherwise.**\nA native submit button contributes `name=value` only when it is the one\nthat submitted, and every other button on the form contributes nothing,\nwhich is what the native button standing in for this one at submit does.\n\n`CopyButton` has a `value` of its own meaning the text to put on the\nclipboard, which is not this and is why it is declared there rather than\ninherited.",
          "name": "value",
          "type": "string"
        },
        {
          "description": "Whether the control is disabled and cannot be used.\n\nReflected, because the styled tier selects on it, and mirrored to\n`aria-disabled` rather than removing the element from the accessibility\ntree, so a screen reader can still announce that the control exists.\n\nThe mirror itself is in `willUpdate` below, here rather than in each\ncontrol, because here is where `disabled` is.",
          "name": "disabled",
          "type": "boolean"
        },
        {
          "description": "The name the control's value is submitted under.\n\n**Reflected, and it is the exception to \"reflect only what CSS selects\non\".** The platform builds a form's entry list from the `name` *content\nattribute*, not from the property, so a control named through the property\nsubmitted under no name at all and was silently dropped. Measured: two\ninputs in one form, one named by attribute and one by property, and the\nform carried a single entry.\n\nSafe for a React consumer, which is what the rule is protecting: React\nrenders a custom element's props as attributes on the server, so the name\nis already in the markup being hydrated and reflecting it writes what is\nthere. It is only a change to the markup when a consumer assigns it\nimperatively later, and nothing is hydrating by then.\n\nHere rather than on each control.",
          "name": "name",
          "type": "string"
        }
      ],
      "cssParts": [
        {
          "description": "The inner control.",
          "name": "button",
          "type": ""
        },
        {
          "description": "The live region that says `pending-label` when the button becomes pending under focus. It is rendered from the first time the button is pending, and the styled button clips it out of view.",
          "name": "live",
          "type": ""
        },
        {
          "description": "The ring a pending button turns, an empty element the styled button draws over the words. It is rendered only while the button is pending.",
          "name": "spinner",
          "type": ""
        },
        {
          "description": "The button's words, which the styled button trims to their capitals and baseline so the letters sit at the middle in any font.",
          "name": "text",
          "type": ""
        }
      ],
      "cssProperties": [],
      "cssStates": [
        {
          "description": "The button is pending: `pending` is set.",
          "name": "pending",
          "type": ""
        },
        {
          "description": "A Button Group in a selection mode has this button pressed.",
          "name": "pressed",
          "type": ""
        },
        {
          "description": "Set one frame after the first render. Transitions wait for it, so an element does not animate its own arrival.",
          "name": "settled",
          "type": ""
        }
      ],
      "description": "Renders a real `<button>`, so activation, focus, and other browser behavior\ncome from the platform.\n\n**It never renders an anchor.** A router renders its own `<a>` to intercept a\nclick, and it cannot render one inside the button's shadow root. For a link\nthat looks like a button, use the base layer recipe on your own anchor:\n`<Link data-button=\"solid\">` draws the same control.\n\n`delegatesFocus` moves focus to the inner control. It is serialized into the\nserver markup, so the button is reachable by keyboard before any JavaScript\nloads.\n\nThe inner control is always `type=\"button\"`, because a\n`<button type=\"submit\">` inside a shadow root cannot submit a form outside\nit. To submit, the button puts a hidden native submit button carrying its\n`name`, `value` and `form*` overrides into the form, calls\n`requestSubmit()` with it, which runs validation, unlike `submit()`, and\ntakes it out again, as Shoelace and Web Awesome do. A `submit` listener\nreads that button as `event.submitter`, so `event.submitter.value` and\n`new FormData(form, event.submitter)` tell two submit buttons apart.\n\nAs the first submit button in its form, it is the form's default button:\nEnter in a text field of the form presses it, as it presses a native one.\n\nA press acts once its `click` has reached your listeners, as a native\nbutton's does, so `preventDefault()` in a `click` listener cancels it: the\nform is not submitted or reset, and no command is sent.\n\nSet `pending` while the action a press started is still running. The button\nkeeps focus and refuses presses, and a reader on it hears `pending-label`.\n\nSet `focusable-when-disabled` on a disabled button whose reason a Tooltip\ngives. It stays in the tab order and takes hover, so the tooltip opens, and\nit refuses every press.\n\nEvery press that acts sends `helix-press` with what pressed it in\n`detail.pointerType`: `mouse`, `pen`, `touch`, `keyboard`, or `virtual` for\na screen reader's press or a script's `click()`, as React Aria's `onPress`\ndoes. It is sent once the press has done its own work, after `click` has\nreached your listeners, and never for a disabled or pending button or a\ncanceled press.\n\n**Keyboard.** Enter and Space press the button, as on a native one.",
      "entry": "button",
      "events": [
        {
          "bubbles": true,
          "description": "Fired when a press acts, with `detail.pointerType` saying what pressed the button.",
          "name": "helix-press",
          "reactProp": "onhelix-press",
          "type": "HelixPressEvent"
        }
      ],
      "label": "Button",
      "methods": [
        {
          "description": "Presses the button, as `click()` presses a native one: the command is sent,\nthe form submits or resets, and `helix-press` says `virtual`.\n\n**Passed to the inner control, because a click on the host reaches nothing\nthat acts.** The control in the shadow root is what answers a click, so\n`click()` from a script, an automation or a test, dispatched on the host,\nsubmitted nothing and sent no `helix-press`. The control's own click reaches\nthe host's listeners once, retargeted, as one from a native button does. A\ndisabled button does nothing, and a pending one refuses the press.",
          "name": "click",
          "type": "() => void"
        },
        {
          "description": "Returns whether the control is valid, and fires `invalid` on it when it is\nnot. The control shows no message for it: `reportValidity()` does that.",
          "name": "checkValidity",
          "type": "() => boolean"
        },
        {
          "description": "Returns whether the control is valid, and when it is not, fires `invalid`\nand shows the browser's message at the control.",
          "name": "reportValidity",
          "type": "() => boolean"
        },
        {
          "description": "Marks the control invalid with `message`, or clears that with an empty\nstring, as on a native control. The form does not submit while it is set.",
          "name": "setCustomValidity",
          "type": "(message: string) => void"
        },
        {
          "description": "Moves focus to the control, as focusing a native control does.",
          "name": "focus",
          "type": "(options: FocusOptions) => void"
        }
      ],
      "package": "@fusion.dev/helix-headless",
      "part": "Root",
      "properties": [
        {
          "default": "''",
          "description": "What to send when the button is activated, and what to send it to.\n\nThe platform's invoker attributes, forwarded onto the inner `<button>`\nrather than left on the host: only a real button is recognized as an\ninvoker, so `commandfor` written on a custom element resolves nothing and\nthe trigger silently does nothing at all. A wrapper has to hand them down,\nthe same way `label` becomes `aria-label` in there.\n\n```html\n<helix-headless-button commandfor=\"confirm\" command=\"--show\">Delete…</helix-headless-button>\n```\n\n`commandFor` spells its attribute out, because Lit lowercases a property\nname to derive one rather than dash-casing it, and what a consumer writes\nis the platform's `commandfor`.",
          "name": "command",
          "readonly": false,
          "references": [
            {
              "label": "Invoker Commands",
              "url": "https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-command"
            }
          ],
          "type": "string"
        },
        {
          "default": "''",
          "description": "The id of the element the command is sent to.",
          "name": "commandFor",
          "readonly": false,
          "references": [
            {
              "label": "Invoker Commands",
              "url": "https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-commandfor"
            }
          ],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether this button is a disclosure trigger, and whether its panel is open.\n\n**Set by whatever owns the panel, and a property rather than an attribute\nfor the reason `Menu` already states about its items**: an attribute\nwritten onto a consumer's element after it upgrades is a hydration mismatch\nwaiting to happen. Measured 28 Aug 2026 against React 19.2.8, server markup\nhydrated with the definitions loaded either side of `hydrateRoot`:\n`Megamenu` and `SpeedDial` wrote `aria-expanded` onto the slotted trigger\nand React reported \"some attributes of the server rendered HTML didn't\nmatch the client properties. This won't be patched up\" in both orders;\n`Popover` and `Menu`, which write nothing, were clean in both.\n\n**A property crosses that gap because React never looks at one.** It diffs\nthe attributes it rendered, and this one is `attribute: false`, so the state\ntravels to the inner `<button>` and lands inside a shadow root React does\nnot own and cannot compare. The consumer's own element is left exactly as\nthey served it and the reader is still told what the control does.\n\n`undefined` renders nothing at all, so a button that is not a trigger\ncarries no `aria-expanded`: the resting state is the attribute's absence,\nwhich is the rule every reflected property here follows.\n\nA consumer who wants to say this statically writes `aria-expanded` on the\nhost instead, which `AriaForwardController` carries to the same place.",
          "name": "expanded",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "true",
          "description": "Whether the button holds its group's tab stop.\n\nA toolbar keeps one tab stop across its children, and the obvious way to do\nthat is to write `tabindex` onto each of them. That is a change to markup\nthe consumer rendered, so a React consumer's hydration reports it and does\nnot patch it up. A parent sets this property instead, which writes nothing,\nand the stop lands on the inner control inside the shadow root.\n\nNo attribute, because it is coordination between elements rather than\nanything a consumer sets.",
          "name": "focusable",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "false",
          "description": "Whether a disabled button stays in the tab order, so a tooltip around it\ncan say why it is disabled.\n\nSet it on a button whose reason a Tooltip gives, such as a Save with no\nchanges to save. While the button is disabled, it keeps focus and takes\nhover, and it refuses every press: a click, Enter and Space, and Enter in\na field of its form. The click does not reach your listeners, and a submit\nbutton submits nothing. The inner control carries `aria-disabled` in place\nof the native `disabled`, so a screen reader still says it is unavailable.\nThe styled button keeps the disabled look.\n\nLeave it unset on a button with no reason to give. A plain disabled button\nleaves the tab order, as a native one does, and a keyboard reader skips a\nstop that does nothing. Set `pending` for the wait after a press.",
          "name": "focusableWhenDisabled",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "Where this button's submission goes, overriding the form's own.\n\n**The five `form*` overrides ride on a native submit button that stands in\nfor this one**, because `requestSubmit(submitter)` refuses the inner\n`<button>` twice over: it is `type=\"button\"`, and it sits in this shadow\nroot, so its form owner is not the consumer's form. The stand-in is a\nhidden `<button type=\"submit\">` in the form for the one synchronous call,\nso the platform applies the overrides to that submission alone and leaves\nthe form as it was. See `submitAs`.",
          "name": "formAction",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "''",
          "description": "How the submission is encoded, overriding the form's own.",
          "name": "formEnctype",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "''",
          "description": "Which HTTP method the submission uses, overriding the form's own.",
          "name": "formMethod",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "false",
          "description": "Whether this submission skips the form's validation.",
          "name": "formNoValidate",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "Which browsing context the response lands in, overriding the form's own.",
          "name": "formTarget",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "The button's name, read by screen readers, for a button whose content is\nnot text.\n\nLives here rather than only on `IconButton` so the two share one render.\nAn icon-only button without it is a control a screen reader announces as\n\"button\" and nothing else.\n\nUndefaulted, so a subclass's own default, such as Copy Button's `Copy`, is\nthe first value its element receives and so the English its words fall\nback to.",
          "name": "label",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "false",
          "description": "Whether the action the button started is still running.\n\nA pending button keeps focus and stays in the tab order, and refuses every\npress: a click, Enter and Space, and Enter in a field of its form. The\nclick does not reach your listeners. The inner control carries\n`aria-busy` and `aria-disabled`, and when the button becomes pending while\nit has focus, a reader hears `pending-label`. The styled button draws a\nturning ring over its words.\n\n`disabled` takes the button out of reach. Use `pending` for the wait after\na press, so focus stays where the reader left it.",
          "name": "pending",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "'Loading'",
          "description": "What a reader hears when the button becomes pending while it has focus.",
          "name": "pendingLabel",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "What kind of panel this button opens, where it opens one: `menu` for a\n`Menu`.\n\nSet by the component that owns the panel, as a property for the reason\n`expanded` gives above. It is what makes a reader say the button opens a\nmenu. Read from Chromium's tree, a Menu's trigger was a plain button that\nreported collapsed and expanded and no popup.",
          "name": "popup",
          "readonly": false,
          "references": [],
          "type": "'dialog' | 'grid' | 'listbox' | 'menu' | 'tree'"
        },
        {
          "default": "'button'",
          "description": "What activating the button does to its form.",
          "name": "type",
          "readonly": false,
          "references": [],
          "type": "'button' | 'submit' | 'reset'"
        },
        {
          "default": "''",
          "description": "The value this button submits under its own `name`, as a native submit\nbutton does.\n\n**Sent with exactly the submission this button starts and not otherwise.**\nA native submit button contributes `name=value` only when it is the one\nthat submitted, and every other button on the form contributes nothing,\nwhich is what the native button standing in for this one at submit does.\n\n`CopyButton` has a `value` of its own meaning the text to put on the\nclipboard, which is not this and is why it is declared there rather than\ninherited.",
          "name": "value",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "The element this button's command is sent to, resolved.\n\nThe platform's own accessor, because the resolution is otherwise kept\ninside: `linkCommand` writes the target onto the inner `<button>`, where a\nconsumer cannot reach it, and `commandFor` answers with an id rather than\nan element. Writing assigns the inner control directly, which is what an\ninvoker without an id in the same tree needs.",
          "name": "commandForElement",
          "readonly": false,
          "references": [],
          "type": "Element | null"
        },
        {
          "default": "false",
          "description": "Whether the control is disabled and cannot be used.\n\nReflected, because the styled tier selects on it, and mirrored to\n`aria-disabled` rather than removing the element from the accessibility\ntree, so a screen reader can still announce that the control exists.\n\nThe mirror itself is in `willUpdate` below, here rather than in each\ncontrol, because here is where `disabled` is.",
          "name": "disabled",
          "readonly": false,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "''",
          "description": "The name the control's value is submitted under.\n\n**Reflected, and it is the exception to \"reflect only what CSS selects\non\".** The platform builds a form's entry list from the `name` *content\nattribute*, not from the property, so a control named through the property\nsubmitted under no name at all and was silently dropped. Measured: two\ninputs in one form, one named by attribute and one by property, and the\nform carried a single entry.\n\nSafe for a React consumer, which is what the rule is protecting: React\nrenders a custom element's props as attributes on the server, so the name\nis already in the markup being hydrated and reflecting it writes what is\nthere. It is only a change to the markup when a consumer assigns it\nimperatively later, and nothing is hydrating by then.\n\nHere rather than on each control.",
          "name": "name",
          "readonly": false,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether the control meets its constraints, and why not.",
          "name": "validity",
          "readonly": true,
          "references": [
            {
              "label": "ElementInternals.validity",
              "url": "https://developer.mozilla.org/docs/Web/API/ElementInternals/validity"
            }
          ],
          "type": "ValidityState"
        },
        {
          "default": "",
          "description": "The message the browser would show for the control, or an empty string.",
          "name": "validationMessage",
          "readonly": true,
          "references": [],
          "type": "string"
        },
        {
          "default": "",
          "description": "Whether the control is checked when its form is submitted.",
          "name": "willValidate",
          "readonly": true,
          "references": [],
          "type": "boolean"
        },
        {
          "default": "",
          "description": "The form the control belongs to, or `null`.",
          "name": "form",
          "readonly": true,
          "references": [
            {
              "label": "form-associated custom elements",
              "url": "https://html.spec.whatwg.org/multipage/custom-elements.html#form-associated-custom-elements"
            }
          ],
          "type": "HTMLFormElement | null"
        },
        {
          "default": "",
          "description": "The labels associated with the control.",
          "name": "labels",
          "readonly": true,
          "references": [
            {
              "label": "form-associated custom elements",
              "url": "https://html.spec.whatwg.org/multipage/custom-elements.html#form-associated-custom-elements"
            }
          ],
          "type": "NodeList"
        },
        {
          "default": "new Promise<void>((resolve) => { this.resolveSettled = resolve })",
          "description": "Resolves a frame after the first update, when transitions are switched on.\n\n**Named `whenSettled` because `settled` is taken four times over**, by\nImage, Splitter and both color controls, none of them about transitions. A\nbase class takes the name every subclass then cannot use.\n\n**The state it reports cannot be watched, which is why this exists.** A\ncustom state is deliberately not in the DOM, so there is no\n`MutationObserver` equivalent for `ElementInternals.states`. Await this\ninstead.\n\nSeparate from `updateComplete`, which resolves in the microtask checkpoint\nthe suppression is still active in. That is the distinction `readyToMove`\nin `@fusion.dev/helix-testing` is written around.\n\nIt never resolves where `requestAnimationFrame` never fires, which is a\nserver, a hidden tab, and happy-dom. Callers that must not hang there race\nit against a timeout, as the happy-dom harness's `readyToMove` does. The\nbrowser harness's awaits it alone: a browser always runs the frame, and a\ntimer beside it was a bet on how long one takes, which a busy run lost.",
          "name": "whenSettled",
          "readonly": true,
          "references": [],
          "type": "Promise<void>"
        }
      ],
      "slots": [
        {
          "description": "The button's label.",
          "name": "",
          "type": ""
        },
        {
          "description": "Content after the label.",
          "name": "end",
          "type": ""
        },
        {
          "description": "Content before the label, usually an icon.",
          "name": "start",
          "type": ""
        }
      ],
      "specifier": "@fusion.dev/helix-headless/button",
      "status": "stable",
      "summary": "Triggers an action, submits a form, or navigates.",
      "tagName": "helix-headless-button",
      "tier": "headless",
      "words": [
        "pendingLabel"
      ]
    }
  ],
  "examples": [
    {
      "css": "",
      "description": "Buttons in each appearance and tone, the two settings that style a button.",
      "html": "<helix-button\n  emphasis=\"solid\"\n  tone=\"primary\"\n>Save changes</helix-button>",
      "imports": [
        "import '@fusion.dev/helix/button'"
      ],
      "label": "Preview",
      "renders": [
        {
          "file": "preview.dark.png",
          "shot": "dark"
        },
        {
          "file": "preview.narrow.png",
          "shot": "narrow"
        },
        {
          "file": "preview.png",
          "shot": "wide"
        }
      ],
      "slug": "preview",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >\n      Save changes\n    </helix-button>\n  )\n}"
    },
    {
      "css": ":root {\n  color-scheme: light dark;\n  font-family: system-ui, sans-serif;\n  --_gray-surface: light-dark(#fafafa, #18181b);\n  --_gray-subtle: light-dark(#f0f0f1, #232326);\n  --_gray-edge: light-dark(#e6e6e8, #313136);\n  --_gray-fill: light-dark(#e2e2e5, #2f2f33);\n  --_gray-line: light-dark(#c9c9ce, #45454b);\n  --_gray-mark: light-dark(#8e8e96, #86868f);\n  --_gray-muted: light-dark(#63636b, #a1a1aa);\n  --_gray-strong: light-dark(#2a2a2e, #e9e9ec);\n  --_radius-control: 0.25rem;\n  --_radius-surface: 0.5rem;\n  --_radius-panel: 0.75rem;\n}\n\nhelix-headless-button {\n  display: inline-flex;\n  max-inline-size: 100%;\n}\n\nhelix-headless-button::part(button) {\n  all: unset;\n  align-items: center;\n  background-color: var(--_gray-strong);\n  block-size: 2.5rem;\n  border: 1px solid transparent;\n  border-radius: var(--_radius-control);\n  box-sizing: border-box;\n  color: var(--_gray-surface);\n  cursor: pointer;\n  display: inline-flex;\n  font-family: inherit;\n  font-size: 0.8125rem;\n  font-weight: 500;\n  gap: 0.5rem;\n  inline-size: 100%;\n  justify-content: center;\n  line-height: 1.2;\n  padding-inline: 1rem;\n  white-space: nowrap;\n}\n\nhelix-headless-button:not(:disabled)::part(button):hover {\n  background-color: var(--_gray-muted);\n}\n\nhelix-headless-button:disabled::part(button) {\n  cursor: not-allowed;\n  opacity: 0.5;\n}\n\nhelix-headless-button::part(button):focus-visible {\n  outline: 2px solid var(--_gray-mark);\n  outline-offset: 2px;\n}",
      "description": "Buttons in each appearance and tone, the two settings that style a button.",
      "html": "<link\n  href=\"styles.css\"\n  rel=\"stylesheet\"\n/>\n\n<helix-headless-button>Save changes</helix-headless-button>",
      "imports": [
        "import '@fusion.dev/helix-headless/button'"
      ],
      "label": "Preview",
      "renders": [],
      "slug": "preview",
      "tier": "headless",
      "tsx": "import './styles.css'\n\nexport function Example() {\n  return <helix-headless-button>Save changes</helix-headless-button>\n}"
    },
    {
      "css": "/* A second theme scope, so the pair below rethemes on its own and the pair\n   above does not. `[data-helix-theme]` re-derives the whole ramp at a subtree,\n   which is what makes the relationship visible: both the brand and the accent\n   move, and they move together. */\n.rethemed {\n  /* **240 and not 140, and the reason is a trap worth knowing about.** Accent is\n     derived by rotating the brand's hue at fixed OKLCH lightness, and WCAG\n     luminance is not hue independent: the same L is a different contrast at\n     every angle. Swept in 5 degree steps at the shipping L and chroma, the ink\n     scores 5.65:1 on white at hue 333 and 4.08:1 at hue 183, so a band roughly\n     150 to 230 wide of cyan and teal cannot clear AA as text at all, however the\n     brand was solved.\n\n     140 put this accent at hue 160, inside that band: measured, white on the\n     fill read 4.45:1 and the ink read 3.41:1 on its own wash. 240 puts it at\n     260 and clears both. Nothing about the example wanted that particular green,\n     and an example demonstrating a relationship should demonstrate one that\n     works. */\n  --helix-seed-accent-hue: 240;\n  /* **0.539 and not 0.58, which is the lightness the solver would have\n     produced.** `primitives.css` records the case in full: a seed handed in at\n     0.58 reads 4.395:1 on white and 4.395:1 under the on-accent ink, under AA on\n     both sides of the pair the palette uses most, and `deriveTheme` solves it\n     down to 0.539 to clear. Setting `--helix-seed-brand` by hand walks straight\n     past that solve, which is what this example was doing: the accent it derived\n     came out at 4.09:1 as text on its own wash. */\n  --helix-seed-brand: oklch(0.539 0.19 20);\n}",
      "description": "The `accent` tone, a second emphasis derived from your brand color.",
      "html": "<link\n  href=\"styles.css\"\n  rel=\"stylesheet\"\n>\n\n<helix-flex\n  direction=\"column\"\n  gap=\"lg\"\n>\n  <helix-flex\n    align-items=\"center\"\n    gap=\"sm\"\n  >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Publish</helix-button\n    >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"accent\"\n    >Schedule</helix-button\n    >\n    <helix-button tone=\"accent\">Preview</helix-button>\n  </helix-flex>\n\n  <!-- Preview frame: the angle is a token, so this section rethemes on its own. -->\n  <helix-flex\n    align-items=\"center\"\n    class=\"rethemed\"\n    data-helix-theme\n    gap=\"sm\"\n  >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Publish</helix-button\n    >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"accent\"\n    >Schedule</helix-button\n    >\n    <helix-button tone=\"accent\">Preview</helix-button>\n  </helix-flex>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Accent",
      "renders": [],
      "slug": "accent",
      "tier": "styled",
      "tsx": "import './styles.css'\n\nexport function Example() {\n  return (\n    <helix-flex\n      direction=\"column\"\n      gap=\"lg\"\n    >\n      <helix-flex\n        align-items=\"center\"\n        gap=\"sm\"\n      >\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"primary\"\n        >\n          Publish\n        </helix-button>\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"accent\"\n        >\n          Schedule\n        </helix-button>\n        <helix-button tone=\"accent\">Preview</helix-button>\n      </helix-flex>\n\n      {/* Preview frame: the angle is a token, so this section rethemes on its own. */}\n      <helix-flex\n        align-items=\"center\"\n        className=\"rethemed\"\n        data-helix-theme\n        gap=\"sm\"\n      >\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"primary\"\n        >\n          Publish\n        </helix-button>\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"accent\"\n        >\n          Schedule\n        </helix-button>\n        <helix-button tone=\"accent\">Preview</helix-button>\n      </helix-flex>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "A button that navigates, rendered as an anchor.",
      "html": "<a\n  data-button=\"solid\"\n  data-button-tone=\"primary\"\n  href=\"https://example.test\"\n  rel=\"noreferrer\"\n  target=\"_blank\"\n>\n  Visit website\n</a>",
      "imports": [],
      "label": "As Link",
      "renders": [],
      "slug": "as-link",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <a\n      data-button=\"solid\"\n      data-button-tone=\"primary\"\n      href=\"https://example.test\"\n      rel=\"noreferrer\"\n      target=\"_blank\"\n    >\n      Visit website\n    </a>\n  )\n}"
    },
    {
      "css": "",
      "description": "Disabled buttons in several appearances, including one disabled by its fieldset.",
      "html": "<helix-flex gap=\"sm\">\n  <helix-button\n    disabled\n    emphasis=\"solid\"\n    tone=\"primary\"\n  >Save changes</helix-button>\n  <helix-button disabled>Cancel</helix-button>\n\n  <!-- The disabled fieldset disables this button. -->\n  <fieldset disabled>\n    <helix-button emphasis=\"ghost\">Reset</helix-button>\n  </fieldset>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Disabled",
      "renders": [],
      "slug": "disabled",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <helix-flex gap=\"sm\">\n      <helix-button\n        disabled\n        emphasis=\"solid\"\n        tone=\"primary\"\n      >\n        Save changes\n      </helix-button>\n      <helix-button disabled>Cancel</helix-button>\n\n      {/* The disabled fieldset disables this button. */}\n      <fieldset disabled>\n        <helix-button emphasis=\"ghost\">Reset</helix-button>\n      </fieldset>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "Each emphasis in the neutral, primary, and danger tones.",
      "html": "<helix-flex\n  direction=\"column\"\n  gap=\"md\"\n>\n  <helix-flex gap=\"sm\">\n    <helix-button>Cancel</helix-button>\n    <helix-button tone=\"primary\">Save</helix-button>\n    <helix-button tone=\"danger\">Delete</helix-button>\n  </helix-flex>\n\n  <helix-flex gap=\"sm\">\n    <helix-button emphasis=\"solid\">Cancel</helix-button>\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"danger\"\n    >Delete</helix-button>\n  </helix-flex>\n\n  <helix-flex gap=\"sm\">\n    <helix-button emphasis=\"subtle\">Cancel</helix-button>\n    <helix-button\n      emphasis=\"subtle\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <helix-button\n      emphasis=\"subtle\"\n      tone=\"danger\"\n    >Delete</helix-button>\n  </helix-flex>\n\n  <helix-flex gap=\"sm\">\n    <helix-button emphasis=\"ghost\">Cancel</helix-button>\n    <helix-button\n      emphasis=\"ghost\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <helix-button\n      emphasis=\"ghost\"\n      tone=\"danger\"\n    >Delete</helix-button>\n  </helix-flex>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Emphasis",
      "renders": [],
      "slug": "emphasis",
      "tier": "styled",
      "tsx": "/* Each row is one emphasis and each column is one tone. `undefined` leaves the\n   attribute off, so the first button uses both defaults. */\nconst EMPHASES = [undefined, 'solid', 'subtle', 'ghost'] as const\nconst TONES = [undefined, 'primary', 'danger'] as const\nconst LABELS = { danger: 'Delete', default: 'Cancel', primary: 'Save' }\n\nexport function Example() {\n  return (\n    <helix-flex\n      direction=\"column\"\n      gap=\"md\"\n    >\n      {EMPHASES.map((emphasis) => (\n        <helix-flex\n          gap=\"sm\"\n          key={emphasis ?? 'default'}\n        >\n          {TONES.map((tone) => (\n            <helix-button\n              emphasis={emphasis}\n              key={tone ?? 'default'}\n              tone={tone}\n            >\n              {LABELS[tone ?? 'default']}\n            </helix-button>\n          ))}\n        </helix-flex>\n      ))}\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "Disabled buttons that stay in the tab order, with a tooltip that says why each one is disabled.",
      "html": "<helix-flex\n  align-items=\"center\"\n  gap=\"sm\"\n>\n  <helix-icon-button\n    aria-describedby=\"back-tip\"\n    disabled\n    focusable-when-disabled\n    id=\"back\"\n    label=\"Back\"\n  >\n    <helix-icon name=\"arrow-left\"></helix-icon>\n  </helix-icon-button>\n  <helix-tooltip\n    content=\"No page to go back to\"\n    for=\"back\"\n    id=\"back-tip\"\n  ></helix-tooltip>\n\n  <helix-button\n    aria-describedby=\"save-tip\"\n    disabled\n    emphasis=\"solid\"\n    focusable-when-disabled\n    id=\"save\"\n    tone=\"primary\"\n  >Save changes</helix-button>\n  <helix-tooltip\n    content=\"No changes to save\"\n    for=\"save\"\n    id=\"save-tip\"\n  ></helix-tooltip>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'",
        "import '@fusion.dev/helix/icon'",
        "import '@fusion.dev/helix/icon-button'",
        "import '@fusion.dev/helix/tooltip'"
      ],
      "label": "Focusable When Disabled",
      "renders": [],
      "slug": "focusable-when-disabled",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <helix-flex\n      align-items=\"center\"\n      gap=\"sm\"\n    >\n      <helix-icon-button\n        aria-describedby=\"back-tip\"\n        disabled\n        focusable-when-disabled\n        id=\"back\"\n        label=\"Back\"\n      >\n        <helix-icon name=\"arrow-left\"></helix-icon>\n      </helix-icon-button>\n      <helix-tooltip\n        content=\"No page to go back to\"\n        for=\"back\"\n        id=\"back-tip\"\n      ></helix-tooltip>\n\n      <helix-button\n        aria-describedby=\"save-tip\"\n        disabled\n        emphasis=\"solid\"\n        focusable-when-disabled\n        id=\"save\"\n        tone=\"primary\"\n      >\n        Save changes\n      </helix-button>\n      <helix-tooltip\n        content=\"No changes to save\"\n        for=\"save\"\n        id=\"save-tip\"\n      ></helix-tooltip>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "Three submit buttons on one form, each setting where and how its own submission goes.",
      "html": "<!-- Preview frame: the submit is swallowed. -->\n<form\n  action=\"/tickets\"\n  enctype=\"multipart/form-data\"\n  method=\"post\"\n  onsubmit=\"event.preventDefault()\"\n>\n  <helix-flex\n    direction=\"column\"\n    gap=\"md\"\n  >\n    <helix-input\n      label=\"Summary\"\n      name=\"summary\"\n      required\n    ></helix-input>\n\n    <helix-textarea\n      label=\"What happened\"\n      name=\"body\"\n      required\n    ></helix-textarea>\n\n    <helix-file-input\n      hint=\"PNG or JPG, up to 10MB\"\n      label=\"Screenshot\"\n      name=\"screenshot\"\n    ></helix-file-input>\n\n    <helix-flex gap=\"sm\">\n      <helix-button\n        emphasis=\"solid\"\n        name=\"intent\"\n        tone=\"primary\"\n        type=\"submit\"\n        value=\"send\"\n      >Send</helix-button>\n\n      <helix-button\n        formaction=\"/tickets/draft\"\n        formenctype=\"application/x-www-form-urlencoded\"\n        formnovalidate\n        name=\"intent\"\n        type=\"submit\"\n        value=\"draft\"\n      >Save draft</helix-button>\n\n      <helix-button\n        formaction=\"/tickets/preview\"\n        formmethod=\"get\"\n        formtarget=\"_blank\"\n        name=\"intent\"\n        type=\"submit\"\n        value=\"preview\"\n      >Preview</helix-button>\n    </helix-flex>\n  </helix-flex>\n</form>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/file-input'",
        "import '@fusion.dev/helix/flex'",
        "import '@fusion.dev/helix/input'",
        "import '@fusion.dev/helix/textarea'"
      ],
      "label": "Form Overrides",
      "renders": [],
      "slug": "form-overrides",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <>\n      {/* Preview frame: the submit is swallowed. */}\n      <form\n        action=\"/tickets\"\n        encType=\"multipart/form-data\"\n        method=\"post\"\n        onSubmit={(event) => event.preventDefault()}\n      >\n        <helix-flex\n          direction=\"column\"\n          gap=\"md\"\n        >\n          <helix-input\n            label=\"Summary\"\n            name=\"summary\"\n            required\n          ></helix-input>\n\n          <helix-textarea\n            label=\"What happened\"\n            name=\"body\"\n            required\n          ></helix-textarea>\n\n          <helix-file-input\n            hint=\"PNG or JPG, up to 10MB\"\n            label=\"Screenshot\"\n            name=\"screenshot\"\n          ></helix-file-input>\n\n          <helix-flex gap=\"sm\">\n            <helix-button\n              emphasis=\"solid\"\n              name=\"intent\"\n              tone=\"primary\"\n              type=\"submit\"\n              value=\"send\"\n            >\n              Send\n            </helix-button>\n\n            <helix-button\n              formaction=\"/tickets/draft\"\n              formenctype=\"application/x-www-form-urlencoded\"\n              formnovalidate\n              name=\"intent\"\n              type=\"submit\"\n              value=\"draft\"\n            >\n              Save draft\n            </helix-button>\n\n            <helix-button\n              formaction=\"/tickets/preview\"\n              formmethod=\"get\"\n              formtarget=\"_blank\"\n              name=\"intent\"\n              type=\"submit\"\n              value=\"preview\"\n            >\n              Preview\n            </helix-button>\n          </helix-flex>\n        </helix-flex>\n      </form>\n    </>\n  )\n}"
    },
    {
      "css": "/* The column is the thing with a width. Nothing is said about the buttons. */\n.column {\n  max-inline-size: 20rem;\n}\n\n/* And this is the whole of \"make a button hug its label\": one line about how\n   the child sits in its parent, written where every other layout decision is\n   written.\n\n   **`width` on the button is a different question and does not answer this\n   one.** It says which of three behaviors the button has, `auto` against\n   `standard` against `full`, and none of them is \"shrink inside whatever is\n   stretching me\": that depends on the parent, so a property would have to be\n   re-answered for every layout the button is ever put in. This file is here to\n   say where the answer lives, and it is the reason the axis has three policy\n   values rather than a fourth for hugging. */\n.hug {\n  align-self: start;\n}",
      "description": "Buttons in a narrow column, one as wide as the column and one as wide as its label.",
      "html": "<link\n  href=\"styles.css\"\n  rel=\"stylesheet\"\n/>\n\n<helix-flex\n  class=\"column\"\n  direction=\"column\"\n  gap=\"sm\"\n>\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"primary\"\n  >Save changes</helix-button>\n  <helix-button class=\"hug\">\n    Cancel\n  </helix-button>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Hugging",
      "renders": [],
      "slug": "hugging",
      "tier": "styled",
      "tsx": "import './styles.css'\n\nexport function Example() {\n  return (\n    <helix-flex\n      className=\"column\"\n      direction=\"column\"\n      gap=\"sm\"\n    >\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"primary\"\n      >\n        Save changes\n      </helix-button>\n      <helix-button className=\"hug\">Cancel</helix-button>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "A button that waits after a press: it keeps focus, refuses another press, and says Saving to a screen reader.",
      "html": "<helix-button\n  emphasis=\"solid\"\n  id=\"save\"\n  pending-label=\"Saving\"\n  tone=\"primary\"\n>Save changes</helix-button>\n\n<script type=\"module\">\n  const save = document.getElementById('save')\n\n  // The time the pretend request takes.\n  const WAIT = 2000\n\n  save.addEventListener('click', () => {\n    save.pending = true\n    setTimeout(() => {\n      save.pending = false\n    }, WAIT)\n  })\n</script>",
      "imports": [
        "import '@fusion.dev/helix/button'"
      ],
      "label": "Pending",
      "renders": [],
      "slug": "pending",
      "tier": "styled",
      "tsx": "import { useState } from 'react'\n\n// The time the pretend request takes.\nconst WAIT = 2000\n\nexport function Example() {\n  const [pending, setPending] = useState(false)\n\n  const save = () => {\n    setPending(true)\n    setTimeout(() => setPending(false), WAIT)\n  }\n\n  return (\n    <helix-button\n      emphasis=\"solid\"\n      onClick={save}\n      pending={pending}\n      pending-label=\"Saving\"\n      tone=\"primary\"\n    >\n      Save changes\n    </helix-button>\n  )\n}"
    },
    {
      "css": "",
      "description": "The four tones, which mark what an action means.",
      "html": "<!-- `accent` matches `primary` until the theme sets a second color. -->\n<helix-flex gap=\"sm\">\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"primary\"\n  >Save</helix-button>\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"accent\"\n  >Share</helix-button>\n  <helix-button tone=\"neutral\">Cancel</helix-button>\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"danger\"\n  >Delete</helix-button>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Tones",
      "renders": [],
      "slug": "tones",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <helix-flex gap=\"sm\">\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"primary\"\n      >\n        {/* `accent` matches `primary` until the theme sets a second color. */}\n        Save\n      </helix-button>\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"accent\"\n      >\n        Share\n      </helix-button>\n      <helix-button tone=\"neutral\">Cancel</helix-button>\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"danger\"\n      >\n        Delete\n      </helix-button>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": ".standard {\n  --button-inline-size: var(--helix-size-button);\n}",
      "description": "Buttons at their own width, at a standard width set by `--button-inline-size`, and at full width with `width`.",
      "html": "<link\n  href=\"styles.css\"\n  rel=\"stylesheet\"\n>\n\n<helix-flex\n  direction=\"column\"\n  gap=\"md\"\n>\n  <helix-flex gap=\"sm\">\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <helix-button>Cancel</helix-button>\n  </helix-flex>\n\n  <helix-flex\n    class=\"standard\"\n    gap=\"sm\"\n  >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <helix-button>Cancel</helix-button>\n  </helix-flex>\n\n  <helix-flex\n    class=\"standard\"\n    gap=\"sm\"\n  >\n    <helix-button\n      emphasis=\"solid\"\n      tone=\"primary\"\n    >Save</helix-button>\n    <a\n      data-button=\"\"\n      href=\"#cancel\"\n    >Cancel</a>\n  </helix-flex>\n\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"primary\"\n    width=\"full\"\n  >Continue</helix-button>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'"
      ],
      "label": "Width",
      "renders": [],
      "slug": "width",
      "tier": "styled",
      "tsx": "import './styles.css'\n\nexport function Example() {\n  return (\n    <helix-flex\n      direction=\"column\"\n      gap=\"md\"\n    >\n      <helix-flex gap=\"sm\">\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"primary\"\n        >\n          Save\n        </helix-button>\n        <helix-button>Cancel</helix-button>\n      </helix-flex>\n\n      <helix-flex\n        className=\"standard\"\n        gap=\"sm\"\n      >\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"primary\"\n        >\n          Save\n        </helix-button>\n        <helix-button>Cancel</helix-button>\n      </helix-flex>\n\n      <helix-flex\n        className=\"standard\"\n        gap=\"sm\"\n      >\n        <helix-button\n          emphasis=\"solid\"\n          tone=\"primary\"\n        >\n          Save\n        </helix-button>\n        <a\n          data-button=\"\"\n          href=\"#cancel\"\n        >\n          Cancel\n        </a>\n      </helix-flex>\n\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"primary\"\n        width=\"full\"\n      >\n        Continue\n      </helix-button>\n    </helix-flex>\n  )\n}"
    },
    {
      "css": "",
      "description": "Icons at either end of the label, placed in the `start` and `end` slots.",
      "html": "<helix-flex gap=\"sm\">\n  <helix-button\n    emphasis=\"solid\"\n    tone=\"primary\"\n  >\n    <helix-icon\n      name=\"play\"\n      slot=\"start\"\n    ></helix-icon>\n    Play\n    <helix-icon\n      mirror\n      name=\"arrow-right\"\n      slot=\"end\"\n    ></helix-icon>\n  </helix-button>\n  <helix-button emphasis=\"outlined\">\n    <helix-icon\n      name=\"pause\"\n      slot=\"start\"\n    ></helix-icon>\n    Pause\n  </helix-button>\n</helix-flex>",
      "imports": [
        "import '@fusion.dev/helix/button'",
        "import '@fusion.dev/helix/flex'",
        "import '@fusion.dev/helix/icon'"
      ],
      "label": "With Slots",
      "renders": [],
      "slug": "with-slots",
      "tier": "styled",
      "tsx": "export function Example() {\n  return (\n    <helix-flex gap=\"sm\">\n      <helix-button\n        emphasis=\"solid\"\n        tone=\"primary\"\n      >\n        <helix-icon\n          name=\"play\"\n          slot=\"start\"\n        ></helix-icon>\n        Play\n        <helix-icon\n          mirror\n          name=\"arrow-right\"\n          slot=\"end\"\n        ></helix-icon>\n      </helix-button>\n      <helix-button emphasis=\"outlined\">\n        <helix-icon\n          name=\"pause\"\n          slot=\"start\"\n        ></helix-icon>\n        Pause\n      </helix-button>\n    </helix-flex>\n  )\n}"
    }
  ],
  "imports": [],
  "label": "Button",
  "package": "@fusion.dev/helix",
  "replaces": [
    "button",
    "input[type=submit]",
    "input[type=button]",
    "input[type=reset]"
  ],
  "since": "0.1.0",
  "slug": "button",
  "source": "helix/styled/src/button",
  "status": "stable",
  "summary": "Triggers an action, submits a form, or navigates.",
  "usage": {
    "headless": [
      "import '@fusion.dev/helix-headless/button'"
    ],
    "styled": [
      "import '@fusion.dev/helix/button'"
    ]
  }
}
