@fpkit/acss 1.0.0 → 2.0.0

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.
Files changed (63) hide show
  1. package/README.md +60 -0
  2. package/libs/{chunk-7XPFW7CB.js → chunk-43TK2ICH.js} +2 -2
  3. package/libs/chunk-5PJYLVFY.cjs +17 -0
  4. package/libs/chunk-5PJYLVFY.cjs.map +1 -0
  5. package/libs/chunk-E4OSROCA.cjs +17 -0
  6. package/libs/chunk-E4OSROCA.cjs.map +1 -0
  7. package/libs/chunk-KVKQLRJG.js +10 -0
  8. package/libs/chunk-KVKQLRJG.js.map +1 -0
  9. package/libs/{chunk-QVW6W76L.cjs → chunk-MGPWZRBX.cjs} +3 -3
  10. package/libs/chunk-NNTBIHSD.js +8 -0
  11. package/libs/chunk-NNTBIHSD.js.map +1 -0
  12. package/libs/{chunk-X3JCTEPD.js → chunk-QKHPHMG2.js} +2 -2
  13. package/libs/{chunk-T4T6GWYQ.cjs → chunk-R7NLLZU2.cjs} +3 -3
  14. package/libs/{chunk-X5LGFCWG.js → chunk-UJAQVHWC.js} +3 -3
  15. package/libs/{chunk-DKTHCQ5P.cjs → chunk-X5RKCLDC.cjs} +3 -3
  16. package/libs/components/breadcrumbs/breadcrumb.cjs +5 -5
  17. package/libs/components/breadcrumbs/breadcrumb.d.cts +1 -1
  18. package/libs/components/breadcrumbs/breadcrumb.d.ts +1 -1
  19. package/libs/components/breadcrumbs/breadcrumb.js +2 -2
  20. package/libs/components/button.cjs +3 -3
  21. package/libs/components/button.d.cts +1 -1
  22. package/libs/components/button.d.ts +1 -1
  23. package/libs/components/button.js +1 -1
  24. package/libs/components/dialog/dialog.cjs +4 -4
  25. package/libs/components/dialog/dialog.js +2 -2
  26. package/libs/components/link/link.cjs +11 -3
  27. package/libs/components/link/link.d.cts +131 -3
  28. package/libs/components/link/link.d.ts +131 -3
  29. package/libs/components/link/link.js +1 -1
  30. package/libs/components/modal.cjs +3 -3
  31. package/libs/components/modal.js +2 -2
  32. package/libs/hooks.cjs +3 -3
  33. package/libs/hooks.d.cts +1 -1
  34. package/libs/hooks.d.ts +1 -1
  35. package/libs/hooks.js +2 -2
  36. package/libs/index.cjs +12 -12
  37. package/libs/index.d.cts +237 -2
  38. package/libs/index.d.ts +237 -2
  39. package/libs/index.js +5 -5
  40. package/package.json +2 -2
  41. package/src/components/breadcrumbs/breadcrumb.test.tsx +1 -2
  42. package/src/components/buttons/README.mdx +19 -9
  43. package/src/components/buttons/button.tsx +19 -15
  44. package/src/components/link/link.stories.tsx +205 -8
  45. package/src/components/link/link.test.tsx +1 -1
  46. package/src/components/link/link.tsx +22 -0
  47. package/src/components/link/link.types.ts +11 -3
  48. package/src/docs/fpkit-developer.mdx +131 -53
  49. package/libs/chunk-33PNJ4LO.cjs +0 -15
  50. package/libs/chunk-33PNJ4LO.cjs.map +0 -1
  51. package/libs/chunk-GT77BX4L.cjs +0 -17
  52. package/libs/chunk-GT77BX4L.cjs.map +0 -1
  53. package/libs/chunk-OVWLQYMK.js +0 -10
  54. package/libs/chunk-OVWLQYMK.js.map +0 -1
  55. package/libs/chunk-UEPAWMDF.js +0 -8
  56. package/libs/chunk-UEPAWMDF.js.map +0 -1
  57. package/libs/link-5192f411.d.ts +0 -323
  58. /package/libs/{chunk-7XPFW7CB.js.map → chunk-43TK2ICH.js.map} +0 -0
  59. /package/libs/{chunk-QVW6W76L.cjs.map → chunk-MGPWZRBX.cjs.map} +0 -0
  60. /package/libs/{chunk-X3JCTEPD.js.map → chunk-QKHPHMG2.js.map} +0 -0
  61. /package/libs/{chunk-T4T6GWYQ.cjs.map → chunk-R7NLLZU2.cjs.map} +0 -0
  62. /package/libs/{chunk-X5LGFCWG.js.map → chunk-UJAQVHWC.js.map} +0 -0
  63. /package/libs/{chunk-DKTHCQ5P.cjs.map → chunk-X5RKCLDC.cjs.map} +0 -0
@@ -6,7 +6,8 @@ import { Meta } from "@storybook/addon-docs/blocks";
6
6
 
7
7
  **Build applications with @fpkit/acss using Claude Code assistance**
8
8
 
9
- A portable Claude Code skill for developers using the @fpkit/acss component library in their applications.
9
+ A portable Claude Code skill for developers using the @fpkit/acss component
10
+ library in their applications.
10
11
 
11
12
  ---
12
13
 
@@ -14,11 +15,10 @@ A portable Claude Code skill for developers using the @fpkit/acss component libr
14
15
 
15
16
  This skill helps you:
16
17
 
17
- ✅ **Compose custom components** from fpkit primitives
18
- ✅ **Validate CSS variables** against fpkit conventions
19
- ✅ **Maintain accessibility** (WCAG 2.1 Level AA)
20
- **Follow best practices** for component composition
21
- ✅ **TypeScript support** for type-safe compositions
18
+ ✅ **Compose custom components** from fpkit primitives ✅ **Validate CSS
19
+ variables** against fpkit conventions ✅ **Maintain accessibility** (WCAG 2.1
20
+ Level AA) ✅ **Follow best practices** for component composition ✅ **TypeScript
21
+ support** for type-safe compositions
22
22
 
23
23
  ---
24
24
 
@@ -43,6 +43,36 @@ ls ~/.claude/skills/fpkit-developer/
43
43
  # Should show: README.md SKILL.md references/ scripts/
