{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"breadcrumb","type":"registry:ui","title":"Breadcrumb","description":"Show where a page sits in a hierarchy.","categories":["disclosure"],"dependencies":["lucide-react","motion"],"registryDependencies":["https://uiarc.dev/r/arc-motion-tokens.json"],"files":[{"path":"registry/components/breadcrumb/breadcrumb.tsx","type":"registry:component","target":"@components/arc/breadcrumb/breadcrumb.tsx","content":"\"use client\";\nimport type { MouseEvent } from \"react\";\nimport { ChevronRight as NavArrowRight } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { ArcLink } from \"../lib/arc-provider\";\nimport { motionTokens } from \"../lib/motion-tokens\";\nimport styles from \"./breadcrumb.module.css\";\nexport interface BreadcrumbItem {\n  label: string;\n  href?: string;\n  /** Runs when the crumb is chosen. Without an href the crumb renders as a button, for paths that live in local state. */\n  onClick?: (event: MouseEvent<HTMLElement>) => void;\n}\nexport interface BreadcrumbProps { items: BreadcrumbItem[]; ariaLabel?: string }\n/** Crumbs present on first render stay still; crumbs added later slide in from the path before them. */\nexport function Breadcrumb({ items, ariaLabel = \"Breadcrumb\" }: BreadcrumbProps) {\n  const reduced = useReducedMotion() ?? false;\n  const still = { duration: 0 };\n  const path = items.map(item => item.label).join(\"/\");\n  return <nav aria-label={ariaLabel}><ol className={styles.list}><AnimatePresence mode=\"popLayout\" initial={false}>{items.map((item, index) => {\n    const current = index === items.length - 1;\n    return <motion.li key={`${item.label}-${index}`}\n      layout={reduced ? false : \"position\"} layoutDependency={path}\n      initial={reduced ? false : { opacity: 0, x: -8, filter: `blur(${motionTokens.blur.subtle}px)` }} animate={{ opacity: 1, x: 0, filter: \"blur(0px)\" }}\n      exit={reduced ? { opacity: 0, transition: still } : { opacity: 0, x: -4, filter: `blur(${motionTokens.blur.subtle}px)`, transition: { duration: motionTokens.duration.instant, ease: [...motionTokens.ease.standard] } }}\n      transition={reduced ? still : { duration: motionTokens.duration.standard, ease: [...motionTokens.ease.enter], layout: motionTokens.spring.smooth }}>\n      {index > 0 && <NavArrowRight width={14} height={14} aria-hidden=\"true\"/>}\n      {!current && item.href ? <ArcLink href={item.href} data-label={item.label} onClick={item.onClick}>{item.label}</ArcLink>\n        : !current && item.onClick ? <button type=\"button\" data-label={item.label} onClick={item.onClick}>{item.label}</button>\n        : <span aria-current={current ? \"page\" : undefined} data-label={item.label}>{item.label}</span>}\n    </motion.li>;\n  })}</AnimatePresence></ol></nav>;\n}\n"},{"path":"registry/components/breadcrumb/breadcrumb.module.css","type":"registry:component","target":"@components/arc/breadcrumb/breadcrumb.module.css","content":"/* The gap between crumbs matches the gap inside one, so every chevron sits centered between its two labels. */\n.list { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; margin: 0; padding: 0; list-style: none; }\n.list li { display: inline-flex; align-items: center; gap: 6px; color: var(--text-muted); font-size: var(--text-sm); line-height: var(--leading-body); white-space: nowrap; }\n.list li > svg { width: 16px; height: 16px; flex: 0 0 auto; color: var(--border-strong); stroke-width: 1.75; }\n/* Each crumb reserves the width of its medium weight label, so becoming the current page never shifts the path. */\n.list a, .list li > span, .list li > button { display: inline-flex; flex-direction: column; border-radius: 7px; padding: 4px 2px; }\n.list a::after, .list li > span::after, .list li > button::after { height: 0; overflow: hidden; content: attr(data-label); font-weight: 500; visibility: hidden; user-select: none; pointer-events: none; }\n.list li > button { border: 0; margin: 0; background: none; font: inherit; line-height: inherit; text-align: start; cursor: pointer; }\n.list a, .list li > button { color: var(--text-secondary); text-decoration: underline 1px; text-decoration-color: transparent; text-underline-offset: 4px; transition: color var(--duration-fast) var(--ease-standard), text-decoration-color var(--duration-fast) var(--ease-standard); }\n.list [aria-current=\"page\"] { color: var(--accent-strong); font-weight: 500; transition: color var(--duration-fast) var(--ease-standard); }\n@media (hover: hover) and (pointer: fine) { .list a:hover, .list li > button:hover { color: var(--accent-strong); text-decoration-color: color-mix(in oklab, currentColor 45%, transparent); } }\n@media (prefers-reduced-motion: reduce) { .list a, .list li > button, .list [aria-current=\"page\"] { transition: none; } }\n"},{"path":"lib/arc-provider.tsx","type":"registry:lib","target":"@components/arc/lib/arc-provider.tsx","content":"\"use client\";\n\n/**\n * Lets an app swap the link and image elements Arc components render (kuratlielia/arc-library#13).\n *\n * Without a provider, Arc renders a plain `<a>` and `<img>`, so components work in any React app (Vite, Remix, Astro,\n * plain React). Wrap the app once to use your framework's or your own components instead:\n *\n *   import Link from \"next/link\";\n *   import Image from \"next/image\";\n *   import { ArcProvider } from \"@/components/arc/lib/arc-provider\";\n *\n *   <ArcProvider link={Link} image={Image}>{children}</ArcProvider>\n *\n * Any component that accepts these props works, such as a locale-aware Link from next-intl or a React Router Link\n * wrapped to map `href` to `to`.\n */\nimport { createContext, createElement, useContext, useMemo, type AnchorHTMLAttributes, type ComponentType, type ImgHTMLAttributes, type ReactNode, type Ref } from \"react\";\n\n/** The props Arc passes to a link: an href plus ordinary anchor attributes. */\nexport interface ArcLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n  href: string;\n  ref?: Ref<HTMLAnchorElement>;\n}\n\n/**\n * The props Arc passes to an image. A subset of next/image's props, so next/image can be passed as is: either `fill`\n * (cover the positioned parent) or `width` and `height`, plus `sizes` and `priority` as hints.\n */\nexport interface ArcImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\" | \"alt\" | \"width\" | \"height\" | \"loading\" | \"srcSet\"> {\n  src: string;\n  alt: string;\n  width?: number;\n  height?: number;\n  /** Fills the nearest positioned parent, like next/image's `fill`. */\n  fill?: boolean;\n  /** Loads the image eagerly with high priority, like next/image's `priority`. */\n  priority?: boolean;\n  loading?: \"eager\" | \"lazy\";\n  ref?: Ref<HTMLImageElement>;\n}\n\nexport type ArcLinkComponent = ComponentType<ArcLinkProps>;\nexport type ArcImageComponent = ComponentType<ArcImageProps>;\n\n/** The default link: a plain anchor. */\nexport function ArcAnchor(props: ArcLinkProps) {\n  return <a {...props} />;\n}\n\nconst fillStyle = { position: \"absolute\", inset: 0, width: \"100%\", height: \"100%\" } as const;\n\n/** The default image: a plain, lazily decoded `<img>` that understands `fill` and `priority`. */\nexport function ArcImg({ alt, fill, priority, loading, style, ...props }: ArcImageProps) {\n  // eslint-disable-next-line @next/next/no-img-element -- the framework-free default; pass next/image through ArcProvider instead.\n  return <img alt={alt} decoding=\"async\" {...props} loading={loading ?? (priority ? \"eager\" : \"lazy\")} fetchPriority={priority ? \"high\" : props.fetchPriority} style={fill ? { ...fillStyle, ...style } : style} />;\n}\n\ninterface ArcComponents {\n  link: ArcLinkComponent;\n  image: ArcImageComponent;\n}\n\nconst ArcContext = createContext<ArcComponents>({ link: ArcAnchor, image: ArcImg });\n\nexport interface ArcProviderProps {\n  /** The link component Arc renders for navigation, such as next/link or your own wrapped Link. Defaults to `<a>`. */\n  link?: ArcLinkComponent;\n  /** The image component Arc renders for photos, such as next/image. Defaults to `<img>`. */\n  image?: ArcImageComponent;\n  children: ReactNode;\n}\n\n/** Sets the link and image components for every Arc component inside it. Nested providers inherit what they do not set. */\nexport function ArcProvider({ link, image, children }: ArcProviderProps) {\n  const parent = useContext(ArcContext);\n  const value = useMemo(() => ({ link: link ?? parent.link, image: image ?? parent.image }), [link, image, parent]);\n  return <ArcContext.Provider value={value}>{children}</ArcContext.Provider>;\n}\n\n/** The link component from the nearest ArcProvider, or a plain anchor. */\nexport const useArcLink = () => useContext(ArcContext).link;\n/** The image component from the nearest ArcProvider, or a plain img. */\nexport const useArcImage = () => useContext(ArcContext).image;\n\n/** What Arc components render for a link: the provider's link component, or a plain anchor. */\nexport function ArcLink(props: ArcLinkProps) {\n  return createElement(useArcLink(), props);\n}\n\n/** What Arc components render for an image: the provider's image component, or a plain img. */\nexport function ArcImage(props: ArcImageProps) {\n  return createElement(useArcImage(), props);\n}\n"}],"docs":"Docs and live preview: https://uiarc.dev/components/breadcrumb","meta":{"tier":"free","kind":"component","tags":["navigation","wayfinding"],"docs":"https://uiarc.dev/components/breadcrumb","markdown":"https://uiarc.dev/components/breadcrumb/markdown"}}