@fpkit/acss 0.5.12 → 0.5.13
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 +89 -0
- package/libs/{chunk-DV56L5YX.cjs → chunk-2LTJ7HHX.cjs} +4 -4
- package/libs/{chunk-EQ67LF46.js → chunk-2Y7W75TT.js} +3 -3
- package/libs/{chunk-KKLTUJFB.cjs → chunk-3MKLDCKQ.cjs} +5 -5
- package/libs/chunk-3MKLDCKQ.cjs.map +1 -0
- package/libs/{chunk-X3EVB7VS.cjs → chunk-5S4ORA4C.cjs} +3 -3
- package/libs/{chunk-O6QZBB6G.js → chunk-772NRB75.js} +5 -5
- package/libs/chunk-772NRB75.js.map +1 -0
- package/libs/{chunk-6BVXFW7U.cjs → chunk-AHDJGCG5.cjs} +3 -3
- package/libs/{chunk-E3XP6BEX.cjs → chunk-B7F5FS6D.cjs} +3 -3
- package/libs/chunk-D4YLRWAO.cjs +18 -0
- package/libs/chunk-D4YLRWAO.cjs.map +1 -0
- package/libs/chunk-ETFLFC2S.js +10 -0
- package/libs/chunk-ETFLFC2S.js.map +1 -0
- package/libs/chunk-GZ4QFPRY.js +9 -0
- package/libs/chunk-GZ4QFPRY.js.map +1 -0
- package/libs/{chunk-LHVJKDMA.cjs → chunk-J32EZPYD.cjs} +3 -3
- package/libs/chunk-JJ43O4Y5.js +8 -0
- package/libs/chunk-JJ43O4Y5.js.map +1 -0
- package/libs/chunk-KUKIVRC2.js +7 -0
- package/libs/chunk-KUKIVRC2.js.map +1 -0
- package/libs/chunk-L75OQKEI.cjs +13 -0
- package/libs/chunk-L75OQKEI.cjs.map +1 -0
- package/libs/{chunk-LL7HTLMS.cjs → chunk-M5RRNTVX.cjs} +3 -3
- package/libs/{chunk-LIQJ7ZZR.js → chunk-NGTJDDFO.js} +2 -2
- package/libs/chunk-OK5QEIMD.cjs +17 -0
- package/libs/chunk-OK5QEIMD.cjs.map +1 -0
- package/libs/chunk-P2DC76ZZ.cjs +18 -0
- package/libs/chunk-P2DC76ZZ.cjs.map +1 -0
- package/libs/chunk-PQ2K3BM6.cjs +17 -0
- package/libs/chunk-PQ2K3BM6.cjs.map +1 -0
- package/libs/{chunk-QCMV4VQZ.js → chunk-QLZWHAMK.js} +2 -2
- package/libs/{chunk-BIP2NY53.js → chunk-RIVUMPOG.js} +2 -2
- package/libs/{chunk-ICCKQ2GC.cjs → chunk-ROZI23GS.cjs} +4 -4
- package/libs/{chunk-NHYXGV3L.js → chunk-SMYRLO3E.js} +2 -2
- package/libs/{chunk-5ZM4XL44.js → chunk-TYRCEX2L.js} +2 -2
- package/libs/chunk-VUH3FXGJ.js +11 -0
- package/libs/chunk-VUH3FXGJ.js.map +1 -0
- package/libs/{chunk-PPOOBUOS.js → chunk-XBA562WW.js} +2 -2
- package/libs/{chunk-QVV34QEH.cjs → chunk-XTQKWY7W.cjs} +3 -3
- package/libs/{chunk-YWOYVRFT.js → chunk-ZANSFMTD.js} +3 -3
- package/libs/components/alert/alert.css +1 -1
- package/libs/components/alert/alert.css.map +1 -1
- package/libs/components/alert/alert.min.css +2 -2
- package/libs/components/badge/badge.css +1 -1
- package/libs/components/badge/badge.css.map +1 -1
- package/libs/components/badge/badge.min.css +2 -2
- package/libs/components/breadcrumbs/breadcrumb.cjs +9 -5
- package/libs/components/breadcrumbs/breadcrumb.d.cts +271 -32
- package/libs/components/breadcrumbs/breadcrumb.d.ts +271 -32
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +4 -4
- package/libs/components/button.d.cts +2 -2
- package/libs/components/button.d.ts +2 -2
- package/libs/components/button.js +2 -2
- package/libs/components/buttons/button.css +1 -1
- package/libs/components/buttons/button.css.map +1 -1
- package/libs/components/buttons/button.min.css +2 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +277 -33
- package/libs/components/card.d.ts +277 -33
- package/libs/components/card.js +2 -2
- package/libs/components/cards/card.css +1 -1
- package/libs/components/cards/card.css.map +1 -1
- package/libs/components/cards/card.min.css +2 -2
- package/libs/components/details/details.css +1 -1
- package/libs/components/details/details.css.map +1 -1
- package/libs/components/details/details.min.css +2 -2
- package/libs/components/dialog/dialog.cjs +7 -7
- package/libs/components/dialog/dialog.css +1 -1
- package/libs/components/dialog/dialog.css.map +1 -1
- package/libs/components/dialog/dialog.d.cts +88 -34
- package/libs/components/dialog/dialog.d.ts +88 -34
- package/libs/components/dialog/dialog.js +5 -5
- package/libs/components/dialog/dialog.min.css +2 -2
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.d.cts +2 -2
- package/libs/components/form/fields.d.ts +2 -2
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/textarea.cjs +4 -4
- package/libs/components/form/textarea.d.cts +2 -2
- package/libs/components/form/textarea.d.ts +2 -2
- package/libs/components/form/textarea.js +2 -2
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.d.cts +3 -14
- package/libs/components/heading/heading.d.ts +3 -14
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +148 -4
- package/libs/components/icons/icon.d.ts +148 -4
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/images/img.css +1 -1
- package/libs/components/images/img.css.map +1 -1
- package/libs/components/images/img.min.css +2 -2
- package/libs/components/link/link.cjs +4 -4
- package/libs/components/link/link.d.cts +2 -2
- package/libs/components/link/link.d.ts +2 -2
- package/libs/components/link/link.js +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.d.cts +3 -3
- package/libs/components/list/list.d.ts +3 -3
- package/libs/components/list/list.js +2 -2
- package/libs/components/modal.cjs +4 -4
- package/libs/components/modal.js +3 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.d.cts +2 -2
- package/libs/components/nav/nav.d.ts +2 -2
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.d.cts +2 -2
- package/libs/components/text/text.d.ts +2 -2
- package/libs/components/text/text.js +2 -2
- package/libs/heading-3648c538.d.ts +250 -0
- package/libs/hooks.cjs +7 -0
- package/libs/hooks.d.cts +5 -0
- package/libs/hooks.d.ts +5 -0
- package/libs/hooks.js +3 -0
- package/libs/icons.cjs +3 -3
- package/libs/icons.d.cts +1 -1
- package/libs/icons.d.ts +1 -1
- package/libs/icons.js +2 -2
- package/libs/index.cjs +112 -91
- package/libs/index.cjs.map +1 -1
- package/libs/index.css +1 -1
- package/libs/index.css.map +1 -1
- package/libs/index.d.cts +515 -31
- package/libs/index.d.ts +515 -31
- package/libs/index.js +31 -19
- package/libs/index.js.map +1 -1
- package/libs/ui-645f95b5.d.ts +285 -0
- package/package.json +2 -83
- package/src/components/README-UI.mdx +416 -0
- package/src/components/alert/ACCESSIBILITY.md +319 -0
- package/src/components/alert/README.mdx +475 -19
- package/src/components/alert/alert.scss +113 -6
- package/src/components/alert/alert.stories.tsx +372 -0
- package/src/components/alert/alert.test.tsx +762 -0
- package/src/components/alert/alert.tsx +331 -66
- package/src/components/alert/views/alert-actions.tsx +13 -0
- package/src/components/alert/views/alert-content.tsx +17 -0
- package/src/components/alert/views/alert-icon.tsx +53 -0
- package/src/components/alert/views/alert-screen-reader-text.tsx +30 -0
- package/src/components/alert/views/alert-title.tsx +23 -0
- package/src/components/alert/views/alert-view.tsx +158 -0
- package/src/components/alert/views/index.ts +12 -0
- package/src/components/badge/badge.mdx +186 -49
- package/src/components/badge/badge.scss +20 -2
- package/src/components/badge/badge.stories.tsx +160 -14
- package/src/components/badge/badge.test.tsx +179 -0
- package/src/components/badge/badge.tsx +97 -4
- package/src/components/breadcrumbs/README.mdx +364 -45
- package/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap +152 -0
- package/src/components/breadcrumbs/breadcrumb.stories.tsx +7 -3
- package/src/components/breadcrumbs/breadcrumb.test.tsx +490 -0
- package/src/components/breadcrumbs/breadcrumb.tsx +427 -170
- package/src/components/buttons/button.scss +34 -31
- package/src/components/buttons/button.stories.tsx +35 -0
- package/src/components/cards/README.mdx +657 -0
- package/src/components/cards/card.scss +22 -0
- package/src/components/cards/card.stories.tsx +167 -5
- package/src/components/cards/card.test.tsx +360 -20
- package/src/components/cards/card.tsx +200 -79
- package/src/components/cards/card.types.ts +135 -0
- package/src/components/cards/card.utils.ts +79 -0
- package/src/components/details/ACCESSIBILITY-REVIEW-LIVE.md +1050 -0
- package/src/components/details/ACCESSIBILITY-REVIEW.md +502 -0
- package/src/components/details/README.mdx +437 -69
- package/src/components/details/details.scss +16 -7
- package/src/components/details/details.test.tsx +385 -0
- package/src/components/details/details.tsx +101 -69
- package/src/components/details/details.types.ts +76 -0
- package/src/components/dialog/README.mdx +513 -110
- package/src/components/dialog/dialog-modal.tsx +79 -56
- package/src/components/dialog/dialog.scss +53 -3
- package/src/components/dialog/dialog.stories.tsx +10 -7
- package/src/components/dialog/dialog.test.tsx +450 -0
- package/src/components/dialog/dialog.tsx +69 -59
- package/src/components/dialog/dialog.types.ts +133 -0
- package/src/components/dialog/views/dialog-footer.tsx +54 -11
- package/src/components/dialog/views/dialog-header.tsx +20 -15
- package/src/components/heading/heading.stories.tsx +44 -4
- package/src/components/heading/heading.tsx +89 -23
- package/src/components/icons/README.mdx +332 -0
- package/src/components/icons/icon.stories.tsx +74 -1
- package/src/components/icons/icon.tsx +89 -1
- package/src/components/icons/types.ts +47 -0
- package/src/components/images/README.mdx +340 -24
- package/src/components/images/img.scss +19 -3
- package/src/components/images/img.stories.tsx +424 -15
- package/src/components/images/img.test.tsx +354 -25
- package/src/components/images/img.tsx +186 -63
- package/src/components/images/img.types.ts +211 -0
- package/src/components/title/MIGRATION.md +199 -0
- package/src/components/title/README.md +326 -0
- package/src/components/title/README.mdx +452 -0
- package/src/components/title/title.stories.tsx +393 -0
- package/src/components/title/title.test.tsx +251 -0
- package/src/components/title/title.tsx +219 -0
- package/src/components/ui.stories.tsx +894 -0
- package/src/components/ui.test.tsx +559 -0
- package/src/components/ui.tsx +266 -15
- package/src/components/word-count/README.md +240 -0
- package/src/hooks.ts +1 -0
- package/src/index.ts +10 -2
- package/src/sass/_properties.scss +1 -0
- package/src/styles/alert/alert.css +94 -4
- package/src/styles/alert/alert.css.map +1 -1
- package/src/styles/badge/badge.css +20 -2
- package/src/styles/badge/badge.css.map +1 -1
- package/src/styles/buttons/button.css +31 -31
- package/src/styles/buttons/button.css.map +1 -1
- package/src/styles/cards/card.css +16 -0
- package/src/styles/cards/card.css.map +1 -1
- package/src/styles/details/details.css +19 -8
- package/src/styles/details/details.css.map +1 -1
- package/src/styles/dialog/dialog.css +43 -2
- package/src/styles/dialog/dialog.css.map +1 -1
- package/src/styles/images/img.css +15 -3
- package/src/styles/images/img.css.map +1 -1
- package/src/styles/index.css +240 -51
- package/src/styles/index.css.map +1 -1
- package/src/test/setup.d.ts +9 -0
- package/src/test/setup.ts +53 -1
- package/libs/chunk-6TE5QEVE.cjs +0 -13
- package/libs/chunk-6TE5QEVE.cjs.map +0 -1
- package/libs/chunk-7K76RW2A.cjs +0 -18
- package/libs/chunk-7K76RW2A.cjs.map +0 -1
- package/libs/chunk-BSPKFLO4.js +0 -8
- package/libs/chunk-BSPKFLO4.js.map +0 -1
- package/libs/chunk-BV5CLH44.cjs +0 -18
- package/libs/chunk-BV5CLH44.cjs.map +0 -1
- package/libs/chunk-DKGJHKGW.js +0 -9
- package/libs/chunk-DKGJHKGW.js.map +0 -1
- package/libs/chunk-ECLD37WN.cjs +0 -16
- package/libs/chunk-ECLD37WN.cjs.map +0 -1
- package/libs/chunk-HYBZBN4G.js +0 -8
- package/libs/chunk-HYBZBN4G.js.map +0 -1
- package/libs/chunk-KKLTUJFB.cjs.map +0 -1
- package/libs/chunk-M5QL5TAE.cjs +0 -14
- package/libs/chunk-M5QL5TAE.cjs.map +0 -1
- package/libs/chunk-NE6YXTMC.js +0 -7
- package/libs/chunk-NE6YXTMC.js.map +0 -1
- package/libs/chunk-O6QZBB6G.js.map +0 -1
- package/libs/chunk-SXVZSWX6.js +0 -11
- package/libs/chunk-SXVZSWX6.js.map +0 -1
- package/libs/ui-9a6f9f8d.d.ts +0 -24
- package/src/components/cards/README.md +0 -80
- package/src/components/dialog/hooks/useClickOutside.ts +0 -33
- /package/libs/{chunk-DV56L5YX.cjs.map → chunk-2LTJ7HHX.cjs.map} +0 -0
- /package/libs/{chunk-EQ67LF46.js.map → chunk-2Y7W75TT.js.map} +0 -0
- /package/libs/{chunk-X3EVB7VS.cjs.map → chunk-5S4ORA4C.cjs.map} +0 -0
- /package/libs/{chunk-6BVXFW7U.cjs.map → chunk-AHDJGCG5.cjs.map} +0 -0
- /package/libs/{chunk-E3XP6BEX.cjs.map → chunk-B7F5FS6D.cjs.map} +0 -0
- /package/libs/{chunk-LHVJKDMA.cjs.map → chunk-J32EZPYD.cjs.map} +0 -0
- /package/libs/{chunk-LL7HTLMS.cjs.map → chunk-M5RRNTVX.cjs.map} +0 -0
- /package/libs/{chunk-LIQJ7ZZR.js.map → chunk-NGTJDDFO.js.map} +0 -0
- /package/libs/{chunk-QCMV4VQZ.js.map → chunk-QLZWHAMK.js.map} +0 -0
- /package/libs/{chunk-BIP2NY53.js.map → chunk-RIVUMPOG.js.map} +0 -0
- /package/libs/{chunk-ICCKQ2GC.cjs.map → chunk-ROZI23GS.cjs.map} +0 -0
- /package/libs/{chunk-NHYXGV3L.js.map → chunk-SMYRLO3E.js.map} +0 -0
- /package/libs/{chunk-5ZM4XL44.js.map → chunk-TYRCEX2L.js.map} +0 -0
- /package/libs/{chunk-PPOOBUOS.js.map → chunk-XBA562WW.js.map} +0 -0
- /package/libs/{chunk-QVV34QEH.cjs.map → chunk-XTQKWY7W.cjs.map} +0 -0
- /package/libs/{chunk-YWOYVRFT.js.map → chunk-ZANSFMTD.js.map} +0 -0
|
@@ -4,98 +4,466 @@ import { Meta } from "@storybook/addon-docs/blocks";
|
|
|
4
4
|
|
|
5
5
|
# Details Component
|
|
6
6
|
|
|
7
|
+
A WCAG 2.1 AA compliant progressive disclosure component that leverages native HTML `<details>` and `<summary>` elements for accessible, semantic expand/collapse functionality.
|
|
8
|
+
|
|
7
9
|
## Summary
|
|
8
10
|
|
|
9
|
-
The `Details` component
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
The `Details` component provides a native, accessible way to show and hide content. Built on the HTML5 `<details>` element, it includes built-in keyboard support, automatic ARIA semantics, and support for accordion-style grouping where only one item can be open at a time.
|
|
12
|
+
|
|
13
|
+
**Latest Version:** v0.5.11+ (Refactored with breaking changes)
|
|
12
14
|
|
|
13
15
|
## Features
|
|
14
16
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
17
|
+
- 🎯 **Semantic HTML** - Uses native `<details>` and `<summary>` elements
|
|
18
|
+
- ⌨️ **Built-in keyboard support** - Space/Enter toggle without custom JavaScript
|
|
19
|
+
- ♿ **WCAG 2.1 AA compliant** - A- rating with excellent accessibility
|
|
20
|
+
- 🎪 **Accordion mode** - Group multiple details with `name` attribute
|
|
21
|
+
- 🔄 **Auto-managed state** - Browser handles `aria-expanded` automatically
|
|
22
|
+
- ⚡ **Performance optimized** - React.forwardRef and useCallback for efficiency
|
|
23
|
+
- 🎨 **Fully customizable** - Supports icons, custom styles, and event handlers
|
|
24
|
+
- 🧪 **Comprehensive testing** - Includes automated accessibility testing with axe-core
|
|
25
|
+
|
|
26
|
+
## Accessibility
|
|
27
|
+
|
|
28
|
+
- ✅ Native `<details>` element with semantic screen reader announcements
|
|
29
|
+
- ✅ Built-in keyboard navigation (Space, Enter to toggle)
|
|
30
|
+
- ✅ Automatic `aria-expanded` state management by browser
|
|
31
|
+
- ✅ Screen readers announce as "disclosure" or "expandable" widget
|
|
32
|
+
- ✅ Focus indicators with visible styling
|
|
33
|
+
- ✅ No keyboard traps
|
|
34
|
+
- ✅ Ref forwarding for programmatic focus management
|
|
35
|
+
- ✅ Optional `aria-label` support for additional context
|
|
36
|
+
|
|
37
|
+
**Accessibility Rating:** ✅ A- (Excellent) - WCAG 2.1 Level AA
|
|
38
|
+
|
|
39
|
+
See [ACCESSIBILITY-REVIEW.md](./ACCESSIBILITY-REVIEW.md) for detailed compliance analysis.
|
|
19
40
|
|
|
20
41
|
## Props
|
|
21
42
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| `styles` | `React.CSSProperties` | CSS styles object. |
|
|
27
|
-
| `classes` | `string` | Classnames string. |
|
|
28
|
-
| `open` | `boolean` | Whether the details is open. |
|
|
29
|
-
| `onToggle` | `(e: React.PointerEvent<HTMLDetailsElement>) => void` | onToggle callback. |
|
|
30
|
-
| `onPointerDown` | `(e: React.PointerEvent<HTMLDetailsElement>) => void` | onPointerDown callback. |
|
|
31
|
-
| `children` | `React.ReactNode` | The content inside the details. |
|
|
32
|
-
| `ref` | `React.Ref<any>` | Ref object. |
|
|
33
|
-
| `name` | `string` | Name attribute for the details element. |
|
|
43
|
+
```ts
|
|
44
|
+
type DetailsProps = {
|
|
45
|
+
/** The summary text or element shown in the clickable header (required) */
|
|
46
|
+
summary: React.ReactNode;
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
/** Optional icon displayed before the summary text */
|
|
49
|
+
icon?: React.ReactNode;
|
|
50
|
+
|
|
51
|
+
/** Accessible label for screen readers (rarely needed with native semantics) */
|
|
52
|
+
ariaLabel?: string;
|
|
53
|
+
|
|
54
|
+
/** Groups multiple details into an accordion (only one open at a time) */
|
|
55
|
+
name?: string;
|
|
56
|
+
|
|
57
|
+
/** Custom CSS styles */
|
|
58
|
+
styles?: React.CSSProperties;
|
|
59
|
+
|
|
60
|
+
/** Custom CSS classes */
|
|
61
|
+
classes?: string;
|
|
62
|
+
|
|
63
|
+
/** Whether the details is initially open */
|
|
64
|
+
open?: boolean;
|
|
65
|
+
|
|
66
|
+
/** Callback fired when details is toggled */
|
|
67
|
+
onToggle?: (e: React.SyntheticEvent<HTMLDetailsElement>) => void;
|
|
68
|
+
|
|
69
|
+
/** Callback fired on pointer down on summary */
|
|
70
|
+
onPointerDown?: (e: React.PointerEvent<HTMLDetailsElement>) => void;
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
/** Content to display when expanded */
|
|
73
|
+
children: React.ReactNode;
|
|
74
|
+
} & React.ComponentProps<"details"> & Partial<React.ComponentProps<typeof UI>>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Default Values
|
|
40
78
|
|
|
41
|
-
|
|
79
|
+
| Prop | Default | Description |
|
|
80
|
+
|------|---------|-------------|
|
|
81
|
+
| `open` | `false` | Details start collapsed |
|
|
82
|
+
| `icon` | `undefined` | No icon by default |
|
|
83
|
+
| `ariaLabel` | `undefined` | Native semantics used |
|
|
84
|
+
| `name` | `undefined` | Independent behavior (not grouped) |
|
|
85
|
+
|
|
86
|
+
## Usage Examples
|
|
42
87
|
|
|
43
88
|
### Basic Usage
|
|
44
89
|
|
|
90
|
+
Simple expand/collapse section:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Details } from '@fpkit/acss';
|
|
94
|
+
|
|
95
|
+
function ShippingInfo() {
|
|
96
|
+
return (
|
|
97
|
+
<Details summary="Shipping Information">
|
|
98
|
+
<p>Ships within 2-3 business days</p>
|
|
99
|
+
<p>Free shipping on orders over $50</p>
|
|
100
|
+
</Details>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### With Icon
|
|
106
|
+
|
|
107
|
+
Add a chevron or custom icon indicator:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { Details } from '@fpkit/acss';
|
|
111
|
+
import { ChevronDownIcon } from '../icons';
|
|
112
|
+
|
|
113
|
+
function ProductDetails() {
|
|
114
|
+
return (
|
|
115
|
+
<Details
|
|
116
|
+
summary="Product Specifications"
|
|
117
|
+
icon={<ChevronDownIcon aria-hidden="true" />}
|
|
118
|
+
>
|
|
119
|
+
<ul>
|
|
120
|
+
<li>Material: 100% Cotton</li>
|
|
121
|
+
<li>Weight: 200g</li>
|
|
122
|
+
<li>Dimensions: 10x15x5 cm</li>
|
|
123
|
+
</ul>
|
|
124
|
+
</Details>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Accordion Mode (Exclusive Opening)
|
|
130
|
+
|
|
131
|
+
Only one details can be open at a time using the `name` attribute:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { Details } from '@fpkit/acss';
|
|
135
|
+
|
|
136
|
+
function FAQ() {
|
|
137
|
+
return (
|
|
138
|
+
<section>
|
|
139
|
+
<h2>Frequently Asked Questions</h2>
|
|
140
|
+
|
|
141
|
+
<Details name="faq-accordion" summary="What is your return policy?">
|
|
142
|
+
<p>We accept returns within 30 days of purchase.</p>
|
|
143
|
+
</Details>
|
|
144
|
+
|
|
145
|
+
<Details name="faq-accordion" summary="How long does shipping take?">
|
|
146
|
+
<p>Standard shipping takes 3-5 business days.</p>
|
|
147
|
+
</Details>
|
|
148
|
+
|
|
149
|
+
<Details name="faq-accordion" summary="Do you ship internationally?">
|
|
150
|
+
<p>Yes, we ship to over 100 countries worldwide.</p>
|
|
151
|
+
</Details>
|
|
152
|
+
</section>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Controlled State with Event Handlers
|
|
158
|
+
|
|
159
|
+
Track open/closed state and respond to changes:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { Details } from '@fpkit/acss';
|
|
163
|
+
import { useState } from 'react';
|
|
164
|
+
|
|
165
|
+
function AnalyticsExample() {
|
|
166
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
167
|
+
|
|
168
|
+
const handleToggle = (e: React.SyntheticEvent<HTMLDetailsElement>) => {
|
|
169
|
+
const newState = e.currentTarget.open;
|
|
170
|
+
setIsOpen(newState);
|
|
171
|
+
|
|
172
|
+
// Track analytics
|
|
173
|
+
console.log('Details toggled:', newState ? 'opened' : 'closed');
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Details
|
|
178
|
+
summary="View Details"
|
|
179
|
+
open={isOpen}
|
|
180
|
+
onToggle={handleToggle}
|
|
181
|
+
>
|
|
182
|
+
<p>Content that can be tracked when opened/closed</p>
|
|
183
|
+
</Details>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### With Custom Styling
|
|
189
|
+
|
|
190
|
+
Apply custom styles and classes:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
import { Details } from '@fpkit/acss';
|
|
194
|
+
|
|
195
|
+
function StyledDetails() {
|
|
196
|
+
return (
|
|
197
|
+
<Details
|
|
198
|
+
summary="Custom Styled Section"
|
|
199
|
+
classes="my-custom-details"
|
|
200
|
+
styles={{
|
|
201
|
+
'--details-border-color': '#0066cc',
|
|
202
|
+
'--details-bg-color': '#f5f5f5',
|
|
203
|
+
} as React.CSSProperties}
|
|
204
|
+
>
|
|
205
|
+
<p>Content with custom styling using CSS variables</p>
|
|
206
|
+
</Details>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### With Dynamic Content Loading
|
|
212
|
+
|
|
213
|
+
Load content when details is opened:
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { Details } from '@fpkit/acss';
|
|
217
|
+
import { useState } from 'react';
|
|
218
|
+
|
|
219
|
+
function DynamicContent() {
|
|
220
|
+
const [data, setData] = useState<string | null>(null);
|
|
221
|
+
const [loading, setLoading] = useState(false);
|
|
222
|
+
|
|
223
|
+
const handleToggle = async (e: React.SyntheticEvent<HTMLDetailsElement>) => {
|
|
224
|
+
if (e.currentTarget.open && !data) {
|
|
225
|
+
setLoading(true);
|
|
226
|
+
const response = await fetch('/api/data');
|
|
227
|
+
const result = await response.text();
|
|
228
|
+
setData(result);
|
|
229
|
+
setLoading(false);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<Details summary="Load More Data" onToggle={handleToggle}>
|
|
235
|
+
{loading ? (
|
|
236
|
+
<div role="status" aria-live="polite">
|
|
237
|
+
Loading...
|
|
238
|
+
</div>
|
|
239
|
+
) : (
|
|
240
|
+
<div>{data || 'Click to load data'}</div>
|
|
241
|
+
)}
|
|
242
|
+
</Details>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### With Ref Forwarding
|
|
248
|
+
|
|
249
|
+
Access the underlying details element:
|
|
250
|
+
|
|
45
251
|
```tsx
|
|
46
|
-
import
|
|
47
|
-
import
|
|
48
|
-
|
|
252
|
+
import { Details } from '@fpkit/acss';
|
|
253
|
+
import { useRef } from 'react';
|
|
254
|
+
|
|
255
|
+
function RefExample() {
|
|
256
|
+
const detailsRef = useRef<HTMLDetailsElement>(null);
|
|
257
|
+
|
|
258
|
+
const openProgrammatically = () => {
|
|
259
|
+
if (detailsRef.current) {
|
|
260
|
+
detailsRef.current.open = true;
|
|
261
|
+
detailsRef.current.focus();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return (
|
|
266
|
+
<>
|
|
267
|
+
<button onClick={openProgrammatically}>
|
|
268
|
+
Open Details
|
|
269
|
+
</button>
|
|
270
|
+
|
|
271
|
+
<Details ref={detailsRef} summary="Programmatically Controlled">
|
|
272
|
+
<p>This can be opened via ref</p>
|
|
273
|
+
</Details>
|
|
274
|
+
</>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Styling
|
|
280
|
+
|
|
281
|
+
The component includes default SCSS styles with CSS custom properties for theming.
|
|
282
|
+
|
|
283
|
+
### CSS Custom Properties
|
|
284
|
+
|
|
285
|
+
```css
|
|
286
|
+
details {
|
|
287
|
+
--details-border-color: currentColor;
|
|
288
|
+
--details-bg-color: transparent;
|
|
289
|
+
--details-padding: 1rem;
|
|
290
|
+
--details-border-radius: 0.25rem;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### SCSS Classes
|
|
295
|
+
|
|
296
|
+
The component applies the following structure:
|
|
297
|
+
|
|
298
|
+
```html
|
|
299
|
+
<details class="your-classes">
|
|
300
|
+
<summary>
|
|
301
|
+
<!-- icon (if provided) -->
|
|
302
|
+
<!-- summary content -->
|
|
303
|
+
</summary>
|
|
304
|
+
<section>
|
|
305
|
+
<!-- children content -->
|
|
306
|
+
</section>
|
|
307
|
+
</details>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Breaking Changes from Previous Version
|
|
311
|
+
|
|
312
|
+
### Component Refactoring (v0.5.11+)
|
|
313
|
+
|
|
314
|
+
1. **TypeScript Types Extracted**
|
|
315
|
+
- Props now defined in separate `details.types.ts` file
|
|
316
|
+
- Better type inference and documentation
|
|
317
|
+
|
|
318
|
+
2. **Event Handler Fixes**
|
|
319
|
+
- Fixed duplicate `onPointerDown` calls
|
|
320
|
+
- Corrected `onToggle` event type from `PointerEvent` to `SyntheticEvent`
|
|
321
|
+
|
|
322
|
+
3. **Performance Improvements**
|
|
323
|
+
- Added `React.forwardRef` for proper ref forwarding
|
|
324
|
+
- Implemented `useCallback` for event handlers
|
|
325
|
+
- Added `displayName` for better debugging
|
|
326
|
+
|
|
327
|
+
4. **Removed Default ARIA Label**
|
|
328
|
+
- Previously defaulted to `"Details dropdown"`
|
|
329
|
+
- Now relies on native semantics unless explicitly provided
|
|
330
|
+
|
|
331
|
+
## Technical Details
|
|
49
332
|
|
|
50
|
-
|
|
51
|
-
<>
|
|
52
|
-
<p>Example content inside the details component.</p>
|
|
53
|
-
</>
|
|
54
|
-
);
|
|
333
|
+
### Component Architecture
|
|
55
334
|
|
|
56
|
-
|
|
335
|
+
- **Functional Component** with React.forwardRef
|
|
336
|
+
- **Performance Optimized** with useCallback for event handlers
|
|
337
|
+
- **Type-Safe** with extracted TypeScript definitions
|
|
338
|
+
- **Accessible** by leveraging native HTML5 elements
|
|
57
339
|
|
|
58
|
-
|
|
59
|
-
<Details summary="Summary Section" icon={icon} ariaLabel="Details Section">
|
|
60
|
-
{content}
|
|
61
|
-
</Details>
|
|
62
|
-
);
|
|
340
|
+
### Browser Support
|
|
63
341
|
|
|
64
|
-
|
|
342
|
+
The `<details>` element is supported in all modern browsers:
|
|
343
|
+
- Chrome 12+
|
|
344
|
+
- Firefox 49+
|
|
345
|
+
- Safari 6+
|
|
346
|
+
- Edge 79+
|
|
347
|
+
|
|
348
|
+
For older browsers, consider using a polyfill or alternative disclosure pattern.
|
|
349
|
+
|
|
350
|
+
### Native `<details>` Accordion Feature
|
|
351
|
+
|
|
352
|
+
The `name` attribute for accordion behavior is a modern HTML feature:
|
|
353
|
+
- Supported in Chrome 120+, Edge 120+
|
|
354
|
+
- Progressive enhancement: older browsers treat details as independent
|
|
355
|
+
- No JavaScript required for accordion functionality
|
|
356
|
+
|
|
357
|
+
## Testing
|
|
358
|
+
|
|
359
|
+
### Automated Testing
|
|
360
|
+
|
|
361
|
+
The component includes comprehensive tests:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
npm test -- details.test.tsx
|
|
65
365
|
```
|
|
66
366
|
|
|
67
|
-
|
|
367
|
+
Tests cover:
|
|
368
|
+
- ✅ Basic rendering
|
|
369
|
+
- ✅ Keyboard interaction (Space, Enter)
|
|
370
|
+
- ✅ Props and attributes
|
|
371
|
+
- ✅ Event handlers (onToggle, onPointerDown)
|
|
372
|
+
- ✅ Accordion mode with `name` attribute
|
|
373
|
+
- ✅ Ref forwarding
|
|
374
|
+
- ✅ Accessibility with axe-core
|
|
375
|
+
|
|
376
|
+
### Manual Testing Checklist
|
|
377
|
+
|
|
378
|
+
#### Keyboard Navigation
|
|
379
|
+
- [ ] Tab to summary - should receive focus with visible indicator
|
|
380
|
+
- [ ] Press Space - should toggle open/closed
|
|
381
|
+
- [ ] Press Enter - should toggle open/closed
|
|
382
|
+
- [ ] No keyboard traps
|
|
383
|
+
|
|
384
|
+
#### Screen Reader Testing (NVDA/VoiceOver/JAWS)
|
|
385
|
+
- [ ] Announces as "disclosure" or "expandable"
|
|
386
|
+
- [ ] Announces current state (expanded/collapsed)
|
|
387
|
+
- [ ] State changes announced when toggling
|
|
388
|
+
|
|
389
|
+
#### Visual Testing
|
|
390
|
+
- [ ] Focus indicator visible with sufficient contrast
|
|
391
|
+
- [ ] Works at 200% browser zoom
|
|
392
|
+
- [ ] Content reflows at 320px viewport width
|
|
393
|
+
|
|
394
|
+
See [ACCESSIBILITY-REVIEW.md](./ACCESSIBILITY-REVIEW.md) for complete testing guide.
|
|
395
|
+
|
|
396
|
+
## Migration Guide
|
|
397
|
+
|
|
398
|
+
### From Previous Version
|
|
68
399
|
|
|
69
400
|
```tsx
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
open={true}
|
|
88
|
-
onToggle={(e) => console.log("Toggled", e)}
|
|
89
|
-
>
|
|
90
|
-
{content}
|
|
91
|
-
</Details>
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
export default AdvancedExample;
|
|
401
|
+
// Before (v0.5.10 and earlier)
|
|
402
|
+
<Details
|
|
403
|
+
summary="My Summary"
|
|
404
|
+
ariaLabel="Details dropdown" // Had default value
|
|
405
|
+
onToggle={(e: PointerEvent) => {}} // Wrong event type
|
|
406
|
+
>
|
|
407
|
+
{content}
|
|
408
|
+
</Details>
|
|
409
|
+
|
|
410
|
+
// After (v0.5.11+)
|
|
411
|
+
<Details
|
|
412
|
+
summary="My Summary"
|
|
413
|
+
ariaLabel="My custom label" // Optional, no default
|
|
414
|
+
onToggle={(e: SyntheticEvent<HTMLDetailsElement>) => {}} // Correct type
|
|
415
|
+
>
|
|
416
|
+
{content}
|
|
417
|
+
</Details>
|
|
95
418
|
```
|
|
96
419
|
|
|
97
|
-
|
|
420
|
+
## Best Practices
|
|
421
|
+
|
|
422
|
+
### Accessibility
|
|
423
|
+
|
|
424
|
+
1. **Don't override native semantics** - The `ariaLabel` prop is rarely needed
|
|
425
|
+
2. **Hide decorative icons** - Use `aria-hidden="true"` on chevron icons
|
|
426
|
+
3. **Provide meaningful summaries** - Summary text should describe the content
|
|
427
|
+
4. **Use accordion mode wisely** - Only when one section should be open at a time
|
|
428
|
+
|
|
429
|
+
### Performance
|
|
430
|
+
|
|
431
|
+
1. **Memoize callbacks** - Wrap `onToggle` handlers in `useCallback`
|
|
432
|
+
2. **Avoid complex summary content** - Keep summaries simple for better performance
|
|
433
|
+
3. **Use ref forwarding** - Access the element directly when needed
|
|
434
|
+
|
|
435
|
+
### Styling
|
|
436
|
+
|
|
437
|
+
1. **Use rem units** - All spacing should use rem for accessibility
|
|
438
|
+
2. **Test color contrast** - Ensure focus indicators meet 3:1 contrast ratio
|
|
439
|
+
3. **Support dark mode** - Use CSS custom properties for theming
|
|
440
|
+
|
|
441
|
+
## Related Components
|
|
442
|
+
|
|
443
|
+
- **Accordion** - For more complex accordion implementations
|
|
444
|
+
- **Tabs** - Alternative pattern for organizing content
|
|
445
|
+
- **Modal** - For overlay content disclosure
|
|
446
|
+
|
|
447
|
+
## Resources
|
|
448
|
+
|
|
449
|
+
- [MDN: Details Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
|
|
450
|
+
- [ARIA Authoring Practices: Disclosure Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/)
|
|
451
|
+
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
|
452
|
+
- [Component Accessibility Review](./ACCESSIBILITY-REVIEW.md)
|
|
453
|
+
|
|
454
|
+
## Changelog
|
|
455
|
+
|
|
456
|
+
### v0.5.11 (Latest)
|
|
457
|
+
- ✨ Refactored with TypeScript types in separate file
|
|
458
|
+
- ✨ Added React.forwardRef for proper ref handling
|
|
459
|
+
- ✨ Implemented useCallback for performance optimization
|
|
460
|
+
- 🐛 Fixed duplicate onPointerDown calls
|
|
461
|
+
- 🐛 Fixed onToggle event type (PointerEvent → SyntheticEvent)
|
|
462
|
+
- 📝 Comprehensive JSDoc documentation
|
|
463
|
+
- 📝 Complete WCAG 2.1 AA accessibility review
|
|
464
|
+
- 🧪 Added comprehensive test suite with axe-core
|
|
465
|
+
- ♿ Accessibility rating: A- (Excellent)
|
|
466
|
+
|
|
467
|
+
---
|
|
98
468
|
|
|
99
|
-
|
|
100
|
-
- Customize the component using the `styles` and `classes` props.
|
|
101
|
-
- Use the `onToggle` and `onPointerDown` callbacks for custom interactions.
|
|
469
|
+
**Need Help?** Check the [Storybook examples](./?path=/docs/fp-react-components-details--docs) or review the [accessibility guide](./ACCESSIBILITY-REVIEW.md).
|
|
@@ -30,6 +30,12 @@ details {
|
|
|
30
30
|
transition: var(--summary-transitions);
|
|
31
31
|
max-height: var(--max-h-closed);
|
|
32
32
|
overflow: clip;
|
|
33
|
+
|
|
34
|
+
@starting-style {
|
|
35
|
+
transition: var(--summary-transitions);
|
|
36
|
+
color: red;
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
& + details {
|
|
34
40
|
border-radius: 0; // remove radius from middle elements
|
|
35
41
|
border-top: none; // optional: remove double borders
|
|
@@ -64,13 +70,6 @@ details {
|
|
|
64
70
|
border-top-right-radius: var(--details-radius);
|
|
65
71
|
transition: var(--summary-transitions);
|
|
66
72
|
|
|
67
|
-
@supports (transition-behavior: allow-discrete) {
|
|
68
|
-
@starting-style {
|
|
69
|
-
border-bottom: none;
|
|
70
|
-
transition: var(--summary-transitions);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
73
|
&::-webkit-details-marker {
|
|
75
74
|
display: none;
|
|
76
75
|
}
|
|
@@ -112,6 +111,16 @@ details {
|
|
|
112
111
|
}
|
|
113
112
|
> section {
|
|
114
113
|
max-height: var(--max-h-open);
|
|
114
|
+
@starting-style {
|
|
115
|
+
max-height: 0;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@supports (transition-behavior: allow-discrete) {
|
|
121
|
+
@starting-style {
|
|
122
|
+
max-height: 0;
|
|
123
|
+
transition: var(--summary-transitions);
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
}
|