@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 Link component is a navigational element that allows users to navigate between pages or external resources. Built on Joy UI's Link, it provides consistent styling and accessibility features while supporting various visual styles and text hierarchy levels. Links are fundamental to web navigation and should be used to connect related content within your application.
6
+
5
7
  ```tsx
6
8
  <Link
7
9
  children="Link"
@@ -21,4 +23,436 @@
21
23
 
22
24
  ```tsx
23
25
  import { Link } from '@ceed/cds';
26
+
27
+ function MyComponent() {
28
+ return (
29
+ <div>
30
+ {/* Basic link */}
31
+ <Link href="/about">About Us</Link>
32
+
33
+ {/* External link */}
34
+ <Link href="https://example.com" target="_blank" rel="noopener noreferrer">
35
+ External Site
36
+ </Link>
37
+
38
+ {/* Styled link */}
39
+ <Link href="/contact" color="primary" underline="hover">
40
+ Contact
41
+ </Link>
42
+ </div>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Examples
48
+
49
+ ### Basic Link
50
+
51
+ The default link appearance with plain variant.
52
+
53
+ ```tsx
54
+ <Link
55
+ children="Link"
56
+ variant="plain"
57
+ />
58
+ ```
59
+
60
+ ### Variants
61
+
62
+ Links support different visual styles through the variant prop.
63
+
64
+ ```
65
+ <Canvas of={Link.Variants} />
66
+ ```
67
+
68
+ ### Colors
69
+
70
+ Apply different semantic colors to links.
71
+
72
+ ```
73
+ <Canvas of={Link.Colors} />
74
+ ```
75
+
76
+ ### Underline Styles
77
+
78
+ Control the underline behavior of links.
79
+
80
+ ```
81
+ <Canvas of={Link.Underline} />
82
+ ```
83
+
84
+ ### Typography Levels
85
+
86
+ Links can inherit or specify typography levels for different text hierarchies.
87
+
88
+ ```
89
+ <Canvas of={Link.Levels} />
90
+ ```
91
+
92
+ ### Disabled State
93
+
94
+ Links can be disabled to prevent interaction.
95
+
96
+ ```
97
+ <Canvas of={Link.Disabled} />
98
+ ```
99
+
100
+ ### External Links
101
+
102
+ Links to external resources with proper security attributes.
103
+
104
+ ```
105
+ <Canvas of={Link.ExternalLink} />
106
+ ```
107
+
108
+ ### Inline with Text
109
+
110
+ Links can be seamlessly integrated within paragraph text.
111
+
112
+ ```
113
+ <Canvas of={Link.InlineWithText} />
114
+ ```
115
+
116
+ ## When to Use
117
+
118
+ ### ✅ Good Use Cases
119
+
120
+ - **Page navigation**: Moving between different pages or sections within your app
121
+ - **External resources**: Linking to external websites, documentation, or references
122
+ - **In-context references**: Linking related content mentioned in text
123
+ - **Skip navigation**: Accessibility links for keyboard users
124
+ - **Breadcrumbs**: Navigation hierarchy indicators
125
+ - **Footer navigation**: Site-wide links in footer sections
126
+
127
+ ### ❌ When Not to Use
128
+
129
+ - **Actions that don't navigate**: Use Button instead for actions like submitting forms, opening modals, or triggering events
130
+ - **Primary calls to action**: Use Button for prominent actions users should take
131
+ - **Menu items**: Use MenuItem components within navigation menus
132
+ - **Tab navigation**: Use Tabs component for content switching within a page
133
+ - **Cards/clickable areas**: Use Card with onClick or a wrapping anchor element
134
+
135
+ ## Common Use Cases
136
+
137
+ ### Navigation Menu Links
138
+
139
+ ```tsx
140
+ function NavigationMenu() {
141
+ return (
142
+ <nav>
143
+ <Stack direction="row" spacing={3}>
144
+ <Link href="/" color="neutral" underline="none">
145
+ Home
146
+ </Link>
147
+ <Link href="/products" color="neutral" underline="none">
148
+ Products
149
+ </Link>
150
+ <Link href="/about" color="neutral" underline="none">
151
+ About
152
+ </Link>
153
+ <Link href="/contact" color="neutral" underline="none">
154
+ Contact
155
+ </Link>
156
+ </Stack>
157
+ </nav>
158
+ );
159
+ }
160
+ ```
161
+
162
+ ### Inline Text Links
163
+
164
+ ```tsx
165
+ function ArticleContent() {
166
+ return (
167
+ <Typography level="body-md">
168
+ For more information about our services, please visit our{' '}
169
+ <Link href="/services">services page</Link> or read our{' '}
170
+ <Link href="/faq">frequently asked questions</Link>.
171
+ </Typography>
172
+ );
173
+ }
174
+ ```
175
+
176
+ ### External Links with Icon
177
+
178
+ ```tsx
179
+ function ExternalResourceLink() {
180
+ return (
181
+ <Link
182
+ href="https://documentation.example.com"
183
+ target="_blank"
184
+ rel="noopener noreferrer"
185
+ endDecorator={<OpenInNewIcon sx={{ fontSize: 16 }} />}
186
+ >
187
+ View Documentation
188
+ </Link>
189
+ );
190
+ }
191
+ ```
192
+
193
+ ### Footer Links
194
+
195
+ ```tsx
196
+ function FooterLinks() {
197
+ return (
198
+ <Stack direction="row" spacing={2} divider={<Divider orientation="vertical" />}>
199
+ <Link href="/privacy" level="body-sm" color="neutral">
200
+ Privacy Policy
201
+ </Link>
202
+ <Link href="/terms" level="body-sm" color="neutral">
203
+ Terms of Service
204
+ </Link>
205
+ <Link href="/cookies" level="body-sm" color="neutral">
206
+ Cookie Policy
207
+ </Link>
208
+ </Stack>
209
+ );
210
+ }
211
+ ```
212
+
213
+ ### Link with React Router
214
+
215
+ ```tsx
216
+ import { Link as RouterLink } from 'react-router-dom';
217
+
218
+ function RouterIntegration() {
219
+ return (
220
+ <Link component={RouterLink} to="/dashboard">
221
+ Go to Dashboard
222
+ </Link>
223
+ );
224
+ }
225
+ ```
226
+
227
+ ### Skip to Content Link
228
+
229
+ ```tsx
230
+ function SkipLink() {
231
+ return (
232
+ <Link
233
+ href="#main-content"
234
+ sx={{
235
+ position: 'absolute',
236
+ left: '-9999px',
237
+ '&:focus': {
238
+ left: 0,
239
+ zIndex: 1000,
240
+ p: 2,
241
+ bgcolor: 'background.surface',
242
+ },
243
+ }}
244
+ >
245
+ Skip to main content
246
+ </Link>
247
+ );
248
+ }
24
249
  ```
250
+
251
+ ### Download Link
252
+
253
+ ```tsx
254
+ function DownloadLink({ filename, url }) {
255
+ return (
256
+ <Link
257
+ href={url}
258
+ download={filename}
259
+ startDecorator={<DownloadIcon />}
260
+ >
261
+ Download {filename}
262
+ </Link>
263
+ );
264
+ }
265
+ ```
266
+
267
+ ## Props and Customization
268
+
269
+ ### Key Props
270
+
271
+ | Prop | Type | Default | Description |
272
+ | ---------------- | -------------------------------------------------------------- | ----------- | ---------------------------------------- |
273
+ | `href` | `string` | - | The URL to navigate to |
274
+ | `variant` | `'plain' \| 'outlined' \| 'soft' \| 'solid'` | `'plain'` | Visual style variant |
275
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'primary'` | Color scheme |
276
+ | `level` | `string` | - | Typography level (e.g., 'body-md', 'h4') |
277
+ | `underline` | `'none' \| 'hover' \| 'always'` | `'hover'` | Underline behavior |
278
+ | `disabled` | `boolean` | `false` | Disable the link |
279
+ | `startDecorator` | `ReactNode` | - | Element before the link text |
280
+ | `endDecorator` | `ReactNode` | - | Element after the link text |
281
+ | `component` | `ElementType` | `'a'` | The root element type |
282
+
283
+ ### Underline Behavior
284
+
285
+ ```tsx
286
+ <Stack spacing={2}>
287
+ {/* No underline */}
288
+ <Link underline="none" href="#">Never underlined</Link>
289
+
290
+ {/* Underline on hover (default) */}
291
+ <Link underline="hover" href="#">Underline on hover</Link>
292
+
293
+ {/* Always underlined */}
294
+ <Link underline="always" href="#">Always underlined</Link>
295
+ </Stack>
296
+ ```
297
+
298
+ ### With Decorators
299
+
300
+ ```tsx
301
+ <Stack spacing={2}>
302
+ <Link href="#" startDecorator={<HomeIcon />}>
303
+ Home
304
+ </Link>
305
+
306
+ <Link href="#" endDecorator={<ArrowForwardIcon />}>
307
+ Learn more
308
+ </Link>
309
+
310
+ <Link
311
+ href="https://external.com"
312
+ target="_blank"
313
+ endDecorator={<OpenInNewIcon sx={{ fontSize: '1rem' }} />}
314
+ >
315
+ External link
316
+ </Link>
317
+ </Stack>
318
+ ```
319
+
320
+ ### Custom Styling
321
+
322
+ ```tsx
323
+ <Link
324
+ href="#"
325
+ sx={{
326
+ fontWeight: 'bold',
327
+ transition: 'color 0.2s',
328
+ '&:hover': {
329
+ color: 'primary.600',
330
+ },
331
+ }}
332
+ >
333
+ Custom styled link
334
+ </Link>
335
+ ```
336
+
337
+ ## Accessibility
338
+
339
+ Link components include comprehensive accessibility features:
340
+
341
+ ### Semantic HTML
342
+
343
+ - Uses native `<a>` element by default for proper semantics
344
+ - Supports custom components for router integration
345
+
346
+ ### Screen Reader Support
347
+
348
+ - Links announce their destination or purpose
349
+ - Use descriptive link text instead of "click here" or "read more"
350
+
351
+ ```tsx
352
+ // ✅ Good: Descriptive link text
353
+ <Link href="/pricing">View our pricing plans</Link>
354
+
355
+ // ❌ Bad: Non-descriptive link text
356
+ <Link href="/pricing">Click here</Link>
357
+ ```
358
+
359
+ ### Keyboard Navigation
360
+
361
+ - **Tab**: Navigate to and from links
362
+ - **Enter**: Activate the link
363
+ - Links receive visible focus indicators
364
+
365
+ ### External Links
366
+
367
+ Always include proper attributes for external links:
368
+
369
+ ```tsx
370
+ <Link
371
+ href="https://external-site.com"
372
+ target="_blank"
373
+ rel="noopener noreferrer"
374
+ aria-label="Visit external site (opens in new tab)"
375
+ >
376
+ External Site
377
+ </Link>
378
+ ```
379
+
380
+ ### Color Contrast
381
+
382
+ All link colors meet WCAG contrast requirements. Avoid relying solely on color to indicate links - use underlines or other visual indicators.
383
+
384
+ ## Best Practices
385
+
386
+ ### ✅ Do
387
+
388
+ 1. **Use descriptive text**: Link text should describe the destination
389
+
390
+ ```tsx
391
+ // ✅ Good
392
+ <Link href="/documentation">Read the documentation</Link>
393
+
394
+ // ❌ Bad
395
+ <Link href="/documentation">Click here</Link>
396
+ ```
397
+
398
+ 2. **Indicate external links**: Show users when links open in new tabs
399
+
400
+ ```tsx
401
+ <Link href="https://external.com" target="_blank" rel="noopener noreferrer">
402
+ External Resource <OpenInNewIcon sx={{ fontSize: 14 }} />
403
+ </Link>
404
+ ```
405
+
406
+ 3. **Use appropriate colors**: Match link colors to their context and importance
407
+
408
+ 4. **Maintain consistency**: Use consistent link styles throughout your application
409
+
410
+ ### ❌ Don't
411
+
412
+ 1. **Don't use links for actions**: Use Button for non-navigation actions
413
+
414
+ ```tsx
415
+ // ❌ Bad: Using link for action
416
+ <Link onClick={handleSubmit}>Submit Form</Link>
417
+
418
+ // ✅ Good: Using button for action
419
+ <Button onClick={handleSubmit}>Submit Form</Button>
420
+ ```
421
+
422
+ 2. **Don't disable links without reason**: If content is unavailable, explain why
423
+
424
+ 3. **Don't open all external links in new tabs**: Only do so when users might lose context
425
+
426
+ 4. **Don't use link styling for non-interactive elements**
427
+
428
+ ## Performance Considerations
429
+
430
+ ### Prefetching
431
+
432
+ When using with routing libraries, consider implementing link prefetching for improved perceived performance:
433
+
434
+ ```tsx
435
+ // With Next.js
436
+ import NextLink from 'next/link';
437
+
438
+ <Link component={NextLink} href="/page" prefetch>
439
+ Prefetched Page
440
+ </Link>
441
+ ```
442
+
443
+ ### Avoid Unnecessary Re-renders
444
+
445
+ For links in lists or frequently updating components, ensure stable references:
446
+
447
+ ```tsx
448
+ // Using useCallback for event handlers in links
449
+ const handleClick = useCallback((e) => {
450
+ trackLinkClick(e);
451
+ }, []);
452
+
453
+ <Link href="/page" onClick={handleClick}>
454
+ Tracked Link
455
+ </Link>
456
+ ```
457
+
458
+ Link is a fundamental navigation component that ensures consistent, accessible navigation throughout your application. Use it thoughtfully to create clear pathways for users to explore your content.