{
  "version": "0.2.0-preview.1",
  "content_digest": "70ac710f4d22dce040e7ac593379ba8aca9dcb59a0b9c0f839819cae0e2bb721",
  "status": "preview",
  "authority": {
    "repository": "cloakedinc/design",
    "path": "packages/design-system",
    "catalog": "packages/design-system/catalog.json",
    "status": "preview"
  },
  "provenance": {
    "repo": "cloakedinc/automation",
    "commit": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "path": "packages/design-system",
    "imported_spec_status": "approved"
  },
  "adaptations": [
    {
      "id": "button-content-gap",
      "status": "preview",
      "description": "Use the specification's 4px content gap instead of the inherited 8px implementation gap."
    },
    {
      "id": "keyboard-focus",
      "status": "preview",
      "description": "Add visible Button and Input keyboard focus and an overridable inset focus default for shared button resets, including FAQ cards and ListItem rows."
    },
    {
      "id": "supporting-text-contrast",
      "status": "preview",
      "description": "Map light secondary text to the existing grey-04-strong primitive for readable helper text on white and cream."
    },
    {
      "id": "danger-text-contrast",
      "status": "preview",
      "description": "Separate readable danger text/borders from destructive fills; preserve the primary danger-button fill and label pair."
    },
    {
      "id": "preview-status",
      "status": "preview",
      "description": "Local components, specs and token adaptations await human sign-off; upstream approved status is provenance only."
    },
    {
      "id": "motion-comparison-accessibility",
      "status": "preview",
      "description": "Preserve the original MotionShowcase comparisons while requiring explicit replay, responding to live reduced-motion changes, and keeping duration tracks usable on small screens."
    },
    {
      "id": "list-item-ownership",
      "status": "preview",
      "description": "List owns listitem wrappers for its content rows, while section labels stay outside the row count and standalone rows retain their existing semantics."
    },
    {
      "id": "card-delta-text-contrast",
      "status": "preview",
      "description": "Mix Card.Delta foregrounds from 60% existing tone solid and 40% primary text for readable contrast in both themes; preserve the four original tone fills."
    },
    {
      "id": "carousel-keyboard-access",
      "status": "preview",
      "description": "Keep all native-scrolling carousel slide controls exposed to assistive technology and keyboard navigation; pause autoplay while focus is inside the carousel."
    }
  ],
  "id": "segmentedcontrol",
  "name": "SegmentedControl",
  "category": "Controls",
  "contract_status": "preview",
  "contract_kind": "source-derived",
  "review": {
    "local_status": "not-reviewed",
    "source_approval": null,
    "imported_spec_status": "approved",
    "spec_origin": "imported-adapted",
    "local_spec_source_status": "approved",
    "imported_source_revision": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "note": "Inherited specification status is provenance, not approval of the current local implementation or extracted contract."
  },
  "approval": "required",
  "visual_reference": {
    "stories": [
      {
        "id": "cloaked-design-system-controls-segmentedcontrol--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Controls/SegmentedControl",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsx",
        "source_digest": "121530dc43e7a965963a481371b64c84b32f04b9f8aacf842582749471006a28",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-segmentedcontrol--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-segmentedcontrol--middle-active",
        "name": "Middle Active",
        "title": "Cloaked Design System/Controls/SegmentedControl",
        "exportName": "MiddleActive",
        "source_path": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsx",
        "source_digest": "121530dc43e7a965963a481371b64c84b32f04b9f8aacf842582749471006a28",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-segmentedcontrol--middle-active&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-segmentedcontrol--with-counts-and-dots",
        "name": "With Counts And Dots",
        "title": "Cloaked Design System/Controls/SegmentedControl",
        "exportName": "WithCountsAndDots",
        "source_path": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsx",
        "source_digest": "121530dc43e7a965963a481371b64c84b32f04b9f8aacf842582749471006a28",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-segmentedcontrol--with-counts-and-dots&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-segmentedcontrol--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Controls/SegmentedControl",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsx",
        "source_digest": "121530dc43e7a965963a481371b64c84b32f04b9f8aacf842582749471006a28",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-segmentedcontrol--dark-theme&viewMode=story"
      }
    ],
    "name": "SegmentedControl",
    "id": "segmentedcontrol",
    "category": "Controls"
  },
  "stories_source": {
    "path": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsx",
    "code": "import React, { useState } from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { disabledControl, hapticArgType } from '../../../storybook/controlOptions';\nimport { SegmentedControl, type SegmentedControlItem } from './SegmentedControl';\n\nconst SEGMENT_ITEMS: SegmentedControlItem[] = [\n    { id: 'week', label: 'Week' },\n    { id: 'month', label: 'Month' },\n    { id: 'year', label: 'Year' },\n];\n\nconst meta: Meta<typeof SegmentedControl> = {\n    title: 'Cloaked Design System/Controls/SegmentedControl',\n    component: SegmentedControl,\n    parameters: {\n        a11y: { test: 'error' },\n        docs: {\n            description: {\n                component:\n                    'An iOS-style single-select switch for up to 5 mutually exclusive view modes that re-filter ' +\n                    'the same view, such as a time range or a chart granularity. The markup is a list of ' +\n                    'buttons carrying `aria-current`, deliberately not a radiogroup (which needs radio ' +\n                    'semantics and radio-group arrow keys) and not a tablist (which promises content panels). ' +\n                    'Use `Tabs` when the options switch ' +\n                    'panels, a `ChipGroup` when the set grows from data, and `Toggle` for a binary input.',\n            },\n        },\n    },\n    args: {\n        items: SEGMENT_ITEMS,\n        activeId: 'week',\n        ariaLabel: 'Time range',\n        testId: 'controls-segmented',\n    },\n    argTypes: {\n        items: { ...disabledControl, description: 'Options rendered in order; each is a `SegmentedControlItem`.' },\n        activeId: { control: 'text', description: 'Controlled: `id` of the selected option.' },\n        onSelect: { ...disabledControl, description: 'Called with the selected option `id`.' },\n        ariaLabel: { control: 'text', description: 'Accessible name announced before the options.' },\n        haptic: {\n            ...hapticArgType,\n            description: 'Haptic on selection change. Defaults to `true` (`selection`).',\n        },\n    },\n};\nexport default meta;\ntype Story = StoryObj<typeof SegmentedControl>;\n\nfunction SegmentedControlPlayground() {\n    const [activeId, setActiveId] = useState(SEGMENT_ITEMS[0].id);\n\n    return (\n        <SegmentedControl\n            items={SEGMENT_ITEMS}\n            activeId={activeId}\n            onSelect={setActiveId}\n            ariaLabel='Time range'\n            testId='controls-segmented'\n        />\n    );\n}\n\nexport const Playground: Story = {\n    parameters: { docs: { description: { story: 'Interactive — `onSelect` drives `activeId` through React state.' } } },\n    render: () => <SegmentedControlPlayground />,\n};\n\nexport const MiddleActive: Story = {\n    args: { activeId: 'month' },\n    parameters: { docs: { description: { story: 'A non-first option selected.' } } },\n};\n\nexport const WithCountsAndDots: Story = {\n    parameters: {\n        docs: {\n            description: {\n                story:\n                    'A count is text, so it is announced with the label. A dot is decorative, so put the ' +\n                    'reason it is there into the label when it carries meaning.',\n            },\n        },\n    },\n    args: {\n        activeId: 'approved',\n        ariaLabel: 'Request state',\n        items: [\n            { id: 'approved', label: 'Approved', count: 12 },\n            { id: 'requests', label: 'Requests', dot: true },\n            { id: 'blocked', label: 'Blocked', count: 0, disabled: true },\n        ],\n    },\n};\n\nexport const DarkTheme: Story = {\n    globals: { theme: 'dark' },\n    parameters: { docs: { description: { story: 'Segmented control on the dark theme surface.' } } },\n};\n"
  },
  "specification": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "SegmentedControl",
      "status": "preview",
      "version": "2.1.0",
      "summary": "A list of buttons for switching one view mode, with the selected option marked by aria-current."
    },
    "parts": [
      {
        "id": "strip",
        "label": "Strip",
        "description": "The list that owns the accessible name and shares width equally between options.",
        "required": true
      },
      {
        "id": "option",
        "label": "Option",
        "description": "One button per option. Carries aria-current and its own disabled state.",
        "required": true
      },
      {
        "id": "count",
        "label": "Count",
        "description": "Optional trailing number, rendered as text so it is announced with the label."
      },
      {
        "id": "dot",
        "label": "Dot",
        "description": "Optional decorative attention indicator."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "pill": {
          "label": "Pill",
          "description": "Equal-width pill segments with filled active and outlined inactive treatments."
        }
      },
      "sizes": {}
    },
    "variants": [
      {
        "id": "pill",
        "label": "Pill",
        "description": "Compact mode switch with one active option.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [],
    "measurements": [
      {
        "id": "option-height",
        "label": "Option height",
        "kind": "exact",
        "token": "--cds-size-32",
        "value": 32,
        "unit": "px",
        "appliesTo": [
          "option"
        ]
      },
      {
        "id": "option-horizontal-padding",
        "label": "Option horizontal padding",
        "kind": "exact",
        "token": "--cds-space-16",
        "value": 16,
        "unit": "px",
        "appliesTo": [
          "option"
        ]
      },
      {
        "id": "option-content-gap",
        "label": "Option content gap",
        "kind": "exact",
        "token": "--cds-space-4",
        "value": 4,
        "unit": "px",
        "appliesTo": [
          "option",
          "count",
          "dot"
        ]
      },
      {
        "id": "option-gap",
        "label": "Gap between options",
        "kind": "exact",
        "token": "--cds-space-8",
        "value": 8,
        "unit": "px",
        "appliesTo": [
          "strip"
        ]
      },
      {
        "id": "dot-size",
        "label": "Dot size",
        "kind": "exact",
        "token": "--cds-size-8",
        "value": 8,
        "unit": "px",
        "appliesTo": [
          "dot"
        ]
      }
    ],
    "layout": {
      "alignment": "Options share equal width and center their content in both axes.",
      "flow": "Two to four options form one horizontal row.",
      "notes": [
        "Use Tabs when options switch distinct content panels rather than one view mode.",
        "Use a ChipGroup when the set grows from data, wraps, or scrolls.",
        "Use Toggle for a binary input rather than a two-option strip."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Segment label",
          "description": "Use the compact link ramp for concise mode labels.",
          "token": "--cds-type-link-size"
        }
      ],
      "colors": [
        {
          "label": "Active segment",
          "description": "Use the bold semantic surface.",
          "token": "--cds-bg-bold"
        },
        {
          "label": "Active label",
          "description": "Use text intended for bold surfaces.",
          "token": "--cds-text-on-bold"
        },
        {
          "label": "Inactive label",
          "description": "Use primary text emphasis.",
          "token": "--cds-text-primary"
        },
        {
          "label": "Inactive border",
          "description": "Use the subtle surface stroke.",
          "token": "--cds-surface-subtle"
        }
      ]
    },
    "states": [
      {
        "id": "active",
        "label": "Active",
        "description": "Bold fill with on-bold text, marked by aria-current."
      },
      {
        "id": "inactive",
        "label": "Inactive",
        "description": "Transparent fill with a subtle border."
      },
      {
        "id": "disabled",
        "label": "Disabled",
        "description": "One option dimmed and blocked through the native disabled attribute."
      },
      {
        "id": "with-count",
        "label": "With count",
        "description": "Shows a trailing number after the label."
      },
      {
        "id": "with-dot",
        "label": "With dot",
        "description": "Shows a trailing attention indicator."
      }
    ],
    "behavior": [
      {
        "label": "Select on press",
        "description": "Invoke onSelect with the pressed option id. Skip it when that option is already active."
      },
      {
        "label": "Per-option disable",
        "description": "An option may be disabled on its own through the native attribute, without disabling the set."
      },
      {
        "label": "Bounded set",
        "description": "Keep the set to 5 or fewer options, per the iOS guideline. Past that, reach for a Chip group or a Select."
      }
    ],
    "accessibility": [
      {
        "label": "Not a radiogroup",
        "description": "A radiogroup needs radio semantics on every option, role=radio with aria-checked, plus radio-group arrow-key behavior. These are ordinary buttons, so the selected one carries aria-current."
      },
      {
        "label": "Not a tablist",
        "description": "A tablist promises content panels and arrow-key traversal. Reach for Tabs when the options switch panels."
      },
      {
        "label": "Explicit list role",
        "description": "The list keeps role=list because removing the bullets strips list semantics in some engines."
      },
      {
        "label": "Group name",
        "description": "Pass ariaLabel so the set is announced before its options."
      },
      {
        "label": "Counts over dots",
        "description": "A count is text and announces. A dot is decorative, so say why it is there in the label when it carries meaning."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Option count",
        "description": "Keep the set to two through four mutually exclusive modes."
      },
      {
        "label": "Concise labels",
        "description": "Use parallel, short labels that remain legible at equal widths."
      },
      {
        "label": "Width sizing",
        "description": "sizing='fill' (default) shares the strip equally and ellipsises a label that does not fit. sizing='hug' sizes each option to its own label, so an auto-width strip is never narrower than its content; use it when the strip sits beside other content rather than filling a row."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Selection treatment",
          "description": "Swap active and inactive color treatments without moving or resizing segments."
        }
      ],
      "haptics": [
        {
          "label": "Selection feedback",
          "description": "Request the selection haptic after the active segment changes."
        }
      ]
    },
    "platformDifferences": [
      {
        "platforms": [
          "web"
        ],
        "description": "Web renders no focus ring and no arrow-key traversal between options.",
        "rationale": "The consumers are a touch iOS WKWebView and a harness previewing it, and per Primer arrow keys must not move selection in a segmented control anyway."
      }
    ]
  },
  "specification_source": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.spec.json",
  "guidance": {
    "summary": "A list of buttons for switching one view mode, with the selected option marked by aria-current.",
    "guidance": [
      {
        "heading": "Overview",
        "body": "An iOS-style single-select switch for up to 5 mutually exclusive modes that re-filter one view in\nplace, such as a time range or a chart granularity."
      },
      {
        "heading": "It is a list of buttons",
        "body": "The markup is a `<ul>` of buttons, and the selected one carries `aria-current`. It is deliberately\nnone of the roles it resembles:\n\n-   Not a **radiogroup**. That role needs radio semantics on every option — `role=\"radio\"` with\n    `aria-checked` — plus radio-group arrow-key behavior. These are ordinary buttons.\n-   Not a **tablist**. That role promises content panels and arrow-key traversal.\n-   Not a **toolbar**. That role is a collection of function buttons.\n\nEach of those roles carries its own keyboard and announcement rules, so borrowing the wrong one\nmisleads anyone using assistive technology."
      },
      {
        "heading": "When to use",
        "body": "Reach for `SegmentedControl` when a small, fixed set of options changes how the same content is\nsliced and all options fit inline. Choosing between the neighbours:\n\n-   The options switch between **distinct content panels** rather than re-filtering one view? Use\n    `Tabs`.\n-   The set **grows from data**, wraps, or scrolls? Use a `ChipGroup`. The boundary is the shape of\n    the set: a segmented control is fixed, connected, and equal-width.\n-   A **binary input**, such as on and off? Use `Toggle`.\n-   You want a **labelled form field** with descriptions, or more than 5 options? Use `RadioGroup`.\n-   Too many options to sit inline? Use `Dropdown` + `Menu`."
      },
      {
        "heading": "Counts and dots",
        "body": "An option may carry a trailing `count`, a decorative `dot`, or its own `disabled` state. A count is\ntext, so it is announced with the label. A dot is not, so put the reason it is there into the label\nwhen it carries meaning."
      }
    ],
    "usage": [],
    "examples": [],
    "source": "packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.mdx",
    "status": "local-preview",
    "note": "Guidance is read from local documentation, originally imported from the frozen source. Platform assertions in guidance are not parity verification."
  },
  "links": {
    "human": "/component-segmentedcontrol",
    "json": "/data/visual/components/segmentedcontrol.json",
    "storybook": "/storybook/?path=/story/cloaked-design-system-controls-segmentedcontrol--playground"
  }
}
