{ "name": "chevron-steps", "type": "registry:ui", "registryDependencies": [], "dependencies": [], "devDependencies": [], "tailwind": {}, "cssVars": { "light": {}, "dark": {} }, "files": [ { "path": "chevron-steps.tsx", "content": "'use client';\n\nimport * as React from 'react';\nimport { cn } from '@/lib/utils';\n\nexport type ChevronStep = {\n id?: string | number;\n label: string;\n /** Optional accessible description (shown via title attr). */\n description?: string;\n /** Optional custom classes per step. */\n className?: string;\n /** Disable click for this step. */\n disabled?: boolean;\n};\n\nexport type ChevronStepsProps = {\n /** Steps in order. */\n steps: ChevronStep[];\n\n /** Zero-based current step index. */\n current?: number;\n\n /** Called when a (non-disabled) step is clicked. */\n onStepClick?: (index: number, step: ChevronStep) => void;\n\n /** Sizes & look */\n size?: 'sm' | 'md' | 'lg';\n variant?: 'brand' | 'neutral';\n\n /** Tail (arrow) width in px (CSS variable) */\n tailWidth?: number;\n\n /** Roundness on the bar */\n radius?: 'md' | 'lg' | 'xl' | '2xl';\n\n /** Allow horizontal scroll on small screens */\n scrollable?: boolean;\n\n /** Root class */\n className?: string;\n\n /** Slot overrides */\n stepClassName?: string;\n stepActiveClassName?: string;\n stepCompletedClassName?: string;\n stepUpcomingClassName?: string;\n};\n\n/**\n * ChevronSteps: horizontally joined chevron items with a configurable arrow tail,\n * a11y-friendly (list+buttons), and simple API.\n */\nexport default function ChevronSteps({\n steps,\n current = 0,\n onStepClick,\n size = 'md',\n variant = 'brand',\n tailWidth = 18,\n radius = '2xl',\n scrollable = true,\n className,\n stepClassName,\n stepActiveClassName,\n stepCompletedClassName,\n stepUpcomingClassName,\n}: ChevronStepsProps) {\n const h =\n size === 'sm'\n ? 'h-8 text-xs'\n : size === 'lg'\n ? 'h-14 text-base'\n : 'h-11 text-sm';\n const pad = size === 'sm' ? 'px-4' : size === 'lg' ? 'px-7' : 'px-6';\n const r =\n radius === 'md'\n ? 'rounded-md'\n : radius === 'lg'\n ? 'rounded-lg'\n : radius === 'xl'\n ? 'rounded-xl'\n : 'rounded-2xl';\n\n // The chevron shape uses a clip-path polygon with --twc-tail custom property\n // to create the arrow head. We mask the first/last to give rounded ends.\n const baseStep =\n 'relative isolate flex grow select-none items-center justify-center whitespace-nowrap font-medium transition-colors';\n\n const theme =\n variant === 'brand'\n ? {\n active: 'bg-blue-700 text-white',\n completed:\n 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200',\n upcoming:\n 'bg-zinc-100 text-zinc-600 dark:bg-zinc-900 dark:text-zinc-400',\n border: 'ring-1 ring-white/70 dark:ring-black/20',\n }\n : {\n active: 'bg-zinc-800 text-white dark:bg-zinc-200 dark:text-zinc-900',\n completed:\n 'bg-zinc-200 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200',\n upcoming:\n 'bg-zinc-100 text-zinc-600 dark:bg-zinc-900 dark:text-zinc-400',\n border: 'ring-1 ring-white/70 dark:ring-black/20',\n };\n\n const containerClasses = cn(\n 'relative',\n r,\n scrollable ? 'overflow-x-auto' : 'overflow-hidden',\n 'bg-transparent',\n className\n );\n\n return (\n
\n \n {steps.map((step, i) => {\n const state =\n i < current ? 'completed' : i === current ? 'active' : 'upcoming';\n\n const color =\n state === 'active'\n ? theme.active\n : state === 'completed'\n ? theme.completed\n : theme.upcoming;\n\n const override =\n state === 'active'\n ? stepActiveClassName\n : state === 'completed'\n ? stepCompletedClassName\n : stepUpcomingClassName;\n\n // leftmost and rightmost need rounded masks\n const roundLeft = i === 0 ? r : '';\n const roundRight = i === steps.length - 1 ? r : '';\n\n // Each step uses clip-path polygon to form a chevron.\n // We add a tiny overlap (-0.5px) to avoid hairline gaps between shapes.\n return (\n
  • \n {\n if (!step.disabled && onStepClick) onStepClick(i, step);\n }}\n >\n {step.label}\n \n
  • \n );\n })}\n \n
    \n );\n}\n", "type": "registry:ui" } ] }