@ceed/cds 1.19.0 → 1.19.1-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/Markdown.md +832 -0
- package/dist/components/feedback/Dialog.md +605 -3
- package/dist/components/feedback/Modal.md +559 -5
- package/dist/components/feedback/llms.txt +1 -1
- package/dist/components/inputs/Autocomplete.md +734 -2
- package/dist/components/inputs/Calendar.md +655 -1
- package/dist/components/inputs/DatePicker.md +699 -3
- package/dist/components/inputs/DateRangePicker.md +815 -1
- package/dist/components/inputs/MonthPicker.md +626 -4
- package/dist/components/inputs/MonthRangePicker.md +682 -4
- package/dist/components/inputs/Select.md +600 -0
- package/dist/components/layout/Container.md +507 -0
- package/dist/components/navigation/Breadcrumbs.md +470 -0
- package/dist/components/navigation/IconMenuButton.md +693 -0
- package/dist/components/navigation/InsetDrawer.md +775 -118
- package/dist/components/navigation/Link.md +434 -0
- package/dist/components/navigation/MenuButton.md +632 -0
- package/dist/components/navigation/NavigationGroup.md +401 -1
- package/dist/components/navigation/NavigationItem.md +311 -0
- package/dist/components/navigation/Navigator.md +373 -0
- package/dist/components/navigation/Pagination.md +532 -2
- package/dist/components/navigation/Tabs.md +466 -7
- package/dist/components/surfaces/Accordions.md +947 -3
- package/dist/index.cjs +3 -1
- package/dist/index.js +3 -1
- package/dist/llms.txt +1 -1
- package/framer/index.js +16 -16
- package/package.json +3 -2
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
+
The Breadcrumbs component displays a navigation trail that helps users understand their current location within a website's hierarchy and navigate back to previous levels. It shows the path from the homepage to the current page, with each level represented as a clickable link (except for the current page). Breadcrumbs improve user experience by providing context and enabling quick navigation.
|
|
6
|
+
|
|
5
7
|
```tsx
|
|
6
8
|
<Breadcrumbs
|
|
7
9
|
crumbs={[{
|
|
@@ -48,4 +50,472 @@
|
|
|
48
50
|
|
|
49
51
|
```tsx
|
|
50
52
|
import { Breadcrumbs } from '@ceed/cds';
|
|
53
|
+
|
|
54
|
+
function MyComponent() {
|
|
55
|
+
const crumbs = [
|
|
56
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
57
|
+
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
58
|
+
{ label: 'Electronics', type: 'link', linkHref: '/products/electronics' },
|
|
59
|
+
{ label: 'Laptops', type: 'text' }, // Current page (not a link)
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
return <Breadcrumbs crumbs={crumbs} />;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
### Basic Breadcrumbs
|
|
69
|
+
|
|
70
|
+
A simple breadcrumb navigation with three levels.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
<Canvas of={Breadcrumbs.BasicExample} />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Sizes
|
|
77
|
+
|
|
78
|
+
Breadcrumbs support different sizes.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<div>
|
|
82
|
+
<Breadcrumbs {...args} size="sm" />
|
|
83
|
+
<Breadcrumbs {...args} size="md" />
|
|
84
|
+
<Breadcrumbs {...args} size="lg" />
|
|
85
|
+
</div>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### With Custom Separator
|
|
89
|
+
|
|
90
|
+
Customize the separator between breadcrumb items.
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
<Canvas of={Breadcrumbs.CustomSeparator} />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Collapsed Breadcrumbs
|
|
97
|
+
|
|
98
|
+
For deep hierarchies, breadcrumbs can be collapsed with an ellipsis.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<Breadcrumbs
|
|
102
|
+
crumbs={[{
|
|
103
|
+
label: 'Home',
|
|
104
|
+
type: 'link',
|
|
105
|
+
linkHref: '/'
|
|
106
|
+
}, {
|
|
107
|
+
label: 'Catalog',
|
|
108
|
+
type: 'link',
|
|
109
|
+
linkHref: '/'
|
|
110
|
+
}, {
|
|
111
|
+
label: 'Category',
|
|
112
|
+
type: 'link',
|
|
113
|
+
linkHref: '/'
|
|
114
|
+
}, {
|
|
115
|
+
label: 'Subcategory',
|
|
116
|
+
type: 'link',
|
|
117
|
+
linkHref: '/'
|
|
118
|
+
}, {
|
|
119
|
+
label: 'Product',
|
|
120
|
+
type: 'link',
|
|
121
|
+
linkHref: '/'
|
|
122
|
+
}, {
|
|
123
|
+
label: 'Product Detail',
|
|
124
|
+
type: 'link',
|
|
125
|
+
linkHref: '/'
|
|
126
|
+
}, {
|
|
127
|
+
label: 'Owner',
|
|
128
|
+
type: 'text'
|
|
129
|
+
}]}
|
|
130
|
+
collapsed
|
|
131
|
+
/>
|
|
51
132
|
```
|
|
133
|
+
|
|
134
|
+
### Collapsed Variants
|
|
135
|
+
|
|
136
|
+
Configure how many items show at the start and end when collapsed.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
<Canvas of={Breadcrumbs.CollapsedVariants} />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Single Crumb
|
|
143
|
+
|
|
144
|
+
When there's only one item in the path.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<Breadcrumbs
|
|
148
|
+
crumbs={[{
|
|
149
|
+
label: 'Home',
|
|
150
|
+
type: 'text'
|
|
151
|
+
}]}
|
|
152
|
+
collapsed
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Expanded View
|
|
157
|
+
|
|
158
|
+
Show all breadcrumb items without collapsing.
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<Breadcrumbs
|
|
162
|
+
crumbs={[{
|
|
163
|
+
label: 'Home',
|
|
164
|
+
type: 'link',
|
|
165
|
+
linkHref: '/'
|
|
166
|
+
}, {
|
|
167
|
+
label: 'Catalog',
|
|
168
|
+
type: 'link',
|
|
169
|
+
linkHref: '/'
|
|
170
|
+
}, {
|
|
171
|
+
label: 'Category',
|
|
172
|
+
type: 'link',
|
|
173
|
+
linkHref: '/'
|
|
174
|
+
}, {
|
|
175
|
+
label: 'Subcategory',
|
|
176
|
+
type: 'link',
|
|
177
|
+
linkHref: '/'
|
|
178
|
+
}, {
|
|
179
|
+
label: 'Product',
|
|
180
|
+
type: 'link',
|
|
181
|
+
linkHref: '/'
|
|
182
|
+
}, {
|
|
183
|
+
label: 'Product Detail',
|
|
184
|
+
type: 'link',
|
|
185
|
+
linkHref: '/'
|
|
186
|
+
}, {
|
|
187
|
+
label: 'Owner',
|
|
188
|
+
type: 'text'
|
|
189
|
+
}]}
|
|
190
|
+
collapsed={false}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## When to Use
|
|
195
|
+
|
|
196
|
+
### ✅ Good Use Cases
|
|
197
|
+
|
|
198
|
+
- **Deep hierarchies**: Sites with 3+ levels of navigation depth
|
|
199
|
+
- **E-commerce sites**: Product category → subcategory → product navigation
|
|
200
|
+
- **Documentation sites**: Section → topic → article navigation
|
|
201
|
+
- **File management**: Folder → subfolder → file navigation
|
|
202
|
+
- **Admin dashboards**: Module → submodule → detail view
|
|
203
|
+
- **Content management**: Categories and nested content structures
|
|
204
|
+
|
|
205
|
+
### ❌ When Not to Use
|
|
206
|
+
|
|
207
|
+
- **Flat sites**: Single-level navigation doesn't need breadcrumbs
|
|
208
|
+
- **Linear flows**: For sequential processes, use Stepper instead
|
|
209
|
+
- **Primary navigation**: Breadcrumbs supplement, not replace, main navigation
|
|
210
|
+
- **Mobile-first**: Consider if space allows; may need alternative patterns
|
|
211
|
+
- **Dynamic content**: When paths frequently change or are user-specific
|
|
212
|
+
|
|
213
|
+
## Common Use Cases
|
|
214
|
+
|
|
215
|
+
### E-commerce Product Page
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
function ProductPage({ product, category, subcategory }) {
|
|
219
|
+
const crumbs = [
|
|
220
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
221
|
+
{ label: 'Shop', type: 'link', linkHref: '/shop' },
|
|
222
|
+
{ label: category.name, type: 'link', linkHref: `/shop/${category.slug}` },
|
|
223
|
+
{ label: subcategory.name, type: 'link', linkHref: `/shop/${category.slug}/${subcategory.slug}` },
|
|
224
|
+
{ label: product.name, type: 'text' },
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<Box>
|
|
229
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
230
|
+
<ProductDetails product={product} />
|
|
231
|
+
</Box>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Documentation Page
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
function DocPage({ section, topic, article }) {
|
|
240
|
+
const crumbs = [
|
|
241
|
+
{ label: 'Docs', type: 'link', linkHref: '/docs' },
|
|
242
|
+
{ label: section.title, type: 'link', linkHref: `/docs/${section.slug}` },
|
|
243
|
+
{ label: topic.title, type: 'link', linkHref: `/docs/${section.slug}/${topic.slug}` },
|
|
244
|
+
{ label: article.title, type: 'text' },
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<Stack spacing={2}>
|
|
249
|
+
<Breadcrumbs crumbs={crumbs} size="sm" />
|
|
250
|
+
<Typography level="h1">{article.title}</Typography>
|
|
251
|
+
<ArticleContent content={article.content} />
|
|
252
|
+
</Stack>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### File Browser
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
function FileBrowser({ path }) {
|
|
261
|
+
const crumbs = [
|
|
262
|
+
{ label: 'Root', type: 'link', linkHref: '/files' },
|
|
263
|
+
...path.map((folder, index) => ({
|
|
264
|
+
label: folder.name,
|
|
265
|
+
type: index === path.length - 1 ? 'text' : 'link',
|
|
266
|
+
linkHref: `/files/${path.slice(0, index + 1).map(f => f.id).join('/')}`,
|
|
267
|
+
})),
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<Box>
|
|
272
|
+
<Breadcrumbs crumbs={crumbs} collapsed={path.length > 4} />
|
|
273
|
+
<FileList folderId={path[path.length - 1].id} />
|
|
274
|
+
</Box>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Admin Dashboard
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
function UserDetailPage({ user }) {
|
|
283
|
+
const crumbs = [
|
|
284
|
+
{ label: 'Dashboard', type: 'link', linkHref: '/admin' },
|
|
285
|
+
{ label: 'Users', type: 'link', linkHref: '/admin/users' },
|
|
286
|
+
{ label: user.name, type: 'text' },
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<Box>
|
|
291
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
292
|
+
<UserProfile user={user} />
|
|
293
|
+
</Box>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Dynamic Breadcrumbs from Route
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
function DynamicBreadcrumbs() {
|
|
302
|
+
const location = useLocation();
|
|
303
|
+
const pathnames = location.pathname.split('/').filter((x) => x);
|
|
304
|
+
|
|
305
|
+
const crumbs = [
|
|
306
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
307
|
+
...pathnames.map((value, index) => {
|
|
308
|
+
const href = `/${pathnames.slice(0, index + 1).join('/')}`;
|
|
309
|
+
const isLast = index === pathnames.length - 1;
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
label: formatLabel(value),
|
|
313
|
+
type: isLast ? 'text' : 'link',
|
|
314
|
+
linkHref: href,
|
|
315
|
+
};
|
|
316
|
+
}),
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
return <Breadcrumbs crumbs={crumbs} />;
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Props and Customization
|
|
324
|
+
|
|
325
|
+
### Key Props
|
|
326
|
+
|
|
327
|
+
| Prop | Type | Default | Description |
|
|
328
|
+
| ----------------- | ---------------------- | ------- | ----------------------------------------------- |
|
|
329
|
+
| `crumbs` | `Crumb[]` | `[]` | Array of breadcrumb items |
|
|
330
|
+
| `collapsed` | `boolean` | `true` | Collapse middle items with ellipsis |
|
|
331
|
+
| `startCrumbCount` | `number` | `1` | Number of items to show at start when collapsed |
|
|
332
|
+
| `endCrumbCount` | `number` | `3` | Number of items to show at end when collapsed |
|
|
333
|
+
| `separator` | `string` | `'/'` | Separator between items |
|
|
334
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the breadcrumbs |
|
|
335
|
+
|
|
336
|
+
### Crumb Type
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
interface Crumb {
|
|
340
|
+
label: string; // Display text
|
|
341
|
+
type: 'link' | 'text'; // Link or static text
|
|
342
|
+
linkHref?: string; // URL for link type
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Collapsed Behavior
|
|
347
|
+
|
|
348
|
+
When `collapsed` is true and there are more items than `startCrumbCount + endCrumbCount`, middle items are replaced with an ellipsis:
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
// With startCrumbCount=1, endCrumbCount=2, and 6 crumbs:
|
|
352
|
+
// Home / ... / Category / Product
|
|
353
|
+
|
|
354
|
+
<Breadcrumbs
|
|
355
|
+
crumbs={crumbs}
|
|
356
|
+
collapsed={true}
|
|
357
|
+
startCrumbCount={1}
|
|
358
|
+
endCrumbCount={2}
|
|
359
|
+
/>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Custom Styling
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
<Breadcrumbs
|
|
366
|
+
crumbs={crumbs}
|
|
367
|
+
sx={{
|
|
368
|
+
'& .MuiBreadcrumbs-separator': {
|
|
369
|
+
mx: 1,
|
|
370
|
+
color: 'neutral.400',
|
|
371
|
+
},
|
|
372
|
+
'& a': {
|
|
373
|
+
color: 'primary.500',
|
|
374
|
+
textDecoration: 'none',
|
|
375
|
+
'&:hover': {
|
|
376
|
+
textDecoration: 'underline',
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Accessibility
|
|
384
|
+
|
|
385
|
+
Breadcrumbs include important accessibility features:
|
|
386
|
+
|
|
387
|
+
### Semantic HTML
|
|
388
|
+
|
|
389
|
+
- Uses `<nav>` element with `aria-label="breadcrumb"`
|
|
390
|
+
- Structured as an ordered list (`<ol>`) to indicate sequence
|
|
391
|
+
- Current page marked with `aria-current="page"`
|
|
392
|
+
|
|
393
|
+
### ARIA Attributes
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
// Generated HTML structure
|
|
397
|
+
<nav aria-label="breadcrumb">
|
|
398
|
+
<ol>
|
|
399
|
+
<li><a href="/">Home</a></li>
|
|
400
|
+
<li><a href="/products">Products</a></li>
|
|
401
|
+
<li aria-current="page">Current Page</li>
|
|
402
|
+
</ol>
|
|
403
|
+
</nav>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Keyboard Navigation
|
|
407
|
+
|
|
408
|
+
- **Tab**: Navigate through breadcrumb links
|
|
409
|
+
- **Enter**: Activate the focused link
|
|
410
|
+
|
|
411
|
+
### Screen Reader Support
|
|
412
|
+
|
|
413
|
+
- Screen readers announce "breadcrumb navigation"
|
|
414
|
+
- Each link is announced with its position in the sequence
|
|
415
|
+
- Current page is identified as the current location
|
|
416
|
+
|
|
417
|
+
### SEO Benefits
|
|
418
|
+
|
|
419
|
+
Breadcrumbs provide:
|
|
420
|
+
|
|
421
|
+
- Clear site structure for search engines
|
|
422
|
+
- Enhanced search result display (rich snippets)
|
|
423
|
+
- Improved internal linking
|
|
424
|
+
|
|
425
|
+
## Best Practices
|
|
426
|
+
|
|
427
|
+
### ✅ Do
|
|
428
|
+
|
|
429
|
+
1. **Start with Home**: Always begin with a link to the homepage
|
|
430
|
+
|
|
431
|
+
```tsx
|
|
432
|
+
// ✅ Good: Starts with Home
|
|
433
|
+
const crumbs = [
|
|
434
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
435
|
+
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
436
|
+
{ label: 'Laptops', type: 'text' },
|
|
437
|
+
];
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
2. **Make current page non-clickable**: The last item should be text, not a link
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
// ✅ Good: Current page is text
|
|
444
|
+
{ label: 'Current Page', type: 'text' }
|
|
445
|
+
|
|
446
|
+
// ❌ Bad: Current page is a link
|
|
447
|
+
{ label: 'Current Page', type: 'link', linkHref: '/current' }
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
3. **Use clear, concise labels**: Keep labels short but descriptive
|
|
451
|
+
|
|
452
|
+
4. **Maintain consistency**: Use the same labels as page titles or navigation
|
|
453
|
+
|
|
454
|
+
5. **Position at top**: Place breadcrumbs near the top of the page, below the header
|
|
455
|
+
|
|
456
|
+
### ❌ Don't
|
|
457
|
+
|
|
458
|
+
1. **Don't use as primary navigation**: Breadcrumbs supplement main navigation
|
|
459
|
+
|
|
460
|
+
2. **Don't duplicate with page title**: If the current page title is visible, breadcrumbs can end with the parent
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
// Consider ending at parent if title is displayed
|
|
464
|
+
const crumbs = [
|
|
465
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
466
|
+
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
467
|
+
// Omit if "Laptops" is shown as page title
|
|
468
|
+
];
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
3. **Don't use for flat navigation**: Sites with only one or two levels don't need breadcrumbs
|
|
472
|
+
|
|
473
|
+
4. **Don't hide on mobile carelessly**: Either show a simplified version or omit entirely
|
|
474
|
+
|
|
475
|
+
## Performance Considerations
|
|
476
|
+
|
|
477
|
+
### Memoize Crumbs
|
|
478
|
+
|
|
479
|
+
When breadcrumbs are computed from props or state, memoize them:
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
const crumbs = useMemo(() => [
|
|
483
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
484
|
+
{ label: category.name, type: 'link', linkHref: `/category/${category.id}` },
|
|
485
|
+
{ label: product.name, type: 'text' },
|
|
486
|
+
], [category, product]);
|
|
487
|
+
|
|
488
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Use Collapsed for Deep Hierarchies
|
|
492
|
+
|
|
493
|
+
For paths with many levels, use collapsing to reduce DOM elements:
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
<Breadcrumbs
|
|
497
|
+
crumbs={deepHierarchy}
|
|
498
|
+
collapsed={true}
|
|
499
|
+
startCrumbCount={1}
|
|
500
|
+
endCrumbCount={2}
|
|
501
|
+
/>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Lazy Load for Dynamic Breadcrumbs
|
|
505
|
+
|
|
506
|
+
If breadcrumb data requires fetching, ensure it doesn't block page render:
|
|
507
|
+
|
|
508
|
+
```tsx
|
|
509
|
+
function Page() {
|
|
510
|
+
const { data: breadcrumbData, isLoading } = useBreadcrumbPath();
|
|
511
|
+
|
|
512
|
+
return (
|
|
513
|
+
<Box>
|
|
514
|
+
{!isLoading && <Breadcrumbs crumbs={breadcrumbData} />}
|
|
515
|
+
<PageContent />
|
|
516
|
+
</Box>
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Breadcrumbs are a simple but powerful navigation aid that helps users understand where they are and navigate efficiently. Use them consistently across your site to improve user experience and SEO.
|