44
44
  ```
45
45
 
46
+ ### Option 3: Install via gitpick (Recommended)
47
+
48
+ The fastest and easiest way to install directly from GitHub:
49
+
50
+ ### User-level installation (available in all projects)
51
+
52
+ ```bash
53
+ npx gitpick shawn-sandy/acss/tree/main/.claude/skills/fpkit-developer ~/.claude/skills/fpkit-developer
54
+ ```
55
+
56
+ ### Project-specific installation
57
+
58
+ ```bash
59
+ cd /path/to/your/project
60
+ ```
61
+
62
+ ```bash
63
+ npx gitpick shawn-sandy/acss/tree/main/.claude/skills/fpkit-developer ./.claude/skills/fpkit-developer
64
+ ```
65
+
66
+ **Why gitpick?**
67
+
68
+ - ✅ Single command - no ZIP download or extraction
69
+ - ✅ Selective cloning - only downloads the skill folder
70
+ - ✅ Zero dependencies - lightweight and fast (`<35kb`)
71
+ - ✅ Always up-to-date - pulls latest version from GitHub
72
+
73
+ **Learn more:**
74
+ [github.com/nrjdalal/gitpick](https://github.com/nrjdalal/gitpick)
75
+
46
76
  3. **Restart Claude Code** to load the skill.
47
77
 
48
78
  ### Option 2: Clone from GitHub (Future)
@@ -79,9 +109,11 @@ pnpm add @fpkit/acss
79
109
 
80
110
  ### Activating the Skill
81
111
 
82
- The skill activates automatically when you're working in a project using @fpkit/acss.
112
+ The skill activates automatically when you're working in a project using
113
+ @fpkit/acss.
83
114
 
84
115
  **Trigger phrases:**
116
+
85
117
  - "Build a custom component using fpkit"
86
118
  - "Create a [component name] with @fpkit/acss"
87
119
  - "Compose a [feature] from fpkit components"
@@ -92,29 +124,37 @@ The skill activates automatically when you're working in a project using @fpkit/
92
124
  #### Example 1: Composing a StatusButton
93
125
 
94
126
  **You:**
127
+
95
128
  > Create a StatusButton component that combines Button and Badge from fpkit
96
129
 
97
130
  **Claude Code:**
131
+
98
132
  > I'll help you create a StatusButton by composing fpkit components.
99
133
  >
100
134
  > ```tsx
101
- > import { Button, Badge } from '@fpkit/acss'
135
+ > import { Button, Badge } from "@fpkit/acss";
102
136
  >
103
- > export interface StatusButtonProps extends React.ComponentProps<typeof Button> {
104
- > status: 'active' | 'inactive' | 'pending'
137
+ > export interface StatusButtonProps
138
+ > extends React.ComponentProps<typeof Button> {
139
+ > status: "active" | "inactive" | "pending";
105
140
  > }
106
141
  >
107
- > export const StatusButton = ({ status, children, ...props }: StatusButtonProps) => {
142
+ > export const StatusButton = ({
143
+ > status,
144
+ > children,
145
+ > ...props
146
+ > }: StatusButtonProps) => {
108
147
  > return (
109
148
  > <Button {...props}>
110
149
  > {children}
111
150
  > <Badge variant={status}>{status}</Badge>
112
151
  > </Button>
113
- > )
114
- > }
152
+ > );
153
+ > };
115
154
  > ```
116
155
  >
117
156
  > This composition:
157
+ >
118
158
  > - ✅ Extends Button props with TypeScript
119
159
  > - ✅ Preserves all fpkit Button functionality
120
160
  > - ✅ Maintains accessibility (ARIA, keyboard support)
@@ -123,17 +163,19 @@ The skill activates automatically when you're working in a project using @fpkit/
123
163
  #### Example 2: Custom Styling
124
164
 
125
165
  **You:**
166
+
126
167
  > How do I customize the button padding in my StatusButton?
127
168
 
128
169
  **Claude Code:**
170
+
129
171
  > You can customize fpkit components using CSS variables:
130
172
  >
131
173
  > ```tsx
132
174
  > <StatusButton
133
175
  > status="active"
134
176
  > style={{
135
- > '--btn-padding-inline': '2rem',
136
- > '--btn-padding-block': '1rem',
177
+ > "--btn-padding-inline": "2rem",
178
+ > "--btn-padding-block": "1rem",
137
179
  > }}
138
180
  > >
139
181
  > Server Status
@@ -155,37 +197,38 @@ The skill activates automatically when you're working in a project using @fpkit/
155
197
  #### Example 3: Accessibility Check
156
198
 
157
199
  **You:**
200
+
158
201
  > Is this component accessible?
159
202
 
160
203
  **Claude Code:**
204
+
161
205
  > Let me check accessibility:
162
206
  >
163
- > ✅ Uses semantic Button element
164
- > Inherits keyboard navigation
165
- > ✅ Maintains focus indicators
166
- > ⚠️ Add `aria-label` if using icon-only button
207
+ > ✅ Uses semantic Button element ✅ Inherits keyboard navigation ✅ Maintains
208
+ > focus indicators ⚠️ Add `aria-label` if using icon-only button
167
209
  >
168
210
  > Example with accessibility:
211
+ >
169
212
  > ```tsx
170
- > <StatusButton
171
- > status="active"
172
- > aria-label="Server status: active"
173
- > >
213
+ > <StatusButton status="active" aria-label="Server status: active">
174
214
  > <ServerIcon aria-hidden="true" />
175
215
  > </StatusButton>
176
216
  > ```
177
217
  >
178
218
  > Run automated testing:
219
+ >
179
220
  > ```tsx
180
- > import { axe, toHaveNoViolations } from 'jest-axe'
221
+ > import { axe, toHaveNoViolations } from "jest-axe";
181
222
  >
182
- > expect.extend(toHaveNoViolations)
223
+ > expect.extend(toHaveNoViolations);
183
224
  >
184
- > it('should not have accessibility violations', async () => {
185
- > const { container } = render(<StatusButton status="active">Server</StatusButton>)
186
- > const results = await axe(container)
187
- > expect(results).toHaveNoViolations()
188
- > })
225
+ > it("should not have accessibility violations", async () => {
226
+ > const { container } = render(
227
+ > <StatusButton status="active">Server</StatusButton>
228
+ > );
229
+ > const results = await axe(container);
230
+ > expect(results).toHaveNoViolations();
231
+ > });
189
232
  > ```
190
233
 
191
234
  ---
