{
  "name": "Tabs",
  "type": "component",
  "description": "Tabbed sections in four visual variants and horizontal or vertical orientation, with manual keyboard activation.",
  "export": "@gtmi/strix-react/atoms/tabs",
  "path": "@gtmi/strix-react/atoms/tabs",
  "figma": {
    "file": "zT3vGm4QlFEI2Jp9nxyFKI",
    "fileName": "Demo components library",
    "nodes": [
      {
        "node": "22:3992",
        "name": ".tab-item",
        "section": "Atoms"
      }
    ],
    "note": "Read inside `Tab` (22:5550), where ink and weight follow the SELECTION. Transcript (22:27950) instantiates the same atom with ink tracking POSITION, which is an authoring slip; Tab is the authority."
  },
  "props": [
    {
      "name": "items",
      "type": "TabItem[]",
      "required": true,
      "description": "Each item: value, label, content, and optionally disabled, icon, notificationCount."
    },
    {
      "name": "variant",
      "type": "default | small | secondary | content-switcher",
      "default": "default",
      "description": "default has a sliding indicator; secondary uses per-tab state underlines and supports icon labels; content-switcher is a scrolling strip that renders notification counts; small is a compact segmented control."
    },
    {
      "name": "orientation",
      "type": "horizontal | vertical",
      "default": "horizontal",
      "description": "Sets layout and arrow-key direction. Vertical places the tab list beside the active panel."
    },
    {
      "name": "value",
      "type": "string",
      "description": "Controlled active tab."
    },
    {
      "name": "defaultValue",
      "type": "string",
      "description": "Initially active tab. Defaults to the first item."
    },
    {
      "name": "onValueChange",
      "type": "(value: string) => void",
      "description": "Fires when the active tab changes."
    },
    {
      "name": "aria-label",
      "type": "string",
      "description": "Names the tab list. Give it one: a list of tabs with no name is hard to navigate."
    },
    {
      "name": "rootClassName / tabsListClassName / panelClassName",
      "type": "string",
      "description": "Per-slot class overrides, each merged through cn()."
    }
  ],
  "parts": [
    {
      "name": "Tabs.Root",
      "description": "Owns the active value and the shared id prefix. Takes `items`."
    },
    {
      "name": "Tabs.List",
      "description": "Renders a tab per item plus the indicator. Props: variant, tabClassName, tabIndicatorClassName, aria-label."
    },
    {
      "name": "Tabs.Panel",
      "description": "One panel. Requires `value` and `index`."
    }
  ],
  "ai_tags": ["tabs", "tablist", "navigation", "primitive"],
  "since": "0.2.0",
  "tokens_used": [
    "--color-border-disabled",
    "--color-border-focus",
    "--color-border-pressed",
    "--color-border-tab",
    "--color-border-tab-active",
    "--color-border-tab-hover",
    "--color-surface-alpha-blue-080",
    "--color-surface-base",
    "--color-surface-neutral-semi-medium",
    "--color-surface-secondary",
    "--color-surface-selection-layer-strong",
    "--color-text-accent-focused",
    "--color-text-accent-hover",
    "--color-text-accent-pressed",
    "--color-text-accent-strong",
    "--color-text-disabled",
    "--color-text-primary",
    "--color-text-subtle-figma",
    "--radius-med-sm",
    "--shadow-xs",
    "--spacing-borders-lg",
    "--spacing-gap-1000"
  ],
  "notes": "The secondary treatment follows the approved icon-and-underline reference; it is distinct from Figma's bordered .tab-item tile. Orientation is delegated to Base UI: Left/Right move focus horizontally and Up/Down move focus vertically. Selection still requires Enter or Space because activateOnFocus remains false. Ids are generated per instance from React.useId(), so two tab sets on one page do not collide, and both the convenience and compound forms share that wiring. Upstream set z-base and z-deep on the tab and indicator - neither is a token nor a Tailwind utility, so both classes did nothing and the indicator's stacking was accidental; tabs are `relative z-10` and the indicator `z-0` here. min-w-[200px] is min-w-50. The scrollbar-hiding declarations are now the `scrollbar-hidden` utility from tokens/utilities.css; they were arbitrary properties here and were hand-written a second time by a consumer before the utility existed.\n\nTHE `secondary` VARIANT IS PORTED FROM FIGMA .tab-item (22:3992), which is an UNDERLINE and not a bordered tile -- a correction the binding had already made, and one that had been quoted in reverse as an argument (docs/AGENTS.md called the atom 'a bordered tile rather than Strix's underline tab trigger'). All three of its rule colours are now Figma's: --color-border-tab (gray-700) resting, --color-border-tab-hover (brand/white, 19.37:1) on hover, --color-border-tab-active (blue-200, 10.50:1) when selected. The resting label is --color-text-subtle-figma (gray-300, 7.43:1), replacing --color-text-secondary #c5d0e3.\n\nTHE RESTING RULE IS A LOGGED CONFLICT: gray-700 measures 1.95:1 against the page where WCAG 1.4.11 wants 3:1. Rule 1 ships it and docs/FIGMA-EXCEPTIONS.md records it; the tabs contrast test excludes that ONE token by name and separately asserts that it is still drawn and still below the floor, so the exemption cannot quietly widen or go stale. Lowering the 3:1 threshold instead would have hidden every future regression on the other two rules.\n\n--color-border-tab-active IS A RE-POINT, NOT A MINT, and the generator's shadow guard is what established that: the role already existed from maker-ds at --palette-primary-050 and nothing read it. Figma names that exact role, so filling it with blue-200 claims an orphan rather than adding a second token with the same meaning. The tabs test previously asserted `not.toContain('data-active:before:bg-border-tab-active')` -- it forbade Figma's own role by name.\n\nA NEAR-MISS WORTH RECORDING: the resting ink is gray-300, and --color-text-muted-figma is gray-400 one step darker. Reaching for the existing 'muted Figma ink' would have rendered the wrong colour under a plausible-looking name; the hex-pinning test is what caught it. Two adjacent grays, two jobs.\n\nHOVER NOW TAKES SEMIBOLD as well as white, which Figma draws and the binding had deferred: 'adopting the hover weight would make the label reflow under the pointer unless the space is reserved.' That hazard needs a content-sized tab; the secondary tab is flex-1, so every tab takes an equal share of the strip and a heavier label cannot move anything.\n\nSTILL NOT ADOPTED: Figma's 53px tab height (20px of leading plus asymmetric 16/18 padding). Taking it means either an asymmetric padding pair no other variant uses or a third fixed tab height, and no drawn colour depends on it. The 2px rule thickness IS Figma's (border-b-2), verified at 2px from decoded pixels, with the list's pb- tracking it so the rule is not clipped."
}
