Breadcrumb Cascade
Two additions to a breadcrumb trail for a file browser — a crumb that takes dragged rows, and a menu of a segment's siblings.
- Status
- Since
0.23.74- Accessibility pattern
- menu with arrow keys; drops are pointer only
Last updated
Install
Add Breadcrumb Cascade from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/breadcrumb-cascadeThe same command installs the registry items it composes: @vegastack/breadcrumb, @vegastack/button, @vegastack/dropdown-menu, @vegastack/use-drag-reorder.
Usage
import {
BreadcrumbDropTarget,
BreadcrumbSiblings,
} from "@/components/ui/breadcrumb-cascade";
<BreadcrumbItem>
<BreadcrumbDropTarget
href="/library/f/2"
targetId="2"
dragScope="library"
onDropInto={move}
>
Projects
</BreadcrumbDropTarget>
<BreadcrumbSiblings label="Projects" items={siblings} />
</BreadcrumbItem>;Both parts sit inside upstream's BreadcrumbItem, beside or in place of its BreadcrumbLink.
BreadcrumbSiblings is a small chevron after a segment that opens a menu of the segment's
siblings — the segment itself included and checked — so a reader can move sideways without going
up a level first. The segment's own link is untouched.
Scope
- Owns: the sibling menu of one segment, and a crumb that takes rows dragged from a list.
- Does not own: the trail (upstream's
Breadcrumbparts), the siblings' data, or the move itself —onDropIntohands the host the ids and the target. - Compose with:
Breadcrumb,DataList(dragScope+onDropInto), and the host's "Move…" dialog as the keyboard path for a move.
Anatomy
Examples
Loading and empty
Pass onOpenChange to load a segment's siblings the first time its menu opens; loading shows
"Loading…" in the menu until they arrive. A segment with nothing beside it says so.
Drop target
BreadcrumbDropTarget renders BreadcrumbLink, so the crumb is still the link it was. Rows dragged
from a DataList with the same dragScope can be dropped on it — moving files up to a parent
folder. While a drag is over it the crumb washes in the primary tint (data-drop-over), or in the
destructive tint when canDropInto refuses (data-drop-invalid). A selected row carries the whole
selection.
| Name | |
|---|---|
| Brief.pdf | |
| Kickoff notes.md | |
| Budget.xlsx |
Drag a file onto a crumb.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
items* | readonly BreadcrumbSibling[] | — | The segment's siblings, the segment itself included (mark it current). |
label* | string | — | The segment's name — the menu trigger's and the menu's accessible names are built from it. |
className | string | — | Extra classes on the trigger. |
emptyMessage | string | "Nothing else here" | Shown when there is nothing to list. |
linkRender | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | <a /> | The element an entry's link renders — a router link such as <Link />. |
loading | boolean | false | The siblings are loading: the menu shows loadingMessage in place of the entries. |
loadingMessage | string | "Loading…" | Shown while loading. |
onOpenChange | ((open: boolean) => void) | — | Called when the menu opens or closes — load the siblings on first open. |
onSelect | ((id: string) => void) | — | Called with an entry's id when it is chosen (after its link, when it has one). |
triggerLabel | ((label: string) => string) | (label) => `Items next to ${label}` | The trigger's accessible name. |
Data attributes and CSS variables on BreadcrumbSiblings
| Attribute | Values |
|---|---|
data-current | "" |
data-slot | "breadcrumb-siblings-item" | "breadcrumb-siblings-menu" | "breadcrumb-siblings-status" | "breadcrumb-siblings-trigger" |
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | A stable id, passed to onSelect. |
label* | string | — | The entry's name. |
current | boolean | — | The segment the menu belongs to — marked with a check and aria-current. |
href | string | — | Where the entry goes. Without it, choosing the entry only calls onSelect. |
icon | React.ReactNode | — | A leading icon (a folder or a file kind). |
| Prop | Type | Default | Description |
|---|---|---|---|
dragScope* | string | — | The drag scope of the list whose rows may drop here (DataList's dragScope). |
onDropInto* | (move: BreadcrumbDropMove) => void | Promise<void> | — | Called for a valid drop. Return a promise for a server-gated move. |
targetId* | string | — | The segment's id — the folder rows dropped here move into. |
canDropInto | ((move: BreadcrumbDropMove) => boolean) | — | Whether the drop is allowed — rows already in this folder, or a folder dropped into its own
descendant. A refused segment shows data-drop-invalid and takes nothing. |
Data attributes and CSS variables on BreadcrumbDropTarget
| Attribute | Values |
|---|---|
data-drop-target | "" |
| Prop | Type | Default | Description |
|---|---|---|---|
ids* | string[] | — | The dragged row ids (a DataList drag carries its whole selection). |
targetId* | string | — | The segment's targetId. |
Accessibility
- The sibling trigger is an icon button named “Items next to” plus the segment’s name. The menu is the DS
DropdownMenu: focus moves into it, the current segment carriesaria-current="page"and a check, and Esc closes it with focus back on the trigger. Whileloadingthe menu isaria-busyand its one entry says "Loading…". - A drop target is pointer only, like every drag in the system: the keyboard path for a move is the
host's "Move…" action (a
MoveDialogor aFolderTreepicker).
| Key | Action |
|---|---|
| Enter / Space | Open the sibling menu; choose the focused entry |
| ↓ / ↑ | Move through the siblings |
| Esc | Close the menu and return focus to the chevron |
| Contract | States tested |
|---|---|
| Behaviour | default, open, current, loading, empty, drop-over, drop-invalid |
| Accessibility | browser-accessibility-test, labeled, busy, current, keyboard-menu |
| Visual | default, hover, focus, drop-over, drop-invalid |
Do / Don't
Breadcrumb
The path to the current resource as a hierarchy of links — separators, the current page, a collapsed ellipsis, and any routing library's link through render.
Navigation Menu
A collection of links for navigating websites — triggers that open one shared panel, plain links styled to match, and any routing library's link through render.