@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,688 @@
1
+ # Component Composition Guide
2
+
3
+ This guide explains how to build custom components by composing existing @fpkit/acss components, following React best practices for reusability and maintainability.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Why Composition?](#why-composition)
8
+ - [Composition vs Creation Decision Tree](#composition-vs-creation-decision-tree)
9
+ - [Common Composition Patterns](#common-composition-patterns)
10
+ - [Anti-Patterns to Avoid](#anti-patterns-to-avoid)
11
+ - [Real-World Examples](#real-world-examples)
12
+
13
+ ---
14
+
15
+ ## Why Composition?
16
+
17
+ **Composition over duplication** is a core principle in React development. When building custom components using @fpkit/acss, you should prefer composing existing components rather than creating from scratch.
18
+
19
+ ### Benefits
20
+
21
+ - ✅ **Consistency**: Reusing existing components ensures UI consistency across your application
22
+ - ✅ **Maintainability**: Bug fixes and improvements in fpkit components propagate to your composed components automatically
23
+ - ✅ **Reduced Code**: Less code to write, test, and maintain
24
+ - ✅ **Tested Components**: Leverage existing test coverage from fpkit
25
+ - ✅ **Faster Development**: Build complex UIs from proven primitives
26
+ - ✅ **Accessibility**: Inherit WCAG-compliant patterns from fpkit components
27
+
28
+ ---
29
+
30
+ ## Composition vs Creation Decision Tree
31
+
32
+ Use this decision tree to determine whether to compose, extend, or create new:
33
+
34
+ ```
35
+ ┌─────────────────────────────────────┐
36
+ │ New Component Need: "ComponentName" │
37
+ └──────────────┬──────────────────────┘
38
+
39
+
40
+ ┌──────────────────────┐
41
+ │ Does fpkit have a │ YES → Use fpkit component directly
42
+ │ component that meets │ Customize with CSS variables
43
+ │ the need exactly? │
44
+ └──────┬───────────────┘
45
+ │ NO
46
+
47
+ ┌──────────────────────┐
48
+ │ Can it be built by │ YES → Compose existing components
49
+ │ combining 2+ fpkit │ Import and combine
50
+ │ components? │
51
+ └──────┬───────────────┘
52
+ │ NO
53
+
54
+ ┌──────────────────────┐
55
+ │ Can I extend an │ YES → Wrap fpkit component
56
+ │ fpkit component with │ Add custom logic/styling
57
+ │ additional features? │
58
+ └──────┬───────────────┘
59
+ │ NO
60
+
61
+ ┌──────────────────────┐
62
+ │ Create custom │
63
+ │ component from │
64
+ │ scratch using fpkit │
65
+ │ styling patterns │
66
+ └─────────────────────┘
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Common Composition Patterns
72
+
73
+ ### Pattern 1: Container + Content
74
+
75
+ **When to use**: Wrapping fpkit components with additional structure or layout.
76
+
77
+ ```tsx
78
+ import { Badge, Button } from '@fpkit/acss'
79
+
80
+ export const StatusButton = ({ status, children, ...props }) => {
81
+ return (
82
+ <Button {...props}>
83
+ {children}
84
+ <Badge variant={status}>{status}</Badge>
85
+ </Button>
86
+ )
87
+ }
88
+
89
+ // Usage
90
+ <StatusButton status="success">Complete</StatusButton>
91
+ ```
92
+
93
+ **Use Cases**: IconButton, TaggedCard, LabeledInput, NotificationButton
94
+
95
+ ---
96
+
97
+ ### Pattern 2: Conditional Composition
98
+
99
+ **When to use**: Different component combinations based on props or state.
100
+
101
+ ```tsx
102
+ import { Alert, Modal } from '@fpkit/acss'
103
+
104
+ export const Notification = ({ variant, inline, children, onClose, ...props }) => {
105
+ if (inline) {
106
+ return (
107
+ <Alert variant={variant} onClose={onClose}>
108
+ {children}
109
+ </Alert>
110
+ )
111
+ }
112
+
113
+ return (
114
+ <Modal isOpen={props.isOpen} onClose={onClose}>
115
+ <Alert variant={variant}>{children}</Alert>
116
+ </Modal>
117
+ )
118
+ }
119
+
120
+ // Usage
121
+ <Notification inline variant="success">Saved!</Notification>
122
+ <Notification isOpen={showModal} variant="error" onClose={handleClose}>
123
+ Error occurred
124
+ </Notification>
125
+ ```
126
+
127
+ **Use Cases**: ResponsiveNav (mobile menu vs desktop nav), AdaptiveDialog, ConditionalAlert
128
+
129
+ ---
130
+
131
+ ### Pattern 3: Enhanced Wrapper
132
+
133
+ **When to use**: Adding behavior/features around an existing fpkit component.
134
+
135
+ ```tsx
136
+ import { Button } from '@fpkit/acss'
137
+ import { useState } from 'react'
138
+
139
+ export const LoadingButton = ({ loading, onClick, children, ...props }) => {
140
+ const [isLoading, setIsLoading] = useState(loading)
141
+
142
+ const handleClick = async (e) => {
143
+ setIsLoading(true)
144
+ try {
145
+ await onClick?.(e)
146
+ } finally {
147
+ setIsLoading(false)
148
+ }
149
+ }
150
+
151
+ return (
152
+ <Button {...props} disabled={isLoading || props.disabled} onClick={handleClick}>
153
+ {isLoading && <span aria-label="Loading">⏳</span>}
154
+ <span style={{ opacity: isLoading ? 0.6 : 1 }}>{children}</span>
155
+ </Button>
156
+ )
157
+ }
158
+
159
+ // Usage
160
+ <LoadingButton onClick={handleSubmit}>Submit Form</LoadingButton>
161
+ ```
162
+
163
+ **Use Cases**: ConfirmButton, TooltipButton, LoadingButton, DebounceInput
164
+
165
+ ---
166
+
167
+ ### Pattern 4: List of Components
168
+
169
+ **When to use**: Rendering multiple instances of the same fpkit component.
170
+
171
+ ```tsx
172
+ import { Tag } from '@fpkit/acss'
173
+
174
+ export const TagList = ({ tags, onRemove, ...props }) => {
175
+ return (
176
+ <div className="tag-list" {...props}>
177
+ {tags.map((tag) => (
178
+ <Tag
179
+ key={tag.id}
180
+ onClose={onRemove ? () => onRemove(tag) : undefined}
181
+ >
182
+ {tag.label}
183
+ </Tag>
184
+ ))}
185
+ </div>
186
+ )
187
+ }
188
+
189
+ // Usage
190
+ <TagList
191
+ tags={[
192
+ { id: 1, label: 'React' },
193
+ { id: 2, label: 'TypeScript' },
194
+ ]}
195
+ onRemove={handleRemoveTag}
196
+ />
197
+ ```
198
+
199
+ **Use Cases**: ButtonGroup, BadgeList, BreadcrumbTrail, PillGroup
200
+
201
+ ---
202
+
203
+ ### Pattern 5: Compound Component
204
+
205
+ **When to use**: Multiple related fpkit components that work together.
206
+
207
+ ```tsx
208
+ import { Card, Button } from '@fpkit/acss'
209
+
210
+ export const ActionCard = ({ title, children, actions, ...props }) => {
211
+ return (
212
+ <Card {...props}>
213
+ <Card.Header>
214
+ <Card.Title>{title}</Card.Title>
215
+ </Card.Header>
216
+ <Card.Content>{children}</Card.Content>
217
+ {actions && (
218
+ <Card.Footer>
219
+ {actions.map((action, i) => (
220
+ <Button key={i} {...action} />
221
+ ))}
222
+ </Card.Footer>
223
+ )}
224
+ </Card>
225
+ )
226
+ }
227
+
228
+ // Usage
229
+ <ActionCard
230
+ title="Confirm Action"
231
+ actions={[
232
+ { children: 'Cancel', variant: 'secondary', onClick: handleCancel },
233
+ { children: 'Confirm', variant: 'primary', onClick: handleConfirm },
234
+ ]}
235
+ >
236
+ Are you sure you want to proceed?
237
+ </ActionCard>
238
+ ```
239
+
240
+ **Use Cases**: FormDialog, ArticleCard, ProductCard, SettingsSection
241
+
242
+ ---
243
+
244
+ ## Anti-Patterns to Avoid
245
+
246
+ ### ❌ Anti-Pattern 1: Over-Composition
247
+
248
+ **Problem**: Too many nested layers make the component hard to understand and debug.
249
+
250
+ ```tsx
251
+ // ❌ Bad: Too many wrappers
252
+ <OuterWrapper>
253
+ <MiddleContainer>
254
+ <InnerBox>
255
+ <ContentWrapper>
256
+ <Button>Click</Button>
257
+ </ContentWrapper>
258
+ </InnerBox>
259
+ </MiddleContainer>
260
+ </OuterWrapper>
261
+
262
+ // ✅ Good: Simplified structure
263
+ <Container>
264
+ <Button>Click</Button>
265
+ </Container>
266
+ ```
267
+
268
+ **Rule**: Keep composition depth ≤ 3 levels when possible.
269
+
270
+ ---
271
+
272
+ ### ❌ Anti-Pattern 2: Prop Drilling Through Composition
273
+
274
+ **Problem**: Passing props through multiple layers of composed components.
275
+
276
+ ```tsx
277
+ // ❌ Bad: Props passed through many layers
278
+ <Wrapper theme={theme} size={size} variant={variant}>
279
+ <Container theme={theme} size={size}>
280
+ <Button theme={theme} size={size} variant={variant} />
281
+ </Container>
282
+ </Wrapper>
283
+
284
+ // ✅ Good: Use context or reduce composition depth
285
+ const ThemeContext = createContext()
286
+
287
+ <ThemeProvider value={{ theme, size, variant }}>
288
+ <Wrapper>
289
+ <Container>
290
+ <Button />
291
+ </Container>
292
+ </Wrapper>
293
+ </ThemeProvider>
294
+ ```
295
+
296
+ **Rule**: If passing >3 props through >2 levels, consider context or refactoring.
297
+
298
+ ---
299
+
300
+ ### ❌ Anti-Pattern 3: Duplicating Instead of Composing
301
+
302
+ **Problem**: Copy-pasting component logic instead of reusing fpkit components.
303
+
304
+ ```tsx
305
+ // ❌ Bad: Duplicating Badge logic
306
+ export const Status = ({ variant, children }) => {
307
+ return (
308
+ <span className={`status status-${variant}`}>
309
+ {children}
310
+ </span>
311
+ )
312
+ }
313
+
314
+ // ✅ Good: Reuse fpkit Badge component
315
+ import { Badge } from '@fpkit/acss'
316
+
317
+ export const Status = ({ variant, children }) => {
318
+ return <Badge variant={variant}>{children}</Badge>
319
+ }
320
+ ```
321
+
322
+ **Rule**: If your code looks similar to an fpkit component, reuse that component instead.
323
+
324
+ ---
325
+
326
+ ### ❌ Anti-Pattern 4: Composing Incompatible Components
327
+
328
+ **Problem**: Forcing components together that create accessibility or semantic issues.
329
+
330
+ ```tsx
331
+ // ❌ Bad: Button inside Link creates nested interactive elements (a11y violation)
332
+ import { Button, Link } from '@fpkit/acss'
333
+
334
+ <Link href="/page">
335
+ <Button>Click me</Button>
336
+ </Link>
337
+
338
+ // ✅ Good: Use Button with polymorphic 'as' prop
339
+ <Button as="a" href="/page">
340
+ Click me
341
+ </Button>
342
+ ```
343
+
344
+ **Rule**: Check component APIs for polymorphic props (`as`) and compatibility before composing.
345
+
346
+ ---
347
+
348
+ ## Real-World Examples
349
+
350
+ ### Example 1: AlertDialog (Composition)
351
+
352
+ **Need**: "Create an AlertDialog component that shows alerts in a modal"
353
+
354
+ **Analysis**:
355
+ - fpkit has `Alert` component ✓
356
+ - fpkit has `Dialog` component ✓
357
+ - Can be composed from both
358
+
359
+ **Implementation**:
360
+
361
+ ```tsx
362
+ import { Alert, Dialog } from '@fpkit/acss'
363
+
364
+ export const AlertDialog = ({ variant, title, children, ...dialogProps }) => {
365
+ return (
366
+ <Dialog {...dialogProps}>
367
+ <Alert variant={variant}>
368
+ {title && <strong>{title}</strong>}
369
+ {children}
370
+ </Alert>
371
+ </Dialog>
372
+ )
373
+ }
374
+
375
+ // Usage
376
+ <AlertDialog
377
+ isOpen={showDialog}
378
+ onClose={handleClose}
379
+ variant="error"
380
+ title="Error"
381
+ >
382
+ An error occurred. Please try again.
383
+ </AlertDialog>
384
+ ```
385
+
386
+ ---
387
+
388
+ ### Example 2: IconButton (Composition)
389
+
390
+ **Need**: "Create an IconButton component with text and icon"
391
+
392
+ **Analysis**:
393
+ - fpkit has `Button` component ✓
394
+ - Icons can be added as children ✓
395
+ - Can be composed
396
+
397
+ **Implementation**:
398
+
399
+ ```tsx
400
+ import { Button } from '@fpkit/acss'
401
+
402
+ export const IconButton = ({ icon, children, iconPosition = 'left', ...props }) => {
403
+ return (
404
+ <Button {...props}>
405
+ {iconPosition === 'left' && <span className="icon">{icon}</span>}
406
+ {children}
407
+ {iconPosition === 'right' && <span className="icon">{icon}</span>}
408
+ </Button>
409
+ )
410
+ }
411
+
412
+ // Usage
413
+ <IconButton icon="💾" variant="primary">
414
+ Save Changes
415
+ </IconButton>
416
+
417
+ <IconButton icon="→" iconPosition="right" variant="secondary">
418
+ Next
419
+ </IconButton>
420
+ ```
421
+
422
+ ---
423
+
424
+ ### Example 3: TagInput (Compound Composition)
425
+
426
+ **Need**: "Create a TagInput component that allows adding/removing tags"
427
+
428
+ **Analysis**:
429
+ - fpkit has `Tag` component ✓
430
+ - Standard `input` element for text entry
431
+ - Complex interaction → compose with custom logic
432
+
433
+ **Implementation**:
434
+
435
+ ```tsx
436
+ import { Tag } from '@fpkit/acss'
437
+ import { useState } from 'react'
438
+
439
+ export const TagInput = ({ value = [], onChange, placeholder, ...props }) => {
440
+ const [inputValue, setInputValue] = useState('')
441
+
442
+ const addTag = () => {
443
+ if (inputValue.trim() && !value.includes(inputValue.trim())) {
444
+ onChange?.([...value, inputValue.trim()])
445
+ setInputValue('')
446
+ }
447
+ }
448
+
449
+ const removeTag = (tagToRemove) => {
450
+ onChange?.(value.filter((tag) => tag !== tagToRemove))
451
+ }
452
+
453
+ return (
454
+ <div className="tag-input" {...props}>
455
+ <div className="tag-list">
456
+ {value.map((tag) => (
457
+ <Tag key={tag} onClose={() => removeTag(tag)}>
458
+ {tag}
459
+ </Tag>
460
+ ))}
461
+ </div>
462
+ <input
463
+ type="text"
464
+ value={inputValue}
465
+ onChange={(e) => setInputValue(e.target.value)}
466
+ onKeyDown={(e) => {
467
+ if (e.key === 'Enter') {
468
+ e.preventDefault()
469
+ addTag()
470
+ }
471
+ }}
472
+ placeholder={placeholder || 'Add tag...'}
473
+ />
474
+ </div>
475
+ )
476
+ }
477
+
478
+ // Usage
479
+ <TagInput
480
+ value={tags}
481
+ onChange={setTags}
482
+ placeholder="Add technology..."
483
+ />
484
+ ```
485
+
486
+ ---
487
+
488
+ ### Example 4: ConfirmButton (Enhanced Wrapper)
489
+
490
+ **Need**: "Button that requires confirmation before executing action"
491
+
492
+ **Implementation**:
493
+
494
+ ```tsx
495
+ import { Button, Dialog } from '@fpkit/acss'
496
+ import { useState } from 'react'
497
+
498
+ export const ConfirmButton = ({
499
+ confirmTitle = 'Confirm Action',
500
+ confirmMessage = 'Are you sure?',
501
+ onConfirm,
502
+ children,
503
+ ...props
504
+ }) => {
505
+ const [showConfirm, setShowConfirm] = useState(false)
506
+
507
+ const handleConfirm = () => {
508
+ setShowConfirm(false)
509
+ onConfirm?.()
510
+ }
511
+
512
+ return (
513
+ <>
514
+ <Button {...props} onClick={() => setShowConfirm(true)}>
515
+ {children}
516
+ </Button>
517
+
518
+ <Dialog isOpen={showConfirm} onClose={() => setShowConfirm(false)}>
519
+ <h2>{confirmTitle}</h2>
520
+ <p>{confirmMessage}</p>
521
+ <div className="dialog-actions">
522
+ <Button variant="secondary" onClick={() => setShowConfirm(false)}>
523
+ Cancel
524
+ </Button>
525
+ <Button variant="primary" onClick={handleConfirm}>
526
+ Confirm
527
+ </Button>
528
+ </div>
529
+ </Dialog>
530
+ </>
531
+ )
532
+ }
533
+
534
+ // Usage
535
+ <ConfirmButton
536
+ variant="danger"
537
+ confirmTitle="Delete Account"
538
+ confirmMessage="This action cannot be undone. Are you sure?"
539
+ onConfirm={handleDeleteAccount}
540
+ >
541
+ Delete Account
542
+ </ConfirmButton>
543
+ ```
544
+
545
+ ---
546
+
547
+ ## Styling Composed Components
548
+
549
+ When composing fpkit components, you can customize styles using CSS variables:
550
+
551
+ ```tsx
552
+ import { Button, Badge } from '@fpkit/acss'
553
+
554
+ export const PriorityButton = ({ priority, children, ...props }) => {
555
+ return (
556
+ <Button
557
+ {...props}
558
+ style={{
559
+ '--btn-padding-inline': '2rem',
560
+ '--btn-gap': '0.75rem',
561
+ }}
562
+ >
563
+ {children}
564
+ <Badge
565
+ variant={priority === 'high' ? 'error' : 'default'}
566
+ style={{
567
+ '--badge-fs': '0.75rem',
568
+ }}
569
+ >
570
+ {priority}
571
+ </Badge>
572
+ </Button>
573
+ )
574
+ }
575
+ ```
576
+
577
+ See the [CSS Variables Guide](./css-variables.md) for complete customization options.
578
+
579
+ ---
580
+
581
+ ## TypeScript Support
582
+
583
+ fpkit components are fully typed. When composing, preserve type safety:
584
+
585
+ ```tsx
586
+ import { Button, ButtonProps } from '@fpkit/acss'
587
+ import { ReactNode } from 'react'
588
+
589
+ interface LoadingButtonProps extends ButtonProps {
590
+ loading?: boolean
591
+ loadingText?: ReactNode
592
+ }
593
+
594
+ export const LoadingButton = ({
595
+ loading,
596
+ loadingText = 'Loading...',
597
+ children,
598
+ ...props
599
+ }: LoadingButtonProps) => {
600
+ return (
601
+ <Button {...props} disabled={loading || props.disabled}>
602
+ {loading ? loadingText : children}
603
+ </Button>
604
+ )
605
+ }
606
+ ```
607
+
608
+ ---
609
+
610
+ ## Testing Composed Components
611
+
612
+ When testing compositions, focus on integration rather than unit testing fpkit components (they're already tested):
613
+
614
+ ```tsx
615
+ import { render, screen } from '@testing-library/react'
616
+ import userEvent from '@testing-library/user-event'
617
+ import { TagInput } from './tag-input'
618
+
619
+ describe('TagInput', () => {
620
+ it('adds tag on Enter key', async () => {
621
+ const handleChange = vi.fn()
622
+ render(<TagInput value={[]} onChange={handleChange} />)
623
+
624
+ const input = screen.getByPlaceholderText('Add tag...')
625
+ await userEvent.type(input, 'React{Enter}')
626
+
627
+ expect(handleChange).toHaveBeenCalledWith(['React'])
628
+ })
629
+
630
+ it('removes tag on close', async () => {
631
+ const handleChange = vi.fn()
632
+ render(<TagInput value={['React']} onChange={handleChange} />)
633
+
634
+ const closeButton = screen.getByRole('button', { name: /close/i })
635
+ await userEvent.click(closeButton)
636
+
637
+ expect(handleChange).toHaveBeenCalledWith([])
638
+ })
639
+ })
640
+ ```
641
+
642
+ See the [Testing Guide](./testing.md) for more patterns.
643
+
644
+ ---
645
+
646
+ ## Guidelines Summary
647
+
648
+ | Scenario | Approach | Strategy |
649
+ |----------|----------|----------|
650
+ | Exact match exists | Use directly | Customize with CSS variables |
651
+ | 2+ components can be combined | Compose | Import and combine |
652
+ | Similar to existing | Wrap/extend | Add custom logic around fpkit component |
653
+ | Needs custom UI | Create from scratch | Follow fpkit styling patterns |
654
+ | Complex multi-part UI | Compound composition | Use multiple related components |
655
+
656
+ ---
657
+
658
+ ## Best Practices
659
+
660
+ ### ✅ Do
661
+
662
+ - **Start with fpkit components** - Check what exists before building custom
663
+ - **Preserve accessibility** - Keep ARIA attributes and keyboard navigation from fpkit components
664
+ - **Use CSS variables** - Customize appearance without modifying component structure
665
+ - **Document composition** - Note which fpkit components are used in JSDoc comments
666
+ - **Test integration** - Focus tests on how composed parts work together
667
+ - **Export cleanly** - Re-export composed components from a single file
668
+
669
+ ### ❌ Don't
670
+
671
+ - **Don't duplicate fpkit logic** - If it exists in fpkit, reuse it
672
+ - **Don't break accessibility** - Nested interactive elements, missing ARIA attributes
673
+ - **Don't over-compose** - Keep composition depth reasonable (≤3 levels)
674
+ - **Don't prop drill** - Use context or reduce composition depth
675
+ - **Don't ignore polymorphism** - Use `as` prop instead of wrapping
676
+
677
+ ---
678
+
679
+ ## Next Steps
680
+
681
+ - **[CSS Variables Guide](./css-variables.md)** - Learn how to customize fpkit components
682
+ - **[Accessibility Guide](./accessibility.md)** - Ensure compositions remain accessible
683
+ - **[Architecture Guide](./architecture.md)** - Understand fpkit component patterns
684
+ - **[Testing Guide](./testing.md)** - Learn testing strategies for composed components
685
+
686
+ ---
687
+
688
+ **Remember**: Composition is about smart reuse. Don't compose for the sake of it – compose when it creates clearer, more maintainable code that leverages the tested, accessible primitives from @fpkit/acss.