@fpkit/acss 1.0.0-beta.1 → 1.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 (43) hide show
  1. package/README.md +32 -0
  2. package/docs/README.md +325 -0
  3. package/docs/guides/accessibility.md +764 -0
  4. package/docs/guides/architecture.md +705 -0
  5. package/docs/guides/composition.md +688 -0
  6. package/docs/guides/css-variables.md +522 -0
  7. package/docs/guides/storybook.md +828 -0
  8. package/docs/guides/testing.md +817 -0
  9. package/docs/testing/focus-indicator-testing.md +437 -0
  10. package/libs/components/buttons/button.css +1 -1
  11. package/libs/components/buttons/button.css.map +1 -1
  12. package/libs/components/buttons/button.min.css +2 -2
  13. package/libs/components/icons/icon.d.cts +32 -32
  14. package/libs/components/icons/icon.d.ts +32 -32
  15. package/libs/components/list/list.css +1 -1
  16. package/libs/components/list/list.min.css +1 -1
  17. package/libs/index.css +1 -1
  18. package/libs/index.css.map +1 -1
  19. package/package.json +4 -3
  20. package/src/components/README.mdx +1 -1
  21. package/src/components/buttons/button.scss +5 -0
  22. package/src/components/buttons/button.stories.tsx +8 -5
  23. package/src/components/cards/card.stories.tsx +1 -1
  24. package/src/components/details/details.stories.tsx +1 -1
  25. package/src/components/form/form.stories.tsx +1 -1
  26. package/src/components/form/input.stories.tsx +1 -1
  27. package/src/components/form/select.stories.tsx +1 -1
  28. package/src/components/heading/README.mdx +292 -0
  29. package/src/components/icons/icon.stories.tsx +1 -1
  30. package/src/components/list/list.scss +1 -1
  31. package/src/components/nav/nav.stories.tsx +1 -1
  32. package/src/components/ui.stories.tsx +53 -19
  33. package/src/docs/accessibility.mdx +484 -0
  34. package/src/docs/composition.mdx +549 -0
  35. package/src/docs/css-variables.mdx +380 -0
  36. package/src/docs/fpkit-developer.mdx +545 -0
  37. package/src/introduction.mdx +356 -0
  38. package/src/styles/buttons/button.css +4 -0
  39. package/src/styles/buttons/button.css.map +1 -1
  40. package/src/styles/index.css +9 -3
  41. package/src/styles/index.css.map +1 -1
  42. package/src/styles/list/list.css +1 -1
  43. package/src/styles/utilities/_disabled.scss +5 -4
