Skip to content

Commit 061007a

Browse files
authored
Update RAC breadcrumbs API (#5014)
1 parent e89b9bc commit 061007a

File tree

5 files changed

+125
-205
lines changed

5 files changed

+125
-205
lines changed

packages/@react-aria/breadcrumbs/docs/useBreadcrumbs.mdx

Lines changed: 1 addition & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ Breadcrumbs provide a list of links to parent pages of the current page in hiera
4646
* Support for navigation links via `<a>` elements or custom element types via ARIA
4747
* Localized ARIA labeling support for landmark navigation region
4848
* Support for disabled breadcrumbs
49-
* Support for breadcrumbs as a heading
5049

5150
## Anatomy
5251

@@ -55,8 +54,7 @@ Breadcrumbs provide a list of links to parent pages of the current page in hiera
5554
Breadcrumbs consist of a navigation landmark element and a list of links, typically with a visual separator
5655
icon between each item. The last link represents the current page in the hierarchy, with the previous links representing the
5756
parent pages of the current page. Each of these parent links can be clicked, tapped, or
58-
triggered via the <Keyboard>Enter</Keyboard> key to navigate to that page. Optionally, breadcrumbs can be used
59-
in place of a page title, in which case the last breadcrumb acts as a heading.
57+
triggered via the <Keyboard>Enter</Keyboard> key to navigate to that page.
6058

6159
`useBreadcrumbs` returns props to be spread onto the navigation element:
6260

@@ -188,85 +186,6 @@ function BreadcrumbItem(props) {
188186
</Breadcrumbs>
189187
```
190188

191-
## Breadcrumbs as a heading
192-
193-
If you use breadcrumbs in the context where a heading would normally be used,
194-
you should make sure that the proper `elementType` for each breadcrumb is communicated to
195-
`useBreadcrumbItem` so that you receive the correct `breadcrumbItemProps` to spread on your breadcrumb.
196-
This can be seen in the example below where we update the `BreadcrumbItem` component to specify that
197-
the current has elementType `h3` and all other breadcrumbs are of type `a`.
198-
199-
```tsx example
200-
///- begin collapse -///
201-
function Breadcrumbs(props) {
202-
let {navProps} = useBreadcrumbs(props);
203-
let childCount = React.Children.count(props.children);
204-
205-
return (
206-
<nav {...navProps}>
207-
<ol style={{display: 'flex', listStyle: 'none', margin: 0, padding: 0}}>
208-
{React.Children.map(props.children, (child, i) =>
209-
React.cloneElement(child, {isCurrent: i === childCount - 1})
210-
)}
211-
</ol>
212-
</nav>
213-
)
214-
}
215-
///- end collapse -///
216-
function BreadcrumbItem(props) {
217-
let ref = React.useRef(null);
218-
let {itemProps} = useBreadcrumbItem({
219-
...props,
220-
elementType: props.isCurrent ? 'h3' : 'a'
221-
}, ref);
222-
let breadcrumbContent;
223-
if (props.isCurrent) {
224-
breadcrumbContent = (
225-
<h3
226-
{...itemProps}
227-
ref={ref}
228-
style={{
229-
margin: 0,
230-
fontSize: '1em',
231-
color: 'var(--blue)'
232-
}}>
233-
{props.children}
234-
</h3>
235-
);
236-
} else {
237-
breadcrumbContent = (
238-
<>
239-
<a
240-
{...itemProps}
241-
ref={ref}
242-
href={props.href}
243-
style={{
244-
color: props.isDisabled ? 'var(--gray)' : 'var(--blue)',
245-
textDecoration: props.isCurrent || props.isDisabled ? 'none' : 'underline',
246-
fontWeight: props.isCurrent ? 'bold' : null,
247-
cursor: props.isCurrent || props.isDisabled ? 'default' : 'pointer'
248-
}}>
249-
{props.children}
250-
</a>
251-
<span aria-hidden="true" style={{padding: '0 5px'}}>{''}</span>
252-
</>
253-
);
254-
}
255-
256-
return (
257-
<li>
258-
{breadcrumbContent}
259-
</li>
260-
);
261-
}
262-
263-
<Breadcrumbs>
264-
<BreadcrumbItem href="/">Home</BreadcrumbItem>
265-
<BreadcrumbItem href="/react-aria">React Aria</BreadcrumbItem>
266-
<BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
267-
</Breadcrumbs>
268-
```
269-
270189
## Usage
271190

272191
The following examples show how to use the `Breadcrumbs` component created in the above examples.

packages/react-aria-components/docs/Breadcrumbs.mdx

Lines changed: 71 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ type: component
4343
## Example
4444

4545
```tsx example
46-
import {Breadcrumbs, Item, Link} from 'react-aria-components';
46+
import {Breadcrumbs, Breadcrumb, Link} from 'react-aria-components';
4747

4848
<Breadcrumbs>
49-
<Item><Link><a href="/">Home</a></Link></Item>
50-
<Item><Link><a href="/react-aria">React Aria</a></Link></Item>
51-
<Item><Link>useBreadcrumbs</Link></Item>
49+
<Breadcrumb><Link><a href="/">Home</a></Link></Breadcrumb>
50+
<Breadcrumb><Link><a href="/react-aria">React Aria</a></Link></Breadcrumb>
51+
<Breadcrumb><Link>Breadcrumbs</Link></Breadcrumb>
5252
</Breadcrumbs>
5353
```
5454

@@ -57,21 +57,14 @@ import {Breadcrumbs, Item, Link} from 'react-aria-components';
5757

5858
```css
5959
.react-aria-Breadcrumbs {
60-
& ol {
61-
display: flex;
62-
align-items: center;
63-
list-style: none;
64-
margin: 0;
65-
padding: 0;
66-
font-size: 18px;
67-
}
68-
69-
.react-aria-Heading {
70-
margin: 0;
71-
font-size: 1em;
72-
}
73-
74-
.react-aria-Item:not(:last-child)::after {
60+
display: flex;
61+
align-items: center;
62+
list-style: none;
63+
margin: 0;
64+
padding: 0;
65+
font-size: 18px;
66+
67+
.react-aria-Breadcrumb:not(:last-child)::after {
7568
content: '';
7669
content: '' / '';
7770
alt: ' ';
@@ -135,25 +128,23 @@ Breadcrumbs provide a list of links to parent pages of the current page in hiera
135128
`Breadcrumbs` helps implement these in an accessible way.
136129

137130
* **Flexible** – Support for navigation links, JavaScript handled links, or custom element types (e.g. router links).
138-
* **Accessible** – Implemented as a navigation landmark with an ordered list of links. Optional support for using breadcrumbs as a heading is available as well.
131+
* **Accessible** – Implemented as an ordered list of links. The last link is automatically marked as the current page using `aria-current`.
139132
* **Styleable** – Hover, press, and keyboard focus states are provided for easy styling. These states only apply when interacting with an appropriate input device, unlike CSS pseudo classes.
140133

141134
## Anatomy
142135

143136
<Anatomy />
144137

145-
Breadcrumbs consist of a navigation landmark element and a list of links, typically with a visual separator
138+
Breadcrumbs consist of a list of links, typically with a visual separator
146139
icon between each item. The last link represents the current page in the hierarchy, with the previous links representing the
147140
parent pages of the current page. Each of these parent links can be clicked, tapped, or
148-
triggered via the <Keyboard>Enter</Keyboard> key to navigate to that page. Optionally, breadcrumbs can be used
149-
in place of a page title, in which case the last breadcrumb acts as a heading.
141+
triggered via the <Keyboard>Enter</Keyboard> key to navigate to that page.
150142

151143
```tsx
152-
import {Breadcrumbs, Item, Link, Heading} from 'react-aria-components';
144+
import {Breadcrumbs, Breadcrumb, Link} from 'react-aria-components';
153145

154146
<Breadcrumbs>
155-
<Item><Link /></Item>
156-
<Item><Heading /></Item>
147+
<Breadcrumb><Link /></Breadcrumb>
157148
</Breadcrumbs>
158149
```
159150

@@ -210,26 +201,12 @@ function Example() {
210201

211202
return (
212203
<Breadcrumbs items={breadcrumbs} onAction={navigate}>
213-
{item => <Item><Link>{item.label}</Link></Item>}
204+
{item => <Breadcrumb><Link>{item.label}</Link></Breadcrumb>}
214205
</Breadcrumbs>
215206
);
216207
}
217208
```
218209

219-
## Heading
220-
221-
The last breadcrumb may be used as a heading for the page by placing a `<Heading>` element within the `<Item>` instead of a `<Link>`. By default, this is an `h3` element, but this can be changed with the `level` prop on the `Heading`.
222-
223-
```tsx example
224-
import {Heading} from 'react-aria-components';
225-
226-
<Breadcrumbs>
227-
<Item><Link><a href="/">Home</a></Link></Item>
228-
<Item><Link><a href="/react-aria">React Aria</a></Link></Item>
229-
<Item><Heading>useBreadcrumbs</Heading></Item>
230-
</Breadcrumbs>
231-
```
232-
233210
## Router links
234211

235212
The `<Link>` component can wrap a custom link element provided by a router like [React Router](https://reactrouter.com/en/main).
@@ -238,8 +215,8 @@ The `<Link>` component can wrap a custom link element provided by a router like
238215
import {Link as RouterLink} from 'react-router-dom';
239216

240217
<Breadcrumbs>
241-
<Item><Link><RouterLink to="/foo">Foo</RouterLink></Link></Item>
242-
<Item><Link>Bar</Link></Item>
218+
<Breadcrumb><Link><RouterLink to="/foo">Foo</RouterLink></Link></Breadcrumb>
219+
<Breadcrumb><Link>Bar</Link></Breadcrumb>
243220
</Breadcrumbs>
244221
```
245222

@@ -251,11 +228,11 @@ The above examples use the CSS `:after` pseudo class to add separators between e
251228
import ChevronIcon from '@spectrum-icons/workflow/ChevronDoubleRight';
252229

253230
<Breadcrumbs>
254-
<Item className="my-item">
231+
<Breadcrumb className="my-item">
255232
<Link><a href="/">Home</a></Link>
256233
<ChevronIcon size="S" />
257-
</Item>
258-
<Item><Link>React Aria</Link></Item>
234+
</Breadcrumb>
235+
<Breadcrumb><Link>React Aria</Link></Breadcrumb>
259236
</Breadcrumbs>
260237
```
261238

@@ -272,32 +249,58 @@ import ChevronIcon from '@spectrum-icons/workflow/ChevronDoubleRight';
272249

273250
</details>
274251

252+
## Landmarks
253+
254+
When breadcrumbs are used as a main navigation element for a page, they can be placed in a [navigation landmark](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/navigation.html). Landmarks help assistive technology users quickly find major sections of a page. Place breadcrumbs inside a `<nav>` element with an `aria-label` to create a navigation landmark.
255+
256+
```tsx example
257+
<nav aria-label="Breadcrumbs">
258+
<Breadcrumbs>
259+
<Breadcrumb><Link><a href="/">Home</a></Link></Breadcrumb>
260+
<Breadcrumb><Link><a href="/react-aria">React Aria</a></Link></Breadcrumb>
261+
<Breadcrumb><Link>Breadcrumbs</Link></Breadcrumb>
262+
</Breadcrumbs>
263+
</nav>
264+
```
265+
266+
It is best to keep the number of landmarks on a page to a minimum, so only place breadcrumbs in a navigation landmark when it represents the primary or secondary navigation for the page. For example, breadcrumbs within table rows or popovers most likely should not be landmarks.
267+
275268
## Disabled
276269

277270
Breadcrumbs can be disabled using the `isDisabled` prop. This indicates that navigation is not currently available. When a breadcrumb is disabled, `onPress` will not be triggered, navigation will not occur, and links will be marked as `aria-disabled` for assistive technologies.
278271

279272
```tsx example
280273
<Breadcrumbs isDisabled>
281-
<Item><Link><a href="/">Home</a></Link></Item>
282-
<Item><Link><a href="/react-aria">React Aria</a></Link></Item>
283-
<Item><Link>useBreadcrumbs</Link></Item>
274+
<Breadcrumb><Link><a href="/">Home</a></Link></Breadcrumb>
275+
<Breadcrumb><Link><a href="/react-aria">React Aria</a></Link></Breadcrumb>
276+
<Breadcrumb><Link>Breadcrumbs</Link></Breadcrumb>
284277
</Breadcrumbs>
285278
```
286279

287280
Individual breadcrumbs can also be disabled by passing the `isDisabled` prop to the `<Link>` element:
288281

289282
```tsx example
290283
<Breadcrumbs>
291-
<Item><Link><a href="/">Home</a></Link></Item>
292-
<Item><Link isDisabled><a href="/react-aria">React Aria</a></Link></Item>
293-
<Item><Link>useBreadcrumbs</Link></Item>
284+
<Breadcrumb><Link><a href="/">Home</a></Link></Breadcrumb>
285+
<Breadcrumb><Link isDisabled><a href="/react-aria">React Aria</a></Link></Breadcrumb>
286+
<Breadcrumb><Link>Breadcrumbs</Link></Breadcrumb>
294287
</Breadcrumbs>
295288
```
296289

297290
## Props
298291

292+
### Breadcrumbs
293+
299294
<PropTable component={docs.exports.Breadcrumbs} links={docs.links} />
300295

296+
### Breadcrumb
297+
298+
<PropTable component={docs.exports.Breadcrumb} links={docs.links} />
299+
300+
### Link
301+
302+
<PropTable component={docs.exports.Link} links={docs.links} />
303+
301304
## Styling
302305

303306
React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin `className` attribute which can be targeted using CSS selectors. These follow the `react-aria-ComponentName` naming convention.
@@ -334,19 +337,11 @@ The states, selectors, and render props for all components used in `Breadcrumbs`
334337

335338
### Breadcrumbs
336339

337-
`Breadcrumbs` can be targed with the `.react-aria-Breadcrumbs` CSS selector, or by overriding with a custom `className`. It is rendered as a `<nav>` element, and contains an `<ol>` element representing the list of items. This additional element can be targed with the `ol` selector.
338-
339-
```css render=false
340-
.react-aria-Breadcrumbs {
341-
& ol {
342-
/* ... */
343-
}
344-
}
345-
```
340+
`Breadcrumbs` can be targed with the `.react-aria-Breadcrumbs` CSS selector, or by overriding with a custom `className`. It is rendered as an `<ol>` element representing the list of items.
346341

347-
### Item
342+
### Breadcrumb
348343

349-
An `Item` can be targeted with the `.react-aria-Item` CSS selector, or by overriding with a custom `className`. It is rendered as an `<li>` element, and should contain a `<Link>` or `<Heading>`. It may also include another element such as a [separator icon](#separator-icons).
344+
A `Breadcrumb` can be targeted with the `.react-aria-Breadcrumb` CSS selector, or by overriding with a custom `className`. It is rendered as an `<li>` element, and should contain a `<Link>`. It may also include another element such as a [separator icon](#separator-icons).
350345

351346
### Link
352347

@@ -410,7 +405,7 @@ Now when you place `Breadcrumbs` inside a `Router`, it automatically has access
410405
```tsx example
411406
<Router>
412407
<Breadcrumbs>
413-
{(item: RouterItem) => <Item><Link>{item.label}</Link></Item>}
408+
{(item: RouterItem) => <Breadcrumb><Link>{item.label}</Link></Breadcrumb>}
414409
</Breadcrumbs>
415410
<ul>
416411
<li><Link>Breadcrumbs</Link></li>
@@ -424,33 +419,34 @@ Now when you place `Breadcrumbs` inside a `Router`, it automatically has access
424419

425420
Breadcrumbs passes props to its child components, such as the links, via their associated contexts. These contexts are exported so you can also consume them in your own custom components. This enables you to reuse existing components from your app or component library together with React Aria Components.
426421

427-
<ContextTable components={['Link', 'Heading']} docs={docs} />
422+
<ContextTable components={['Link']} docs={docs} />
428423

429-
This example consumes from `HeadingContext` in an existing styled heading component to make it compatible with React Aria Components. The <TypeLink links={docs.links} type={docs.exports.useContextProps} /> hook merges the local props and ref with the ones provided via context by Breadcrumbs.
424+
This example consumes from `LinkContext` in an existing styled link component to make it compatible with React Aria Components. The <TypeLink links={docs.links} type={docs.exports.useContextProps} /> hook merges the local props and ref with the ones provided via context by Breadcrumbs. [useLink](useLink.html) returns DOM props to spread onto the link element.
430425

431426
```tsx
432-
import type {HeadingProps} from 'react-aria-components';
433-
import {HeadingContext, useContextProps} from 'react-aria-components';
427+
import type {LinkProps} from 'react-aria-components';
428+
import {LinkContext, useContextProps} from 'react-aria-components';
434429

435-
const MyCustomHeading = React.forwardRef((props: HeadingProps, ref: React.ForwardedRef<HTMLHeadingElement>) => {
430+
const MyCustomLink = React.forwardRef((props: LinkProps, ref: React.ForwardedRef<HTMLAnchorElement>) => {
436431
// Merge the local props and ref with the ones provided via context.
437432
///- begin highlight -///
438-
[props, ref] = useContextProps(props, ref, HeadingContext);
433+
[props, ref] = useContextProps(props, ref, LinkContext);
439434
///- end highlight -///
440435

441-
// ... your existing Heading component
442-
return <h2 {...props} ref={ref} />;
436+
// ... your existing Link component
437+
let {linkProps} = useLink(props, ref);
438+
return <a {...linkProps} ref={ref} />;
443439
});
444440
```
445441

446-
Now you can use `MyCustomHeading` within `Breadcrumbs`, in place of the builtin React Aria Components `Heading`.
442+
Now you can use `MyCustomLink` within `Breadcrumbs`, in place of the builtin React Aria Components `Link`.
447443

448444
```tsx
449445
<Breadcrumbs>
450-
{/* ... */}
451446
{/*- begin highlight -*/}
452-
<Item><MyCustomHeading>Custom heading</MyCustomHeading></Item>
447+
<Breadcrumb><MyCustomLink>Custom link</MyCustomLink></Breadcrumb>
453448
{/*- end highlight -*/}
449+
{/* ... */}
454450
</Breadcrumbs>
455451
```
456452

0 commit comments

Comments
 (0)