Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/www/src/components/playground/breadcrumb-examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ export function BreadcrumbExamples() {
<Breadcrumb.Item
dropdownItems={[
{
label: 'Clothes',
children: 'Clothes',
onClick: () => {
console.log('Clothes');
}
},
{
label: 'Electronics',
children: 'Electronics',
onClick: () => {
console.log('Electronics');
}
Expand Down
57 changes: 54 additions & 3 deletions apps/www/src/content/docs/components/breadcrumb/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,30 @@ export const dropdownDemo = {
<Breadcrumb.Item href="/category">Category</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item dropdownItems={[
{ label: 'Option 1', onClick: () => {console.log('Option 1')}},
{ label: 'Option 2', onClick: () => {console.log('Option 2')}}
{ children: 'Option 1', onClick: () => {console.log('Option 1')}},
{ children: 'Option 2', onClick: () => {console.log('Option 2')}}
]}>Subcategory</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/category/subcategory/current">Current Page</Breadcrumb.Item>
</Breadcrumb>`
};

export const dropdownLinksDemo = {
type: 'code',
code: `
<Breadcrumb>
<Breadcrumb.Item href="/">Home</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item dropdownItems={[
{ children: 'Electronics', render: <a href="/electronics" target="_blank" rel="noopener noreferrer" /> },
{ children: 'Clothing', render: <a href="/clothing" /> },
{ children: 'Books', onClick: () => {console.log('Books')}}
]}>Categories</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/current" current>Current</Breadcrumb.Item>
</Breadcrumb>`
};

export const asDemo = {
type: 'code',
code: `
Expand All @@ -113,11 +130,23 @@ export const asDemo = {
</Breadcrumb>`
};

export const disabledDemo = {
type: 'code',
code: `
<Breadcrumb>
<Breadcrumb.Item href="/">Home</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item disabled>Loading…</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/products" current>Products</Breadcrumb.Item>
</Breadcrumb>`
};

export const iconsDemo = {
type: 'code',
tabs: [
{
name: 'Text with Icon',
name: 'Leading Icon',
code: `
<Breadcrumb>
<Breadcrumb.Item href="/" leadingIcon={<BellIcon />}>Home</Breadcrumb.Item>
Expand All @@ -127,6 +156,28 @@ export const iconsDemo = {
<Breadcrumb.Item href="/settings" leadingIcon={<ShoppingBagFilledIcon />}>Settings</Breadcrumb.Item>
</Breadcrumb>`
},
{
name: 'Trailing Icon',
code: `
<Breadcrumb>
<Breadcrumb.Item href="/" trailingIcon={<BellIcon />}>Home</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/documents" trailingIcon={<FilterIcon />}>Documents</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/settings" trailingIcon={<ShoppingBagFilledIcon />}>Settings</Breadcrumb.Item>
</Breadcrumb>`
},
{
name: 'Both Icons',
code: `
<Breadcrumb>
<Breadcrumb.Item href="/" leadingIcon={<BellIcon />} trailingIcon={<FilterIcon />}>Home</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/documents" leadingIcon={<FilterIcon />} trailingIcon={<ShoppingBagFilledIcon />}>Documents</Breadcrumb.Item>
<Breadcrumb.Separator/>
<Breadcrumb.Item href="/settings" leadingIcon={<ShoppingBagFilledIcon />} trailingIcon={<BellIcon />}>Settings</Breadcrumb.Item>
</Breadcrumb>`
},
{
name: 'Only Icon',
code: `
Expand Down
39 changes: 34 additions & 5 deletions apps/www/src/content/docs/components/breadcrumb/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
iconsDemo,
ellipsisDemo,
dropdownDemo,
dropdownLinksDemo,
asDemo,
disabledDemo,
} from "./demo.ts";

<Demo data={playground} />
Expand Down Expand Up @@ -42,7 +44,25 @@ Groups all parts of the breadcrumb navigation.

### Item

Renders an individual breadcrumb link. Use the `current` prop on the item that represents the current page so it is styled and exposed to assistive tech (e.g. `aria-current="page"`).
Renders an individual breadcrumb link. Use the `current` prop on the item that represents the current page so it is styled and exposed to assistive tech (e.g. `aria-current="page"`). Use the `disabled` prop for non-clickable, visually muted items (e.g. loading or no access).

Item elements expose data attributes for CSS state targeting so you can style current and disabled states without relying on internal class names:

| Attribute | When present |
|-----------|----------------|
| `data-current="true"` | Item is the current page (`current` prop) |
| `data-disabled="true"` | Item is disabled (`disabled` prop) |

Example:

```css
[data-current="true"] {
color: var(--my-brand);
}
[data-disabled="true"] {
opacity: 0.6;
}
```

<auto-type-table path="./props.ts" name="BreadcrumbItem" />

Expand Down Expand Up @@ -80,18 +100,20 @@ Use the `Breadcrumb.Ellipsis` component to truncate the breadcrumb trail when yo

### Icons

Breadcrumb items can include icons either alongside text or as standalone elements.
Breadcrumb items can include icons via `leadingIcon` (before the label) or `trailingIcon` (after the label), either alongside text or as standalone elements.

<Demo data={iconsDemo} />

### Dropdown

Breadcrumb items can include dropdown menus for additional navigation options. Specify the dropdown items using the `dropdownItems` prop.
Breadcrumb items can include dropdown menus for additional navigation options. Specify them with the `dropdownItems` prop: each entry is the same props as `<Menu.Item>` (e.g. `children` for the label, `onClick`, `disabled`, `render` for a link such as `<a href="…" />`, etc.). You can also pass `key` for stable list keys.

**Note:** When `dropdownItems` is provided, the `as` and `href` props are ignored.
**Note:** When `dropdownItems` is provided, the `as` and `href` props on the breadcrumb item are ignored.

<Demo data={dropdownDemo} />

<Demo data={dropdownLinksDemo} />

### As

Use the `as` prop to render the breadcrumb item as a custom component. By default, breadcrumb items are rendered as `a` tags.
Expand All @@ -100,8 +122,15 @@ When a custom component is provided, the props are merged, with the custom compo

<Demo data={asDemo} />

### Disabled

Use the `disabled` prop for non-clickable, visually muted items—for example, loading states or segments the user does not have access to. Disabled items render as a span with `aria-disabled="true"` and do not navigate.

<Demo data={disabledDemo} />

## Accessibility

- Uses `nav` element with `aria-label="Breadcrumb"` for proper landmark identification
- Current page is indicated with `aria-current="page"`
- Separator elements are hidden from screen readers with `aria-hidden`
- Disabled items use `aria-disabled="true"`
- Separator elements are decorative and use `role="presentation"` and `aria-hidden="true"` so screen readers skip them
27 changes: 17 additions & 10 deletions apps/www/src/content/docs/components/breadcrumb/props.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactElement, ReactEventHandler, ReactNode } from 'react';
import { ReactElement, ReactNode } from 'react';

export interface BreadcrumbItem {
/** Text to display for the item */
Expand All @@ -7,28 +7,35 @@ export interface BreadcrumbItem {
/** URL for the item link */
href?: string;

/** Optional icon element to display */
/** Optional icon element to display before the label */
leadingIcon?: ReactNode;

/** Optional icon element to display after the label */
trailingIcon?: ReactNode;

/**
* Whether the item is the current page
* @defaultValue false
*/
current?: boolean;

/**
* Optional array of dropdown items
* When true, the item is non-clickable and visually muted (e.g. loading or no access).
* @defaultValue false
*/
disabled?: boolean;

/**
* Optional array of dropdown entries; each object is passed to `<Menu.Item>`
* (e.g. `children`, `onClick`, `render` for a link, etc.), plus optional `key`
* for React list reconciliation (not forwarded to `Menu.Item`).
*
* When `dropdownItems` is provided, the `as` and `href` props are ignored.
*/
dropdownItems?: {
/** Optional stable key for list reconciliation. Falls back to index if omitted. */
dropdownItems?: (Record<string, unknown> & {
key?: string;
/** Text to display for the dropdown item */
label: string;
/** Callback function when a dropdown item is clicked */
onClick?: ReactEventHandler<HTMLDivElement>;
}[];
children?: ReactNode;
})[];

/**
* Custom element used to render the Item.
Expand Down
Loading