@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.
@@ -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.