{
  "name": "Button",
  "type": "component",
  "description": "Flexible button component with multiple variants. One size — `.button-main` (22:3854) draws a single 40px height.",
  "export": "@gtmi/strix-react/atoms/button",
  "path": "@gtmi/strix-react/atoms/button",
  "figma": {
    "file": "zT3vGm4QlFEI2Jp9nxyFKI",
    "fileName": "Demo components library",
    "nodes": [
      {
        "node": "22:3854",
        "name": ".button-main",
        "section": "Atoms"
      },
      {
        "node": "22:3899",
        "name": ".cellphone-button",
        "section": "Atoms"
      },
      {
        "node": "1520:37925",
        "name": "Link",
        "file": "85sD2HI8wksP9Y7uDmJ9DK",
        "fileName": "Multi product demo",
        "basis": "instance",
        "note": "`ghost`. An INSTANCE, not a master: the master (1:4438 / 503:4194) is remote and inDocument: false in both inventories, so only the resting state is readable — a weaker basis than the other two nodes here, stated because it is invisible later. States derived under rule 2; see docs/FIGMA-EXCEPTIONS.md."
      }
    ],
    "note": "Ports THREE nodes, and the third is in a different file. `.button-main` and `.cellphone-button` disagree on height with .button-group, which draws its .button-main instances at 42px; see button-main.json. Figma draws TWO different links: `.button-main`'s Link type (`link`, underlined 14/20) and `Link` 1520:37925 (`ghost`, 12/18, no border). They are separate components, not states of one."
  },
  "props": [
    {
      "name": "variant",
      "type": "primary | secondary | link | ghost | destructive",
      "default": "primary",
      "description": "Button variant. `link` is `.button-main`'s underlined Link type; `ghost` is Figma's other link, `Link` 1520:37925 — 12/18, no underline, for RCS action rows. `destructive` has no Figma member; `ghost`'s non-resting states are derived — see docs/FIGMA-EXCEPTIONS.md."
    },
    {
      "name": "disabled",
      "type": "boolean",
      "default": "false",
      "description": "Is button disabled"
    },
    {
      "name": "isLoading",
      "type": "boolean",
      "default": "false",
      "description": "Is button loading"
    }
  ],
  "ai_tags": ["button", "interactive", "cta"],
  "since": "0.2.0",
  "tokens_used": [
    "--color-bg-button-primary(+hover/pressed/disabled)",
    "--color-text-button-primary(+disabled)",
    "--color-bg-button-destructive(+hover/pressed/disabled)",
    "--color-border-button-outline(+pressed)",
    "--color-bg-button-outline-hover",
    "--color-text-button-outline(+hover)",
    "--color-border-button-button-secondary-disabled",
    "--color-text-button-secondary-disabled",
    "--color-highlight",
    "--color-text-button-link-pressed",
    "--color-border-focus",
    "--font-body-medium-medium",
    "--font-body-small-medium",
    "--spacing-gap-*"
  ],
  "notes": "Styled with plain Tailwind v4 utilities generated from the token @theme block, not arbitrary [var(...)] syntax. className merges through cn() so a caller can always override. Disabled is expressed two ways depending on how the component renders, and both now work. As a native <button> it carries the `disabled` attribute; through the `render` seam — as an anchor, or inside a Base UI composite such as Toolbar.Button, where the attribute is invalid or simply never set — the state travels as `aria-disabled`. Every `disabled:` variant used to fire only on the first path, so a disabled Button-as-link looked enabled AND stayed clickable: aria-disabled announces the state and tabIndex -1 removes it from the tab order, but neither stops activation, because an anchor has no disabled attribute to honour. Both halves are fixed. Each variant in `variantClasses` states its own `disabled:`/`aria-disabled:` pair directly, and a test derives the parity between the two prefixes from the rendered class list across all four variants so they cannot drift apart. `pointer-events-none` is deliberately aria-only: a native disabled button is already inert and suppressing pointer events there would hide the not-allowed cursor. On the aria path a click and an Enter keydown are also blocked, for a programmatic click or an element focused before it became disabled. Found while building ButtonGroup on Base UI's Toolbar, which expresses disabled this way by design."
}