@@ -226,10 +269,12 @@ python ~/.claude/skills/fpkit-developer/scripts/validate_css_vars.py .
226
269
  ```
227
270
 
228
271
  **What it checks:**
272
+
229
273
  - ✅ Naming pattern: `--{component}-{property}`
230
274
  - ✅ rem units (not px)
231
275
  - ✅ Approved abbreviations: `bg`, `fs`, `fw`, `radius`, `gap`
232
- - ✅ Full words for: `padding`, `margin`, `color`, `border`, `display`, `width`, `height`
276
+ - ✅ Full words for: `padding`, `margin`, `color`, `border`, `display`, `width`,
277
+ `height`
233
278
 
234
279
  ### 3. Reference Documentation
235
280
 
@@ -256,6 +301,7 @@ cat ~/.claude/skills/fpkit-developer/references/storybook.md
256
301
  ```
257
302
 
258
303
  Or ask Claude Code to reference them:
304
+
259
305
  > "Show me composition patterns from the fpkit skill"
260
306
 
261
307
  ### 4. Documentation Sync
@@ -274,12 +320,13 @@ Keep documentation up-to-date with the latest fpkit guides:
274
320
  ```
275
321
 
276
322
  **When to sync:**
323
+
277
324
  - After updating @fpkit/acss to a new version
278
325
  - When fpkit documentation is updated
279
326
  - Periodically to ensure you have the latest patterns and examples
280
327
 
281
- **Configuration:**
282
- Edit `config.json` to customize documentation source:
328
+ **Configuration:** Edit `config.json` to customize documentation source:
329
+
283
330
  - `docsSource`: "local" (default) or "online"
284
331
  - `onlineDocsUrl`: GitHub raw URL for online fallback
285
332
  - `fpkitDocsPath`: Relative path to fpkit docs in monorepo
@@ -309,10 +356,11 @@ fpkit-developer/
309
356
 
310
357
  ## Compatibility
311
358
 
312
- - **@fpkit/acss:** v1.x
359
+ - **@fpkit/acss:** v1.0.0+
313
360
  - **Claude Code:** Latest version
314
361
  - **Python:** 3.7+ (for validation script)
315
- - **Node.js:** 18+ (for npm package)
362
+ - **Node.js:** 22.12.0+ (for npm package)
363
+ - **Storybook:** 10.x (if using in monorepo)
316
364
 
317
365
  ---
318
366
 
@@ -321,33 +369,43 @@ fpkit-developer/
321
369
  The skill knows about these fpkit components:
322
370
 
323
371
  **Layout:**
372
+
324
373
  - Header, Main, Footer, Aside, Nav
325
374
 
326
375
  **Content:**
376
+
327
377
  - Heading, Text, Badge, Tag
328
378
 
329
379
  **Forms:**
380
+
330
381
  - Input, Field, FieldLabel, FieldInput, FieldTextarea
331
382
 
332
383
  **Buttons & Actions:**
384
+
333
385
  - Button
334
386
 
335
387
  **Cards:**
388
+
336
389
  - Card, CardHeader, CardTitle, CardContent, CardFooter
337
390
 
338
391
  **Dialogs:**
392
+
339
393
  - Dialog, Modal
340
394
 
341
395
  **Feedback:**
396
+
342
397
  - Alert
343
398
 
344
399
  **Data Display:**
400
+
345
401
  - Table, List
346
402
 
347
403
  **Interactive:**
404
+
348
405
  - Details, Popover
349
406
 
350
407
  **Icons:**
408
+
351
409
  - Icon library
352
410
 
353
411
  ---
@@ -357,34 +415,36 @@ The skill knows about these fpkit components:
357
415
  ### Workflow 1: Build a Custom Component
358
416
 
359
417
  1. **Describe what you want:**
360
- > "Create a ConfirmButton that shows a confirmation dialog before executing an action"
418
+
419
+ > "Create a ConfirmButton that shows a confirmation dialog before executing
420
+ > an action"
361
421
 
362
422
  2. **Claude Code identifies fpkit components:**
423
+
363
424
  - Button (for the trigger)
364
425
  - Dialog (for the confirmation modal)
365
426
 
366
427
  3. **Composes solution:**
428
+
367
429
  ```tsx
368
- import { Button, Dialog } from '@fpkit/acss'
369
- import { useState } from 'react'
430
+ import { Button, Dialog } from "@fpkit/acss";
431
+ import { useState } from "react";
370
432
 
371
433
  export const ConfirmButton = ({ onConfirm, children, ...props }) => {
372
- const [showConfirm, setShowConfirm] = useState(false)
434
+ const [showConfirm, setShowConfirm] = useState(false);
373
435
  // ... implementation
374
- }
436
+ };
375
437
  ```
376
438
 
377
439
  4. **Validates accessibility:**
440
+
378
441
  - Keyboard navigation ✅
379
442
  - ARIA attributes ✅
380
443
  - Focus management ✅
381
444
 
382
445
  5. **Provides usage example:**
383
446
  ```tsx
384
- <ConfirmButton
385
- variant="danger"
386
- onConfirm={handleDelete}
387
- >
447
+ <ConfirmButton variant="danger" onConfirm={handleDelete}>
388
448
  Delete Account
389
449
  </ConfirmButton>
390
450
  ```
@@ -392,9 +452,11 @@ The skill knows about these fpkit components:
392
452
  ### Workflow 2: Customize Styling
393
453
 
394
454
  1. **Ask about customization:**
455
+
395
456
  > "How do I make the buttons larger and change the primary color?"
396
457
 
397
458
  2. **Claude Code provides CSS variable solution:**
459
+
398
460
  ```css
