{
  "site": {
    "name": "Iconiq",
    "url": "https://iconiqui.com",
    "description": "Iconiq is an open-source React component library built around the shadcn registry workflow. Browse polished UI primitives, install them as local files, and adapt them directly inside modern interfaces.",
    "llms": {
      "overview": "https://iconiqui.com/llms.txt",
      "full": "https://iconiqui.com/llms-full.txt"
    },
    "catalogs": {
      "aiIndex": "https://iconiqui.com/ai-index.json"
    }
  },
  "guides": [
    {
      "title": "Overview",
      "href": "/",
      "url": "https://iconiqui.com",
      "summary": "Homepage with the full live component playground and the primary installation path for the registry."
    },
    {
      "title": "Introduction",
      "href": "/introduction",
      "url": "https://iconiqui.com/introduction",
      "summary": "Product overview, design principles, and the delivery model behind the Iconiq component library."
    },
    {
      "title": "Installation",
      "href": "/installation",
      "url": "https://iconiqui.com/installation",
      "summary": "Installation guide for the shadcn registry flow, direct registry JSON URLs, and sample component entries."
    },
    {
      "title": "Changelog",
      "href": "/changelog",
      "url": "https://iconiqui.com/changelog",
      "summary": "Release notes rendered from the local changelog source file, covering added, updated, and fixed work."
    }
  ],
  "components": [
    {
      "slug": "accordion",
      "name": "Accordion",
      "href": "/components/accordion",
      "url": "https://iconiqui.com/components/accordion",
      "installPackage": "@iconiq/accordion",
      "installCommand": "npx shadcn@latest add @iconiq/accordion",
      "registryPath": "accordion.json",
      "registryUrl": "https://iconiqui.com/r/accordion.json",
      "summary": "Each row is described by a simple object and rendered as a single-expand accordion item.",
      "apiSections": [
        {
          "id": "accordion-item",
          "title": "AccordionItem",
          "summary": "Each row is described by a simple object and rendered as a single-expand accordion item.",
          "notes": [],
          "fields": [
            {
              "name": "id",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Stable key used for React rendering, internal open-state comparison, and the generated aria-controls id."
            },
            {
              "name": "title",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Text shown in the trigger row."
            },
            {
              "name": "content",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Body copy shown inside the open panel with a horizontal masked wipe and a soft lift into place."
            }
          ]
        },
        {
          "id": "accordion",
          "title": "Accordion",
          "summary": "Stateful accordion component with one internal openId value and no controlled API.",
          "notes": [
            "Clicking an already open row closes it again because toggle compares against the current openId.",
            "There is no prop for default open, controlled open, or multiple-open behavior in this implementation."
          ],
          "fields": [
            {
              "name": "items",
              "type": "AccordionItem[]",
              "defaultValue": "",
              "required": true,
              "description": "Rows to render in order."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the max-w-2xl root wrapper so you can stretch or reposition the accordion in your layout."
            }
          ]
        },
        {
          "id": "accordion-motion",
          "title": "Motion and accessibility",
          "summary": "The accordion uses native buttons and animated height transitions rather than a headless primitive.",
          "notes": [
            "Each trigger button sets aria-expanded and aria-controls, and each open panel receives a matching id.",
            "The content body reveals through a horizontal clipped wipe while the paragraph settles upward with a soft blur fade.",
            "Keyboard support is limited to standard button tab and click semantics; there is no arrow-key roving between items."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "alert",
      "name": "Alert",
      "href": "/components/alert",
      "url": "https://iconiqui.com/components/alert",
      "installPackage": "@iconiq/alert",
      "installCommand": "npx shadcn@latest add @iconiq/alert",
      "registryPath": "alert.json",
      "registryUrl": "https://iconiqui.com/r/alert.json",
      "summary": "Default export for a single dismissible notice. It can render inline with surrounding content or portal to the viewport when you provide a position.",
      "apiSections": [
        {
          "id": "alert",
          "title": "Alert",
          "summary": "Default export for a single dismissible notice. It can render inline with surrounding content or portal to the viewport when you provide a position.",
          "notes": [
            "Every positioned alert snaps to a full-width top placement on small screens, then switches to the requested corner at the sm breakpoint.",
            "The alert keeps its own visible state internally, so it is designed for fire-and-forget notifications rather than parent-controlled open state."
          ],
          "fields": [
            {
              "name": "icon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Leading visual passed straight into the icon slot. The wrapper applies fallback sizing to nested SVGs so Lucide icons land around 18px without extra setup."
            },
            {
              "name": "title",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Primary line rendered in the stronger label style."
            },
            {
              "name": "message",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Secondary copy rendered below the title with a lighter foreground tone."
            },
            {
              "name": "dismissible",
              "type": "boolean",
              "defaultValue": "true",
              "required": false,
              "description": "Controls whether the close button is rendered. Disabling it removes manual dismissal only; timeout still applies unless it is set to 0."
            },
            {
              "name": "position",
              "type": "\"top-left\" | \"top-center\" | \"top-right\" | \"bottom-left\" | \"bottom-center\" | \"bottom-right\"",
              "defaultValue": "",
              "required": false,
              "description": "When present, the component portals to document.body and uses fixed positioning classes. When omitted, the alert stays in normal flow and maxes out at max-w-sm."
            },
            {
              "name": "timeout",
              "type": "number",
              "defaultValue": "10000",
              "required": false,
              "description": "Auto-dismiss delay in milliseconds. Passing 0 disables the timer and removes the progress-bar countdown."
            },
            {
              "name": "onDismiss",
              "type": "() => void",
              "defaultValue": "",
              "required": false,
              "description": "Called after the component marks itself hidden, regardless of whether dismissal came from the close button or the timeout effect."
            }
          ]
        },
        {
          "id": "alert-lifecycle",
          "title": "Motion and lifecycle",
          "summary": "Alert uses AnimatePresence for mount and exit, with separate variants for the container, icon, and text stack.",
          "notes": [
            "Entry direction is derived from position: top placements slide down slightly, bottom placements rise upward, and inline alerts use a smaller upward offset.",
            "The timeout effect is cleared on cleanup, so unmounting or rerendering the alert does not leak timers.",
            "When position is set, the component waits until after mount before calling createPortal to avoid touching document during server render."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "avatar",
      "name": "Avatar",
      "href": "/components/avatar",
      "url": "https://iconiqui.com/components/avatar",
      "installPackage": "@iconiq/avatar",
      "installCommand": "npx shadcn@latest add @iconiq/avatar",
      "registryPath": "avatar.json",
      "registryUrl": "https://iconiqui.com/r/avatar.json",
      "summary": "Compact motion avatar with a canonical Avatar export, optional image source, and fallback text for empty states.",
      "apiSections": [
        {
          "id": "avatar",
          "title": "Avatar",
          "summary": "Compact motion avatar with a canonical Avatar export, optional image source, and fallback text for empty states.",
          "notes": [
            "The component does not forward refs or arbitrary DOM props; the public surface is only src, fallback, and className.",
            "The root is a motion.div with pointer-style hover and tap animation, but it is not a button or link by itself."
          ],
          "fields": [
            {
              "name": "src",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Image URL rendered into a framework-agnostic img element. When present, the image fills the 42x42 circular mask with a blur-and-clip reveal animation."
            },
            {
              "name": "fallback",
              "type": "string",
              "defaultValue": "?",
              "required": false,
              "description": "Text rendered in the center when no src is provided, typically initials or a short placeholder character."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root motion.div. The base component already fixes the size to 42x42 and applies the circular overflow mask."
            }
          ]
        },
        {
          "id": "avatar-image-motion",
          "title": "Image and motion behavior",
          "summary": "Motion behavior changes slightly when reduced motion is enabled, but the same two rendering paths stay intact.",
          "notes": [
            "The image path always uses a fixed alt value of \"Avatar\" plus width and height of 42, so this version is best suited to decorative or generic avatars.",
            "Hover adds a soft lift and pulse ring, while tap slightly compresses the root when reduced motion is not requested.",
            "Without src, the fallback text springs in over the primary-colored background instead of showing an empty frame."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "badge",
      "name": "Badge",
      "href": "/components/badge",
      "url": "https://iconiqui.com/components/badge",
      "installPackage": "@iconiq/badge",
      "installCommand": "npx shadcn@latest add @iconiq/badge",
      "registryPath": "badge.json",
      "registryUrl": "https://iconiqui.com/r/badge.json",
      "summary": "Default export for a compact label pill with a permanent shimmer pass layered behind the content.",
      "apiSections": [
        {
          "id": "badge",
          "title": "Badge",
          "summary": "Default export for a compact label pill with a permanent shimmer pass layered behind the content.",
          "notes": [
            "The public API does not spread other span props, so data attributes or click handlers require a local wrapper or a component fork.",
            "Color overrides are applied through inline styles, which means they take priority over the default Tailwind utility classes."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Badge content rendered in the foreground layer so it always stays above the animated shimmer."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "\"\"",
              "required": false,
              "description": "Appended directly to the root span. Useful for size, radius, spacing, or border overrides."
            },
            {
              "name": "bgColor",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Inline backgroundColor override. If omitted, the component uses a dark neutral surface in light mode and a white surface in dark mode."
            },
            {
              "name": "textColor",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Inline text color override. If omitted, the badge picks the inverse of its default background palette."
            },
            {
              "name": "waveColor",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Color used for the middle stop of the animated gradient sweep. Defaults to the local CSS variable declared on the shimmer layer."
            }
          ]
        },
        {
          "id": "badge-visuals",
          "title": "Visual behavior",
          "summary": "Badge uses a quick mount animation and a continuously repeating shimmer sweep.",
          "notes": [
            "The root fades and scales from 0.95 to 1 on mount over 0.3 seconds.",
            "The shimmer travels from left to right over 2 seconds, waits 1.5 seconds, then repeats indefinitely."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "breadcrumbs",
      "name": "Breadcrumbs",
      "href": "/components/breadcrumbs",
      "url": "https://iconiqui.com/components/breadcrumbs",
      "installPackage": "@iconiq/breadcrumbs",
      "installCommand": "npx shadcn@latest add @iconiq/breadcrumbs",
      "registryPath": "breadcrumbs.json",
      "registryUrl": "https://iconiqui.com/r/breadcrumbs.json",
      "summary": "Each breadcrumb segment is described by a small object consumed by the Breadcrumbs component.",
      "apiSections": [
        {
          "id": "breadcrumb-item",
          "title": "BreadcrumbItem",
          "summary": "Each breadcrumb segment is described by a small object consumed by the Breadcrumbs component.",
          "notes": [],
          "fields": [
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Visible label and React key for the item. Labels should therefore be unique inside a single breadcrumb trail."
            },
            {
              "name": "href",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Link destination used by the rendered anchor. If it is omitted, the component falls back to '#'."
            },
            {
              "name": "icon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Optional icon shown before the label, typically a Lucide icon or any other inline React node."
            }
          ]
        },
        {
          "id": "breadcrumbs",
          "title": "Breadcrumbs",
          "summary": "Animated breadcrumb trail that maps over the items array and styles the last entry as the current location.",
          "notes": [
            "The last item still renders as a motion.a element, even if href is omitted. Its classes remove the interactive cursor but the fallback anchor target is still '#'.",
            "Separators only render after the first item and use the built-in ChevronRight icon from lucide-react."
          ],
          "fields": [
            {
              "name": "items",
              "type": "BreadcrumbItem[]",
              "defaultValue": "",
              "required": true,
              "description": "Ordered list of segments. Earlier items receive hover and tap motion, while the last item gets the shimmer and pulsing dot treatment."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root nav wrapper so you can place the trail inside your own header layout."
            }
          ]
        },
        {
          "id": "breadcrumbs-a11y",
          "title": "Accessibility and motion",
          "summary": "The component keeps semantic breadcrumb structure while layering Motion on top.",
          "notes": [
            "The root nav uses aria-label=\"breadcrumb\" and wraps items in an ordered list.",
            "AnimatePresence runs in popLayout mode so reordering or changing the trail keeps the transitions coherent.",
            "This implementation does not add aria-current to the final item, so add that yourself if you need stricter breadcrumb semantics."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "button",
      "name": "Button",
      "href": "/components/button",
      "url": "https://iconiqui.com/components/button",
      "installPackage": "@iconiq/button",
      "installCommand": "npx shadcn@latest add @iconiq/button",
      "registryPath": "button.json",
      "registryUrl": "https://iconiqui.com/r/button.json",
      "summary": "Ref-forwarding button built on motion.button plus a CVA recipe for size and variant styling.",
      "apiSections": [
        {
          "id": "button",
          "title": "Button",
          "summary": "Ref-forwarding button built on motion.button plus a CVA recipe for size and variant styling.",
          "notes": [
            "Standard button attributes such as onClick, aria-*, name, form, and data-* are forwarded to the underlying motion.button.",
            "The local pointer-down handler always calls your onPointerDown first, then decides whether to spawn a ripple.",
            "Reduced-motion users still get the button component, but the ripple effect is skipped."
          ],
          "fields": [
            {
              "name": "variant",
              "type": "\"default\" | \"destructive\" | \"outline\" | \"secondary\" | \"ghost\" | \"link\"",
              "defaultValue": "default",
              "required": false,
              "description": "Chooses the visual recipe from the exported buttonVariants map."
            },
            {
              "name": "size",
              "type": "\"sm\" | \"md\" | \"lg\" | \"custom\"",
              "defaultValue": "md",
              "required": false,
              "description": "Controls height and padding. The custom size leaves sizing classes empty so the caller can drive layout entirely through className."
            },
            {
              "name": "type",
              "type": "\"button\" | \"submit\" | \"reset\"",
              "defaultValue": "button",
              "required": false,
              "description": "Passed directly to the underlying motion.button so the component does not submit forms accidentally by default."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Content rendered above the ripple layer inside a z-10 span."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged after the generated CVA classes, making it the main escape hatch for one-off layout changes."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Native disabled state. It also prevents ripple creation because the pointer-down handler exits early."
            }
          ]
        },
        {
          "id": "button-variants",
          "title": "buttonVariants",
          "summary": "The CVA recipe is exported alongside the component so matching button classes can be reused on links or custom wrappers.",
          "notes": [
            "Variants ship with six visual states and four size tokens.",
            "Because buttonVariants is a plain CVA export, you can compose it independently from the Button component when you do not want a motion.button element."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "class-variance-authority"
      ]
    },
    {
      "slug": "button-group",
      "name": "Button group",
      "href": "/components/button-group",
      "url": "https://iconiqui.com/components/button-group",
      "installPackage": "@iconiq/button-group",
      "installCommand": "npx shadcn@latest add @iconiq/button-group",
      "registryPath": "button-group.json",
      "registryUrl": "https://iconiqui.com/r/button-group.json",
      "summary": "Standalone motion button with a light upward entrance, hover scale, and a small label nudge inside the content span.",
      "apiSections": [
        {
          "id": "button-group-button",
          "title": "Button",
          "summary": "Standalone motion button with a light upward entrance, hover scale, and a small label nudge inside the content span.",
          "notes": [
            "Most standard button props such as type, disabled, onClick, name, value, aria-*, and data-* are forwarded to the underlying motion button.",
            "The public prop surface intentionally leaves out the native drag and CSS animation callback props because they conflict with Motion's own handler names."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Button content rendered inside an animated inline span so the label can shift slightly on hover."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root button. Use it for local width, spacing, or surface overrides."
            }
          ]
        },
        {
          "id": "button-group-icon-button",
          "title": "IconButton",
          "summary": "Compact icon-only version of the same button surface, with a stronger hover scale and a rotating inner icon span.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Icon content rendered inside the motion span. SVG children inherit the built-in 1rem sizing utility."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the icon button root for size or surface overrides."
            }
          ]
        },
        {
          "id": "button-group-layout",
          "title": "ButtonGroup",
          "summary": "Simple flex wrapper for arranging several button surfaces in one row with shared staggered entrance motion.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Buttons, icon buttons, or any other inline controls you want to keep in the same row."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the outer motion div. The base layout already applies a horizontal flex row with a small gap."
            }
          ]
        },
        {
          "id": "button-group-items",
          "title": "ButtonGroupItems",
          "summary": "Segmented button shell that turns each valid child element into an internal motion button with shared borders and equal visual rhythm.",
          "notes": [
            "Only valid React elements are rendered. Non-element children are ignored.",
            "The child node itself is not preserved; ButtonGroupItems reads each child's props and children, then renders a fresh motion button for that slot."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Pass plain button-like elements as children. Their props and children are hoisted into the internal motion buttons rendered by the group."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the outer segmented wrapper for width or surface overrides."
            }
          ]
        },
        {
          "id": "segmented-control",
          "title": "SegmentedControl",
          "summary": "String-based segmented selector with internal state support, hover wash, and a spring-driven selected indicator.",
          "notes": [],
          "fields": [
            {
              "name": "options",
              "type": "string[]",
              "defaultValue": "",
              "required": true,
              "description": "Ordered list of visible segments. The first option becomes the uncontrolled initial selection when value is not provided."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Controlled selected option. When provided, the internal state syncs to this prop through an effect."
            },
            {
              "name": "onChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called with the selected option whenever a segment is pressed."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the segmented wrapper for width, alignment, or spacing overrides."
            },
            {
              "name": "layoutId",
              "type": "string",
              "defaultValue": "\"segmented-indicator\"",
              "required": false,
              "description": "Motion layout id used by the selected indicator. Override it when you render multiple segmented controls on the same page and want isolated indicator motion."
            }
          ]
        },
        {
          "id": "button-group-motion",
          "title": "Motion and interaction",
          "summary": "Each export shares the same spring-heavy motion language, but the interaction style changes slightly by surface.",
          "notes": [
            "Button and IconButton both animate in on mount, scale on hover and tap, and apply a muted background shift during hover.",
            "ButtonGroup only handles layout and entrance motion; the interactive behavior still comes from the child buttons inside it.",
            "SegmentedControl animates each option into place individually, then uses a shared layout indicator for the active segment and a lighter hover wash for inactive segments."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "calendar",
      "name": "Calendar",
      "href": "/components/calendar",
      "url": "https://iconiqui.com/components/calendar",
      "installPackage": "@iconiq/calendar",
      "installCommand": "npx shadcn@latest add @iconiq/calendar",
      "registryPath": "calendar.json",
      "registryUrl": "https://iconiqui.com/r/calendar.json",
      "summary": "Animated monthly calendar card that now supports both controlled and uncontrolled month and selection state.",
      "apiSections": [
        {
          "id": "calendar",
          "title": "Calendar",
          "summary": "Animated monthly calendar card that now supports both controlled and uncontrolled month and selection state.",
          "notes": [
            "Controlled mode: pass selected/month and respond to onSelect/onMonthChange.",
            "Uncontrolled mode: omit selected/month and optionally seed with defaultSelected/defaultMonth.",
            "When no defaults are provided, selected date and visible month both start from new Date()."
          ],
          "fields": [
            {
              "name": "selected",
              "type": "Date",
              "defaultValue": "",
              "required": false,
              "description": "Controlled selected day. When provided, the highlighted day is always derived from this prop."
            },
            {
              "name": "defaultSelected",
              "type": "Date",
              "defaultValue": "",
              "required": false,
              "description": "Initial selected day for uncontrolled usage when selected is not provided."
            },
            {
              "name": "onSelect",
              "type": "(date: Date) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when the user picks an interactive day in the current month."
            },
            {
              "name": "month",
              "type": "Date",
              "defaultValue": "",
              "required": false,
              "description": "Controlled visible month. Prev/next navigation requests flow through onMonthChange."
            },
            {
              "name": "defaultMonth",
              "type": "Date",
              "defaultValue": "",
              "required": false,
              "description": "Initial visible month for uncontrolled usage when month is not provided."
            },
            {
              "name": "onMonthChange",
              "type": "(month: Date) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever the user navigates to a previous or next month."
            },
            {
              "name": "disabled",
              "type": "(date: Date) => boolean",
              "defaultValue": "",
              "required": false,
              "description": "Marks dates as non-interactive. Disabled days keep the same visuals but cannot be selected."
            }
          ]
        },
        {
          "id": "calendar-grid",
          "title": "Date math and layout behavior",
          "summary": "The grid is rebuilt with date-fns whenever the visible month changes.",
          "notes": [
            "The rendered range runs from startOfWeek(startOfMonth(currentMonth)) through endOfWeek(endOfMonth(currentMonth)), so leading and trailing days from adjacent months are always visible.",
            "Days outside the active month are dimmed and made non-interactive with pointer-events-none, which keeps context without allowing cross-month selection from the overflow cells.",
            "Weekday labels are hardcoded as Sun through Sat and displayed as single-letter headers, so localization or alternate week starts require editing the component source."
          ],
          "fields": []
        },
        {
          "id": "calendar-motion-a11y",
          "title": "Motion and interaction model",
          "summary": "Month transitions, selected-day changes, and the footer summary each animate independently.",
          "notes": [
            "Prev and next controls are real buttons with aria-label values, and each in-month day is rendered as a button with hover and tap motion.",
            "The selected day highlight uses a shared layoutId of selected-day so the active surface glides between dates instead of remounting abruptly.",
            "This is still not a full calendar input primitive: there is no keyboard date navigation, no ARIA grid semantics, and no range or multi-select mode."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react",
        "date-fns"
      ]
    },
    {
      "slug": "carousels",
      "name": "Carousel",
      "href": "/components/carousels",
      "url": "https://iconiqui.com/components/carousels",
      "installPackage": "@iconiq/carousels",
      "installCommand": "npx shadcn@latest add @iconiq/carousels",
      "registryPath": "carousels.json",
      "registryUrl": "https://iconiqui.com/r/carousels.json",
      "summary": "Single exported testimonial carousel with internal pagination state, swipe gestures, animated slide transitions, and built-in arrow and dot controls.",
      "apiSections": [
        {
          "id": "carousel",
          "title": "Carousel",
          "summary": "Single exported testimonial carousel with internal pagination state, swipe gestures, animated slide transitions, and built-in arrow and dot controls.",
          "notes": [
            "The component does not expose a controlled index, change callback, or autoplay API. Navigation state is fully internal.",
            "Because the public surface only accepts testimonials, layout width, labels, and control styling require a local wrapper or a source edit if you want to change them."
          ],
          "fields": [
            {
              "name": "testimonials",
              "type": "Testimonial[]",
              "defaultValue": "",
              "required": false,
              "description": "Optional testimonial list shown by the carousel. When omitted, the component falls back to the sample items declared in the source file."
            }
          ]
        },
        {
          "id": "carousel-testimonial",
          "title": "Testimonial item",
          "summary": "Each item passed into the testimonials array follows a small typed shape used for the quote, author, and avatar row.",
          "notes": [
            "The active slide clamps the quote to three lines, so longer testimonials should still be edited to read cleanly inside the fixed-height card."
          ],
          "fields": [
            {
              "name": "quote",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Main testimonial copy rendered in the large italic text block inside the active slide."
            },
            {
              "name": "name",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Author name shown in the lower identity row beside the avatar or initials fallback."
            },
            {
              "name": "handle",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Secondary identity label rendered below the name, usually a short username or role marker."
            },
            {
              "name": "avatar",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional avatar image source. When omitted, the component shows the initials fallback chip instead."
            },
            {
              "name": "initials",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional fallback text rendered when no avatar string is provided. Leave it empty when you want the author row to render without any leading media."
            }
          ]
        },
        {
          "id": "carousel-interaction",
          "title": "Interaction and layout behavior",
          "summary": "The component couples motion and navigation into one fixed layout, so consumers get a ready-made interaction shell rather than a headless slider primitive.",
          "notes": [
            "The root width is capped at max-w-md and the slide stage uses a fixed 230px height, so very different aspect ratios require a source edit.",
            "Slide direction is used by AnimatePresence to decide whether the next card enters from the left or right."
          ],
          "fields": [
            {
              "name": "swipe threshold",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Dragging left or right past 80px changes slides. Smaller drags snap back to the current item."
            },
            {
              "name": "pagination dots",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Each testimonial maps to a dot button. The active dot stretches wider and clicking any dot jumps to that index."
            },
            {
              "name": "arrow controls",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Previous and next buttons wrap around the array length instead of stopping at the edges."
            }
          ]
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "collapsible",
      "name": "Collapsible",
      "href": "/components/collapsible",
      "url": "https://iconiqui.com/components/collapsible",
      "installPackage": "@iconiq/collapsible",
      "installCommand": "npx shadcn@latest add @iconiq/collapsible",
      "registryPath": "collapsible.json",
      "registryUrl": "https://iconiqui.com/r/collapsible.json",
      "summary": "Root wrapper around Radix Collapsible.Root with a small internal context used by the custom content animation.",
      "apiSections": [
        {
          "id": "collapsible-root",
          "title": "Collapsible",
          "summary": "Root wrapper around Radix Collapsible.Root with a small internal context used by the custom content animation.",
          "notes": [
            "Other Radix root props continue to work because the wrapper forwards the remaining root props to CollapsiblePrimitive.Root.",
            "The root stores the resolved open state in React context so CollapsibleContent can animate without reading Radix data attributes."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controlled open state. When provided, the component follows this prop instead of its own internal state."
            },
            {
              "name": "defaultOpen",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Initial open value for uncontrolled usage."
            },
            {
              "name": "onOpenChange",
              "type": "(open: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever the trigger toggles the root. It runs for both controlled and uncontrolled usage."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Composition surface for the trigger and content primitives rendered inside the root."
            }
          ]
        },
        {
          "id": "collapsible-trigger",
          "title": "CollapsibleTrigger",
          "summary": "Thin wrapper around Radix CollapsibleTrigger.",
          "notes": [
            "Any remaining trigger props and event handlers are forwarded directly to CollapsiblePrimitive.CollapsibleTrigger.",
            "The wrapper only adds data-slot=\"collapsible-trigger\" so callers can target it in CSS."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Interactive content rendered inside the trigger surface."
            },
            {
              "name": "asChild",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Lets you supply your own trigger element while preserving the Radix trigger behavior."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Disables trigger interaction through the underlying Radix primitive."
            }
          ]
        },
        {
          "id": "collapsible-content",
          "title": "CollapsibleContent",
          "summary": "Custom motion-driven content area that intentionally exposes a smaller API than the Radix primitive.",
          "notes": [
            "This component does not forward arbitrary CollapsiblePrimitive.Content props such as forceMount, asChild, or custom ids.",
            "Height is animated on an outer wrapper and clip-path is animated on an inner wrapper, producing the open and close effect without relying on CSS keyframes."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Rendered inside the animated content wrapper."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Applied to the inner motion.div that reveals the content."
            }
          ]
        }
      ],
      "dependencies": [
        "@radix-ui/react-collapsible",
        "motion"
      ]
    },
    {
      "slug": "combobox",
      "name": "Combobox",
      "href": "/components/combobox",
      "url": "https://iconiqui.com/components/combobox",
      "installPackage": "@iconiq/combobox",
      "installCommand": "npx shadcn@latest add @iconiq/combobox",
      "registryPath": "combobox.json",
      "registryUrl": "https://iconiqui.com/r/combobox.json",
      "summary": "Each selectable row is described by a plain object and can optionally include a secondary description line.",
      "apiSections": [
        {
          "id": "combobox-option",
          "title": "ComboboxOption",
          "summary": "Each selectable row is described by a plain object and can optionally include a secondary description line.",
          "notes": [],
          "fields": [
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Stable identifier used for the selected state and returned through onChange."
            },
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Primary text shown in the closed field and inside the dropdown row."
            },
            {
              "name": "description",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional helper copy rendered under the label inside the dropdown list."
            }
          ]
        },
        {
          "id": "combobox",
          "title": "Combobox",
          "summary": "Searchable single-select field that owns open state, search query, and active row internally, while the selected value stays parent-driven.",
          "notes": [
            "This is effectively controlled for selection. If the parent does not feed the next onChange result back into value, the checkmark and closed-field label do not update.",
            "Closing the popover resets the live query string, so reopening the field starts from the full option list again."
          ],
          "fields": [
            {
              "name": "options",
              "type": "ComboboxOption[]",
              "defaultValue": "",
              "required": true,
              "description": "Available rows rendered in display order and used as the filtering source."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Currently selected option value. The component derives the visible label entirely from this prop when the field is closed."
            },
            {
              "name": "onChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when a row is selected and also when the clear action resets the field to an empty string."
            },
            {
              "name": "placeholder",
              "type": "string",
              "defaultValue": "\"Select an option...\"",
              "required": false,
              "description": "Shown when no value is selected, and again while the field is open with an empty query."
            },
            {
              "name": "emptyMessage",
              "type": "string",
              "defaultValue": "\"No results found.\"",
              "required": false,
              "description": "Fallback copy rendered when filtering produces no matching options."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the outer relative wrapper so width or placement can be adjusted externally."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Disables the input, blocks opening, and applies a reduced-opacity presentation."
            },
            {
              "name": "clearable",
              "type": "boolean",
              "defaultValue": "true",
              "required": false,
              "description": "Controls whether the clear button appears once a value has been selected."
            }
          ]
        },
        {
          "id": "combobox-filtering",
          "title": "Filtering, keyboard, and layout behavior",
          "summary": "The dropdown is portaled to document.body, positioned from the field bounds, and supports a stronger keyboard model than the simpler select component.",
          "notes": [
            "Filtering is a case-insensitive substring match across label, value, and description.",
            "ArrowUp, ArrowDown, Home, End, Enter, Escape, and Tab are all handled directly on the input to drive the internal activeIndex and open state.",
            "The clear button uses tabIndex={-1}, so it is pointer-accessible but not keyboard reachable in this version.",
            "Because the list is rendered in a portal with fixed positioning, overflow-hidden ancestors do not clip it. Placement is recalculated from the trigger rect while it is open."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "context-menu",
      "name": "Context menu",
      "href": "/components/context-menu",
      "url": "https://iconiqui.com/components/context-menu",
      "installPackage": "@iconiq/context-menu",
      "installCommand": "npx shadcn@latest add @iconiq/context-menu",
      "registryPath": "context-menu.json",
      "registryUrl": "https://iconiqui.com/r/context-menu.json",
      "summary": "Stateful wrapper that listens for the native contextmenu event on a local surface and renders a fixed-position floating menu.",
      "apiSections": [
        {
          "id": "context-menu",
          "title": "ContextMenu",
          "summary": "Stateful wrapper that listens for the native contextmenu event on a local surface and renders a fixed-position floating menu.",
          "notes": [
            "Open state is fully internal. This implementation does not expose controlled open props, state callbacks, or an imperative API.",
            "The menu opens only from the native contextmenu event, then flips horizontally or vertically when the estimated panel would overflow the viewport.",
            "Closing is handled internally on outside mousedown, scroll, resize, and Escape. ArrowUp and ArrowDown move the highlighted row in local state, but DOM focus does not move into the menu."
          ],
          "fields": [
            {
              "name": "items",
              "type": "ContextMenuItem[]",
              "defaultValue": "",
              "required": true,
              "description": "Ordered list of menu rows. Each item defines its own label, optional icon and shortcut, click handler, and row state."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Content wrapped by the local context-click target. The component always renders a div around this content and attaches the right-click handler there."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the wrapper div that receives the native context menu event."
            },
            {
              "name": "menuClassName",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the floating motion.div for local surface styling overrides. The component still applies an inline width of 232px."
            }
          ]
        },
        {
          "id": "context-menu-item",
          "title": "ContextMenuItem",
          "summary": "Data shape used by the items prop to define each row in the menu.",
          "notes": [
            "Keyboard navigation skips disabled rows in both directions. If every row is disabled, the highlighted index stays unchanged."
          ],
          "fields": [
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Primary row copy rendered as the main action label."
            },
            {
              "name": "icon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Optional leading visual rendered inside the fixed 16px icon slot."
            },
            {
              "name": "shortcut",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional trailing helper text, typically a keyboard hint such as R or Cmd+D."
            },
            {
              "name": "onSelect",
              "type": "() => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when the row is activated with a click or Enter on the currently highlighted item."
            },
            {
              "name": "destructive",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Switches the row into the destructive color treatment and changes the active highlight tint."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Dims the row and blocks pointer and Enter selection."
            },
            {
              "name": "separatorAfter",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Inserts a thin divider after the row unless it is already the last rendered item."
            }
          ]
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "drawer",
      "name": "Drawer",
      "href": "/components/drawer",
      "url": "https://iconiqui.com/components/drawer",
      "installPackage": "@iconiq/drawer",
      "installCommand": "npx shadcn@latest add @iconiq/drawer",
      "registryPath": "drawer.json",
      "registryUrl": "https://iconiqui.com/r/drawer.json",
      "summary": "Single exported overlay drawer controlled entirely from parent state, with side-based panel motion and built-in body scroll locking.",
      "apiSections": [
        {
          "id": "drawer",
          "title": "Drawer",
          "summary": "Single exported overlay drawer controlled entirely from parent state, with side-based panel motion and built-in body scroll locking.",
          "notes": [
            "This component does not forward arbitrary DOM props or refs to the overlay or panel. The public API is limited to the controlled props above.",
            "While open, the component sets document.body.style.overflow to hidden and restores it during cleanup.",
            "There is no focus trap, portal primitive, or aria dialog wiring here, so this version is closer to a visual application drawer than a full modal accessibility primitive."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": true,
              "description": "Controls whether the drawer and overlay render at all. The component is fully controlled and does not keep its own open state."
            },
            {
              "name": "onClose",
              "type": "() => void",
              "defaultValue": "",
              "required": true,
              "description": "Called when the overlay is clicked or when Escape is pressed while the drawer is open."
            },
            {
              "name": "side",
              "type": "\"left\" | \"right\" | \"top\" | \"bottom\"",
              "defaultValue": "\"right\"",
              "required": false,
              "description": "Chooses the panel edge, slide direction, and the matching border placement from the internal panelVariants map."
            },
            {
              "name": "title",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Primary heading rendered in the header row. When omitted, the heading node still renders but stays empty."
            },
            {
              "name": "description",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Secondary helper line rendered under the title when present."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Content rendered inside the scrolling body area below the header."
            }
          ]
        },
        {
          "id": "drawer-motion-layout",
          "title": "Motion, layout, and close behavior",
          "summary": "The drawer uses a spring-driven panel plus staggered header and body children, with a softer duration fallback when reduced motion is enabled.",
          "notes": [
            "Top and bottom drawers cap their height at 80vh, while left and right drawers cap their width at max-w-md and otherwise fill the viewport edge-to-edge.",
            "A decorative top shimmer is always rendered inside the panel. Remove or restyle it locally if you want a flatter surface."
          ],
          "fields": [
            {
              "name": "overlay",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "A fixed full-screen overlay is always rendered behind the panel and closes the drawer on click."
            },
            {
              "name": "close button",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "The header always includes a close button with the Lucide X icon wired to onClose."
            }
          ]
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "dialog",
      "name": "Dialog",
      "href": "/components/dialog",
      "url": "https://iconiqui.com/components/dialog",
      "installPackage": "@iconiq/dialog",
      "installCommand": "npx shadcn@latest add @iconiq/dialog",
      "registryPath": "dialog.json",
      "registryUrl": "https://iconiqui.com/r/dialog.json",
      "summary": "Dialog, DialogTrigger, DialogClose, and DialogPortal are direct re-exports of the matching Radix dialog primitives.",
      "apiSections": [
        {
          "id": "dialog-root",
          "title": "Dialog",
          "summary": "Dialog, DialogTrigger, DialogClose, and DialogPortal are direct re-exports of the matching Radix dialog primitives.",
          "notes": [
            "Any remaining Dialog.Root props continue to work because the root export is the Radix primitive itself.",
            "Accessibility and focus-trap behavior come from Radix rather than additional wrapper logic here."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controlled open state on the Dialog root when you want the parent component to own visibility."
            },
            {
              "name": "defaultOpen",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Uncontrolled initial state forwarded to Radix Dialog.Root."
            },
            {
              "name": "onOpenChange",
              "type": "(open: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever Radix requests a state change through triggers, overlay clicks, or escape key handling."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Composition surface for the trigger, content, and any related dialog helpers."
            }
          ]
        },
        {
          "id": "dialog-content",
          "title": "DialogContent",
          "summary": "Motion-enhanced content wrapper built around DialogPrimitive.Content and AnimatePresence.",
          "notes": [
            "Accessibility props and Radix callbacks such as onEscapeKeyDown, onPointerDownOutside, aria-describedby, and aria-labelledby are forwarded to DialogPrimitive.Content.",
            "DialogContent always renders its own close button in the top-right corner using DialogPrimitive.Close and the Lucide X icon."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controls whether the animated portal branch renders at all. In practice this must mirror the root open state for the content to appear and exit correctly."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the inner motion panel rather than the full-screen DialogPrimitive.Content wrapper."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Content rendered inside the animated panel. Each direct child is wrapped in its own motion.div for staggered entry."
            }
          ]
        },
        {
          "id": "dialog-trigger-close",
          "title": "DialogTrigger and DialogClose",
          "summary": "These exports are direct Radix aliases used to open or close the dialog from any custom element.",
          "notes": [
            "Because both exports come directly from Radix, they also accept the remaining primitive props for event handling and accessibility wiring."
          ],
          "fields": [
            {
              "name": "asChild",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Lets you turn a custom button or link into the trigger or close control without adding an extra wrapper element."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Interactive content rendered by the trigger or close primitive."
            }
          ]
        },
        {
          "id": "dialog-layout-helpers",
          "title": "DialogHeader and DialogFooter",
          "summary": "Layout helpers used to structure dialog content without changing dialog behavior.",
          "notes": [
            "Both helpers accept the normal div HTML attribute surface in addition to className and children."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Content rendered inside the helper container."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the helper wrapper so spacing and alignment can be adjusted per dialog."
            }
          ]
        },
        {
          "id": "dialog-text-helpers",
          "title": "DialogTitle and DialogDescription",
          "summary": "Semantic text helpers that forward to the matching Radix title and description primitives.",
          "notes": [
            "Both helpers forward refs to the underlying Radix primitives."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Text or inline markup rendered inside the title or description primitive."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged with the default title or description styles."
            }
          ]
        }
      ],
      "dependencies": [
        "@radix-ui/react-dialog",
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "dropdown",
      "name": "Dropdown",
      "href": "/components/dropdown",
      "url": "https://iconiqui.com/components/dropdown",
      "installPackage": "@iconiq/dropdown",
      "installCommand": "npx shadcn@latest add @iconiq/dropdown",
      "registryPath": "dropdown.json",
      "registryUrl": "https://iconiqui.com/r/dropdown.json",
      "summary": "Root provider that coordinates open state, selected value state, and the shared behavior used by the trigger, content, and item primitives.",
      "apiSections": [
        {
          "id": "dropdown",
          "title": "Dropdown",
          "summary": "Root provider that coordinates open state, selected value state, and the shared behavior used by the trigger, content, and item primitives.",
          "notes": [
            "The menu stays local to the trigger wrapper and is absolutely positioned under it instead of being portaled to document.body.",
            "Escape and outside clicks close the menu. This version does not ship a full roving-focus keyboard model like Radix dropdown-menu."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Compose DropdownTrigger, DropdownContent, DropdownItem, and optional helpers like DropdownValue or DropdownSeparator inside the root."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Controlled selected value for the select variant. Action mode usually leaves this unset."
            },
            {
              "name": "defaultValue",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Initial selected value for uncontrolled select usage."
            },
            {
              "name": "onValueChange",
              "type": "(value: string | undefined) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when a select item updates the current value."
            },
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controlled open state for the menu surface."
            },
            {
              "name": "defaultOpen",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Initial open state for uncontrolled usage."
            },
            {
              "name": "onOpenChange",
              "type": "(open: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever the trigger, outside click handling, or Escape key changes the open state."
            },
            {
              "name": "variant",
              "type": "\"select\" | \"action\"",
              "defaultValue": "select",
              "required": false,
              "description": "Use select when items should commit a persistent value with a checkmark, or action when items should behave like immediate commands."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the outer relative wrapper around the trigger and content."
            }
          ]
        },
        {
          "id": "dropdown-trigger",
          "title": "DropdownTrigger",
          "summary": "Interactive trigger button that opens and closes the menu. It works with plain children, DropdownValue, or custom trigger content like an avatar.",
          "notes": [
            "The trigger is always rendered as a button in this version, so custom trigger visuals should be passed as children and styled with className."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Trigger content. In select mode this usually includes DropdownValue, while action menus can pass custom content such as an avatar or label row."
            },
            {
              "name": "showChevron",
              "type": "boolean",
              "defaultValue": "true",
              "required": false,
              "description": "Hides the default chevron when you want a cleaner custom trigger, such as an avatar-only action menu."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the trigger button shell."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Prevents opening and dims the trigger styling."
            }
          ]
        },
        {
          "id": "dropdown-value",
          "title": "DropdownValue",
          "summary": "Small helper for select mode that reads the current value from context and prints the matching item label or a placeholder.",
          "notes": [
            "DropdownValue is only useful in select mode. Action menus usually provide their own trigger content instead."
          ],
          "fields": [
            {
              "name": "placeholder",
              "type": "string",
              "defaultValue": "\"Select an option\"",
              "required": false,
              "description": "Text shown when no matching selected value is currently registered."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the rendered span inside the trigger."
            }
          ]
        },
        {
          "id": "dropdown-content",
          "title": "DropdownContent",
          "summary": "Animated menu surface that positions itself under the trigger and renders the item list for either variant.",
          "notes": [
            "The content stays mounted only while the menu is open, and its spring animation is handled internally."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Usually DropdownItem children, with optional DropdownSeparator nodes between groups."
            },
            {
              "name": "align",
              "type": "\"start\" | \"center\" | \"end\"",
              "defaultValue": "start",
              "required": false,
              "description": "Horizontal alignment relative to the trigger wrapper."
            },
            {
              "name": "sideOffset",
              "type": "number",
              "defaultValue": "8",
              "required": false,
              "description": "Vertical gap between the trigger and the dropdown surface."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the dropdown surface, which is useful for setting a custom width or changing shadows in docs/examples."
            }
          ]
        },
        {
          "id": "dropdown-item",
          "title": "DropdownItem",
          "summary": "Single interactive row used by both variants. In select mode it can register a value, and in action mode it acts like a plain command item.",
          "notes": [
            "Select items do not render a filled selected background in this version; only the trailing checkmark indicates the chosen value.",
            "If you omit value in select mode, the item behaves like a plain closing action and will not update the current value."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Row content. You can place icons inline before the label for action menus or richer item layouts."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Selection key for select mode. When it matches the root value, the item renders the checkmark state."
            },
            {
              "name": "textValue",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional explicit label used by DropdownValue when your item children are not plain text."
            },
            {
              "name": "onClick",
              "type": "(event: MouseEvent<HTMLButtonElement>) => void",
              "defaultValue": "",
              "required": false,
              "description": "Runs before the item closes the menu. Action menus typically use this for immediate commands like profile or logout."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Prevents interaction and dims the row."
            }
          ]
        },
        {
          "id": "dropdown-separator",
          "title": "DropdownSeparator",
          "summary": "Simple divider for grouping related items inside the content surface.",
          "notes": [
            "The base separator uses the shared border token and a small vertical margin between item groups."
          ],
          "fields": [
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the divider element when you want to adjust spacing or tone locally."
            }
          ]
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "file-upload",
      "name": "File upload",
      "href": "/components/file-upload",
      "url": "https://iconiqui.com/components/file-upload",
      "installPackage": "@iconiq/file-upload",
      "installCommand": "npx shadcn@latest add @iconiq/file-upload",
      "registryPath": "file-upload.json",
      "registryUrl": "https://iconiqui.com/r/file-upload.json",
      "summary": "Drag-and-drop uploader with an internal queue, hidden file input, keyboard-triggerable drop zone, and callback hooks for parent integrations.",
      "apiSections": [
        {
          "id": "file-upload",
          "title": "FileUpload",
          "summary": "Drag-and-drop uploader with an internal queue, hidden file input, keyboard-triggerable drop zone, and callback hooks for parent integrations.",
          "notes": [
            "The drop zone is keyboard accessible and opens the hidden file input on Enter or Space.",
            "Both drag-and-drop and click-to-browse flow through the same queue logic, so accept filtering and max file limits stay consistent."
          ],
          "fields": [
            {
              "name": "accept",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional accept string passed to the hidden file input and also enforced for dropped files, including MIME types like image/* and extensions like .pdf."
            },
            {
              "name": "multiple",
              "type": "boolean",
              "defaultValue": "true",
              "required": false,
              "description": "Allows selecting or dropping multiple files. When set to false, the next selection replaces the existing queue."
            },
            {
              "name": "maxFiles",
              "type": "number",
              "defaultValue": "",
              "required": false,
              "description": "Caps the queue length. New files are prepended, and anything beyond the limit is trimmed from the end."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Disables click, drag, keyboard activation, and the hidden file input without changing the component structure."
            },
            {
              "name": "name",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Passes a form field name through to the hidden file input for form integrations."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Adds classes to the outer wrapper without changing the component internals."
            },
            {
              "name": "onFilesChange",
              "type": "(files: File[]) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when files are added or removed from the queue. It does not fire on every progress tick."
            },
            {
              "name": "onFileRemove",
              "type": "(file: File, nextFiles: File[]) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called after a queued file is removed. The second argument contains the remaining files in queue order."
            },
            {
              "name": "onUploadComplete",
              "type": "(files: File[]) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called once the current queue reaches 100% completion for every item."
            }
          ]
        },
        {
          "id": "file-upload-behavior",
          "title": "Built-in behavior",
          "summary": "The component still owns its progress visuals and preview lifecycle, even when you attach callbacks from parent code.",
          "notes": [
            "Preview object URLs are cleaned up on remove and during component unmount.",
            "Each queue item id is built from the file name, file size, and a random suffix to reduce collisions between repeated uploads."
          ],
          "fields": [
            {
              "name": "progress state",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Each added file starts in an uploading state and advances through the built-in simulated progress loop until it reaches done."
            },
            {
              "name": "image previews",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Image files receive object URL previews and render as thumbnails; non-image files fall back to a file icon surface."
            },
            {
              "name": "remove action",
              "type": "built-in",
              "defaultValue": "",
              "required": false,
              "description": "Each queued file can be removed individually from the trailing action button, with preview URLs revoked immediately."
            }
          ]
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "hover-card",
      "name": "Hover card",
      "href": "/components/hover-card",
      "url": "https://iconiqui.com/components/hover-card",
      "installPackage": "@iconiq/hover-card",
      "installCommand": "npx shadcn@latest add @iconiq/hover-card",
      "registryPath": "hover-card.json",
      "registryUrl": "https://iconiqui.com/r/hover-card.json",
      "summary": "Stateful wrapper that opens and closes a local callout from pointer and focus events with configurable delays.",
      "apiSections": [
        {
          "id": "hover-card",
          "title": "HoverCard",
          "summary": "Stateful wrapper that opens and closes a local callout from pointer and focus events with configurable delays.",
          "notes": [
            "Open state is internal only. This implementation does not expose a controlled open prop or state-change callback.",
            "The root renders a relative inline-block span and attaches the hover and focus handlers there, so the content stays anchored to that local wrapper.",
            "Pending timers are cleared before every new open or close request and again during unmount cleanup."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Composition surface for the trigger and content primitives rendered inside the hover card root."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root span that anchors the trigger and positioned content."
            },
            {
              "name": "openDelay",
              "type": "number",
              "defaultValue": "80",
              "required": false,
              "description": "Delay in milliseconds before the card opens after pointer or focus entry."
            },
            {
              "name": "closeDelay",
              "type": "number",
              "defaultValue": "120",
              "required": false,
              "description": "Delay in milliseconds before the card closes after pointer or focus leaves the root."
            }
          ]
        },
        {
          "id": "hover-card-trigger",
          "title": "HoverCardTrigger",
          "summary": "Trigger surface that renders a button by default or forwards behavior into a custom child through Radix Slot.",
          "notes": [
            "When asChild is false, the component renders a plain button element. Pass type='button' yourself if you place it inside a form.",
            "Standard button props such as disabled, onClick, aria-*, and data-* are forwarded to the rendered trigger element."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Interactive content rendered by the trigger or by the child passed through asChild."
            },
            {
              "name": "asChild",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Lets you supply your own trigger element while keeping the hover-card trigger behavior and class merging."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the rendered trigger element for local layout or visual styling."
            }
          ]
        },
        {
          "id": "hover-card-content",
          "title": "HoverCardContent",
          "summary": "Animated content panel that appears below the trigger with a spring entrance and blur fade.",
          "notes": [
            "Additional motion.div props such as style, role, onClick, aria-*, and data-* are forwarded, but initial, animate, exit, and transition are reserved by the component.",
            "The panel is absolutely positioned relative to the root wrapper rather than portaled to document.body, so overflow-hidden ancestors can clip it.",
            "By default the content is centered below the trigger with mt-3 spacing and a fixed w-72 width."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Content rendered inside the hover card panel."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the motion.div panel so width, spacing, or surface styles can be adjusted."
            }
          ]
        }
      ],
      "dependencies": [
        "@radix-ui/react-slot",
        "motion"
      ]
    },
    {
      "slug": "input",
      "name": "Input",
      "href": "/components/input",
      "url": "https://iconiqui.com/components/input",
      "installPackage": "@iconiq/input",
      "installCommand": "npx shadcn@latest add @iconiq/input",
      "registryPath": "input.json",
      "registryUrl": "https://iconiqui.com/r/input.json",
      "summary": "Animated text input with a canonical Input export, always-visible label, and built-in helper behaviors for password, search, and email states.",
      "apiSections": [
        {
          "id": "input",
          "title": "Input",
          "summary": "Animated text input with a canonical Input export, always-visible label, and built-in helper behaviors for password, search, and email states.",
          "notes": [
            "Standard input attributes such as name, autoComplete, min, max, step, inputMode, onBlur, and onFocus are forwarded to the native input element.",
            "Your onBlur and onFocus callbacks still run after the component updates its internal focused and validation state.",
            "The visible character animation is driven by an overlay display, but editing still happens through a real native input element."
          ],
          "fields": [
            {
              "name": "label",
              "type": "string",
              "defaultValue": "Type here",
              "required": false,
              "description": "Always-visible label rendered above the field and linked to the input via htmlFor."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Controlled string value. When omitted, the component uses local state starting from an empty string."
            },
            {
              "name": "onChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Receives the next string value rather than the original change event."
            },
            {
              "name": "type",
              "type": "HTMLInputTypeAttribute",
              "defaultValue": "text",
              "required": false,
              "description": "Native input type used by the underlying input element. Password, email, search, and number each trigger additional local behavior."
            },
            {
              "name": "placeholder",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Forwarded to the native input so empty fields can show guidance copy before any text is entered."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Disables the native input and also turns off interactive affordances such as the password toggle and search clear button."
            },
            {
              "name": "readOnly",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Leaves the field focusable but disables local editing affordances and prevents value changes through the built-in helper actions."
            },
            {
              "name": "id",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional input id. If you leave it out, the component creates one with useId."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Applied to the outer wrapper that contains both the label and the animated input shell."
            }
          ]
        },
        {
          "id": "input-type-specific",
          "title": "Type-specific behavior",
          "summary": "Several input types ship with extra runtime behavior beyond what the browser gives you for free.",
          "notes": [
            "Password inputs add a local show-or-hide toggle that flips the input type between password and text.",
            "Search inputs render a clear button whenever the field is interactive and the current value is non-empty.",
            "Email inputs apply a built-in pattern, title, and aria-invalid flag. The destructive border only appears after blur when the field is non-empty and fails the regex.",
            "Number, password, and search inputs opt out of overflow clipping so their trailing affordances can sit outside the text flow cleanly."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "input-group",
      "name": "Input group",
      "href": "/components/input-group",
      "url": "https://iconiqui.com/components/input-group",
      "installPackage": "@iconiq/input-group",
      "installCommand": "npx shadcn@latest add @iconiq/input-group",
      "registryPath": "input-group.json",
      "registryUrl": "https://iconiqui.com/r/input-group.json",
      "summary": "Floating-label input field with optional prefix and suffix slots, a center-out focus rule, and inline error messaging driven entirely by props.",
      "apiSections": [
        {
          "id": "inputgroups",
          "title": "Inputgroups",
          "summary": "Floating-label input field with optional prefix and suffix slots, a center-out focus rule, and inline error messaging driven entirely by props.",
          "notes": [
            "Standard native input props such as name, type, value, defaultValue, placeholder, autoComplete, disabled, onFocus, onBlur, and onChange are forwarded directly to the underlying input element.",
            "The outer field shell uses a mouse-down focus handoff, so clicking the label area or prefix icon focuses the input without stealing focus from the suffix button.",
            "Value presence is tracked from the real input value, which keeps the floating label aligned for both controlled and uncontrolled usage."
          ],
          "fields": [
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Field label rendered inside the shell until the input becomes focused or contains a value, then animated upward into its floating position."
            },
            {
              "name": "error",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional validation message rendered below the field. Passing a value also switches the label and underline to the destructive palette."
            },
            {
              "name": "prefixIcon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Leading visual placed before the input area. The slot is best suited to compact icons and inherits the same focus-aware color shift as the suffix."
            },
            {
              "name": "suffixIcon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Optional trailing visual rendered inside a button, which makes it useful for actions such as show-password toggles or clear controls."
            },
            {
              "name": "onSuffixClick",
              "type": "() => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when the suffix button is pressed. It only matters when suffixIcon is also present."
            },
            {
              "name": "id",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional input id. When omitted, the component creates one with useId and links the animated label automatically."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Applied to the native input element itself, not the outer shell. Use it for text sizing, placeholder, or input-level spacing overrides."
            }
          ]
        },
        {
          "id": "input-group",
          "title": "InputGroup",
          "summary": "Lightweight vertical wrapper for stacking multiple Inputgroups fields with consistent spacing.",
          "notes": [
            "All remaining div props are forwarded to the wrapper, so data attributes and layout helpers can be attached at the group level."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "One or more Inputgroups fields, or any other custom content you want to keep in the same vertical flow."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the wrapper div. The base layout already uses a full-width flex column with a 1.5rem gap."
            }
          ]
        },
        {
          "id": "input-group-motion",
          "title": "Motion and behavior",
          "summary": "The component keeps its motion treatment quiet and focused on form affordances rather than full-panel animation.",
          "notes": [
            "The label, prefix slot, and suffix slot all use spring transitions, so they respond to focus without introducing layout shift.",
            "The bottom accent line grows from the center outward only while the field is focused, then collapses away on blur.",
            "Error copy mounts and exits through AnimatePresence, which keeps the spacing stable while still giving feedback a small fade-and-rise transition."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "popover",
      "name": "Popover",
      "href": "/components/popover",
      "url": "https://iconiqui.com/components/popover",
      "installPackage": "@iconiq/popover",
      "installCommand": "npx shadcn@latest add @iconiq/popover",
      "registryPath": "popover.json",
      "registryUrl": "https://iconiqui.com/r/popover.json",
      "summary": "Standard shadcn-style export: `Popover` is `PopoverPrimitive.Root`, matching what the CLI installs into `components/ui/popover`.",
      "apiSections": [
        {
          "id": "popover-root",
          "title": "Popover",
          "summary": "Standard shadcn-style export: `Popover` is `PopoverPrimitive.Root`, matching what the CLI installs into `components/ui/popover`.",
          "notes": [
            "Any remaining root props continue to work because the export is the Radix primitive itself."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controlled open state on the Radix root when you want React state to own visibility."
            },
            {
              "name": "defaultOpen",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Uncontrolled initial state forwarded to the underlying Radix popover root."
            },
            {
              "name": "onOpenChange",
              "type": "(open: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever Radix requests a state change through the trigger, outside interaction, or escape handling."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Composition surface for the trigger, optional anchor, and content primitives."
            }
          ]
        },
        {
          "id": "popover-trigger",
          "title": "PopoverTrigger and PopoverAnchor",
          "summary": "These exports are direct aliases of the matching Radix primitives and are used to open the popover or redefine its positioning anchor.",
          "notes": [
            "Because both exports come directly from Radix, they also accept the remaining primitive props for event handling and accessibility wiring."
          ],
          "fields": [
            {
              "name": "asChild",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Lets you render your own button, link, or wrapper element without adding an extra DOM node."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Interactive or layout content rendered by the trigger or anchor primitive."
            }
          ]
        },
        {
          "id": "popover-content",
          "title": "PopoverContent",
          "summary": "Animated content wrapper built on Radix Popover.Content and AnimatePresence.",
          "notes": [
            "Remaining Radix content props are forwarded through to PopoverPrimitive.Content, including side, collisionPadding, onEscapeKeyDown, and accessibility props.",
            "The component always renders inside a Radix portal and uses the Radix transform-origin CSS variable so the motion scales from the resolved placement.",
            "Entry and exit animation are owned internally, so initial, animate, exit, and transition are not part of the public prop surface."
          ],
          "fields": [
            {
              "name": "open",
              "type": "boolean",
              "defaultValue": "",
              "required": true,
              "description": "Controls whether the animated portal branch renders at all. In practice this must mirror the root open state for entry and exit motion to run correctly."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Content rendered inside the animated panel."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the motion.div panel for local width, spacing, or surface overrides."
            },
            {
              "name": "align",
              "type": "\"start\" | \"center\" | \"end\"",
              "defaultValue": "center",
              "required": false,
              "description": "Forwarded to Radix Popover.Content to control horizontal alignment relative to the trigger or anchor."
            },
            {
              "name": "sideOffset",
              "type": "number",
              "defaultValue": "8",
              "required": false,
              "description": "Forwarded to Radix Popover.Content to control the gap between the anchor and the floating panel."
            }
          ]
        }
      ],
      "dependencies": [
        "@radix-ui/react-popover",
        "motion"
      ]
    },
    {
      "slug": "checkbox-group",
      "name": "Checkbox group",
      "href": "/components/checkbox-group",
      "url": "https://iconiqui.com/components/checkbox-group",
      "installPackage": "@iconiq/checkbox-group",
      "installCommand": "npx shadcn@latest add @iconiq/checkbox-group",
      "registryPath": "checkbox-group.json",
      "registryUrl": "https://iconiqui.com/r/checkbox-group.json",
      "summary": "Each option row is described with a plain object and rendered as an animated button.",
      "apiSections": [
        {
          "id": "checkbox-option",
          "title": "CheckboxGroupOption",
          "summary": "Each option row is described with a plain object and rendered as an animated button.",
          "notes": [],
          "fields": [
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Primary copy shown for the row."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Stable identifier used when checking whether the row is selected and when producing the next selection array."
            },
            {
              "name": "description",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional secondary text rendered below the label."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Disables the row button and blocks hover, active, and toggle behavior for that option."
            }
          ]
        },
        {
          "id": "checkbox-group",
          "title": "CheckboxGroup",
          "summary": "Animated multi-select list whose checked state is derived entirely from the value prop.",
          "notes": [
            "If you want the UI to update when a user clicks, you must feed the next array from onChange back into value. Without that parent state loop, the component stays visually unchanged.",
            "Rows are buttons, not native checkbox inputs, so form submission and checkbox semantics are not provided out of the box."
          ],
          "fields": [
            {
              "name": "options",
              "type": "CheckboxGroupOption[]",
              "defaultValue": "",
              "required": true,
              "description": "Array of rows to render, in display order."
            },
            {
              "name": "value",
              "type": "string[]",
              "defaultValue": "[]",
              "required": false,
              "description": "Current selected values. The component does not keep internal selection state beyond this prop."
            },
            {
              "name": "onChange",
              "type": "(value: string[]) => void",
              "defaultValue": "",
              "required": false,
              "description": "Receives the next selected values array after a row is toggled."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root flex column wrapper."
            }
          ]
        },
        {
          "id": "checkbox-motion",
          "title": "Motion and accessibility",
          "summary": "The component leans on hover surfaces and AnimatePresence rather than native checkbox UI.",
          "notes": [
            "Selection is represented by a Lucide Check icon instead of a filled checkbox background.",
            "There is no role='group', role='checkbox', or aria-checked wiring in this version, so accessibility-sensitive forms should add hidden inputs or extend the component."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "radiogroup",
      "name": "Radio group",
      "href": "/components/radiogroup",
      "url": "https://iconiqui.com/components/radiogroup",
      "installPackage": "@iconiq/radiogroup",
      "installCommand": "npx shadcn@latest add @iconiq/radiogroup",
      "registryPath": "radiogroup.json",
      "registryUrl": "https://iconiqui.com/r/radiogroup.json",
      "summary": "Options are plain objects consumed by the RadioGroup component.",
      "apiSections": [
        {
          "id": "radio-option",
          "title": "Radio option shape",
          "summary": "Options are plain objects consumed by the RadioGroup component.",
          "notes": [],
          "fields": [
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Unique identifier for the option and the selected value reported through onChange."
            },
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Primary line shown for the option."
            },
            {
              "name": "description",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional secondary line shown below the label with reduced emphasis."
            }
          ]
        },
        {
          "id": "radio-group",
          "title": "RadioGroup",
          "summary": "Single-choice selector that can run uncontrolled from local state or sync to a controlled value prop.",
          "notes": [
            "If options is empty, the uncontrolled initial state becomes undefined and no row is selected.",
            "Controlled updates are synced through an effect that watches the value prop."
          ],
          "fields": [
            {
              "name": "options",
              "type": "{ value: string; label: string; description?: string }[]",
              "defaultValue": "",
              "required": true,
              "description": "Available choices in display order."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Controlled selected value. If omitted, the component starts from the first option's value and manages selection locally."
            },
            {
              "name": "onChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever a user selects a row."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root wrapper for spacing or sizing adjustments."
            }
          ]
        },
        {
          "id": "radio-motion-a11y",
          "title": "Motion and accessibility",
          "summary": "The component layers Motion over custom radio semantics rather than using native input[type=radio].",
          "notes": [
            "The active background uses a fixed layoutId of radio-active-bg. Multiple RadioGroup instances on the same screen can therefore share cross-layout animation unless you fork the implementation.",
            "The root sets role='radiogroup' and each row sets role='radio' plus aria-checked, but there is no arrow-key navigation or roving tabindex behavior."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "select",
      "name": "Select",
      "href": "/components/select",
      "url": "https://iconiqui.com/components/select",
      "installPackage": "@iconiq/select",
      "installCommand": "npx shadcn@latest add @iconiq/select",
      "registryPath": "select.json",
      "registryUrl": "https://iconiqui.com/r/select.json",
      "summary": "Each selectable row is described by a plain object and rendered inside a portaled menu.",
      "apiSections": [
        {
          "id": "select-option",
          "title": "Option shape",
          "summary": "Each selectable row is described by a plain object and rendered inside a portaled menu.",
          "notes": [],
          "fields": [
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Stable identifier used to determine the selected option and what onChange returns."
            },
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Text shown in both the trigger and the dropdown row."
            },
            {
              "name": "icon",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Optional leading visual shown only inside the dropdown row."
            }
          ]
        },
        {
          "id": "select",
          "title": "Select",
          "summary": "Animated single-select dropdown that manages open state internally but expects the selected value to come from the parent.",
          "notes": [
            "This implementation is effectively controlled for selection. If you call onChange without updating value, the visible selection and checkmark do not move.",
            "The public API still does not expose disabled or native form props."
          ],
          "fields": [
            {
              "name": "options",
              "type": "{ value: string; label: string; icon?: ReactNode }[]",
              "defaultValue": "",
              "required": true,
              "description": "Rows available in the dropdown menu."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Currently selected option value. The component derives its selected label entirely from this prop."
            },
            {
              "name": "onChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when a row is chosen. The menu closes immediately afterward."
            },
            {
              "name": "placeholder",
              "type": "string",
              "defaultValue": "Select an option…",
              "required": false,
              "description": "Fallback trigger text shown when no option matches the current value."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the outer wrapper so width and placement can be adjusted without editing the component source."
            }
          ]
        },
        {
          "id": "select-overlay",
          "title": "Overlay and interaction behavior",
          "summary": "The dropdown menu is portaled to document.body and continuously repositioned while open.",
          "notes": [
            "A mounted flag prevents createPortal from running during the initial server render.",
            "useLayoutEffect starts a requestAnimationFrame loop that keeps the menu aligned to the trigger while the viewport changes.",
            "Clicking outside the trigger and menu closes the dropdown. There is no keyboard list navigation or highlighted-option state in this version."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "slider",
      "name": "Slider",
      "href": "/components/slider",
      "url": "https://iconiqui.com/components/slider",
      "installPackage": "@iconiq/slider",
      "installCommand": "npx shadcn@latest add @iconiq/slider",
      "registryPath": "slider.json",
      "registryUrl": "https://iconiqui.com/r/slider.json",
      "summary": "Pointer-driven range control with optional controlled or uncontrolled value management.",
      "apiSections": [
        {
          "id": "slider",
          "title": "Slider",
          "summary": "Pointer-driven range control with optional controlled or uncontrolled value management.",
          "notes": [
            "When value is undefined, the component stores the current value internally and updates it during drag operations.",
            "The displayed number is derived from the animated motion value, so the readout stays in sync with the spring animation rather than jumping immediately."
          ],
          "fields": [
            {
              "name": "value",
              "type": "number",
              "defaultValue": "",
              "required": false,
              "description": "Controlled value. When provided, the parent owns the current position."
            },
            {
              "name": "defaultValue",
              "type": "number",
              "defaultValue": "50",
              "required": false,
              "description": "Initial internal value used when value is not supplied."
            },
            {
              "name": "min",
              "type": "number",
              "defaultValue": "0",
              "required": false,
              "description": "Lower bound used for clamping and display mapping."
            },
            {
              "name": "max",
              "type": "number",
              "defaultValue": "100",
              "required": false,
              "description": "Upper bound used for clamping and display mapping."
            },
            {
              "name": "step",
              "type": "number",
              "defaultValue": "1",
              "required": false,
              "description": "Step size applied after translating pointer position into a raw numeric value."
            },
            {
              "name": "onChange",
              "type": "(value: number) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called whenever pointer interaction computes a new clamped value."
            },
            {
              "name": "showValue",
              "type": "boolean",
              "defaultValue": "true",
              "required": false,
              "description": "Controls whether the live numeric readout is shown on the right side of the label row."
            },
            {
              "name": "label",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Optional label shown on the left side of the header row above the track."
            }
          ]
        },
        {
          "id": "slider-interaction",
          "title": "Interaction model",
          "summary": "Slider is currently optimized for pointer interaction only.",
          "notes": [
            "The thumb and filled track animate with springs whenever the current value changes.",
            "Pointer capture is taken on pointer down and released on pointer up or cancel, which keeps dragging stable even when the pointer leaves the track.",
            "This version does not expose keyboard controls, role='slider', or aria-valuenow, so accessible form-heavy experiences should wrap or extend it."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "spinner",
      "name": "Spinner",
      "href": "/components/spinner",
      "url": "https://iconiqui.com/components/spinner",
      "installPackage": "@iconiq/spinner",
      "installCommand": "npx shadcn@latest add @iconiq/spinner",
      "registryPath": "spinner.json",
      "registryUrl": "https://iconiqui.com/r/spinner.json",
      "summary": "Default export for a lightweight loading indicator built around an output element.",
      "apiSections": [
        {
          "id": "spinner",
          "title": "Spinner",
          "summary": "Default export for a lightweight loading indicator built around an output element.",
          "notes": [
            "The ring variant uses motion.create('output'), while the dots variant renders a plain output element with animated child spans.",
            "Both variants ship with aria-label=\"Loading\" and aria-live=\"polite\". The component does not currently accept a custom accessible label prop.",
            "No additional DOM props are forwarded beyond className, so wrap the component if you need extra data attributes or inline event handlers."
          ],
          "fields": [
            {
              "name": "variant",
              "type": "\"ring\" | \"dots\"",
              "defaultValue": "ring",
              "required": false,
              "description": "Chooses between the rotating circular border and the three bouncing dots treatment."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root output element so you can resize or recolor the spinner with Tailwind utilities."
            }
          ]
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "switch",
      "name": "Switch",
      "href": "/components/switch",
      "url": "https://iconiqui.com/components/switch",
      "installPackage": "@iconiq/switch",
      "installCommand": "npx shadcn@latest add @iconiq/switch",
      "registryPath": "switch.json",
      "registryUrl": "https://iconiqui.com/r/switch.json",
      "summary": "Animated switch with a canonical Switch export. Checked state is expected to come from the parent.",
      "apiSections": [
        {
          "id": "switch",
          "title": "Switch",
          "summary": "Animated switch with a canonical Switch export. Checked state is expected to come from the parent.",
          "notes": [
            "This implementation does not keep its own checked state. If you do not update checked in response to onCheckedChange, the visual state stays where it was.",
            "A forwarded ref points at the native button element."
          ],
          "fields": [
            {
              "name": "checked",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Current switch state shown by the thumb position and track color."
            },
            {
              "name": "onCheckedChange",
              "type": "(checked: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called with the next boolean when the button is clicked."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Disables the native button and dims the control."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root button for placement and sizing overrides."
            },
            {
              "name": "size",
              "type": "\"sm\" | \"md\" | \"lg\"",
              "defaultValue": "md",
              "required": false,
              "description": "Selects the track dimensions, thumb size, and travel distance from the internal SWITCH_LAYOUT map."
            }
          ]
        },
        {
          "id": "switch-size-motion",
          "title": "Size and motion behavior",
          "summary": "The switch uses separate transitions for the track and the thumb so the two parts settle at slightly different tempos.",
          "notes": [
            "sm uses a 42x22 track with 20px of thumb travel, md uses 52x30 with 22px travel, and lg uses 64x36 with 28px travel.",
            "Reduced-motion mode switches the thumb to a short tween and shortens the track color transition.",
            "The button carries role='switch' and aria-checked, plus focus-visible ring styles for keyboard users."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "table",
      "name": "Table",
      "href": "/components/table",
      "url": "https://iconiqui.com/components/table",
      "installPackage": "@iconiq/table",
      "installCommand": "npx shadcn@latest add @iconiq/table",
      "registryPath": "table.json",
      "registryUrl": "https://iconiqui.com/r/table.json",
      "summary": "Root provider for the animated table primitives. It sets the shared column template so header and body rows stay aligned.",
      "apiSections": [
        {
          "id": "table",
          "title": "Table",
          "summary": "Root provider for the animated table primitives. It sets the shared column template so header and body rows stay aligned.",
          "notes": [
            "The canonical JSX export is `Table`, and the lowercase `table` alias still ships for backward compatibility.",
            "The file also exports `TABLE_DEFAULT_COLUMNS`, `TableAlign`, `TableRowVariant`, and `TableSortDirection` for stronger TypeScript reuse in app code.",
            "The registry component no longer owns demo data, search state, or add/remove actions. Those behaviors are expected to live in app code.",
            "Semantic roles default to a div-based table structure (`table`, `rowgroup`, `row`, `columnheader`, and `cell`) so the installed primitive is more accessible without changing the visual layout."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Compose TableHeader, TableBody, TableRow, TableHead, TableCell, and optional helper primitives inside the root."
            },
            {
              "name": "columns",
              "type": "string",
              "defaultValue": "\"minmax(0,1.4fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1fr)\"",
              "required": false,
              "description": "Shared grid-template-columns value applied to every header and body row so custom layouts stay aligned."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root wrapper when you need to adjust width, spacing, or placement."
            }
          ]
        },
        {
          "id": "table-toolbar",
          "title": "TableToolbar",
          "summary": "Optional layout helper for the control row above the table, matching the original spacing and alignment treatment.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Usually a search field, actions, filters, or bulk controls placed above the table."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the toolbar wrapper."
            }
          ]
        },
        {
          "id": "table-header",
          "title": "TableHeader",
          "summary": "Top shell for the header area. It preserves the original top border before the first row.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Usually one header TableRow."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the header wrapper."
            }
          ]
        },
        {
          "id": "table-body",
          "title": "TableBody",
          "summary": "Body wrapper that adds LayoutGroup and AnimatePresence so row insertions, removals, and reordering stay animated.",
          "notes": [
            "Exit animations for removed rows only run when body rows are rendered as direct children of TableBody."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "One or more TableRow elements, plus optional TableEmpty when no rows are visible."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the body wrapper."
            }
          ]
        },
        {
          "id": "table-row",
          "title": "TableRow",
          "summary": "Motion-enabled row primitive used for both header and body layouts.",
          "notes": [
            "Every row reads the shared columns string from Table and applies it as grid-template-columns.",
            "Rows expose `data-slot=\"table-row\"`, plus `data-variant` and `data-hoverable`, which makes local styling overrides easier after installation."
          ],
          "fields": [
            {
              "name": "variant",
              "type": "\"header\" | \"body\"",
              "defaultValue": "body",
              "required": false,
              "description": "Header rows skip mount and exit motion, while body rows use the original spring-based row transitions."
            },
            {
              "name": "index",
              "type": "number",
              "defaultValue": "0",
              "required": false,
              "description": "Optional row index used to apply a subtle stagger to body row entry motion."
            },
            {
              "name": "hoverable",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "When true, body rows keep the original muted hover wash. Defaults to false for header rows and true for body rows."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the row shell for spacing or color overrides."
            },
            {
              "name": "Motion div props",
              "type": "ComponentPropsWithoutRef<typeof motion.div>",
              "defaultValue": "",
              "required": false,
              "description": "Additional motion.div props such as layout, transition, whileHover, and exit can still be passed directly."
            }
          ]
        },
        {
          "id": "table-head",
          "title": "TableHead",
          "summary": "Header cell wrapper for labels, sort buttons, and right-aligned controls.",
          "notes": [],
          "fields": [
            {
              "name": "align",
              "type": "\"left\" | \"right\"",
              "defaultValue": "left",
              "required": false,
              "description": "Controls left or right alignment for the header cell content."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Header label or a custom control such as TableSortButton."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the header cell wrapper."
            }
          ]
        },
        {
          "id": "table-cell",
          "title": "TableCell",
          "summary": "Body cell wrapper for row content, status pills, numeric values, and row actions.",
          "notes": [],
          "fields": [
            {
              "name": "align",
              "type": "\"left\" | \"right\"",
              "defaultValue": "left",
              "required": false,
              "description": "Controls left or right alignment for the cell content."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Rendered cell content."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the cell wrapper."
            }
          ]
        },
        {
          "id": "table-caption",
          "title": "TableCaption",
          "summary": "Low-emphasis caption line below the table, matching the original entry count styling.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Caption copy, summary text, or count information."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the caption paragraph."
            }
          ]
        },
        {
          "id": "table-empty",
          "title": "TableEmpty",
          "summary": "Animated empty-state block for zero-result or no-data states inside TableBody.",
          "notes": [],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Empty-state copy or a richer no-data message."
            },
            {
              "name": "Motion div props",
              "type": "ComponentPropsWithoutRef<typeof motion.div>",
              "defaultValue": "",
              "required": false,
              "description": "You can still override animate, initial, transition, or className when customizing the empty state."
            }
          ]
        },
        {
          "id": "table-sort-button",
          "title": "TableSortButton",
          "summary": "Optional header helper that preserves the original label-plus-chevron treatment and direction animation.",
          "notes": [
            "The helper defaults to type='button', so it stays safe inside forms."
          ],
          "fields": [
            {
              "name": "active",
              "type": "boolean",
              "defaultValue": "false",
              "required": false,
              "description": "Strengthens the icon opacity and enables the active sort direction treatment."
            },
            {
              "name": "direction",
              "type": "\"asc\" | \"desc\"",
              "defaultValue": "asc",
              "required": false,
              "description": "Rotates the chevron when the current active sort direction is descending."
            },
            {
              "name": "align",
              "type": "\"left\" | \"right\"",
              "defaultValue": "left",
              "required": false,
              "description": "Keeps the sort button aligned with the header cell it lives in."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Visible sort label."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the button wrapper."
            }
          ]
        }
      ],
      "dependencies": [
        "motion",
        "lucide-react"
      ]
    },
    {
      "slug": "tabs",
      "name": "Tabs",
      "href": "/components/tabs",
      "url": "https://iconiqui.com/components/tabs",
      "installPackage": "@iconiq/tabs",
      "installCommand": "npx shadcn@latest add @iconiq/tabs",
      "registryPath": "tabs.json",
      "registryUrl": "https://iconiqui.com/r/tabs.json",
      "summary": "Root provider that manages the active value, hover state, trigger measurements, and the shared animated panel transition.",
      "apiSections": [
        {
          "id": "tabs",
          "title": "Tabs",
          "summary": "Root provider that manages the active value, hover state, trigger measurements, and the shared animated panel transition.",
          "notes": [
            "Underline positions are still measured from the live trigger layout, so the indicators follow the actual tab widths instead of fixed columns.",
            "The root renders the active panel through a single AnimatePresence block, which preserves the existing blur-and-slide motion while exposing a composable API."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Compose TabsList, TabsTrigger, and TabsContent inside the root, following the same structure people expect from shadcn tabs."
            },
            {
              "name": "defaultValue",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Initial active tab value for uncontrolled usage. When omitted, the first TabsContent value becomes active."
            },
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Controlled active tab value."
            },
            {
              "name": "onValueChange",
              "type": "(value: string) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called when a trigger changes the active tab through click or keyboard navigation."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the root wrapper around the tab rail and the animated content area."
            }
          ]
        },
        {
          "id": "tabs-list",
          "title": "TabsList",
          "summary": "Measured trigger rail that renders the active underline and hover ghost underline without changing the visual design of the original component.",
          "notes": [
            "The list sets role='tablist' and clears the hover underline when the pointer leaves the rail."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": false,
              "description": "Usually a row of TabsTrigger elements."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the inline-flex rail around the triggers."
            }
          ]
        },
        {
          "id": "tabs-trigger",
          "title": "TabsTrigger",
          "summary": "Native button trigger that drives the active tab, hover state, keyboard navigation, and underline measurements.",
          "notes": [
            "Arrow keys, Home, and End move between enabled triggers and also activate the next tab so the panel transition stays in sync with focus.",
            "Trigger color treatment is unchanged from the original component: active tabs are strongest, hover/focus tabs are mid-tone, and inactive tabs stay quieter."
          ],
          "fields": [
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Unique tab identifier used for active state, focus movement, and content matching."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Label content rendered inside the trigger button."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the trigger button if you need local spacing or typography overrides."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Prevents the trigger from receiving focus or changing the active tab."
            }
          ]
        },
        {
          "id": "tabs-content",
          "title": "TabsContent",
          "summary": "Declarative content marker for a single tab panel. The root reads these nodes and renders the active one through the shared animated content shell.",
          "notes": [
            "TabsContent itself does not render in place. It acts as a declarative child so the root can preserve a single shared animated panel area below the list.",
            "Place TabsContent as direct or nested children of Tabs; the root collects them recursively before picking the active panel.",
            "Standard div attributes such as data-*, aria-*, id, style, and event handlers are forwarded to the rendered active panel."
          ],
          "fields": [
            {
              "name": "value",
              "type": "string",
              "defaultValue": "",
              "required": true,
              "description": "Matches the corresponding TabsTrigger value."
            },
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Panel body shown when the content value is active."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the animated panel wrapper for the active content only."
            }
          ]
        }
      ],
      "dependencies": [
        "motion"
      ]
    },
    {
      "slug": "toggle",
      "name": "Toggle",
      "href": "/components/toggle",
      "url": "https://iconiqui.com/components/toggle",
      "installPackage": "@iconiq/toggle",
      "installCommand": "npx shadcn@latest add @iconiq/toggle",
      "registryPath": "toggle.json",
      "registryUrl": "https://iconiqui.com/r/toggle.json",
      "summary": "Single pressed-state toggle built on Radix Toggle, with Motion-driven button, icon, and ripple feedback layered over shadcn-style size and variant classes.",
      "apiSections": [
        {
          "id": "toggle",
          "title": "Toggle",
          "summary": "Single pressed-state toggle built on Radix Toggle, with Motion-driven button, icon, and ripple feedback layered over shadcn-style size and variant classes.",
          "notes": [
            "Additional Radix toggle button props such as aria-label, name, value, and type are forwarded through the underlying Toggle.Root surface.",
            "The component renders Radix Root with asChild internally, then supplies its own motion.button as the child node."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Visible button content rendered inside the animated inner span. This can be plain text, icons, or both."
            },
            {
              "name": "variant",
              "type": "\"default\" | \"outline\"",
              "defaultValue": "default",
              "required": false,
              "description": "Visual treatment from the internal CVA config. Outline adds the input border and shadow-sm treatment."
            },
            {
              "name": "size",
              "type": "\"default\" | \"sm\" | \"lg\"",
              "defaultValue": "default",
              "required": false,
              "description": "Height and horizontal padding preset applied through the shared toggleVariants helper."
            },
            {
              "name": "pressed",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Controlled pressed state from Radix Toggle.Root. Use this when the parent should own the on/off state."
            },
            {
              "name": "defaultPressed",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Initial pressed state for uncontrolled usage."
            },
            {
              "name": "onPressedChange",
              "type": "(pressed: boolean) => void",
              "defaultValue": "",
              "required": false,
              "description": "Called after the component kicks off its local motion sequence, with the next pressed value from Radix."
            },
            {
              "name": "disabled",
              "type": "boolean",
              "defaultValue": "",
              "required": false,
              "description": "Disables the toggle and prevents the hover, tap, and pressed-state interaction flow."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the rendered motion button for local spacing or surface overrides."
            }
          ]
        },
        {
          "id": "toggle-motion",
          "title": "Motion and state behavior",
          "summary": "Every pressed change triggers a button squash, a center ripple, and a separate icon animation sequence.",
          "notes": [
            "The outer button uses useAnimationControls so the pressed sequence can run immediately whenever Radix reports a state change.",
            "The icon motion differs between on and off transitions, so enabling and disabling the toggle do not feel identical.",
            "Hover always applies a slight upward lift and tap applies an extra scale-down, independent of whether the toggle is currently pressed."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "@radix-ui/react-toggle",
        "class-variance-authority",
        "motion"
      ]
    },
    {
      "slug": "tooltip",
      "name": "Tooltip",
      "href": "/components/tooltip",
      "url": "https://iconiqui.com/components/tooltip",
      "installPackage": "@iconiq/tooltip",
      "installCommand": "npx shadcn@latest add @iconiq/tooltip",
      "registryPath": "tooltip.json",
      "registryUrl": "https://iconiqui.com/r/tooltip.json",
      "summary": "Animated tooltip with a canonical Tooltip export. It owns its own open state and toggles in response to hover and focus events.",
      "apiSections": [
        {
          "id": "tooltip",
          "title": "Tooltip",
          "summary": "Animated tooltip with a canonical Tooltip export. It owns its own open state and toggles in response to hover and focus events.",
          "notes": [
            "The trigger wrapper handles onMouseEnter, onMouseLeave, onFocus, and onBlur. No trigger props are forwarded directly to the child node.",
            "The timeout used for delayed open is cleared on leave and again on unmount."
          ],
          "fields": [
            {
              "name": "children",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Trigger content wrapped in a relative inline-flex container."
            },
            {
              "name": "content",
              "type": "ReactNode",
              "defaultValue": "",
              "required": true,
              "description": "Tooltip body rendered inside the animated bubble."
            },
            {
              "name": "side",
              "type": "\"top\" | \"bottom\" | \"left\" | \"right\"",
              "defaultValue": "top",
              "required": false,
              "description": "Controls which positioning class and directional motion offset are used."
            },
            {
              "name": "delay",
              "type": "number",
              "defaultValue": "0.15",
              "required": false,
              "description": "Open delay in seconds. The implementation multiplies it by 1000 before scheduling the timer."
            },
            {
              "name": "className",
              "type": "string",
              "defaultValue": "",
              "required": false,
              "description": "Merged onto the tooltip bubble for local surface styling overrides."
            }
          ]
        },
        {
          "id": "tooltip-positioning",
          "title": "Positioning and accessibility",
          "summary": "This tooltip is absolutely positioned inside its local wrapper rather than portaled to the document body.",
          "notes": [
            "Because the bubble is not portaled, overflow-hidden ancestors can clip it and z-index stacking still depends on the surrounding layout.",
            "The bubble itself gets role='tooltip', but the trigger does not receive aria-describedby or an id link automatically.",
            "The arrow is a rotated square whose placement changes with the side prop."
          ],
          "fields": []
        }
      ],
      "dependencies": [
        "motion"
      ]
    }
  ],
  "latestRelease": {
    "version": "v1.0.4",
    "date": "7 May 2026",
    "title": "GEO and AI discovery rollout",
    "summary": "Adds a full Generative Engine Optimization layer across the product, with AI-readable discovery routes, richer metadata, and structured component indexing.",
    "groups": [
      {
        "items": [
          "New AI discovery endpoints: /llms.txt, /llms-full.txt, and /ai-index.json.",
          "A machine-readable component catalog generated from the existing docs and API reference data."
        ],
        "label": "Added"
      },
      {
        "items": [
          "Site metadata, JSON-LD, sitemap, and robots coverage improved for AI crawlers and answer engines.",
          "Component documentation pages now expose stronger structured data for individual component understanding."
        ],
        "label": "Updated"
      },
      {
        "items": [
          "Discovery signals are now more consistent across the site instead of being split between isolated SEO surfaces."
        ],
        "label": "Fixed"
      }
    ]
  },
  "generatedAt": "2026-05-06T23:27:38.553Z"
}