Breadcrumb
A path of links that shows where you are and lets you jump up. Nested pages and deep product trees.
Editorial
<!-- F12c editorial — non-derivable only. Review: Karen. -->
## Ejemplos
Deep product path:
```tsx import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, } from '@/components/atoms/Breadcrumb';
<Breadcrumb> <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem><BreadcrumbLink href="/settings">Settings</BreadcrumbLink></BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem><BreadcrumbPage>Profile</BreadcrumbPage></BreadcrumbItem> </Breadcrumb> ```
## Accesibilidad
- Root is a `nav` with `aria-label="Breadcrumb"`. - Current page uses `BreadcrumbPage` (`aria-current="page"`), never a link to the same URL.
## Cuándo no usar
- Primary app sections switching in-place → `Tabs` or sidebar nav. - A single-level page with no hierarchy — omit breadcrumbs.
## Criterio de uso
- Úsalo cuando la página pertenece a una jerarquía navegable y los ancestros son destinos útiles. - Los ancestros son enlaces; la página actual es texto no enlazado y debe anunciarse como tal con `aria-current="page"`. - Mantén la ruta corta. Si la profundidad no ayuda a orientarse, reduce niveles en lugar de truncar nombres esenciales.
## Gotchas
- El contenedor debe ser un `nav` con `aria-label="Breadcrumb"`. - No uses breadcrumbs como sustituto de la navegación principal ni para representar pasos lineales de un formulario.
Uso
import {
Breadcrumb, BreadcrumbItem, BreadcrumbLink,
BreadcrumbPage, BreadcrumbSeparator,
} from '@/components/atoms/Breadcrumb';
<Breadcrumb>
<BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem><BreadcrumbPage>Settings</BreadcrumbPage></BreadcrumbItem>
</Breadcrumb>Props
children: ReactNode (required)
Gotchas
- a11y
Root is a nav with aria-label=Breadcrumb; mark the current page with BreadcrumbPage (aria-current=page), not a link.
- react
Compose Breadcrumb > BreadcrumbItem + BreadcrumbSeparator; put BreadcrumbLink on ancestors and BreadcrumbPage on the leaf.
Anatomía CSS
<div class="breadcrumb"> <span class="breadcrumb__ellipsis"></span> <span class="breadcrumb__item"></span> <span class="breadcrumb__link"></span> <span class="breadcrumb__list"></span> <span class="breadcrumb__page"></span> <span class="breadcrumb__separator"></span> </div>
| Clase | Propósito |
|---|---|
breadcrumb | root |
breadcrumb__ellipsis | element |
breadcrumb__item | element |
breadcrumb__link | element |
breadcrumb__list | element |
breadcrumb__page | element |
breadcrumb__separator | element |
Tokens resueltos
Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.
| Variante | Prop | Valor | Token |
|---|---|---|---|
| all | fg | #525252 | muted.foreground |
| all | hover-fg | #0a0a0a | foreground |
Animaciones
| Propiedad | Duración | Easing |
|---|---|---|
color | var(--duration-150) | var(--easing-in-out) |
CSS mínimo funcional
Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).
/* -------------------------------------------------------------------------
Breadcrumb
Navigation trail showing current location in hierarchy.
All semantic tokens. Inherits font from parent.
Parts: .breadcrumb, .breadcrumb__list, .breadcrumb__item,
.breadcrumb__link, .breadcrumb__page, .breadcrumb__separator
------------------------------------------------------------------------- */
.breadcrumb {
font-family: inherit;
font-size: 12.8px;
line-height: 1;
}
.breadcrumb__list {
display: flex;
align-items: center;
gap: 4px;
flex-wrap: wrap;
list-style: none;
margin: 0;
padding: 0;
}
.breadcrumb__item {
display: inline-flex;
align-items: center;
gap: 4px;
}
.breadcrumb__link {
color: #525252;
text-decoration: none;
transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.breadcrumb__link:hover {
color: #0a0a0a;
}
.breadcrumb__link:focus-visible {
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
border-radius: 4px;
}
.breadcrumb__page {
color: #0a0a0a;
font-weight: 500;
}
.breadcrumb__separator {
color: #525252;
display: inline-flex;
align-items: center;
}
.breadcrumb__separator svg {
width: 1em;
height: 1em;
}
.breadcrumb__ellipsis {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
color: #525252;
}
@media (prefers-reduced-motion: reduce) {
.breadcrumb__link {
transition-duration: 0ms;
}
}
Codigo fuente
import { type ReactNode, type AnchorHTMLAttributes } from 'react'; function cn(...classes: (string | false | undefined | null)[]) { return classes.filter(Boolean).join(' '); } const ChevronRight = () => ( <svg width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <path d="M9 18l6-6-6-6" /> </svg> ); const Dots = () => ( <svg width="100%" height="100%" viewBox="0 0 24 24" fill="currentColor"> <circle cx="6" cy="12" r="1.5" /> <circle cx="12" cy="12" r="1.5" /> <circle cx="18" cy="12" r="1.5" /> </svg> ); export function Breadcrumb({ children, className }: { children: ReactNode; className?: string }) { return ( <nav aria-label="Breadcrumb" className={cn('breadcrumb', className)}> <ol className="breadcrumb__list">{children}</ol> </nav> ); } export function BreadcrumbItem({ children, className }: { children: ReactNode; className?: string }) { return <li className={cn('breadcrumb__item', className)}>{children}</li>; } export type BreadcrumbLinkProps = { children: ReactNode; className?: string; } & AnchorHTMLAttributes<HTMLAnchorElement>; export function BreadcrumbLink({ children, className, ...props }: BreadcrumbLinkProps) { return <a className={cn('breadcrumb__link', className)} {...props}>{children}</a>; } export function BreadcrumbPage({ children, className }: { children: ReactNode; className?: string }) { return <span className={cn('breadcrumb__page', className)} aria-current="page">{children}</span>; } export function BreadcrumbSeparator({ children, className }: { children?: ReactNode; className?: string }) { return ( <li role="presentation" aria-hidden="true" className={cn('breadcrumb__separator', className)}> {children || <ChevronRight />} </li> ); } export function BreadcrumbEllipsis({ className }: { className?: string }) { return ( <li className={cn('breadcrumb__ellipsis', className)}> <Dots /> </li> ); }
Webflow
Pega en el Designer como application/json, luego convierte a Component (Atom / Breadcrumb) y publica. Formato interno no documentado de Webflow — regenerable, no dependencia de runtime.
Setup del sitio (una vez)
Custom Code → Head:
<link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/tokens.css"> <link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/components.css">
Unsupported (no silencioso)
:focus-visibleon.breadcrumb__link:focus-visible— pseudo-class not a safe Designer variant — moved to head Custom Codeselectoron.breadcrumb__separator svg— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.breadcrumb__link:focus-visible— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)@mediaon(prefers-reduced-motion: reduce)— not a Designer breakpoint — moved to head Custom Code block--focus-ring-coloron:root— token not found in tokens-nested.json — resolve upstream or the declaration stays invalid on paste--focus-ring-widthon:root— token not found in tokens-nested.json — resolve upstream or the declaration stays invalid on paste
Tras pegar: Create component → nombre Atom / Breadcrumb → Publish.