@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.
- package/README.md +60 -0
- package/libs/{chunk-7XPFW7CB.js → chunk-43TK2ICH.js} +2 -2
- package/libs/chunk-5PJYLVFY.cjs +17 -0
- package/libs/chunk-5PJYLVFY.cjs.map +1 -0
- package/libs/chunk-E4OSROCA.cjs +17 -0
- package/libs/chunk-E4OSROCA.cjs.map +1 -0
- package/libs/chunk-KVKQLRJG.js +10 -0
- package/libs/chunk-KVKQLRJG.js.map +1 -0
- package/libs/{chunk-QVW6W76L.cjs → chunk-MGPWZRBX.cjs} +3 -3
- package/libs/chunk-NNTBIHSD.js +8 -0
- package/libs/chunk-NNTBIHSD.js.map +1 -0
- package/libs/{chunk-X3JCTEPD.js → chunk-QKHPHMG2.js} +2 -2
- package/libs/{chunk-T4T6GWYQ.cjs → chunk-R7NLLZU2.cjs} +3 -3
- package/libs/{chunk-X5LGFCWG.js → chunk-UJAQVHWC.js} +3 -3
- package/libs/{chunk-DKTHCQ5P.cjs → chunk-X5RKCLDC.cjs} +3 -3
- package/libs/components/breadcrumbs/breadcrumb.cjs +5 -5
- package/libs/components/breadcrumbs/breadcrumb.d.cts +1 -1
- package/libs/components/breadcrumbs/breadcrumb.d.ts +1 -1
- package/libs/components/breadcrumbs/breadcrumb.js +2 -2
- package/libs/components/button.cjs +3 -3
- package/libs/components/button.d.cts +1 -1
- package/libs/components/button.d.ts +1 -1
- package/libs/components/button.js +1 -1
- package/libs/components/dialog/dialog.cjs +4 -4
- package/libs/components/dialog/dialog.js +2 -2
- package/libs/components/link/link.cjs +11 -3
- package/libs/components/link/link.d.cts +131 -3
- package/libs/components/link/link.d.ts +131 -3
- package/libs/components/link/link.js +1 -1
- package/libs/components/modal.cjs +3 -3
- package/libs/components/modal.js +2 -2
- package/libs/hooks.cjs +3 -3
- package/libs/hooks.d.cts +1 -1
- package/libs/hooks.d.ts +1 -1
- package/libs/hooks.js +2 -2
- package/libs/index.cjs +12 -12
- package/libs/index.d.cts +237 -2
- package/libs/index.d.ts +237 -2
- package/libs/index.js +5 -5
- package/package.json +2 -2
- package/src/components/breadcrumbs/breadcrumb.test.tsx +1 -2
- package/src/components/buttons/README.mdx +19 -9
- package/src/components/buttons/button.tsx +19 -15
- package/src/components/link/link.stories.tsx +205 -8
- package/src/components/link/link.test.tsx +1 -1
- package/src/components/link/link.tsx +22 -0
- package/src/components/link/link.types.ts +11 -3
- package/src/docs/fpkit-developer.mdx +131 -53
- package/libs/chunk-33PNJ4LO.cjs +0 -15
- package/libs/chunk-33PNJ4LO.cjs.map +0 -1
- package/libs/chunk-GT77BX4L.cjs +0 -17
- package/libs/chunk-GT77BX4L.cjs.map +0 -1
- package/libs/chunk-OVWLQYMK.js +0 -10
- package/libs/chunk-OVWLQYMK.js.map +0 -1
- package/libs/chunk-UEPAWMDF.js +0 -8
- package/libs/chunk-UEPAWMDF.js.map +0 -1
- package/libs/link-5192f411.d.ts +0 -323
- /package/libs/{chunk-7XPFW7CB.js.map → chunk-43TK2ICH.js.map} +0 -0
- /package/libs/{chunk-QVW6W76L.cjs.map → chunk-MGPWZRBX.cjs.map} +0 -0
- /package/libs/{chunk-X3JCTEPD.js.map → chunk-QKHPHMG2.js.map} +0 -0
- /package/libs/{chunk-T4T6GWYQ.cjs.map → chunk-R7NLLZU2.cjs.map} +0 -0
- /package/libs/{chunk-X5LGFCWG.js.map → chunk-UJAQVHWC.js.map} +0 -0
- /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
|
|
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
|
-
|
|
19
|
-
✅ **
|
|
20
|
-
|
|
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
|
|
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
|
|
135
|
+
> import { Button, Badge } from "@fpkit/acss";
|
|
102
136
|
>
|
|
103
|
-
> export interface StatusButtonProps
|
|
104
|
-
>
|
|
137
|
+
> export interface StatusButtonProps
|
|
138
|
+
> extends React.ComponentProps<typeof Button> {
|
|
139
|
+
> status: "active" | "inactive" | "pending";
|
|
105
140
|
> }
|
|
106
141
|
>
|
|
107
|
-
> export const StatusButton = ({
|
|
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
|
-
>
|
|
136
|
-
>
|
|
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
|
-
>
|
|
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
|
|
221
|
+
> import { axe, toHaveNoViolations } from "jest-axe";
|
|
181
222
|
>
|
|
182
|
-
> expect.extend(toHaveNoViolations)
|
|
223
|
+
> expect.extend(toHaveNoViolations);
|
|
183
224
|
>
|
|
184
|
-
> it(
|
|
185
|
-
> const { container } = render(
|
|
186
|
-
>
|
|
187
|
-
>
|
|
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`,
|
|
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
|
-
|
|
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.
|
|
359
|
+
- **@fpkit/acss:** v1.0.0+
|
|
313
360
|
- **Claude Code:** Latest version
|
|
314
361
|
- **Python:** 3.7+ (for validation script)
|
|
315
|
-
- **Node.js:**
|
|
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
|
-
|
|
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
|
|
369
|
-
import { useState } from
|
|
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
|
|
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:**
|
|
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
|
-
|
|
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
|
|
597
|
+
- **[npm Package](https://www.npmjs.com/package/@fpkit/acss)** - Installation
|
|
598
|
+
and API reference
|
|
523
599
|
|
|
524
600
|
### Learning Resources
|
|
525
|
-
|
|
526
|
-
- **[
|
|
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
|
-
|
|
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
|
|
package/libs/chunk-33PNJ4LO.cjs
DELETED
|
@@ -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"]}
|
package/libs/chunk-GT77BX4L.cjs
DELETED
|
@@ -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"]}
|
package/libs/chunk-OVWLQYMK.js
DELETED
|
@@ -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"]}
|
package/libs/chunk-UEPAWMDF.js
DELETED
|
@@ -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"]}
|