399
461
  :root {
400
462
  --btn-padding-inline: 2rem;
@@ -412,9 +474,11 @@ The skill knows about these fpkit components:
412
474
  ### Workflow 3: Ensure Accessibility
413
475
 
414
476
  1. **Request accessibility review:**
477
+
415
478
  > "Check if my component is accessible"
416
479
 
417
480
  2. **Claude Code reviews:**
481
+
418
482
  - Semantic HTML ✅
419
483
  - Keyboard navigation ✅
420
484
  - ARIA attributes ⚠️ (suggests improvements)
@@ -422,7 +486,7 @@ The skill knows about these fpkit components:
422
486
 
423
487
  3. **Suggests automated testing:**
424
488
  ```tsx
425
- import { axe, toHaveNoViolations } from 'jest-axe'
489
+ import { axe, toHaveNoViolations } from "jest-axe";
426
490
  // ... test code
427
491
  ```
428
492
 
@@ -433,24 +497,29 @@ The skill knows about these fpkit components:
433
497
  The skill helps you follow these best practices:
434
498
 
435
499
  ### ✅ Composition Over Duplication
500
+
436
501
  - Reuse fpkit components instead of creating from scratch
437
502
  - Compose 2-3 fpkit components to build complex UIs
438
503
 
439
504
  ### ✅ Type Safety
505
+
440
506
  - Extend fpkit prop types with TypeScript
441
507
  - Preserve all fpkit functionality with `...props` spreading
442
508
 
443
509
  ### ✅ Accessibility
510
+
444
511
  - Maintain ARIA attributes from fpkit
445
512
  - Support keyboard navigation
446
513
  - Ensure color contrast meets WCAG AA
447
514
 
448
515
  ### ✅ Styling Conventions
516
+
449
517
  - Use CSS variables for customization
450
518
  - Use rem units (not px)
451
519
  - Follow naming pattern: `--{component}-{property}`
452
520
 
453
521
  ### ✅ Testing
522
+
454
523
  - Focus on testing your composition logic
455
524
  - Trust fpkit's internal testing
456
525
  - Use jest-axe for accessibility testing
@@ -462,6 +531,7 @@ The skill helps you follow these best practices:
462
531
  ### Skill Not Activating
463
532
 
464
533
  1. **Check installation path:**
534
+
465
535
  ```bash
466
536
  ls ~/.claude/skills/fpkit-developer/SKILL.md
467
537
  ```
@@ -476,11 +546,13 @@ The skill helps you follow these best practices:
476
546
  ### Validation Script Not Working
477
547
 
478
548
  1. **Check Python version:**
549
+
479
550
  ```bash
480
551
  python --version # Should be 3.7+
481
552
  ```
482
553
 
483
554
  2. **Make script executable:**
555
+
484
556
  ```bash
485
557
  chmod +x ~/.claude/skills/fpkit-developer/scripts/validate_css_vars.py
486
558
  ```
@@ -508,7 +580,8 @@ cat ~/.claude/skills/fpkit-developer/references/composition.md
508
580
 
509
581
  This skill is part of the @fpkit/acss project. To suggest improvements:
510
582
 
511
- 1. **Report issues:** [GitHub Issues](https://github.com/shawn-sandy/acss/issues)
583
+ 1. **Report issues:**
584
+ [GitHub Issues](https://github.com/shawn-sandy/acss/issues)
512
585
  2. **Suggest features:** Open a discussion on GitHub
513
586
  3. **Contribute code:** Submit a pull request
514
587
 
@@ -517,13 +590,19 @@ This skill is part of the @fpkit/acss project. To suggest improvements:
517
590
  ## Resources
518
591
 
519
592
  ### Official Documentation
520
- - **[fpkit Docs](https://github.com/shawn-sandy/acss/tree/main/packages/fpkit/docs)** - Complete documentation
593
+
594
+ - **[fpkit Docs](https://github.com/shawn-sandy/acss/tree/main/packages/fpkit/docs)** -
595
+ Complete documentation
521
596
  - **[Storybook](https://fpkit.netlify.app/)** - Interactive component examples
522
- - **[npm Package](https://www.npmjs.com/package/@fpkit/acss)** - Installation and API reference
597
+ - **[npm Package](https://www.npmjs.com/package/@fpkit/acss)** - Installation
598
+ and API reference
523
599
 
524
600
  ### Learning Resources
525
- - **[WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)** - Accessibility standards
526
- - **[React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)** - TypeScript patterns
601
+
602
+ - **[WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)** -
603
+ Accessibility standards
604
+ - **[React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)** -
605
+ TypeScript patterns
527
606
  - **[Testing Library](https://testing-library.com/)** - Testing best practices
528
607
 
529
608
  ---
@@ -536,9 +615,8 @@ MIT License - Same as @fpkit/acss
536
615
 
537
616
  ## Version
538
617
 
539
- **Skill Version:** 1.0.0
540
- **Compatible with:** @fpkit/acss v1.x
541
- **Last Updated:** 2025-01-06
618
+ **Skill Version:** 1.0.0 **Compatible with:** @fpkit/acss v1.0.0+ **Last
619
+ Updated:** 2025-11-15
542
620
 
543
621
  ---
544
622
 
@@ -1,15 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkENTCUJ3A_cjs = require('./chunk-ENTCUJ3A.cjs');
4
- var n = require('react');
5
-
6
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
-
8
- var n__default = /*#__PURE__*/_interopDefault(n);
9
-
10
- var s=n__default.default.forwardRef(({href:a,target:o,rel:r,children:p,styles:m,prefetch:t=!1,btnStyle:d,onClick:c,onPointerDown:l,...k},u)=>{let y=n__default.default.useMemo(()=>{if(o==="_blank"){let e=new Set(["noopener","noreferrer"]);return t&&e.add("prefetch"),r&&r.split(/\s+/).forEach(i=>{i&&e.add(i);}),Array.from(e).join(" ")}return r},[o,r,t]);return n__default.default.createElement(chunkENTCUJ3A_cjs.a,{as:"a",ref:u,href:a,target:o,rel:y,styles:m,"data-btn":d,onClick:c,onPointerDown:l,...k},p)});s.displayName="Link";var w=s;
11
-
12
- exports.a = s;
13
- exports.b = w;
14
- //# sourceMappingURL=out.js.map
15
- //# sourceMappingURL=chunk-33PNJ4LO.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/link/link.tsx"],"names":["React","Link","href","target","rel","children","styles","prefetch","btnStyle","onClick","onPointerDown","props","ref","computedRel","securityTokens","token","ui_default","link_default"],"mappings":"yCAAA,OAAOA,MAAW,QA+FX,IAAMC,EAAOD,EAAM,WACxB,CACE,CACE,KAAAE,EACA,OAAAC,EACA,IAAAC,EACA,SAAAC,EACA,OAAAC,EACA,SAAAC,EAAW,GACX,SAAAC,EACA,QAAAC,EACA,cAAAC,EACA,GAAGC,CACL,EACAC,IACG,CAWH,IAAMC,EAAcb,EAAM,QAAQ,IAAM,CACtC,GAAIG,IAAW,SAAU,CAEvB,IAAMW,EAAiB,IAAI,IAAI,CAAC,WAAY,YAAY,CAAC,EAGzD,OAAIP,GACFO,EAAe,IAAI,UAAU,EAI3BV,GACFA,EAAI,MAAM,KAAK,EAAE,QAASW,GAAU,CAC9BA,GAAOD,EAAe,IAAIC,CAAK,CACrC,CAAC,EAGI,MAAM,KAAKD,CAAc,EAAE,KAAK,GAAG,CAC5C,CAGA,OAAOV,CACT,EAAG,CAACD,EAAQC,EAAKG,CAAQ,CAAC,EAE1B,OACEP,EAAA,cAACgB,EAAA,CACC,GAAG,IACH,IAAKJ,EACL,KAAMV,EACN,OAAQC,EACR,IAAKU,EACL,OAAQP,EACR,WAAUE,EACV,QAASC,EACT,cAAeC,EACd,GAAGC,GAEHN,CACH,CAEJ,CACF,EAEAJ,EAAK,YAAc,OAEnB,IAAOgB,EAAQhB","sourcesContent":["import React from \"react\";\nimport UI from \"../ui\";\nimport type { LinkProps } from \"./link.types\";\n\n/**\n * Link - A semantic, accessible anchor component with enhanced security and styling.\n *\n * The Link component renders accessible `<a>` elements with automatic security\n * attributes for external links, customizable styling variants, and full WCAG 2.1\n * AA compliance. It supports traditional text links, button-styled links, and\n * programmatic focus management via ref forwarding.\n *\n * ## Features\n *\n * - 🔒 **Automatic Security**: External links get `rel=\"noopener noreferrer\"`\n * - ♿ **WCAG 2.1 AA Compliant**: Accessible focus indicators and semantic HTML\n * - 🎨 **Flexible Styling**: Text links, button links, and pill variants\n * - ⚡ **Performance**: Optional prefetch hints for faster navigation\n * - 🎯 **Ref Forwarding**: Direct DOM access for focus management and scroll\n * - 🧪 **Type-Safe**: Full TypeScript support with comprehensive prop types\n *\n * ## Accessibility\n *\n * - ✅ Semantic `<a>` element for proper keyboard navigation\n * - ✅ Focus indicators meet WCAG 2.4.7 (3:1 contrast ratio)\n * - ✅ Screen readers announce link purpose and destination\n * - ✅ External links include security attributes automatically\n * - ✅ Supports `aria-label` for icon-only or ambiguous links\n * - ✅ Ref forwarding enables skip-link patterns\n *\n * @example\n * // Basic internal link\n * <Link href=\"/about\">About Us</Link>\n *\n * @example\n * // External link with automatic security\n * <Link href=\"https://example.com\" target=\"_blank\">\n * Visit Example\n * </Link>\n *\n * @example\n * // Button-styled call-to-action link\n * <Link href=\"/signup\">\n * <b>Get Started</b>\n * </Link>\n *\n * @example\n * // Icon-only link with accessible label\n * <Link href=\"/settings\" aria-label=\"Open settings\">\n * <SettingsIcon aria-hidden=\"true\" />\n * </Link>\n *\n * @example\n * // Analytics tracking with onClick (includes keyboard users)\n * <Link\n * href=\"/products\"\n * onClick={(e) => trackEvent('link_click', { href: '/products' })}\n * >\n * Browse Products\n * </Link>\n *\n * @example\n * // Skip link with ref forwarding for focus management\n * const mainRef = useRef<HTMLAnchorElement>(null);\n *\n * <Link ref={mainRef} href=\"#main-content\">\n * Skip to main content\n * </Link>\n *\n * @example\n * // Custom styled link with CSS variables\n * <Link\n * href=\"/products\"\n * styles={{\n * '--link-color': '#0066cc',\n * '--link-decoration': 'underline',\n * }}\n * >\n * Browse Products\n * </Link>\n *\n * @example\n * // ✅ GOOD: Descriptive link text\n * <Link href=\"/docs/installation\">\n * Read installation guide\n * </Link>\n *\n * @example\n * // ❌ BAD: Generic link text (poor for screen readers)\n * <Link href=\"/docs/installation\">\n * Click here\n * </Link>\n *\n * @see {@link LinkProps} for complete prop documentation\n */\nexport const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(\n (\n {\n href,\n target,\n rel,\n children,\n styles,\n prefetch = false,\n btnStyle,\n onClick,\n onPointerDown,\n ...props\n },\n ref\n ) => {\n /**\n * Compute the final `rel` attribute value with security defaults.\n *\n * For external links (target=\"_blank\"), we merge user-provided `rel` values\n * with security defaults `noopener noreferrer` to prevent:\n * - window.opener exploitation (noopener)\n * - Referrer header leakage (noreferrer)\n *\n * If prefetch is enabled, we also add the `prefetch` hint.\n */\n const computedRel = React.useMemo(() => {\n if (target === \"_blank\") {\n // Start with security defaults\n const securityTokens = new Set([\"noopener\", \"noreferrer\"]);\n\n // Add prefetch if enabled\n if (prefetch) {\n securityTokens.add(\"prefetch\");\n }\n\n // Merge with user-provided rel tokens (if any)\n if (rel) {\n rel.split(/\\s+/).forEach((token) => {\n if (token) securityTokens.add(token);\n });\n }\n\n return Array.from(securityTokens).join(\" \");\n }\n\n // For non-external links, use provided rel as-is\n return rel;\n }, [target, rel, prefetch]);\n\n return (\n <UI\n as=\"a\"\n ref={ref}\n href={href}\n target={target}\n rel={computedRel}\n styles={styles}\n data-btn={btnStyle}\n onClick={onClick}\n onPointerDown={onPointerDown}\n {...props}\n >\n {children}\n </UI>\n );\n }\n);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n"]}
@@ -1,17 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkPNWIRCG3_cjs = require('./chunk-PNWIRCG3.cjs');
4
- var chunkTON2YGMD_cjs = require('./chunk-TON2YGMD.cjs');
5
- var chunkENTCUJ3A_cjs = require('./chunk-ENTCUJ3A.cjs');
6
- var D = require('react');
7
-
8
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
-
10
- var D__default = /*#__PURE__*/_interopDefault(D);
11
-
12
- var a=({type:r="button",children:p,styles:i,disabled:l,isDisabled:n,classes:m,onPointerDown:d,onPointerOver:u,onPointerLeave:b,onClick:y,onKeyDown:P,...c})=>{let f=chunkPNWIRCG3_cjs.a(l,n),{disabledProps:t,handlers:B}=chunkTON2YGMD_cjs.a(f,{handlers:{onClick:y,onPointerDown:d,onKeyDown:P},className:m});return D__default.default.createElement(chunkENTCUJ3A_cjs.a,{as:"button",type:r,"aria-disabled":t["aria-disabled"],onPointerOver:u,onPointerLeave:b,style:i,className:t.className,...B,...c},p)},x=a;a.displayName="Button";
13
-
14
- exports.a = a;
15
- exports.b = x;
16
- //# sourceMappingURL=out.js.map
17
- //# sourceMappingURL=chunk-GT77BX4L.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/buttons/button.tsx"],"names":["React","Button","type","children","styles","disabled","isDisabled","classes","onPointerDown","onPointerOver","onPointerLeave","onClick","onKeyDown","props","isActuallyDisabled","resolveDisabledState","disabledProps","handlers","useDisabledState","ui_default","button_default"],"mappings":"2HACA,OAAOA,MAAW,QAmEX,IAAMC,EAAS,CAAC,CACrB,KAAAC,EAAO,SACP,SAAAC,EACA,OAAAC,EACA,SAAAC,EACA,WAAAC,EACA,QAAAC,EACA,cAAAC,EACA,cAAAC,EACA,eAAAC,EACA,QAAAC,EACA,UAAAC,EACA,GAAGC,CACL,IAAmB,CAEjB,IAAMC,EAAqBC,EAAqBV,EAAUC,CAAU,EAG9D,CAAE,cAAAU,EAAe,SAAAC,CAAS,EAAIC,EAClCJ,EACA,CACE,SAAU,CACR,QAAAH,EACA,cAAAH,EACA,UAAAI,CACF,EAEA,UAAWL,CAGb,CACF,EAGA,OACEP,EAAA,cAACmB,EAAA,CACC,GAAG,SACH,KAAMjB,EACN,gBAAec,EAAc,eAAe,EAC5C,cAAeP,EACf,eAAgBC,EAChB,MAAON,EACP,UAAWY,EAAc,UACxB,GAAGC,EACH,GAAGJ,GAEHV,CACH,CAEJ,EAEOiB,EAAQnB,EACfA,EAAO,YAAc","sourcesContent":["import UI from '../ui'\nimport React from 'react'\nimport { useDisabledState } from '../../hooks/use-disabled-state'\nimport { resolveDisabledState } from '../../utils/accessibility'\nimport type { DisabledStateProps } from '../../types/shared'\n\nexport type ButtonProps = Partial<React.ComponentProps<typeof UI>> &\n DisabledStateProps & {\n /**\n * The button type\n * Required - 'button' | 'submit' | 'reset'\n */\n type: 'button' | 'submit' | 'reset'\n }\n\n/**\n * Accessible Button component with WCAG 2.1 Level AA compliant disabled state.\n *\n * **Key Accessibility Features:**\n * - Uses `aria-disabled` pattern instead of native `disabled` attribute\n * - Maintains keyboard focusability when disabled (stays in tab order)\n * - Prevents all interactions when disabled via optimized `useDisabledState` hook\n * - Automatic className merging for seamless styling\n * - Supports both modern `disabled` and legacy `isDisabled` props\n *\n * **Why aria-disabled?**\n * - Elements remain in keyboard tab order (WCAG 2.1.1 - Keyboard)\n * - Screen readers can discover and announce disabled state (WCAG 4.1.2)\n * - Enables tooltips and help text on disabled buttons\n * - Better visual styling control for WCAG AA contrast compliance\n *\n * **Performance:**\n * - Uses optimized `useDisabledState` hook with stable references\n * - Automatic className merging eliminates boilerplate\n * - ~90% reduction in unnecessary re-renders compared to previous implementation\n *\n * @example\n * // Basic usage\n * <Button type=\"button\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * @example\n * // Disabled state (prevents all interactions but stays focusable)\n * <Button type=\"button\" disabled={true} onClick={handleClick}>\n * Cannot click (but can focus for screen readers)\n * </Button>\n *\n * @example\n * // With custom classes (automatic merging with .is-disabled)\n * <Button\n * type=\"button\"\n * disabled={true}\n * classes=\"my-custom-btn\"\n * >\n * Custom disabled button\n * </Button>\n *\n * @example\n * // Legacy isDisabled prop (still supported)\n * <Button type=\"button\" isDisabled={true} onClick={handleClick}>\n * Legacy disabled\n * </Button>\n *\n * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/keyboard WCAG 2.1.1 - Keyboard}\n * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/name-role-value WCAG 4.1.2 - Name, Role, Value}\n * @see {@link file://./../../hooks/useDisabledState.md useDisabledState Hook Documentation}\n */\nexport const Button = ({\n type = 'button',\n children,\n styles,\n disabled,\n isDisabled,\n classes,\n onPointerDown,\n onPointerOver,\n onPointerLeave,\n onClick,\n onKeyDown,\n ...props\n}: ButtonProps) => {\n // Resolve disabled state from both props (disabled takes precedence)\n const isActuallyDisabled = resolveDisabledState(disabled, isDisabled)\n\n // Use the disabled state hook with enhanced API for automatic className merging\n const { disabledProps, handlers } = useDisabledState<HTMLButtonElement>(\n isActuallyDisabled,\n {\n handlers: {\n onClick,\n onPointerDown,\n onKeyDown,\n },\n // Automatic className merging - hook combines disabled class with user classes\n className: classes,\n // Note: onPointerOver and onPointerLeave are intentionally NOT wrapped\n // to allow hover effects on disabled buttons for visual feedback\n }\n )\n\n /* Returning a button element with accessible disabled state */\n return (\n <UI\n as=\"button\"\n type={type}\n aria-disabled={disabledProps['aria-disabled']}\n onPointerOver={onPointerOver}\n onPointerLeave={onPointerLeave}\n style={styles}\n className={disabledProps.className}\n {...handlers}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nexport default Button\nButton.displayName = 'Button'\n"]}
@@ -1,10 +0,0 @@
1
- import { a as a$1 } from './chunk-BFK62VX5.js';
2
- import { a as a$2 } from './chunk-75QHTLFO.js';
3
- import { a as a$3 } from './chunk-HHLNOC5T.js';
4
- import D from 'react';
5
-
6
- var a=({type:r="button",children:p,styles:i,disabled:l,isDisabled:n,classes:m,onPointerDown:d,onPointerOver:u,onPointerLeave:b,onClick:y,onKeyDown:P,...c})=>{let f=a$1(l,n),{disabledProps:t,handlers:B}=a$2(f,{handlers:{onClick:y,onPointerDown:d,onKeyDown:P},className:m});return D.createElement(a$3,{as:"button",type:r,"aria-disabled":t["aria-disabled"],onPointerOver:u,onPointerLeave:b,style:i,className:t.className,...B,...c},p)},x=a;a.displayName="Button";
7
-
8
- export { a, x as b };
9
- //# sourceMappingURL=out.js.map
10
- //# sourceMappingURL=chunk-OVWLQYMK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/buttons/button.tsx"],"names":["React","Button","type","children","styles","disabled","isDisabled","classes","onPointerDown","onPointerOver","onPointerLeave","onClick","onKeyDown","props","isActuallyDisabled","resolveDisabledState","disabledProps","handlers","useDisabledState","ui_default","button_default"],"mappings":"wHACA,OAAOA,MAAW,QAmEX,IAAMC,EAAS,CAAC,CACrB,KAAAC,EAAO,SACP,SAAAC,EACA,OAAAC,EACA,SAAAC,EACA,WAAAC,EACA,QAAAC,EACA,cAAAC,EACA,cAAAC,EACA,eAAAC,EACA,QAAAC,EACA,UAAAC,EACA,GAAGC,CACL,IAAmB,CAEjB,IAAMC,EAAqBC,EAAqBV,EAAUC,CAAU,EAG9D,CAAE,cAAAU,EAAe,SAAAC,CAAS,EAAIC,EAClCJ,EACA,CACE,SAAU,CACR,QAAAH,EACA,cAAAH,EACA,UAAAI,CACF,EAEA,UAAWL,CAGb,CACF,EAGA,OACEP,EAAA,cAACmB,EAAA,CACC,GAAG,SACH,KAAMjB,EACN,gBAAec,EAAc,eAAe,EAC5C,cAAeP,EACf,eAAgBC,EAChB,MAAON,EACP,UAAWY,EAAc,UACxB,GAAGC,EACH,GAAGJ,GAEHV,CACH,CAEJ,EAEOiB,EAAQnB,EACfA,EAAO,YAAc","sourcesContent":["import UI from '../ui'\nimport React from 'react'\nimport { useDisabledState } from '../../hooks/use-disabled-state'\nimport { resolveDisabledState } from '../../utils/accessibility'\nimport type { DisabledStateProps } from '../../types/shared'\n\nexport type ButtonProps = Partial<React.ComponentProps<typeof UI>> &\n DisabledStateProps & {\n /**\n * The button type\n * Required - 'button' | 'submit' | 'reset'\n */\n type: 'button' | 'submit' | 'reset'\n }\n\n/**\n * Accessible Button component with WCAG 2.1 Level AA compliant disabled state.\n *\n * **Key Accessibility Features:**\n * - Uses `aria-disabled` pattern instead of native `disabled` attribute\n * - Maintains keyboard focusability when disabled (stays in tab order)\n * - Prevents all interactions when disabled via optimized `useDisabledState` hook\n * - Automatic className merging for seamless styling\n * - Supports both modern `disabled` and legacy `isDisabled` props\n *\n * **Why aria-disabled?**\n * - Elements remain in keyboard tab order (WCAG 2.1.1 - Keyboard)\n * - Screen readers can discover and announce disabled state (WCAG 4.1.2)\n * - Enables tooltips and help text on disabled buttons\n * - Better visual styling control for WCAG AA contrast compliance\n *\n * **Performance:**\n * - Uses optimized `useDisabledState` hook with stable references\n * - Automatic className merging eliminates boilerplate\n * - ~90% reduction in unnecessary re-renders compared to previous implementation\n *\n * @example\n * // Basic usage\n * <Button type=\"button\" onClick={handleClick}>\n * Click me\n * </Button>\n *\n * @example\n * // Disabled state (prevents all interactions but stays focusable)\n * <Button type=\"button\" disabled={true} onClick={handleClick}>\n * Cannot click (but can focus for screen readers)\n * </Button>\n *\n * @example\n * // With custom classes (automatic merging with .is-disabled)\n * <Button\n * type=\"button\"\n * disabled={true}\n * classes=\"my-custom-btn\"\n * >\n * Custom disabled button\n * </Button>\n *\n * @example\n * // Legacy isDisabled prop (still supported)\n * <Button type=\"button\" isDisabled={true} onClick={handleClick}>\n * Legacy disabled\n * </Button>\n *\n * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/keyboard WCAG 2.1.1 - Keyboard}\n * @see {@link https://www.w3.org/WAI/WCAG21/Understanding/name-role-value WCAG 4.1.2 - Name, Role, Value}\n * @see {@link file://./../../hooks/useDisabledState.md useDisabledState Hook Documentation}\n */\nexport const Button = ({\n type = 'button',\n children,\n styles,\n disabled,\n isDisabled,\n classes,\n onPointerDown,\n onPointerOver,\n onPointerLeave,\n onClick,\n onKeyDown,\n ...props\n}: ButtonProps) => {\n // Resolve disabled state from both props (disabled takes precedence)\n const isActuallyDisabled = resolveDisabledState(disabled, isDisabled)\n\n // Use the disabled state hook with enhanced API for automatic className merging\n const { disabledProps, handlers } = useDisabledState<HTMLButtonElement>(\n isActuallyDisabled,\n {\n handlers: {\n onClick,\n onPointerDown,\n onKeyDown,\n },\n // Automatic className merging - hook combines disabled class with user classes\n className: classes,\n // Note: onPointerOver and onPointerLeave are intentionally NOT wrapped\n // to allow hover effects on disabled buttons for visual feedback\n }\n )\n\n /* Returning a button element with accessible disabled state */\n return (\n <UI\n as=\"button\"\n type={type}\n aria-disabled={disabledProps['aria-disabled']}\n onPointerOver={onPointerOver}\n onPointerLeave={onPointerLeave}\n style={styles}\n className={disabledProps.className}\n {...handlers}\n {...props}\n >\n {children}\n </UI>\n )\n}\n\nexport default Button\nButton.displayName = 'Button'\n"]}
@@ -1,8 +0,0 @@
1
- import { a } from './chunk-HHLNOC5T.js';
2
- import n from 'react';
3
-
4
- var s=n.forwardRef(({href:a$1,target:o,rel:r,children:p,styles:m,prefetch:t=!1,btnStyle:d,onClick:c,onPointerDown:l,...k},u)=>{let y=n.useMemo(()=>{if(o==="_blank"){let e=new Set(["noopener","noreferrer"]);return t&&e.add("prefetch"),r&&r.split(/\s+/).forEach(i=>{i&&e.add(i);}),Array.from(e).join(" ")}return r},[o,r,t]);return n.createElement(a,{as:"a",ref:u,href:a$1,target:o,rel:y,styles:m,"data-btn":d,onClick:c,onPointerDown:l,...k},p)});s.displayName="Link";var w=s;
5
-
6
- export { s as a, w as b };
7
- //# sourceMappingURL=out.js.map
8
- //# sourceMappingURL=chunk-UEPAWMDF.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/link/link.tsx"],"names":["React","Link","href","target","rel","children","styles","prefetch","btnStyle","onClick","onPointerDown","props","ref","computedRel","securityTokens","token","ui_default","link_default"],"mappings":"wCAAA,OAAOA,MAAW,QA+FX,IAAMC,EAAOD,EAAM,WACxB,CACE,CACE,KAAAE,EACA,OAAAC,EACA,IAAAC,EACA,SAAAC,EACA,OAAAC,EACA,SAAAC,EAAW,GACX,SAAAC,EACA,QAAAC,EACA,cAAAC,EACA,GAAGC,CACL,EACAC,IACG,CAWH,IAAMC,EAAcb,EAAM,QAAQ,IAAM,CACtC,GAAIG,IAAW,SAAU,CAEvB,IAAMW,EAAiB,IAAI,IAAI,CAAC,WAAY,YAAY,CAAC,EAGzD,OAAIP,GACFO,EAAe,IAAI,UAAU,EAI3BV,GACFA,EAAI,MAAM,KAAK,EAAE,QAASW,GAAU,CAC9BA,GAAOD,EAAe,IAAIC,CAAK,CACrC,CAAC,EAGI,MAAM,KAAKD,CAAc,EAAE,KAAK,GAAG,CAC5C,CAGA,OAAOV,CACT,EAAG,CAACD,EAAQC,EAAKG,CAAQ,CAAC,EAE1B,OACEP,EAAA,cAACgB,EAAA,CACC,GAAG,IACH,IAAKJ,EACL,KAAMV,EACN,OAAQC,EACR,IAAKU,EACL,OAAQP,EACR,WAAUE,EACV,QAASC,EACT,cAAeC,EACd,GAAGC,GAEHN,CACH,CAEJ,CACF,EAEAJ,EAAK,YAAc,OAEnB,IAAOgB,EAAQhB","sourcesContent":["import React from \"react\";\nimport UI from \"../ui\";\nimport type { LinkProps } from \"./link.types\";\n\n/**\n * Link - A semantic, accessible anchor component with enhanced security and styling.\n *\n * The Link component renders accessible `<a>` elements with automatic security\n * attributes for external links, customizable styling variants, and full WCAG 2.1\n * AA compliance. It supports traditional text links, button-styled links, and\n * programmatic focus management via ref forwarding.\n *\n * ## Features\n *\n * - 🔒 **Automatic Security**: External links get `rel=\"noopener noreferrer\"`\n * - ♿ **WCAG 2.1 AA Compliant**: Accessible focus indicators and semantic HTML\n * - 🎨 **Flexible Styling**: Text links, button links, and pill variants\n * - ⚡ **Performance**: Optional prefetch hints for faster navigation\n * - 🎯 **Ref Forwarding**: Direct DOM access for focus management and scroll\n * - 🧪 **Type-Safe**: Full TypeScript support with comprehensive prop types\n *\n * ## Accessibility\n *\n * - ✅ Semantic `<a>` element for proper keyboard navigation\n * - ✅ Focus indicators meet WCAG 2.4.7 (3:1 contrast ratio)\n * - ✅ Screen readers announce link purpose and destination\n * - ✅ External links include security attributes automatically\n * - ✅ Supports `aria-label` for icon-only or ambiguous links\n * - ✅ Ref forwarding enables skip-link patterns\n *\n * @example\n * // Basic internal link\n * <Link href=\"/about\">About Us</Link>\n *\n * @example\n * // External link with automatic security\n * <Link href=\"https://example.com\" target=\"_blank\">\n * Visit Example\n * </Link>\n *\n * @example\n * // Button-styled call-to-action link\n * <Link href=\"/signup\">\n * <b>Get Started</b>\n * </Link>\n *\n * @example\n * // Icon-only link with accessible label\n * <Link href=\"/settings\" aria-label=\"Open settings\">\n * <SettingsIcon aria-hidden=\"true\" />\n * </Link>\n *\n * @example\n * // Analytics tracking with onClick (includes keyboard users)\n * <Link\n * href=\"/products\"\n * onClick={(e) => trackEvent('link_click', { href: '/products' })}\n * >\n * Browse Products\n * </Link>\n *\n * @example\n * // Skip link with ref forwarding for focus management\n * const mainRef = useRef<HTMLAnchorElement>(null);\n *\n * <Link ref={mainRef} href=\"#main-content\">\n * Skip to main content\n * </Link>\n *\n * @example\n * // Custom styled link with CSS variables\n * <Link\n * href=\"/products\"\n * styles={{\n * '--link-color': '#0066cc',\n * '--link-decoration': 'underline',\n * }}\n * >\n * Browse Products\n * </Link>\n *\n * @example\n * // ✅ GOOD: Descriptive link text\n * <Link href=\"/docs/installation\">\n * Read installation guide\n * </Link>\n *\n * @example\n * // ❌ BAD: Generic link text (poor for screen readers)\n * <Link href=\"/docs/installation\">\n * Click here\n * </Link>\n *\n * @see {@link LinkProps} for complete prop documentation\n */\nexport const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(\n (\n {\n href,\n target,\n rel,\n children,\n styles,\n prefetch = false,\n btnStyle,\n onClick,\n onPointerDown,\n ...props\n },\n ref\n ) => {\n /**\n * Compute the final `rel` attribute value with security defaults.\n *\n * For external links (target=\"_blank\"), we merge user-provided `rel` values\n * with security defaults `noopener noreferrer` to prevent:\n * - window.opener exploitation (noopener)\n * - Referrer header leakage (noreferrer)\n *\n * If prefetch is enabled, we also add the `prefetch` hint.\n */\n const computedRel = React.useMemo(() => {\n if (target === \"_blank\") {\n // Start with security defaults\n const securityTokens = new Set([\"noopener\", \"noreferrer\"]);\n\n // Add prefetch if enabled\n if (prefetch) {\n securityTokens.add(\"prefetch\");\n }\n\n // Merge with user-provided rel tokens (if any)\n if (rel) {\n rel.split(/\\s+/).forEach((token) => {\n if (token) securityTokens.add(token);\n });\n }\n\n return Array.from(securityTokens).join(\" \");\n }\n\n // For non-external links, use provided rel as-is\n return rel;\n }, [target, rel, prefetch]);\n\n return (\n <UI\n as=\"a\"\n ref={ref}\n href={href}\n target={target}\n rel={computedRel}\n styles={styles}\n data-btn={btnStyle}\n onClick={onClick}\n onPointerDown={onPointerDown}\n {...props}\n >\n {children}\n </UI>\n );\n }\n);\n\nLink.displayName = \"Link\";\n\nexport default Link;\n"]}