{
  "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": "menu",
  "name": "Menu",
  "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-menu--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Controls/Menu",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
        "source_digest": "a03b1877898a0a6c14e604f947a4216db5c419e904efdb54a01e7285d096e2ff",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-menu--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-menu--action-menu",
        "name": "Action Menu",
        "title": "Cloaked Design System/Controls/Menu",
        "exportName": "ActionMenu",
        "source_path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
        "source_digest": "a03b1877898a0a6c14e604f947a4216db5c419e904efdb54a01e7285d096e2ff",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-menu--action-menu&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-menu--with-leading-icons",
        "name": "With Leading Icons",
        "title": "Cloaked Design System/Controls/Menu",
        "exportName": "WithLeadingIcons",
        "source_path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
        "source_digest": "a03b1877898a0a6c14e604f947a4216db5c419e904efdb54a01e7285d096e2ff",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-menu--with-leading-icons&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-menu--with-dropdown-sheet",
        "name": "With Dropdown Sheet",
        "title": "Cloaked Design System/Controls/Menu",
        "exportName": "WithDropdownSheet",
        "source_path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
        "source_digest": "a03b1877898a0a6c14e604f947a4216db5c419e904efdb54a01e7285d096e2ff",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-menu--with-dropdown-sheet&viewMode=story"
      },
      {
        "id": "cloaked-design-system-controls-menu--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Controls/Menu",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
        "source_digest": "a03b1877898a0a6c14e604f947a4216db5c419e904efdb54a01e7285d096e2ff",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-controls-menu--dark-theme&viewMode=story"
      }
    ],
    "name": "Menu",
    "id": "menu",
    "category": "Controls"
  },
  "stories_source": {
    "path": "packages/design-system/src/components/controls/Menu/Menu.stories.tsx",
    "code": "import React, { useState } from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { BottomSheet } from '../../overlays/BottomSheet/BottomSheet';\nimport { Icon } from '../../primitives/Icon/Icon';\nimport { Dropdown } from '../Dropdown/Dropdown';\nimport { disabledControl, hapticArgType, selectArgType } from '../../../storybook/controlOptions';\nimport { Menu, type MenuItem, type MenuSelectionMode } from './Menu';\n\nconst MENU_SELECTION_MODES = ['none', 'single'] as const satisfies readonly MenuSelectionMode[];\n\nconst FILTER_ITEMS: MenuItem[] = [\n    { value: 'all', label: 'All activity' },\n    { value: 'in-progress', label: 'In progress' },\n    { value: 'completed', label: 'Completed' },\n];\n\nconst ACTION_ITEMS: MenuItem[] = [\n    {\n        value: 'rename',\n        label: 'Rename dashboard',\n        description: 'Update the visible dashboard title',\n        leading: <Icon name='settings' size='small' />,\n    },\n    {\n        value: 'share',\n        label: 'Share dashboard',\n        description: 'Send a read-only link',\n        leading: <Icon name='upload' size='small' />,\n    },\n    {\n        value: 'archive',\n        label: 'Archive dashboard',\n        description: 'Unavailable on the free plan',\n        disabled: true,\n        leading: <Icon name='circle-outline' size='small' />,\n    },\n    {\n        value: 'delete',\n        label: 'Delete dashboard',\n        destructive: true,\n        leading: <Icon name='circle-outline' size='small' />,\n    },\n];\n\nconst meta: Meta<typeof Menu> = {\n    title: 'Cloaked Design System/Controls/Menu',\n    component: Menu,\n    parameters: {\n        a11y: { test: 'error' },\n        docs: {\n            description: {\n                component:\n                    'The option-list body for bottom sheets, popovers, and dropdown content — the rows, not the ' +\n                    'trigger (that is `Dropdown`). `selectionMode=\"single\"` renders a picker (role listbox/option ' +\n                    'with a check on the current `value`); `selectionMode=\"none\"` renders an action list (role ' +\n                    'menu/menuitem) for one-off commands. Items can be `disabled` or `destructive`.',\n            },\n        },\n    },\n    args: {\n        ariaLabel: 'Activity filter',\n        items: FILTER_ITEMS,\n        selectionMode: 'single',\n        value: 'all',\n        testId: 'controls-menu',\n    },\n    argTypes: {\n        items: { ...disabledControl, description: 'Rows rendered in order; each is a `MenuItem`.' },\n        ariaLabel: { control: 'text', description: 'Accessible name for the menu/listbox.' },\n        selectionMode: {\n            ...selectArgType(MENU_SELECTION_MODES),\n            description: '`single` = picker (checkmark on `value`); `none` = action list of commands.',\n        },\n        value: { control: 'text', description: 'Selected item value when `selectionMode` is `single`.' },\n        onSelect: { ...disabledControl, description: 'Called with the pressed item’s `value`.' },\n        haptic: {\n            ...hapticArgType,\n            description: 'Haptic per press. Defaults to `true` (`selection` for single-select, else `light`).',\n        },\n    },\n};\nexport default meta;\ntype Story = StoryObj<typeof Menu>;\n\nfunction DropdownMenuSheetDemo() {\n    const [open, setOpen] = useState(false);\n    const [selected, setSelected] = useState(FILTER_ITEMS[0].value);\n    const selectedLabel = FILTER_ITEMS.find(item => item.value === selected)?.label ?? FILTER_ITEMS[0].label;\n\n    return (\n        <>\n            <Dropdown\n                label={String(selectedLabel)}\n                size='sm'\n                expanded={open}\n                onToggle={() => setOpen(true)}\n                testId='activity-filter'\n            />\n            <BottomSheet isOpen={open} onDismiss={() => setOpen(false)} size='small' testId='activity-filter-sheet'>\n                <BottomSheet.Header>\n                    <BottomSheet.Title>Filter activity</BottomSheet.Title>\n                </BottomSheet.Header>\n                <Menu\n                    ariaLabel='Filter activity'\n                    selectionMode='single'\n                    value={selected}\n                    items={FILTER_ITEMS}\n                    onSelect={value => {\n                        setSelected(value);\n                        setOpen(false);\n                    }}\n                    testId='activity-filter-menu'\n                />\n            </BottomSheet>\n        </>\n    );\n}\n\nexport const Playground: Story = {\n    parameters: {\n        docs: {\n            description: { story: 'Single-select picker: role listbox/option, checkmark on the current `value`.' },\n        },\n    },\n};\n\nexport const ActionMenu: Story = {\n    args: {\n        ariaLabel: 'Dashboard actions',\n        selectionMode: 'none',\n        items: ACTION_ITEMS,\n    },\n    parameters: {\n        docs: {\n            description: {\n                story: '`selectionMode=\"none\"` — a command list (role menu/menuitem) with a disabled item and a destructive action.',\n            },\n        },\n    },\n};\n\nexport const WithLeadingIcons: Story = {\n    args: {\n        items: [\n            { value: 'all', label: 'All activity', leading: <Icon name='search' size='small' /> },\n            { value: 'in-progress', label: 'In progress', leading: <Icon name='clock' size='small' /> },\n            { value: 'completed', label: 'Completed', leading: <Icon name='check' size='small' /> },\n        ],\n    },\n    parameters: { docs: { description: { story: 'Picker rows with `leading` icons for scannable categories.' } } },\n};\n\nexport const WithDropdownSheet: Story = {\n    parameters: {\n        docs: {\n            description: { story: 'The full trigger→sheet flow: a `Dropdown` opens this `Menu` in a `BottomSheet`.' },\n        },\n    },\n    render: () => <DropdownMenuSheetDemo />,\n};\n\nexport const DarkTheme: Story = {\n    globals: { theme: 'dark' },\n    parameters: { docs: { description: { story: 'Single-select picker on the dark theme surface.' } } },\n};\n"
  },
  "specification": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Menu",
      "status": "preview",
      "version": "1.1.0",
      "summary": "A compact row list that switches between one-off action semantics and controlled single-selection semantics."
    },
    "parts": [
      {
        "id": "container",
        "label": "Container",
        "description": "Owns the menu or listbox role, accessible name, and row spacing."
      },
      {
        "id": "item",
        "label": "Item",
        "description": "Native button exposing action or option semantics and disabled state."
      },
      {
        "id": "leading",
        "label": "Leading visual",
        "description": "Optional icon or visual before item copy."
      },
      {
        "id": "label",
        "label": "Item label",
        "description": "Primary action or option name."
      },
      {
        "id": "description",
        "label": "Item description",
        "description": "Optional supporting copy beneath an action label."
      },
      {
        "id": "selection-mark",
        "label": "Selection mark",
        "description": "Trailing check shown only on the selected single-choice item."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "base": {
          "label": "Base",
          "description": "One row treatment configured through the independent selection-mode axis."
        }
      },
      "sizes": {},
      "selectionModes": {
        "none": {
          "label": "Actions",
          "description": "Exposes menu and menuitem semantics for one-off commands."
        },
        "single": {
          "label": "Single selection",
          "description": "Exposes listbox and option semantics with one selected value."
        }
      }
    },
    "variants": [
      {
        "id": "base",
        "label": "Base",
        "description": "Compact menu rows whose role is selected through a separate semantic mode.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [],
    "measurements": [
      {
        "id": "container-gap",
        "label": "Item gap",
        "kind": "exact",
        "token": "--cds-space-4",
        "value": 4,
        "unit": "px",
        "appliesTo": [
          "container",
          "item"
        ]
      },
      {
        "id": "item-min-height",
        "label": "Item minimum height",
        "kind": "exact",
        "token": "--cds-size-48",
        "value": 48,
        "unit": "px",
        "appliesTo": [
          "item"
        ]
      },
      {
        "id": "item-horizontal-padding",
        "label": "Item horizontal padding",
        "kind": "exact",
        "token": "--cds-space-16",
        "value": 16,
        "unit": "px",
        "appliesTo": [
          "item"
        ]
      },
      {
        "id": "item-corner-radius",
        "label": "Item corner radius",
        "kind": "exact",
        "token": "--cds-radius-md",
        "value": 16,
        "unit": "px",
        "appliesTo": [
          "item"
        ]
      }
    ],
    "layout": {
      "alignment": "Leading visual and copy align at the row start while the selected check remains trailing.",
      "flow": "Rows stack vertically; each row contains optional leading visual, flexible copy, then optional selection mark.",
      "notes": [
        "Use action mode for commands and single-selection mode for values; do not mix both meanings in one menu.",
        "Place Menu inside an owned surface such as Modal or popover."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Item label",
          "description": "Use the body ramp for primary row labels.",
          "token": "--cds-type-body-size"
        },
        {
          "label": "Description",
          "description": "Use body-small for supporting copy.",
          "token": "--cds-type-body-small-size"
        }
      ],
      "colors": [
        {
          "label": "Selected row",
          "description": "Fill the selected single-choice row with the bold surface so selection reads as a high-contrast pill, consistent with other selected controls.",
          "token": "--cds-bg-bold"
        },
        {
          "label": "Selected row content",
          "description": "Flip the selected row's label and check to on-bold so they read on the bold fill.",
          "token": "--cds-text-on-bold"
        },
        {
          "label": "Primary label",
          "description": "Use primary text emphasis for normal actions and unselected options.",
          "token": "--cds-text-primary"
        },
        {
          "label": "Description and leading visual",
          "description": "Use secondary text emphasis.",
          "token": "--cds-text-secondary"
        },
        {
          "label": "Destructive action",
          "description": "Use the solid danger role only for irreversible actions.",
          "token": "--cds-status-danger-solid"
        },
        {
          "label": "Selection mark",
          "description": "Use on-bold for the selected check, which sits on the bold selected row.",
          "token": "--cds-text-on-bold"
        }
      ]
    },
    "states": [
      {
        "id": "default",
        "label": "Default",
        "description": "Shows an enabled normal item."
      },
      {
        "id": "selected",
        "label": "Selected",
        "description": "Fills the row with the bold surface and shows an on-bold check and option state in single-selection mode."
      },
      {
        "id": "focused",
        "label": "Focused",
        "description": "Shows a visible focus ring on the row button."
      },
      {
        "id": "disabled",
        "label": "Disabled",
        "description": "Blocks the item and dims its full contents."
      },
      {
        "id": "destructive",
        "label": "Destructive",
        "description": "Uses danger emphasis for an enabled irreversible action."
      }
    ],
    "behavior": [
      {
        "label": "Action mode",
        "description": "Emit the pressed value from menu items without maintaining selected state."
      },
      {
        "label": "Single-selection mode",
        "description": "Expose the controlled value as one selected option and emit a new value on press."
      },
      {
        "label": "Disabled items",
        "description": "Never emit selection or action callbacks from disabled rows."
      }
    ],
    "accessibility": [
      {
        "label": "Container semantics",
        "description": "Require an accessible name and expose menu for actions or listbox for single selection."
      },
      {
        "label": "Row semantics",
        "description": "Expose menuitem or option roles consistently with the container mode."
      },
      {
        "label": "Selection and disabled state",
        "description": "Expose selected state only in single-selection mode and preserve native disabled behavior."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Long copy",
        "description": "Wrap labels and descriptions within the flexible copy column."
      },
      {
        "label": "Destructive labels",
        "description": "Name the affected object and action clearly; danger color does not replace explicit wording."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Selection change",
          "description": "Update selected surface and check without changing row geometry."
        }
      ],
      "haptics": [
        {
          "label": "Mode-aware feedback",
          "description": "Request selection feedback for single-choice items and light feedback for action items."
        }
      ]
    },
    "platformDifferences": [
      {
        "platforms": [
          "web"
        ],
        "description": "Web maps modes to ARIA menu/menuitem or listbox/option roles; native platforms map the same distinction to action and selectable-row accessibility traits.",
        "rationale": "Semantic intent is shared while each platform exposes its native assistive-technology vocabulary."
      }
    ]
  },
  "specification_source": "packages/design-system/src/components/controls/Menu/Menu.spec.json",
  "guidance": {
    "summary": "A compact row list that switches between one-off action semantics and controlled single-selection semantics.",
    "guidance": [
      {
        "heading": "Overview",
        "body": "The option-list **body** for bottom sheets, popovers, and dropdown content — the rows, not the\ntrigger. `selectionMode` switches its meaning: `single` is a picker, `none` is an action list."
      },
      {
        "heading": "When to use",
        "body": "Reach for `Menu` whenever you need a list of rows inside an opened surface. Two modes:\n\n-   `selectionMode=\"single\"` renders a **picker** (role listbox/option, a check on the current `value`) — the body of a `Select` or `Dropdown` sheet.\n-   `selectionMode=\"none\"` renders an **action list** (role menu/menuitem) of one-off commands like Rename, Share, Delete. Items can be `disabled` or `destructive`.\n\nChoosing between the neighbors:\n\n-   Need the collapsed trigger that opens this list? That's `Dropdown` (or `Select` for a form value).\n-   Picking one value from a short fixed set inside a form? Use `Select`, which composes a `Menu` internally.\n-   Filtering a long list by typing? Use `Autocomplete`.\n\nIn-app it renders the activity-filter rows opened from the `Dropdown` in `ActivityFeed.tsx`."
      }
    ],
    "usage": [],
    "examples": [],
    "source": "packages/design-system/src/components/controls/Menu/Menu.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-menu",
    "json": "/data/visual/components/menu.json",
    "storybook": "/storybook/?path=/story/cloaked-design-system-controls-menu--playground"
  }
}
