Breadcrumb
Where you are, and every way back — all of it in the accessible tree.
src/components/rahti_ui/breadcrumb.rs
Basic
A trail of links ending in the page you are on.
shadcn's own demo. Every step but the last is a `<BreadcrumbLink>`; the last is a `<BreadcrumbPage>`, which is a `<span>` rather than an anchor — an anchor with no `href` is not focusable, and one with an `href` would navigate to the page you are already on.
The separators are `<li>`s, which is what keeps the `<ol>` valid, and they are `aria-hidden`, which is what keeps the list counting three crumbs rather than five.
Custom separator
Anything you put inside the separator replaces the chevron.
The chevron is a default, not a fixture — shadcn writes `{children ?? <ChevronRight />}` and this does the same, so children replace it. An icon or a character both work.
`[&>svg]:size-3.5` lives on the `<li>` rather than on the chevron, so a swapped icon is measured the same without the caller being told about it. A text separator is sized by the row's `text-sm` instead.
Dropdown
One crumb opening a menu of its siblings.
A crumb that is a menu rather than a link, which is shadcn's own recipe for a level with siblings worth jumping to. The `<BreadcrumbItem>` is `inline-flex items-center gap-1`, so the trigger and its chevron line up with the crumbs either side without any extra layout.
The menu is a real `<DropdownMenu>` here rather than hand-written markup, because none of its items need anything from the page's scope — so it arrives with the root's keyboard and pointer handling. See `/components/data-table` for the case where that is not true.
Collapsed
A trail too long to show in full.
The ellipsis stands in for the levels left out. It is `role="presentation" aria-hidden`, with the word `More` inside it in `sr-only` text — the glyph would otherwise be announced as "ellipsis", which says nothing useful.
The mark is a *trigger*, not decoration — that is shadcn's own demo, and it is the point of the pattern: crumbs you hide still have to be reachable. The `<BreadcrumbEllipsis>` goes inside a ghost icon button, so the ellipsis keeps its `role="presentation"` and the button supplies the name, the focus ring and the hover state.
Which crumbs to collapse is still the page's decision; the component only draws the mark, and the menu is composed around it exactly as in the Dropdown section above.
Link component
The prop shadcn needs for a router's link, and why this port does not.
shadcn's "link component" section is about swapping the anchor for a framework's `<Link>` with `asChild`. There is no such thing to swap here — Rahti's navigation is an ordinary `<a href>` intercepted by the runtime, so a `<BreadcrumbLink>` already *is* the framework's link and gets SPA navigation for free.
What the section demonstrates instead is the override surface every part shares: a `class` on any of them, and a different `aria_label` on the nav for a page with two trails on it.
The accessible tree
What the component is actually for.
This component is eleven utilities and four ARIA decisions, and the ARIA is the part that is easy to get wrong. The current crumb carries all three of `role="link"`, `aria-disabled="true"` and `aria-current="page"` — which look redundant and are not.
`role="link"` makes it announce as the same kind of thing as its siblings, so a screen reader reads a list of links rather than two links and a stray phrase. `aria-disabled` says this one cannot be followed. `aria-current="page"` says *which* one it is, and is what gets announced as "current page". Leave any of the three off and it still looks right, which is why they are asserted in tests rather than trusted.
The separators and the ellipsis are `role="presentation" aria-hidden`, so they are in the `<ol>` for validity and out of the accessible tree for counting: a three-crumb trail reads as three items, not five. The `<nav>` around it is a landmark, which is how someone jumps straight to it.
The port
Where the class strings came from, and the four the registry has wrong.
The docs page's published source, which is a generation ahead of `new-york-v4/breadcrumb.json` in four places: the list drops `sm:gap-2.5` and renames `break-words`, the item tightens from `gap-1.5` to `gap-1`, and the ellipsis shrinks from `size-9` to `size-5` with its icon sized from the box rather than on the glyph.
The page wins, as it does everywhere in this library except `Pagination` — and that exception was specifically that its newer strings were tuned against a Button generation this library does not have. Nothing here depends on another component, so there is no such conflict.
Two of the four are worth naming. `break-words` became `wrap-break-word` in Tailwind itself; this project builds with 4.3.3, so the new spelling is the live one and the old one would be a dead class. And `size-9` on the ellipsis was a *button* measurement — inline in a `text-sm` row it should be the height of the text beside it, which is what `size-5` gives.
`cn-rtl-flip` on the separator's chevron is dropped, the same call `Pagination` and `Empty` made: a Base UI style hook whose definition lives in a stylesheet this library does not ship, and nothing in these strings selects on it.