@@ -0,0 +1,705 @@
1
+ # Architecture Guide
2
+
3
+ ## Overview
4
+
5
+ @fpkit/acss follows consistent architectural patterns that ensure maintainability, type safety, accessibility, and reusability. This guide explains these patterns to help you use and compose fpkit components effectively.
6
+
7
+ ---
8
+
9
+ ## The UI Component Foundation
10
+
11
+ All fpkit components are built on a polymorphic `UI` base component that provides:
12
+
13
+ - **Polymorphic rendering** - Render as any HTML element via `as` prop
14
+ - **Type-safe prop spreading** - Full TypeScript support
15
+ - **Style merging** - Combine default and custom styles
16
+ - **Ref forwarding** - For focus management
17
+ - **Automatic ARIA** - Attribute forwarding
18
+
19
+ ### Understanding Polymorphism
20
+
21
+ The `as` prop lets you change the rendered HTML element while preserving component behavior:
22
+
23
+ ```tsx
24
+ import { Button, Card } from '@fpkit/acss'
25
+
26
+ // Button as link
27
+ <Button as="a" href="/page">
28
+ Navigate
29
+ </Button>
30
+ // Renders: <a href="/page">Navigate</a>
31
+
32
+ // Card as section instead of article
33
+ <Card as="section">
34
+ Content
35
+ </Card>
36
+ // Renders: <section>Content</section>
37
+
38
+ // Badge as span instead of sup
39
+ <Badge as="span">
40
+ Label
41
+ </Badge>
42
+ // Renders: <span>Label</span>
43
+ ```
44
+
45
+ **Why this matters:**
46
+ - Semantic HTML flexibility
47
+ - Better accessibility (correct element for the job)
48
+ - SEO benefits (proper HTML structure)
49
+ - CSS targeting (style based on element type)
50
+
51
+ ---
52
+
53
+ ## Component Patterns
54
+
55
+ ### Simple Components
56
+
57
+ Standalone components with no sub-components.
58
+
59
+ **Examples**: Badge, Button, Tag, Icon
60
+
61
+ ```tsx
62
+ import { Badge } from '@fpkit/acss'
63
+
64
+ // Simple component with variant
65
+ <Badge variant="rounded">New</Badge>
66
+
67
+ // Polymorphic - render as different element
68
+ <Badge as="span">Label</Badge>
69
+
70
+ // Custom styles via CSS variables
71
+ <Badge style={{ '--badge-bg': '#ff0000' }}>
72
+ Alert
73
+ </Badge>
74
+ ```
75
+
76
+ **Characteristics:**
77
+ - Single export
78
+ - Props extend base HTML element props
79
+ - Variants via `data-*` attributes
80
+ - Customizable via CSS variables
81
+
82
+ ---
83
+
84
+ ### Compound Components
85
+
86
+ Complex components with multiple related sub-components.
87
+
88
+ **Examples**: Card, Dialog, Alert, Form
89
+
90
+ ```tsx
91
+ import { Card } from '@fpkit/acss'
92
+
93
+ // Using sub-components
94
+ <Card>
95
+ <Card.Header>
96
+ <Card.Title>Title</Card.Title>
97
+ </Card.Header>
98
+ <Card.Content>
99
+ Content goes here
100
+ </Card.Content>
101
+ <Card.Footer>
102
+ <Button>Action</Button>
103
+ </Card.Footer>
104
+ </Card>
105
+
106
+ // Or import individually
107
+ import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@fpkit/acss'
108
+
109
+ <Card>
110
+ <CardHeader>
111
+ <CardTitle>Title</CardTitle>
112
+ </CardHeader>
113
+ <CardContent>Content</CardContent>
114
+ <CardFooter>
115
+ <Button>Action</Button>
116
+ </CardFooter>
117
+ </Card>
118
+ ```
119
+
120
+ **Characteristics:**
121
+ - Main component + sub-components
122
+ - Available as `Component.SubComponent` or individual exports
123
+ - Each sub-component is independently customizable
124
+ - Flexible composition (use what you need)
125
+
126
+ ---
127
+
128
+ ## TypeScript Support
129
+
130
+ ### Component Props
131
+
132
+ All fpkit components are fully typed:
133
+
134
+ ```tsx
135
+ import type { ButtonProps, CardProps } from '@fpkit/acss'
136
+
137
+ // Extend fpkit component props
138
+ interface CustomButtonProps extends ButtonProps {
139
+ loading?: boolean
140
+ loadingText?: string
141
+ }
142
+
143
+ const CustomButton = ({
144
+ loading,
145
+ loadingText = 'Loading...',
146
+ children,
147
+ ...props
148
+ }: CustomButtonProps) => {
149
+ return (
150
+ <Button {...props}>
151
+ {loading ? loadingText : children}
152
+ </Button>
153
+ )
154
+ }
155
+ ```
156
+
157
+ ### Polymorphic Types
158
+
159
+ Types adapt based on the `as` prop:
160
+
161
+ ```tsx
162
+ // Button as button - button props available
163
+ <Button type="submit" onClick={handleClick}>
164
+ Submit
165
+ </Button>
166
+
167
+ // Button as link - anchor props available
168
+ <Button as="a" href="/page" target="_blank">
169
+ Link
170
+ </Button>
171
+
172
+ // Button as div - div props available
173
+ <Button as="div" onKeyDown={handleKeyDown}>
174
+ Custom
175
+ </Button>
176
+ ```
177
+
178
+ ### Generic Props Pattern
179
+
180
+ ```tsx
181
+ import type { ComponentProps } from 'react'
182
+ import { Button } from '@fpkit/acss'
183
+
184
+ // Get button props type
185
+ type BaseButtonProps = ComponentProps<typeof Button>
186
+
187
+ // Extend with custom props
188
+ interface MyButtonProps extends BaseButtonProps {
189
+ icon?: string
190
+ badge?: number
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Composition Patterns
197
+
198
+ ### Pattern 1: Container + Content
199
+
200
+ Wrap fpkit components with additional structure:
201
+
202
+ ```tsx
203
+ import { Button, Badge } from '@fpkit/acss'
204
+
205
+ export const StatusButton = ({ status, children, ...props }) => {
206
+ return (
207
+ <Button {...props}>
208
+ {children}
209
+ <Badge variant={status}>{status}</Badge>
210
+ </Button>
211
+ )
212
+ }
213
+
214
+ // Usage
215
+ <StatusButton status="active">Server Status</StatusButton>
216
+ ```
217
+
218
+ ### Pattern 2: Conditional Composition
219
+
220
+ Different combinations based on props:
221
+
222
+ ```tsx
223
+ import { Alert, Dialog } from '@fpkit/acss'
224
+
225
+ export const Notification = ({ inline, variant, children, ...props }) => {
226
+ if (inline) {
227
+ return <Alert variant={variant}>{children}</Alert>
228
+ }
229
+
230
+ return (
231
+ <Dialog {...props}>
232
+ <Alert variant={variant}>{children}</Alert>
233
+ </Dialog>
234
+ )
235
+ }
236
+
237
+ // Usage
238
+ <Notification inline variant="success">Saved!</Notification>
239
+ <Notification isOpen={showModal} variant="error">Error!</Notification>
240
+ ```
241
+
242
+ ### Pattern 3: Enhanced Wrapper
243
+
244
+ Add behavior around fpkit components:
245
+
246
+ ```tsx
247
+ import { Button } from '@fpkit/acss'
248
+ import { useState } from 'react'
249
+
250
+ export const LoadingButton = ({ loading, onClick, children, ...props }) => {
251
+ const [isLoading, setIsLoading] = useState(loading)
252
+
253
+ const handleClick = async (e) => {
254
+ setIsLoading(true)
255
+ try {
256
+ await onClick?.(e)
257
+ } finally {
258
+ setIsLoading(false)
259
+ }
260
+ }
261
+
262
+ return (
263
+ <Button
264
+ {...props}
265
+ disabled={isLoading || props.disabled}
266
+ onClick={handleClick}
267
+ >
268
+ {isLoading ? 'Loading...' : children}
269
+ </Button>
270
+ )
271
+ }
272
+
273
+ // Usage
274
+ <LoadingButton onClick={async () => await saveData()}>
275
+ Save
276
+ </LoadingButton>
277
+ ```
278
+
279
+ See the [Composition Guide](./composition.md) for more patterns and examples.
280
+
281
+ ---
282
+
283
+ ## Styling Architecture
284
+
285
+ ### Attribute-Based Variants
286
+
287
+ fpkit uses `data-*` attributes for variants:
288
+
289
+ ```tsx
290
+ // Component
291
+ <Button variant="primary" size="large">Click me</Button>
292
+
293
+ // Renders as
294
+ <button data-btn="primary large">Click me</button>
295
+
296
+ // Styled with SCSS
297
+ button[data-btn~="primary"] {
298
+ background: var(--btn-primary-bg);
299
+ }
300
+
301
+ button[data-btn~="large"] {
302
+ font-size: var(--btn-size-lg);
303
+ }
304
+ ```
305
+
306
+ **Benefits:**
307
+ - Multiple variants on same element
308
+ - Clean HTML output
309
+ - Type-safe variants in TypeScript
310
+ - Easy CSS targeting
311
+
312
+ ### CSS Variable Integration
313
+
314
+ All styling uses CSS custom properties:
315
+
316
+ ```tsx
317
+ // Global overrides
318
+ :root {
319
+ --btn-primary-bg: #0066cc;
320
+ --btn-padding-inline: 2rem;
321
+ }
322
+
323
+ // Component-specific overrides
324
+ <Button
325
+ style={{
326
+ '--btn-bg': '#e63946',
327
+ '--btn-color': 'white',
328
+ }}
329
+ >
330
+ Custom
331
+ </Button>
332
+
333
+ // CSS class overrides
334
+ .hero-button {
335
+ --btn-padding-inline: 3rem;
336
+ --btn-fs: 1.25rem;
337
+ }
338
+ ```
339
+
340
+ See the [CSS Variables Guide](./css-variables.md) for complete customization options.
341
+
342
+ ---
343
+
344
+ ## Props Patterns
345
+
346
+ ### Common Props
347
+
348
+ All fpkit components accept these common props:
349
+
350
+ ```tsx
351
+ // className - additional CSS classes
352
+ <Button className="custom-button">Click</Button>
353
+
354
+ // style - inline styles
355
+ <Button style={{ marginTop: '1rem' }}>Click</Button>
356
+
357
+ // data-* - custom data attributes
358
+ <Button data-testid="submit-btn">Click</Button>
359
+
360
+ // aria-* - accessibility attributes
361
+ <Button aria-label="Close dialog">×</Button>
362
+
363
+ // ref - React ref
364
+ const ref = useRef()
365
+ <Button ref={ref}>Click</Button>
366
+
367
+ // as - polymorphic element
368
+ <Button as="a" href="/page">Link</Button>
369
+ ```
370
+
371
+ ### Variant Props
372
+
373
+ Components use semantic variant names:
374
+
375
+ ```tsx
376
+ // Button variants
377
+ <Button variant="primary">Primary</Button>
378
+ <Button variant="secondary">Secondary</Button>
379
+ <Button variant="danger">Danger</Button>
380
+
381
+ // Alert variants
382
+ <Alert variant="error">Error message</Alert>
383
+ <Alert variant="success">Success message</Alert>
384
+ <Alert variant="warning">Warning message</Alert>
385
+ <Alert variant="info">Info message</Alert>
386
+
387
+ // Badge variants
388
+ <Badge variant="rounded">Rounded</Badge>
389
+ <Badge variant="pill">Pill</Badge>
390
+ ```
391
+
392
+ ### Size Props
393
+
394
+ When components support sizing:
395
+
396
+ ```tsx
397
+ <Button size="small">Small</Button>
398
+ <Button size="medium">Medium</Button>
399
+ <Button size="large">Large</Button>
400
+ ```
401
+
402
+ ### Boolean Props
403
+
404
+ State and behavior props:
405
+
406
+ ```tsx
407
+ // Disabled state
408
+ <Button disabled>Disabled</Button>
409
+
410
+ // Interactive cards
411
+ <Card interactive onClick={handleClick}>Clickable</Card>
412
+
413
+ // Modal/dialog states
414
+ <Dialog isOpen={isOpen} onClose={handleClose}>Content</Dialog>
415
+
416
+ // Dismissable alerts
417
+ <Alert onClose={handleClose}>Dismissable</Alert>
418
+ ```
419
+
420
+ ---
421
+
422
+ ## Accessibility Architecture
423
+
424
+ ### Semantic HTML
425
+
426
+ fpkit components render as appropriate semantic elements:
427
+
428
+ | Component | Default Element | Purpose |
429
+ |-----------|----------------|---------|
430
+ | Button | `<button>` | Interactive action |
431
+ | Card | `<article>` | Self-contained content |
432
+ | Alert | `<div role="alert">` | Important message |
433
+ | Dialog | `<dialog>` | Modal content |
434
+ | Nav | `<nav>` | Navigation menu |
435
+
436
+ ### ARIA Attributes
437
+
438
+ Components include built-in ARIA:
439
+
440
+ ```tsx
441
+ // Button disabled state
442
+ <Button disabled>
443
+ // Renders: <button aria-disabled="true">
444
+
445
+ // Alert role
446
+ <Alert variant="error">
447
+ // Renders: <div role="alert">
448
+
449
+ // Dialog modal
450
+ <Dialog isOpen>
451
+ // Renders: <dialog aria-modal="true">
452
+
453
+ // Expandable elements
454
+ <Accordion expanded>
455
+ // Renders: <div aria-expanded="true">
456
+ ```
457
+
458
+ ### Keyboard Navigation
459
+
460
+ All interactive components support:
461
+ - **Tab**: Focus navigation
462
+ - **Enter/Space**: Activation
463
+ - **Escape**: Close modals/dialogs
464
+ - **Arrow keys**: Navigate lists/menus
465
+
466
+ See the [Accessibility Guide](./accessibility.md) for complete patterns.
467
+
468
+ ---
469
+
470
+ ## Component Lifecycle
471
+
472
+ ### Initialization
473
+
474
+ ```tsx
475
+ // Components accept default HTML element props
476
+ <Button onClick={handleClick}>Click</Button>
477
+
478
+ // Plus component-specific props
479
+ <Button variant="primary" disabled>Click</Button>
480
+
481
+ // Props spread to underlying element
482
+ <Button data-testid="btn" aria-label="Submit">
483
+ Submit
484
+ </Button>
485
+ ```
486
+
487
+ ### Updates
488
+
489
+ ```tsx
490
+ // Props update reactively
491
+ const [disabled, setDisabled] = useState(false)
492
+
493
+ <Button disabled={disabled}>
494
+ {disabled ? 'Disabled' : 'Enabled'}
495
+ </Button>
496
+
497
+ // CSS variables update in real-time
498
+ const [color, setColor] = useState('#0066cc')
499
+
500
+ <Button style={{ '--btn-bg': color }}>
501
+ Dynamic Color
502
+ </Button>
503
+ ```
504
+
505
+ ### Cleanup
506
+
507
+ ```tsx
508
+ // Refs are properly forwarded
509
+ const buttonRef = useRef<HTMLButtonElement>(null)
510
+
511
+ useEffect(() => {
512
+ // Access DOM element directly
513
+ buttonRef.current?.focus()
514
+
515
+ return () => {
516
+ // Cleanup if needed
517
+ }
518
+ }, [])
519
+
520
+ <Button ref={buttonRef}>Click</Button>
521
+ ```
522
+
523
+ ---
524
+
525
+ ## Best Practices
526
+
527
+ ### ✅ Do
528
+
529
+ - **Use semantic elements** - Leverage the `as` prop for correct HTML
530
+ - **Compose over create** - Combine fpkit components rather than building from scratch
531
+ - **Extend props properly** - Use TypeScript to extend component props
532
+ - **Customize with CSS variables** - Override styles without modifying components
533
+ - **Preserve accessibility** - Keep ARIA attributes and keyboard navigation
534
+ - **Forward refs** - When wrapping components, forward refs appropriately
535
+
536
+ ```tsx
537
+ // ✅ Good - proper composition
538
+ import { forwardRef } from 'react'
539
+ import { Button, type ButtonProps } from '@fpkit/acss'
540
+
541
+ interface LoadingButtonProps extends ButtonProps {
542
+ loading?: boolean
543
+ }
544
+
545
+ export const LoadingButton = forwardRef<HTMLButtonElement, LoadingButtonProps>(
546
+ ({ loading, children, ...props }, ref) => {
547
+ return (
548
+ <Button ref={ref} {...props} disabled={loading || props.disabled}>
549
+ {loading ? 'Loading...' : children}
550
+ </Button>
551
+ )
552
+ }
553
+ )
554
+ ```
555
+
556
+ ### ❌ Don't
557
+
558
+ - **Don't duplicate components** - Reuse existing fpkit components
559
+ - **Don't break accessibility** - Maintain ARIA attributes and keyboard support
560
+ - **Don't hardcode styles** - Use CSS variables for customization
561
+ - **Don't ignore types** - Leverage TypeScript for type safety
562
+ - **Don't nest interactive elements** - Avoid `<button>` inside `<a>`
563
+
564
+ ```tsx
565
+ // ❌ Bad - duplicating fpkit logic
566
+ export const MyBadge = ({ children }) => {
567
+ return <span className="my-badge">{children}</span>
568
+ }
569
+
570
+ // ✅ Good - reuse fpkit component
571
+ import { Badge } from '@fpkit/acss'
572
+ export const MyBadge = Badge
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Component API Patterns
578
+
579
+ ### Children Prop
580
+
581
+ ```tsx
582
+ // String children
583
+ <Button>Click me</Button>
584
+
585
+ // Element children
586
+ <Button>
587
+ <Icon name="save" />
588
+ Save
589
+ </Button>
590
+
591
+ // Render prop pattern
592
+ <Card>
593
+ {({ isHovered }) => (
594
+ <div>Content {isHovered ? 'hovered' : ''}</div>
595
+ )}
596
+ </Card>
597
+ ```
598
+
599
+ ### Event Handlers
600
+
601
+ ```tsx
602
+ // Mouse events
603
+ <Button onClick={handleClick}>Click</Button>
604
+ <Button onMouseEnter={handleHover}>Hover</Button>
605
+
606
+ // Keyboard events
607
+ <Input onKeyDown={handleKeyPress} />
608
+
609
+ // Form events
610
+ <Input onChange={handleChange} />
611
+ <Form onSubmit={handleSubmit} />
612
+
613
+ // Custom events
614
+ <Dialog onClose={handleClose} />
615
+ <Alert onDismiss={handleDismiss} />
616
+ ```
617
+
618
+ ### Render Props
619
+
620
+ ```tsx
621
+ // Custom rendering
622
+ <Select>
623
+ {(option) => (
624
+ <div>
625
+ <Icon name={option.icon} />
626
+ {option.label}
627
+ </div>
628
+ )}
629
+ </Select>
630
+ ```
631
+
632
+ ---
633
+
634
+ ## Framework Integration
635
+
636
+ ### React
637
+
638
+ fpkit is designed for React:
639
+
640
+ ```tsx
641
+ import { Button, Card } from '@fpkit/acss'
642
+
643
+ export default function App() {
644
+ return (
645
+ <Card>
646
+ <Card.Title>Welcome</Card.Title>
647
+ <Card.Content>
648
+ <p>Content here</p>
649
+ <Button variant="primary">Action</Button>
650
+ </Card.Content>
651
+ </Card>
652
+ )
653
+ }
654
+ ```
655
+
656
+ ### Next.js
657
+
658
+ Works seamlessly with Next.js:
659
+
660
+ ```tsx
661
+ import { Button } from '@fpkit/acss'
662
+ import Link from 'next/link'
663
+
664
+ // Button as Next.js Link
665
+ <Button as={Link} href="/page">
666
+ Navigate
667
+ </Button>
668
+ ```
669
+
670
+ ### TypeScript
671
+
672
+ Full type safety:
673
+
674
+ ```tsx
675
+ import type { ButtonProps } from '@fpkit/acss'
676
+
677
+ // Type-safe custom component
678
+ const CustomButton = (props: ButtonProps) => {
679
+ return <Button {...props} />
680
+ }
681
+ ```
682
+
683
+ ---
684
+
685
+ ## Additional Resources
686
+
687
+ - **[Composition Guide](./composition.md)** - Component composition patterns and strategies
688
+ - **[CSS Variables Guide](./css-variables.md)** - Styling and customization
689
+ - **[Accessibility Guide](./accessibility.md)** - WCAG compliance and ARIA patterns
690
+ - **[Testing Guide](./testing.md)** - Testing strategies for fpkit components
691
+
692
+ ---
693
+
694
+ ## Summary
695
+
696
+ @fpkit/acss architecture provides:
697
+
698
+ 1. **Polymorphic Components** - Flexible HTML element rendering via `as` prop
699
+ 2. **Compound Components** - Complex UIs with sub-components
700
+ 3. **Composition-First** - Build custom components by combining primitives
701
+ 4. **Type Safety** - Full TypeScript support with proper prop types
702
+ 5. **CSS Variables** - Customizable styling without component modification
703
+ 6. **Accessibility** - Built-in WCAG 2.1 AA compliance
704
+
705
+ Understanding these patterns helps you use fpkit effectively and build maintainable, accessible applications.