{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "title": "Accordion",
  "description": "Accordion from the Get IT Done design system.",
  "dependencies": [
    "@radix-ui/react-accordion@^1.3.1",
    "lucide-react@^0.379.0"
  ],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/card.json",
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/accordion.tsx",
      "content": "'use client';\n\nimport * as AccordionPrimitive from '@radix-ui/react-accordion';\nimport { ChevronDown } from 'lucide-react';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { InsideCardContext } from '@/components/ui/card';\nimport { usePreset } from '@/components/ui/labels-context';\n\n// Horizontal inset of trigger and content. Set on the root or per item, the item wins. Undefined means follow the context: inset on a standalone accordion, none inside a Card.\nconst AccordionInsetContext = React.createContext<boolean | undefined>(undefined);\n\ntype AccordionInsetProp = {\n  // Horizontal padding (px-3) of trigger and content. Default follows the context: true standalone (page, menu, panel), false inside a Card so the labels align with the card heading (the workload.zero prototype aligns card content with the card heading). Set true or false to override.\n  inset?: boolean;\n};\n\nconst Accordion = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & AccordionInsetProp\n>(({ inset, ...props }, ref) => (\n  <AccordionInsetContext.Provider value={inset}>\n    <AccordionPrimitive.Root ref={ref} {...(props as React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>)} />\n  </AccordionInsetContext.Provider>\n));\nAccordion.displayName = 'Accordion';\n\nconst AccordionItem = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> & AccordionInsetProp\n>(({ className, inset: insetProp, ...props }, ref) => {\n  const zero = usePreset() === 'zero';\n  const rootInset = React.useContext(AccordionInsetContext);\n  const inCard = React.useContext(InsideCardContext);\n  const inset = insetProp ?? rootInset ?? !inCard;\n  return (\n  <AccordionInsetContext.Provider value={inset}>\n  <AccordionPrimitive.Item\n    ref={ref}\n    className={cn(\n      // Dedicated Accordion surface tokens (Design Tokens \"colors/accordion/background/default\" +\n      // \"colors/accordion/foreground/default\") — replaces the previous implicit (unstyled) surface\n      // and the raw `border-slate-200` palette value (ACCORDION-MAT-01: no raw hex/palette in\n      // product code). `border-form-border-default` is the same general-purpose content-divider\n      // token already used for this exact role by Card/Dialog/Popover/Tooltip/NavigationMenu/\n      // Drawer/Toast/DropdownMenu.\n      'border-b border-solid bg-transparent text-foreground',\n      // Zero: the default border role, the other themes keep the hairline.\n      zero ? 'border-border-default' : 'border-hairline',\n      className,\n    )}\n    {...props}\n  />\n  </AccordionInsetContext.Provider>\n  );\n});\nAccordionItem.displayName = 'AccordionItem';\n\nconst AccordionTrigger = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Trigger>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => {\n  const zero = usePreset() === 'zero';\n  const inset = React.useContext(AccordionInsetContext) ?? true;\n  return (\n  <AccordionPrimitive.Header className=\"flex\">\n    <AccordionPrimitive.Trigger\n      ref={ref}\n      className={cn(\n        // `text-left` corrects the default `<button>` UA style (`text-align: center`) — Radix\n        // renders the Trigger as a real button (see react-collapsible's `Collapsible.Trigger`), and\n        // without this a wrapping multi-line header label would center instead of following the\n        // left-aligned \"label | chevron\" row every other disclosure/menu trigger in this system uses.\n        'flex flex-1 items-center justify-between gap-2 py-4 text-left transition-all [&[data-state=open]>svg]:rotate-180',\n        // Type comes from the dedicated `typography-accordion-title` role (size/weight/line-height/\n        // letter-spacing token, currently 16px/500/24px) rather than the ad hoc `font-medium` this\n        // used before. `leading-normal` is required alongside it because Tailwind Preflight resets\n        // `line-height: inherit` on real `<button>` elements — same reset `button.tsx`/`input.tsx`/\n        // `tabs.tsx` already work around for the same reason.\n        inset && 'px-3',\n        zero ? 'typography-ui-medium min-h-row py-0' : 'typography-accordion-title leading-normal',\n        // Hover — underline is the non-color-dependent cue (WCAG \"don't rely on color alone\");\n        // paired with a full-row background wash so the entire clickable header shows affordance,\n        // not just the text. No dedicated `accordion-*-hover` token exists yet (ACCORDION-MAT-02,\n        // flagged below) — reuses the general-purpose `background-muted` surface wash, the same\n        // fallback Chip's `default`/`secondary` variants already use for an identical gap.\n        'hover:underline hover:bg-background-neutral-default',\n        // Press feedback (mouse-down) — same generic fallback as Chip's `active:bg-background-neutral-default\n        // active:brightness-95` for the same \"no dedicated active token\" gap.\n        'active:bg-background-neutral-default active:brightness-95',\n        // Keyboard focus ring — the system-standard treatment (matches Button, Tabs, Alert, Chip,\n        // Dialog, Select, …). Previously this had no focus-visible treatment at all (WCAG 2.4.7).\n        'rounded-item',\n        'focus-visible:outline-none',\n        'focus-visible:ring-focus',\n        'focus-visible:ring-offset-focus',\n        'focus-visible:ring-offset-background',\n        'focus-visible:ring-ring-default',\n        // Disabled — Radix's `Collapsible.Trigger` sets both a real `disabled` attribute and\n        // `data-disabled` when the parent `AccordionItem` has `disabled` (ACCORDION-A11Y-01:\n        // previously unsupported — a `disabled` item rendered fully interactive/clickable).\n        'disabled:pointer-events-none disabled:opacity-disabled',\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronDown aria-hidden=\"true\" className=\"h-4 w-4 shrink-0 transition-transform duration-200\" />\n    </AccordionPrimitive.Trigger>\n  </AccordionPrimitive.Header>\n  );\n});\nAccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;\n\nconst AccordionContent = React.forwardRef<\n  React.ElementRef<typeof AccordionPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(({ className, children, ...props }, ref) => {\n  const zero = usePreset() === 'zero';\n  const inset = React.useContext(AccordionInsetContext) ?? true;\n  return (\n  <AccordionPrimitive.Content\n    ref={ref}\n    className={cn(\n      // Type comes from the dedicated `typography-accordion-content` role (size/weight/line-height/\n      // letter-spacing token, currently 14px/400/24px), replacing the ad hoc `text-sm`.\n      'overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',\n      // Reduced motion — instant show/hide instead of the height animation, matching\n      // DropdownMenuContent/SelectContent/TooltipContent's identical `motion-reduce:` fallback.\n      'motion-reduce:animate-none motion-reduce:transition-none',\n      zero ? 'typography-ui-relaxed' : 'typography-accordion-content',\n    )}\n    {...props}\n  >\n    {/* Same horizontal padding as the trigger so the content text stays aligned with the header label. */}\n    <div className={cn(inset && 'px-3', 'pb-4 pt-0', className)}>{children}</div>\n  </AccordionPrimitive.Content>\n  );\n});\nAccordionContent.displayName = AccordionPrimitive.Content.displayName;\n\nexport { Accordion, AccordionContent, AccordionItem, AccordionTrigger };\n\n// --- KNOWN TOKEN / SPEC GAPS (component-library-maintainer cannot close these from accordion.tsx —\n// they are either color VALUES owned by the design tokens, or a missing Figma\n// frame; this file only consumes token names and must not hardcode a raw hex/palette value or\n// fabricate a state matrix that isn't backed by a real spec) ---\n//\n// 1. ACCORDION-A11Y-02 / ACCORDION-A11Y-03 (light/dark parity): re-verified 2026-07-09 against the\n//    installed the design tokens (the only version published to the\n//    registry — the token package returns just \"3.4.10\") —\n//    its `dist/theme.css` still defines `--colors-accordion-background-default` and\n//    `--colors-accordion-foreground-default` exactly once, in `:root`, with no `.dark` /\n//    `prefers-color-scheme: dark` override, so hover:/active:bg-background-neutral-default (which DOES\n//    invert in dark) is paired against a foreground that never adapts — contrast collapses to\n//    ~1.2:1 in dark mode (needs >=4.5:1, WCAG 1.4.3 AA).\n//    IMPORTANT: the source of truth is already fixed — `figma-tokens/default_theme_dark.json` in\n//    the kette-tokens repo already carries distinct dark values (accordion/background →\n//    slate.900, accordion/foreground → slate.50), and running `npm run build:css-vars` from that\n//    repo's current HEAD locally reproduces the correct `.dark`/media overrides. The gap is purely\n//    that the last published package (3.4.10) was not rebuilt/republished after that source fix\n//    landed, so consumers are stuck on stale compiled output. BLOCKED on design-tokens-maintainer\n//    cutting a new release (rebuild + `npm publish`, human release approval required) — no change\n//    is possible or needed in accordion.tsx, which already consumes the correct token names. Same\n//    published-vs-source staleness likely affects every other component whose only Figma-driven\n//    delta is a single default-state key (e.g. dropdown-menu.tsx's own flagged token gap) and is\n//    worth a repo-wide re-publish rather than a per-component patch.\n//\n// 2. ACCORDION-MAT-02 (no dedicated hover/active tokens): unlike Dropdown/Select's \"_list item\"\n//    component sets, the Figma Component Library file has no Accordion frame with\n//    Hover/Focus/Disabled state symbols to source exact values from — only the two surface color\n//    tokens above plus the `typography/accordion-title` and `typography/accordion-content` text\n//    styles exist for this component. The hover/active treatment above reuses the same\n//    `background-muted` / `brightness-95` fallback Chip already uses for an identical gap, rather\n//    than inventing a new one-off value.\n//\n// 3. \"Invalid\" state — not applicable: Accordion is a disclosure widget with no form-validation\n//    semantics (like DropdownMenuItem), so `invalid` is intentionally not modeled here.\n//\n// 4. Missing Figma spec frame (routed to figma-design-sync / design-system-usage maintainers): a\n//    `search_design_system` query for \"Accordion\" against this file (bzBnRLfRJNknGg9UYJIHb0) only\n//    surfaces a stale \"FAQ section\" component_set hit (last updated 2024-07-23) that is not reachable\n//    from any of the file's current top-level pages (Welcome, Avatars, Button, Select, Tooltips,\n//    CardMetrics, Logos, Icons, Misc icons, Utilities) — i.e. no live Accordion/FAQ frame exists in\n//    the file today, only the token pair + two text styles consumed above. Recommend either\n//    re-adding an explicit Accordion component frame (with Hover/Focus/Disabled symbols) to the\n//    Component Library file, or removing the stale search-index entry, so a future alignment pass\n//    has a real states matrix to verify against instead of the Material/repo-precedent fallback\n//    used here.\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}