{
  "name": "SelectableTile",
  "type": "component",
  "description": "A bordered tile the user picks from a set: an optional leading icon, a label, and an optional second line. Renders a toggle button with aria-pressed.",
  "export": "@gtmi/strix-react/atoms/selectable-tile",
  "path": "@gtmi/strix-react/atoms/selectable-tile",
  "figma": {
    "file": "zT3vGm4QlFEI2Jp9nxyFKI",
    "fileName": "Demo components library",
    "nodes": [
      {
        "node": "22:5367",
        "name": ".button-selectable",
        "section": "Atoms"
      }
    ]
  },
  "props": [
    {
      "name": "label",
      "type": "string",
      "required": true,
      "description": "The tile's primary text."
    },
    {
      "name": "description",
      "type": "string",
      "description": "Optional second line, for a detail like a phone number. Figma's additional-text."
    },
    {
      "name": "icon",
      "type": "ReactNode",
      "description": "Optional leading graphic. Figma pairs this with a product logo."
    },
    {
      "name": "variant",
      "type": "tile | tab | demo",
      "default": "demo",
      "description": "tab is Figma's .tab-item: square corners and its own padding. demo is the opt-in Figma .button-selectable reference treatment with red interaction states."
    },
    {
      "name": "size",
      "type": "sm | md",
      "default": "sm",
      "description": "36px or 44px, the two heights Figma fixes. Ignored when orientation is vertical, which grows with its content."
    },
    {
      "name": "orientation",
      "type": "horizontal | vertical",
      "default": "horizontal",
      "description": "Vertical stacks the label above the description and has no fixed height."
    },
    {
      "name": "selected",
      "type": "boolean",
      "default": "false",
      "description": "Whether this tile is the chosen one. Emitted as aria-pressed and as data-selected."
    },
    {
      "name": "disabled",
      "type": "boolean",
      "description": "Standard button disabled. Leaves the tab order."
    }
  ],
  "ai_tags": ["selectable", "tile", "card", "choice", "toggle", "tab", "primitive"],
  "since": "0.2.0",
  "tokens_used": [
    "--color-border-accent",
    "--color-border-destructive",
    "--color-border-disabled",
    "--color-border-focus",
    "--color-border-hover",
    "--color-border-tile",
    "--color-border-tile-selected",
    "--color-surface-hover",
    "--color-surface-tile-hover",
    "--color-text-disabled",
    "--color-text-primary",
    "--color-text-subtle-figma",
    "--leading-height-figma-12",
    "--leading-height-figma-14",
    "--radius-large",
    "--spacing-borders-lg",
    "--spacing-borders-sm",
    "--spacing-gap-100",
    "--spacing-gap-200",
    "--spacing-padding-horizontal-md",
    "--spacing-padding-horizontal-med-sm",
    "--spacing-padding-vertical-sm",
    "--text-size-figma-12",
    "--text-size-figma-14"
  ],
  "notes": "Ported from the Figma atom `.button-selectable` (32 variants: Size Big/Small x Alignment Horizontal/Vertical x State Default/Hover/Selected/Disabled x Show additional text Yes/No), with `.tab-item` expressed as the `tab` variant rather than as a second component. The two atoms are the same mechanism -- a bordered row carrying a logo and a label whose border colour is its state -- differing only in radius and padding, and in which accent the file reached for (.tab-item the highlight blue, .button-selectable the brand red). In Strix's semantic tokens that last difference disappears: both mean \"this one is chosen\". Figma's State axis is NOT a prop: Hover and Disabled are CSS states, and only Selected is a genuine one. Three of Figma's decisions are declined, each measured, with the numbers in scripts/figma/bindings/button-selectable.json. (1) The resting border: Figma uses gray-700, 1.95:1 against the page where WCAG 1.4.11 wants 3:1 -- the third atom whose resting boundary fails the same way, after .input-text and .tab-item, and the remedy is the same each time, --color-border-control at 4.78:1 light and 5.55:1 dark. (2) Selection by colour alone: Figma's Selected differs from Default only in border hue, gray to red, 2.35:1 between the two, which is what WCAG 1.4.1 prohibits; aria-pressed answers assistive technology but not a sighted user who cannot separate the hues, so selection also thickens the border to 2px, with the outer size held by the explicit height and border-box. (3) The hover fill: Figma tints the tile with a 20%-opacity red -- an unbound literal rather than a variable, equal to but not bound to color/brand/twilio-red -- which composites to 1.1:1 against the page, invisible, and cannot cross to a light theme at all since a translucent fill's composite depends on the backdrop; --color-surface-hover is the measured equivalent in both themes. Two smaller deviations: Figma brightens the secondary line to white on hover but leaves it grey when selected, which reads as an artefact of how the file was assembled rather than a decision, so the line keeps one ink; and the 36px/44px heights are set explicitly rather than derived, because 20px of leading plus a padding step lands on 38px. The contrast assertions derive their tokens from the component's own cva output and were mutation-tested: the hover check first named --color-surface-hover and stayed green when the fill was swapped for bg-highlight (1.84:1 light), which is exactly the failure docs/AGENTS.md warns about; deriving the surfaces catches it. Figma's `Show tooltip` boolean is false in every one of the 32 variants, so the file shows no tooltip to port; compose Tooltip beside the tile, as `.label` does.\n\nTHREE OF FOUR DECLINES REVERSED (.button-selectable, 22:5367). The resting border is Figma's gray-700 via --color-border-tile at 1.95:1 -- a logged conflict, the third atom with this same defect after .input-text and .tab-item, and all three now ship Figma's value with a keyed exemption rather than a substitution. It gets its own role rather than sharing .tab-item's: Figma draws a 1px box here and a 2px underline there, so one token for both would assert they are one treatment.\n\nWHAT NO DECLINE DESCRIBED: the component reached straight into --palette-brand-accesible-red-dark-theme for its selected and hover borders -- a palette literal inside a component, which rule 5 forbids -- and its own test PINNED that reach-through. The atom's red was right and its plumbing was wrong, and the check made the wrong plumbing load-bearing.\n\nTHE HOVER FILL WAS INVERTED IN INTENT, not merely substituted. Figma tints at 20% (--color-surface-tile-hover, compositing to 1.10:1 -- a logged conflict, and a fill nobody can see) where the component painted --color-bg-button-destructive, an OPAQUE #9a1c2b at 2.37:1: MORE visible than Figma's, not less. The decline reasoned that a translucent fill cannot cross to a light theme, which rule 3 retired, and the replacement then overshot the other way. Dev Mode confirms the tint is an unbound raw bg-[rgba(219,19,42,0.2)] with no variable, so it is minted at the palette layer as --palette-alpha-twilio-red-alpha-200 -- the THIRD 20% red in this file and distinct from the other two.\n\nadditionalInkOnHover WAS A CLAIM ABOUT INTENT rather than evidence: Figma 'brightens the second line to white on hover but leaves it grey when selected. That reads as an artefact of assembly rather than a decision.' Whether a designer meant it is not ours to judge, and the ratio only improves (7.43:1 -> 19.37:1). Both halves ship, including the asymmetry -- no brightening when selected -- because that is the drawing too.\n\nWHAT STANDS: the 2px selected border on the tile and tab variants. Selected differs from Default by hue alone and the two hues are 2.35:1 apart, which WCAG 1.4.1 prohibits. Both of Figma's colours are taken; the extra 1px is an addition Figma never drew, which under rule 4 breaks no tie. The demo variant keeps Figma's 1px exactly.\n\nA REAL BUG FOUND BY TIGHTENING A CHECK: the shared `selected: true` variant applied border-border-accent to EVERY variant including demo, so a selected demo tile carried both that and Figma's red and tailwind-merge kept whichever came last -- the boundary depended on class order rather than on the variant. The looser assertion could not see it because the hover compound supplied the same class; the check now measures the DELTA between selected and unselected class lists. The selected colour is per variant now."
}
