{
  "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": "accordion",
  "name": "Accordion",
  "category": "Organization",
  "summary": "Grouped disclosure of independent expandable sections driven by an items array, with animated panels.",
  "documentation": {
    "summary": "Grouped disclosure of independent expandable sections driven by an items array, with animated panels.",
    "guidance": [
      {
        "heading": "Overview",
        "body": "Standalone grouped disclosure — independent collapsible sections, each with its own chrome, driven\nby an `items` array."
      },
      {
        "heading": "When to use",
        "body": "Reach for `Accordion` when you have several sections whose bodies should stay hidden until tapped —\nFAQ panels, settings groups, \"learn more\" blocks. `multiple` (the default) lets panels open\nindependently; set it `false` for radio-style single-open.\n\n-   Rows that are always visible and just stack flat? Use `List` — the container owns the hairlines.\n-   One \"show more\" row that reveals extra rows _inside_ an existing `List`? Use `List.Disclosure`,\n    which shares the list's separators instead of adding its own chrome.\n-   Peer panels the user switches between (only one shown at a time, no stacking)? Use `Tabs`."
      }
    ],
    "usage": [],
    "examples": [],
    "source": "packages/design-system/src/components/organization/Accordion/Accordion.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."
  },
  "source": {
    "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
    "code": "import React, { useEffect, useId, useRef, useState } from 'react';\nimport { cn, MILLISECOND } from '../../../utilities';\nimport { useHaptic } from '../../../hooks/useHaptic';\nimport type { HapticFeedback } from '../../../tokens/CdsHaptic';\nimport { Icon } from '../../primitives/Icon/Icon';\nimport { Text } from '../../primitives/Text/Text';\nimport styles from './Accordion.module.scss';\n\n// Must match the close animations in Accordion.module.scss — the panel stays mounted until they finish.\nconst CLOSE_DURATION = 400 * MILLISECOND;\n\n/**\n * One disclosure row: the trigger line and the panel it expands. `leading`, `title`, and\n * `description` all render inside the trigger `<button>`, so keep them non-interactive.\n */\nexport interface AccordionItem {\n    /** The value callers put in `openItemIds` / `defaultOpenItemIds`, and the suffix of the generated trigger and panel element ids that wire up aria. */\n    id: string;\n    /** Element shown before the copy, such as an `Avatar` or a brand logo. */\n    leading?: React.ReactNode;\n    /** Primary row copy, on the `body` ramp above `description`. */\n    title: React.ReactNode;\n    /** Optional supporting copy shown in the trigger row. */\n    description?: React.ReactNode;\n    /** Panel body. Always mounted — a collapsed panel is hidden, not unmounted. */\n    content: React.ReactNode;\n    /** Dims the row and blocks toggling. A panel that is already open stays open. */\n    disabled?: boolean;\n    /**\n     * Row test selector, and `-panel` for its panel. Defaults to the Accordion's own `testId`\n     * suffixed with `id` — pass this when `id` is a raw value that would not read as kebab-case.\n     */\n    testId?: string;\n}\n\n/**\n * Props for `Accordion`. Open state is either controlled through `openItemIds` + `onOpenChange`\n * or left uncontrolled from `defaultOpenItemIds`.\n */\nexport interface AccordionProps {\n    /** Rows in render order. Each `id` must be unique and stable — it keys React and the open set. */\n    items: AccordionItem[];\n    /** Controlled open item ids. */\n    openItemIds?: string[];\n    /** Initial open item ids for uncontrolled usage. */\n    defaultOpenItemIds?: string[];\n    /** Allows more than one item to be open. Defaults to true. */\n    multiple?: boolean;\n    /** Fires on every toggle with the full next open set, including while controlled. */\n    onOpenChange?: (openItemIds: string[]) => void;\n    /** Haptic feedback for row toggles. Defaults to `true` (`selection`). */\n    haptic?: HapticFeedback;\n    className?: string;\n    style?: React.CSSProperties;\n    /** Root selector, and the prefix each row and panel selector falls back to. */\n    testId?: string;\n}\n\n/**\n * Grouped disclosure: independent collapsible sections driven by `items` — FAQ panels, settings\n * groups. Use `List.Disclosure` for a show-more row inside a `List`, or `Tabs` for peer panels.\n */\nexport function Accordion({\n    items,\n    openItemIds,\n    defaultOpenItemIds = [],\n    multiple = true,\n    onOpenChange,\n    haptic = true,\n    className,\n    style,\n    testId,\n}: AccordionProps) {\n    const generatedId = useId();\n    const fireHaptic = useHaptic('selection');\n    const [internalOpenItemIds, setInternalOpenItemIds] = useState(defaultOpenItemIds);\n    const [closingItemIds, setClosingItemIds] = useState<string[]>([]);\n    const previousOpenItemIdsRef = useRef<string[]>(defaultOpenItemIds);\n    const closeTimersRef = useRef<Record<string, number>>({});\n    // Items open before the user ever touched them. Membership is what suppresses the expand\n    // animation, so it must be per-item and sticky: a plain \"have we mounted yet\" flag re-animates\n    // an open panel on the next unrelated re-render, such as appending items to a paginated list.\n    const untouchedOpenItemIdsRef = useRef(new Set(openItemIds ?? defaultOpenItemIds));\n    const activeOpenItemIds = openItemIds ?? internalOpenItemIds;\n\n    useEffect(() => {\n        const closingIds = previousOpenItemIdsRef.current.filter(openItemId => !activeOpenItemIds.includes(openItemId));\n        previousOpenItemIdsRef.current = activeOpenItemIds;\n\n        if (closingIds.length === 0) return;\n\n        // Closing is a user-visible transition, so reopening this item should animate from now on.\n        closingIds.forEach(closingId => untouchedOpenItemIdsRef.current.delete(closingId));\n        setClosingItemIds(current => mergeUniqueIds(current, closingIds));\n\n        closingIds.forEach(closingId => {\n            if (closeTimersRef.current[closingId]) window.clearTimeout(closeTimersRef.current[closingId]);\n            closeTimersRef.current[closingId] = window.setTimeout(() => {\n                setClosingItemIds(current => current.filter(itemId => itemId !== closingId));\n                delete closeTimersRef.current[closingId];\n            }, CLOSE_DURATION);\n        });\n    }, [activeOpenItemIds]);\n\n    useEffect(() => {\n        const closeTimers = closeTimersRef.current;\n        return () => {\n            Object.values(closeTimers).forEach(timer => window.clearTimeout(timer));\n        };\n    }, []);\n\n    const setOpenItemIds = (nextOpenItemIds: string[]) => {\n        if (!openItemIds) setInternalOpenItemIds(nextOpenItemIds);\n        onOpenChange?.(nextOpenItemIds);\n    };\n\n    const toggleItem = (item: AccordionItem) => {\n        if (item.disabled) return;\n\n        const nextOpenItemIds = nextAccordionOpenIds(activeOpenItemIds, item.id, multiple);\n        fireHaptic(haptic);\n        setOpenItemIds(nextOpenItemIds);\n    };\n\n    return (\n        <div className={cn(styles.accordion, className)} style={style} data-testid={testId}>\n            {items.map(item => {\n                const isOpen = activeOpenItemIds.includes(item.id);\n                const isClosing = closingItemIds.includes(item.id) && !isOpen;\n                const isRendered = isOpen || isClosing;\n                const panelState = resolvePanelState(isOpen, isClosing, untouchedOpenItemIdsRef.current.has(item.id));\n                const triggerId = `${generatedId}-${item.id}-trigger`;\n                const panelId = `${generatedId}-${item.id}-panel`;\n                const rowTestId = item.testId ?? (testId ? `${testId}-${item.id}` : undefined);\n\n                return (\n                    <div className={styles.item} key={item.id} data-open={isOpen || undefined}>\n                        <button\n                            type='button'\n                            className={styles.trigger}\n                            id={triggerId}\n                            aria-expanded={isOpen}\n                            aria-controls={panelId}\n                            onClick={() => toggleItem(item)}\n                            disabled={item.disabled}\n                            data-testid={rowTestId}\n                        >\n                            {item.leading && <span className={styles.leading}>{item.leading}</span>}\n                            <span className={styles.triggerCopy}>\n                                <Text variant='body' color='primary'>\n                                    {item.title}\n                                </Text>\n                                {item.description && (\n                                    <Text variant='body-small' color='secondary'>\n                                        {item.description}\n                                    </Text>\n                                )}\n                            </span>\n                            <Icon name='chevron-down' size='small' strokeWidth={1.5} className={styles.chevron} />\n                        </button>\n                        <div\n                            className={styles.panel}\n                            id={panelId}\n                            role='region'\n                            aria-labelledby={triggerId}\n                            hidden={!isRendered}\n                            data-state={panelState}\n                            data-testid={rowTestId ? `${rowTestId}-panel` : undefined}\n                        >\n                            <div className={styles.panelInner}>{item.content}</div>\n                        </div>\n                    </div>\n                );\n            })}\n        </div>\n    );\n}\n\nfunction resolvePanelState(\n    isOpen: boolean,\n    isClosing: boolean,\n    isUntouchedOpen: boolean\n): 'initial-open' | 'open' | 'closing' {\n    if (isClosing) return 'closing';\n    if (isOpen && isUntouchedOpen) return 'initial-open';\n    return 'open';\n}\n\nfunction nextAccordionOpenIds(openItemIds: string[], itemId: string, multiple: boolean): string[] {\n    const isOpen = openItemIds.includes(itemId);\n    if (isOpen) return openItemIds.filter(openItemId => openItemId !== itemId);\n    if (multiple) return openItemIds.concat(itemId);\n    return [itemId];\n}\n\nfunction mergeUniqueIds(currentIds: string[], nextIds: string[]): string[] {\n    const mergedIds = currentIds.slice();\n    for (let i = 0; i < nextIds.length; i += 1) {\n        const nextId = nextIds[i];\n        if (!mergedIds.includes(nextId)) {\n            mergedIds.push(nextId);\n        }\n    }\n    return mergedIds;\n}\n"
  },
  "spec": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Accordion",
      "status": "preview",
      "version": "1.0.0",
      "summary": "Grouped disclosure of independent expandable sections driven by an items array, with animated panels."
    },
    "parts": [
      {
        "id": "root",
        "label": "Root",
        "description": "Edge-to-edge surface stacking disclosure items with separators; carries no radius or inset of its own.",
        "required": true
      },
      {
        "id": "item",
        "label": "Item",
        "description": "A single expandable section."
      },
      {
        "id": "trigger",
        "label": "Trigger",
        "description": "Row button that toggles its panel and rotates the chevron."
      },
      {
        "id": "leading",
        "label": "Leading",
        "description": "Optional element before the trigger copy, such as an avatar or brand logo."
      },
      {
        "id": "panel",
        "label": "Panel",
        "description": "Animated region revealed when the item is open."
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "grouped": {
          "label": "Grouped",
          "description": "The single grouped-disclosure treatment."
        }
      },
      "sizes": {}
    },
    "variants": [
      {
        "id": "grouped",
        "label": "Grouped",
        "description": "Independent expandable sections with shared chrome.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [],
    "measurements": [
      {
        "id": "trigger-gap",
        "label": "Trigger content gap",
        "kind": "exact",
        "token": "--cds-space-12",
        "value": 12,
        "unit": "px",
        "appliesTo": [
          "trigger"
        ]
      },
      {
        "id": "trigger-min-height",
        "label": "Trigger minimum height",
        "kind": "at-least",
        "token": "--cds-size-48",
        "value": 48,
        "unit": "px",
        "appliesTo": [
          "trigger"
        ]
      },
      {
        "id": "trigger-inset",
        "label": "Trigger horizontal inset",
        "kind": "exact",
        "token": "--cds-space-20",
        "value": 20,
        "unit": "px",
        "appliesTo": [
          "trigger",
          "panel"
        ]
      }
    ],
    "layout": {
      "alignment": "An optional leading element sits first at a fixed width, trigger copy is flexible, and the chevron stays trailing.",
      "flow": "Items stack vertically with a hairline between adjacent rows and an animated panel per item.",
      "notes": [
        "Use List.Disclosure for a single show-more row that shares a list's separators.",
        "Edge to edge like ListRoot: the enclosing Screen.Section or Card owns radius and inset. Rows match List.Item padding='card' metrics."
      ]
    },
    "typographyAndColor": {
      "typography": [
        {
          "label": "Title",
          "description": "Trigger titles use the body ramp.",
          "token": "--cds-type-body-size"
        },
        {
          "label": "Description",
          "description": "Trigger descriptions use the body-small ramp.",
          "token": "--cds-type-body-small-size"
        }
      ],
      "colors": [
        {
          "label": "Surface",
          "description": "The accordion paints the raised surface edge to edge; the parent owns shape and inset.",
          "token": "--cds-bg-surface"
        },
        {
          "label": "Separator",
          "description": "Adjacent items are divided with the shared list divider tone.",
          "token": "--cds-divider"
        },
        {
          "label": "Chevron",
          "description": "The chevron uses secondary text color.",
          "token": "--cds-text-secondary"
        }
      ]
    },
    "states": [
      {
        "id": "closed",
        "label": "Closed",
        "description": "Collapsed item with a downward chevron."
      },
      {
        "id": "open",
        "label": "Open",
        "description": "Expanded item, aria-expanded true, chevron rotated."
      },
      {
        "id": "disabled",
        "label": "Disabled",
        "description": "Inert item that cannot be toggled."
      }
    ],
    "behavior": [
      {
        "label": "Multiple open",
        "description": "multiple (default) lets several panels stay open; false collapses siblings."
      },
      {
        "label": "Controlled or uncontrolled",
        "description": "Drive openItemIds for controlled use or defaultOpenItemIds for uncontrolled."
      }
    ],
    "accessibility": [
      {
        "label": "Disclosure semantics",
        "description": "Each trigger sets aria-expanded and aria-controls; the panel is a labelled region."
      },
      {
        "label": "Keyboard",
        "description": "Triggers are buttons reachable and toggleable by keyboard."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Wrapping copy",
        "description": "Long titles and descriptions wrap without widening the container."
      },
      {
        "label": "Optional description",
        "description": "The description line renders only when provided."
      },
      {
        "label": "Optional leading element",
        "description": "The leading slot renders only when provided and never shrinks under long copy."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Expand and collapse",
          "description": "Panels open on a 500ms decelerating curve and close on a 400ms ease-in; disabled under reduced motion."
        }
      ],
      "haptics": [
        {
          "label": "Toggle haptic",
          "description": "Toggling a row requests the selection haptic."
        }
      ]
    },
    "platformDifferences": []
  },
  "spec_source": "packages/design-system/src/components/organization/Accordion/Accordion.spec.json",
  "guidance_source": {
    "path": "packages/design-system/src/components/organization/Accordion/Accordion.mdx",
    "code": "import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';\nimport { ComponentSpec } from '../../../../.storybook/blocks';\nimport * as Stories from './Accordion.stories';\nimport accordionSpec from './Accordion.spec.json';\n\n<Meta of={Stories} />\n\n# Accordion\n\nStandalone grouped disclosure — independent collapsible sections, each with its own chrome, driven\nby an `items` array.\n\n<Canvas of={Stories.Playground} />\n\n## When to use\n\nReach for `Accordion` when you have several sections whose bodies should stay hidden until tapped —\nFAQ panels, settings groups, \"learn more\" blocks. `multiple` (the default) lets panels open\nindependently; set it `false` for radio-style single-open.\n\n-   Rows that are always visible and just stack flat? Use `List` — the container owns the hairlines.\n-   One \"show more\" row that reveals extra rows _inside_ an existing `List`? Use `List.Disclosure`,\n    which shares the list's separators instead of adding its own chrome.\n-   Peer panels the user switches between (only one shown at a time, no stacking)? Use `Tabs`.\n\n## Design Guidelines\n\n<ComponentSpec spec={accordionSpec} />\n\n## Props\n\n<Controls of={Stories.Playground} />\n"
  },
  "props": [
    {
      "name": "items",
      "type": "AccordionItem[]",
      "declared_type": "AccordionItem[]",
      "required": true,
      "description": "Rows in render order. Each `id` must be unique and stable — it keys React and the open set.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 42
      }
    },
    {
      "name": "openItemIds",
      "type": "string[] | undefined",
      "declared_type": "string[]",
      "required": false,
      "description": "Controlled open item ids.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 44
      }
    },
    {
      "name": "defaultOpenItemIds",
      "type": "string[] | undefined",
      "declared_type": "string[]",
      "required": false,
      "description": "Initial open item ids for uncontrolled usage.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 46
      },
      "default": "[]",
      "default_kind": "source-expression"
    },
    {
      "name": "multiple",
      "type": "boolean | undefined",
      "declared_type": "boolean",
      "required": false,
      "description": "Allows more than one item to be open. Defaults to true.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 48
      },
      "values": [
        false,
        true
      ],
      "values_complete": true,
      "default": "true",
      "default_kind": "source-expression"
    },
    {
      "name": "onOpenChange",
      "type": "((openItemIds: string[]) => void) | undefined",
      "declared_type": "(openItemIds: string[]) => void",
      "required": false,
      "description": "Fires on every toggle with the full next open set, including while controlled.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 50
      }
    },
    {
      "name": "haptic",
      "type": "HapticFeedback | undefined",
      "declared_type": "HapticFeedback",
      "required": false,
      "description": "Haptic feedback for row toggles. Defaults to `true` (`selection`).",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 52
      },
      "values": [
        "error",
        "heavy",
        "light",
        "medium",
        "selection",
        "success",
        "warning",
        false,
        true
      ],
      "values_complete": true,
      "default": "true",
      "default_kind": "source-expression"
    },
    {
      "name": "className",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 53
      }
    },
    {
      "name": "style",
      "type": "React.CSSProperties | undefined",
      "declared_type": "React.CSSProperties",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 54
      }
    },
    {
      "name": "testId",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Root selector, and the prefix each row and panel selector falls back to.",
      "source": {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 56
      }
    }
  ],
  "prop_shapes": {},
  "examples": [],
  "tokens": [
    {
      "name": "--cds-space-12",
      "light": "12px",
      "dark": "12px"
    },
    {
      "name": "--cds-size-48",
      "light": "48px",
      "dark": "48px"
    },
    {
      "name": "--cds-space-20",
      "light": "20px",
      "dark": "20px"
    },
    {
      "name": "--cds-type-body-size",
      "light": "1rem",
      "dark": "1rem"
    },
    {
      "name": "--cds-type-body-small-size",
      "light": "0.875rem",
      "dark": "0.875rem"
    },
    {
      "name": "--cds-bg-surface",
      "light": "#ffffff",
      "dark": "#1b1b18"
    },
    {
      "name": "--cds-divider",
      "light": "#e0ddd6",
      "dark": "#ffffff1a"
    },
    {
      "name": "--cds-text-secondary",
      "light": "#646260",
      "dark": "#ffffff99"
    }
  ],
  "visual_reference": {
    "stories": [
      {
        "id": "cloaked-design-system-organization-accordion--playground",
        "name": "Playground",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "Playground",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--playground&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--initially-open",
        "name": "Initially Open",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "InitiallyOpen",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--initially-open&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--single-open",
        "name": "Single Open",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "SingleOpen",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--single-open&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--with-leading",
        "name": "With Leading",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "WithLeading",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--with-leading&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--with-disabled-item",
        "name": "With Disabled Item",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "WithDisabledItem",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--with-disabled-item&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--many-items",
        "name": "Many Items",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "ManyItems",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--many-items&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--long-text",
        "name": "Long Text",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "LongText",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--long-text&viewMode=story"
      },
      {
        "id": "cloaked-design-system-organization-accordion--dark-theme",
        "name": "Dark Theme",
        "title": "Cloaked Design System/Organization/Accordion",
        "exportName": "DarkTheme",
        "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
        "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
        "status": "preview",
        "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--dark-theme&viewMode=story"
      }
    ],
    "name": "Accordion",
    "id": "accordion",
    "category": "Organization"
  },
  "stories": [
    {
      "id": "cloaked-design-system-organization-accordion--playground",
      "name": "Playground",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "Playground",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--playground&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--initially-open",
      "name": "Initially Open",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "InitiallyOpen",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--initially-open&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--single-open",
      "name": "Single Open",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "SingleOpen",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--single-open&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--with-leading",
      "name": "With Leading",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "WithLeading",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--with-leading&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--with-disabled-item",
      "name": "With Disabled Item",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "WithDisabledItem",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--with-disabled-item&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--many-items",
      "name": "Many Items",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "ManyItems",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--many-items&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--long-text",
      "name": "Long Text",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "LongText",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--long-text&viewMode=story"
    },
    {
      "id": "cloaked-design-system-organization-accordion--dark-theme",
      "name": "Dark Theme",
      "title": "Cloaked Design System/Organization/Accordion",
      "exportName": "DarkTheme",
      "source_path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
      "source_digest": "a40422c09fb39a8bc152ede951eecdd1ba390683f7a2e6c63c3badbbc71ff5a2",
      "status": "preview",
      "local_url": "/storybook/iframe.html?id=cloaked-design-system-organization-accordion--dark-theme&viewMode=story"
    }
  ],
  "stories_source": {
    "path": "packages/design-system/src/components/organization/Accordion/Accordion.stories.tsx",
    "code": "import React from 'react';\nimport type { Meta, StoryObj } from '@storybook/react-vite';\nimport { Avatar } from '../../primitives/Avatar/Avatar';\nimport { Text } from '../../primitives/Text/Text';\nimport { disabledControl, hapticArgType } from '../../../storybook/controlOptions';\nimport { Accordion } from './Accordion';\n\nconst ITEMS = [\n    {\n        id: 'monitoring',\n        title: 'Monitoring',\n        description: 'What Cloaked watches for you',\n        content: (\n            <Text variant='body-small' color='secondary' as='p'>\n                We monitor broker sites, dark web exposure, and account activity.\n            </Text>\n        ),\n    },\n    {\n        id: 'notifications',\n        title: 'Notifications',\n        content: (\n            <Text variant='body-small' color='secondary' as='p'>\n                You will receive alerts when something needs your attention.\n            </Text>\n        ),\n    },\n];\n\nconst ITEMS_WITH_DISABLED = [\n    ...ITEMS,\n    {\n        id: 'disabled',\n        title: 'Locked section',\n        description: 'Available after setup',\n        content: 'Locked content',\n        disabled: true,\n    },\n];\n\nconst ITEMS_WITH_LEADING = [\n    {\n        id: 'accudata',\n        leading: <Avatar type='initials' size='md' initial='A' ariaLabel='AccuData' />,\n        title: 'AccuData',\n        description: '1 record removed',\n        content: 'Sent on August 11, 2026.',\n    },\n    {\n        id: 'americaphonebook',\n        leading: <Avatar type='initials' size='md' initial='A' ariaLabel='AmericaPhoneBook' />,\n        title: 'AmericaPhoneBook',\n        description: '2 records removed',\n        content: 'Sent on August 11, 2026.',\n    },\n];\n\nconst MANY_ITEMS = [\n    ...ITEMS,\n    {\n        id: 'data',\n        title: 'Data sources',\n        description: 'Where the scan looks',\n        content: 'Public records, broker sites, and known exposure sources.',\n    },\n    {\n        id: 'support',\n        title: 'Support',\n        description: 'How to get help',\n        content: 'Contact support if you need help with a removal request.',\n    },\n];\n\nconst meta: Meta<typeof Accordion> = {\n    title: 'Cloaked Design System/Organization/Accordion',\n    component: Accordion,\n    parameters: {\n        a11y: { test: 'error' },\n        docs: {\n            description: {\n                component:\n                    'Standalone grouped disclosure — independent expandable sections with their own chrome, ' +\n                    'driven by an `items` array (FAQ panels, settings groups). Reach for `List.Disclosure` ' +\n                    'instead when you need a single \"show more\" row that reveals extra `List.Item`s inside an ' +\n                    'existing `List` and shares its separators. `multiple` (the default) lets several panels ' +\n                    'stay open at once; set it `false` for radio-style single-open.',\n            },\n        },\n    },\n    args: {\n        items: ITEMS,\n        testId: 'accordion',\n    },\n    argTypes: {\n        items: {\n            ...disabledControl,\n            description:\n                'Ordered `AccordionItem[]` — each has an `id`, `title`, optional `leading`, `description`, `content`, and `disabled`.',\n        },\n        openItemIds: {\n            ...disabledControl,\n            description: 'Controlled set of open item ids. Pair with `onOpenChange`; omit for uncontrolled use.',\n        },\n        defaultOpenItemIds: {\n            ...disabledControl,\n            description: 'Item ids expanded on mount for uncontrolled usage.',\n        },\n        multiple: {\n            control: 'boolean',\n            description:\n                'Allow multiple panels open at once. `false` collapses siblings when a row opens. Defaults to `true`.',\n        },\n        onOpenChange: {\n            ...disabledControl,\n            description: 'Fires with the next open-id array whenever a row toggles.',\n        },\n        haptic: {\n            ...hapticArgType,\n            description: 'Haptic fired on row toggle. Defaults to `true` (`selection`).',\n        },\n    },\n    decorators: [\n        Story => (\n            <div\n                style={{\n                    width: 'min(calc(100vw - var(--cds-space-32)), calc(var(--cds-size-240) + var(--cds-size-80)))',\n                }}\n            >\n                {Story()}\n            </div>\n        ),\n    ],\n};\nexport default meta;\ntype Story = StoryObj<typeof Accordion>;\n\nexport const Playground: Story = {};\n\nexport const InitiallyOpen: Story = {\n    args: {\n        items: ITEMS,\n        defaultOpenItemIds: ['monitoring'],\n    },\n    parameters: {\n        docs: { description: { story: 'Uncontrolled, with `defaultOpenItemIds` expanding a panel on mount.' } },\n    },\n};\n\nexport const SingleOpen: Story = {\n    args: {\n        items: ITEMS,\n        multiple: false,\n        defaultOpenItemIds: ['monitoring'],\n    },\n    parameters: {\n        docs: { description: { story: '`multiple={false}` — opening a panel collapses the others (radio-style).' } },\n    },\n};\n\nexport const WithLeading: Story = {\n    args: {\n        items: ITEMS_WITH_LEADING,\n        defaultOpenItemIds: ['accudata'],\n    },\n    parameters: {\n        docs: {\n            description: {\n                story: 'A `leading` element identifies each row. It holds its width, so a long title wraps beside it rather than squeezing it.',\n            },\n        },\n    },\n};\n\nexport const WithDisabledItem: Story = {\n    args: {\n        items: ITEMS_WITH_DISABLED,\n    },\n    parameters: {\n        docs: { description: { story: 'A `disabled` item reads as inert and cannot be toggled.' } },\n    },\n};\n\nexport const ManyItems: Story = {\n    args: {\n        items: MANY_ITEMS,\n        defaultOpenItemIds: ['monitoring', 'data'],\n    },\n    parameters: {\n        docs: { description: { story: 'Multiple panels open at once (the default `multiple` behavior).' } },\n    },\n};\n\nexport const LongText: Story = {\n    parameters: {\n        docs: { description: { story: 'Long titles and body copy wrap without widening the container.' } },\n    },\n    args: {\n        items: [\n            {\n                id: 'long',\n                title: 'A longer disclosure title that should wrap without resizing the sheet horizontally',\n                description: 'Long supporting copy should wrap naturally.',\n                content: (\n                    <Text variant='body-small' color='secondary' as='p'>\n                        Long panel content should remain readable inside the mobile-width container and should not push\n                        the accordion wider than its parent surface.\n                    </Text>\n                ),\n            },\n        ],\n        defaultOpenItemIds: ['long'],\n    },\n};\n\nexport const DarkTheme: Story = {\n    globals: { theme: 'dark' },\n    args: {\n        items: ITEMS,\n        defaultOpenItemIds: ['monitoring'],\n    },\n    parameters: {\n        docs: { description: { story: 'Rows and animated panels on the dark theme surface.' } },\n    },\n};\n"
  },
  "links": {
    "human": "/component-accordion",
    "json": "/data/components/accordion.json",
    "markdown": "/data/components/accordion.md",
    "storybook": "/storybook/?path=/story/cloaked-design-system-organization-accordion--playground"
  },
  "contract_status": "preview",
  "contract_kind": "source-derived",
  "approval": "required",
  "api": {
    "method": "typescript-type-checker",
    "typescript_version": "5.9.3",
    "public_export": "Accordion",
    "public_path": "Accordion",
    "import_path": "@cloakedinc/design-system",
    "root_kind": "component",
    "entrypoints": [
      {
        "name": "Accordion",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
          "line": 63
        },
        "props_type": "AccordionProps",
        "props": [
          {
            "name": "items",
            "type": "AccordionItem[]",
            "declared_type": "AccordionItem[]",
            "required": true,
            "description": "Rows in render order. Each `id` must be unique and stable — it keys React and the open set.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 42
            }
          },
          {
            "name": "openItemIds",
            "type": "string[] | undefined",
            "declared_type": "string[]",
            "required": false,
            "description": "Controlled open item ids.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 44
            }
          },
          {
            "name": "defaultOpenItemIds",
            "type": "string[] | undefined",
            "declared_type": "string[]",
            "required": false,
            "description": "Initial open item ids for uncontrolled usage.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 46
            },
            "default": "[]",
            "default_kind": "source-expression"
          },
          {
            "name": "multiple",
            "type": "boolean | undefined",
            "declared_type": "boolean",
            "required": false,
            "description": "Allows more than one item to be open. Defaults to true.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 48
            },
            "values": [
              false,
              true
            ],
            "values_complete": true,
            "default": "true",
            "default_kind": "source-expression"
          },
          {
            "name": "onOpenChange",
            "type": "((openItemIds: string[]) => void) | undefined",
            "declared_type": "(openItemIds: string[]) => void",
            "required": false,
            "description": "Fires on every toggle with the full next open set, including while controlled.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 50
            }
          },
          {
            "name": "haptic",
            "type": "HapticFeedback | undefined",
            "declared_type": "HapticFeedback",
            "required": false,
            "description": "Haptic feedback for row toggles. Defaults to `true` (`selection`).",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 52
            },
            "values": [
              "error",
              "heavy",
              "light",
              "medium",
              "selection",
              "success",
              "warning",
              false,
              true
            ],
            "values_complete": true,
            "default": "true",
            "default_kind": "source-expression"
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 53
            }
          },
          {
            "name": "style",
            "type": "React.CSSProperties | undefined",
            "declared_type": "React.CSSProperties",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 54
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Root selector, and the prefix each row and panel selector falls back to.",
            "source": {
              "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
              "line": 56
            }
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      }
    ],
    "type_sources": [
      {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 16,
        "name": "AccordionItem",
        "code": "export interface AccordionItem {\n    /** The value callers put in `openItemIds` / `defaultOpenItemIds`, and the suffix of the generated trigger and panel element ids that wire up aria. */\n    id: string;\n    /** Element shown before the copy, such as an `Avatar` or a brand logo. */\n    leading?: React.ReactNode;\n    /** Primary row copy, on the `body` ramp above `description`. */\n    title: React.ReactNode;\n    /** Optional supporting copy shown in the trigger row. */\n    description?: React.ReactNode;\n    /** Panel body. Always mounted — a collapsed panel is hidden, not unmounted. */\n    content: React.ReactNode;\n    /** Dims the row and blocks toggling. A panel that is already open stays open. */\n    disabled?: boolean;\n    /**\n     * Row test selector, and `-panel` for its panel. Defaults to the Accordion's own `testId`\n     * suffixed with `id` — pass this when `id` is a raw value that would not read as kebab-case.\n     */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/components/organization/Accordion/Accordion.tsx",
        "line": 40,
        "name": "AccordionProps",
        "code": "export interface AccordionProps {\n    /** Rows in render order. Each `id` must be unique and stable — it keys React and the open set. */\n    items: AccordionItem[];\n    /** Controlled open item ids. */\n    openItemIds?: string[];\n    /** Initial open item ids for uncontrolled usage. */\n    defaultOpenItemIds?: string[];\n    /** Allows more than one item to be open. Defaults to true. */\n    multiple?: boolean;\n    /** Fires on every toggle with the full next open set, including while controlled. */\n    onOpenChange?: (openItemIds: string[]) => void;\n    /** Haptic feedback for row toggles. Defaults to `true` (`selection`). */\n    haptic?: HapticFeedback;\n    className?: string;\n    style?: React.CSSProperties;\n    /** Root selector, and the prefix each row and panel selector falls back to. */\n    testId?: string;\n}"
      },
      {
        "path": "packages/design-system/src/tokens/CdsHaptic.ts",
        "line": 6,
        "name": "HapticFeedback",
        "code": "export type HapticFeedback = boolean | HapticType;"
      },
      {
        "path": "packages/design-system/src/tokens/HapticType.ts",
        "line": 2,
        "name": "HapticType",
        "code": "export type HapticType = 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error' | 'selection';"
      }
    ],
    "defaults_policy": "Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source."
  },
  "platforms": {
    "implemented": [
      "web"
    ],
    "native": "Imported design guidance only; no native implementation or parity verification in this preview.",
    "react": {
      "implementation": "local",
      "status": "preview",
      "parity": "web-reference"
    },
    "vue": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "swift": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "kotlin": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    }
  },
  "missing_facts": [
    {
      "code": "human-review-pending",
      "detail": "This local preview has no recorded human design or engineering sign-off."
    },
    {
      "code": "platform-parity-unverified",
      "detail": "Vue, Swift and Kotlin implementations are absent from this package; platform parity has not been verified."
    },
    {
      "code": "figma-mapping-unverified",
      "detail": "No current component-level Figma node and immutable revision have been verified for this contract."
    }
  ],
  "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."
  }
}
