@fpkit/acss 0.5.13 → 0.6.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/libs/{chunk-PQ2K3BM6.cjs → chunk-2NRIP6RB.cjs} +3 -3
- package/libs/chunk-33PNJ4LO.cjs +15 -0
- package/libs/chunk-33PNJ4LO.cjs.map +1 -0
- package/libs/chunk-4BZKFPEC.cjs +17 -0
- package/libs/chunk-4BZKFPEC.cjs.map +1 -0
- package/libs/{chunk-772NRB75.js → chunk-5QD3DWFI.js} +2 -2
- package/libs/chunk-6SAHIYCZ.js +7 -0
- package/libs/chunk-6SAHIYCZ.js.map +1 -0
- package/libs/{chunk-3MKLDCKQ.cjs → chunk-6WTC4JXH.cjs} +3 -3
- package/libs/chunk-75QHTLFO.js +7 -0
- package/libs/chunk-75QHTLFO.js.map +1 -0
- package/libs/{chunk-ZANSFMTD.js → chunk-7XPFW7CB.js} +3 -3
- package/libs/chunk-BFK62VX5.js +5 -0
- package/libs/chunk-BFK62VX5.js.map +1 -0
- package/libs/{chunk-ROZI23GS.cjs → chunk-DKTHCQ5P.cjs} +4 -4
- package/libs/chunk-E2AJURUW.cjs +13 -0
- package/libs/chunk-E2AJURUW.cjs.map +1 -0
- package/libs/{chunk-L75OQKEI.cjs → chunk-ENTCUJ3A.cjs} +3 -3
- package/libs/chunk-ENTCUJ3A.cjs.map +1 -0
- package/libs/chunk-F5EYMVQM.js +10 -0
- package/libs/chunk-F5EYMVQM.js.map +1 -0
- package/libs/chunk-FVROL3V5.js +9 -0
- package/libs/chunk-FVROL3V5.js.map +1 -0
- package/libs/chunk-GT77BX4L.cjs +17 -0
- package/libs/chunk-GT77BX4L.cjs.map +1 -0
- package/libs/chunk-GUJSMQ3V.cjs +16 -0
- package/libs/chunk-GUJSMQ3V.cjs.map +1 -0
- package/libs/chunk-HHLNOC5T.js +7 -0
- package/libs/chunk-HHLNOC5T.js.map +1 -0
- package/libs/chunk-HRRHPLER.js +8 -0
- package/libs/chunk-HRRHPLER.js.map +1 -0
- package/libs/chunk-IEB64SWY.js +8 -0
- package/libs/chunk-IEB64SWY.js.map +1 -0
- package/libs/{chunk-NGTJDDFO.js → chunk-IQ76HGVP.js} +2 -2
- package/libs/chunk-IRLFZ3OL.js +9 -0
- package/libs/chunk-IRLFZ3OL.js.map +1 -0
- package/libs/{chunk-JJ43O4Y5.js → chunk-KK47SYZI.js} +2 -2
- package/libs/chunk-O3JIHC5M.cjs +15 -0
- package/libs/chunk-O3JIHC5M.cjs.map +1 -0
- package/libs/chunk-O5XAJ7BY.cjs +18 -0
- package/libs/chunk-O5XAJ7BY.cjs.map +1 -0
- package/libs/chunk-OVWLQYMK.js +10 -0
- package/libs/chunk-OVWLQYMK.js.map +1 -0
- package/libs/chunk-PNWIRCG3.cjs +7 -0
- package/libs/chunk-PNWIRCG3.cjs.map +1 -0
- package/libs/{chunk-D4YLRWAO.cjs → chunk-QVW6W76L.cjs} +6 -6
- package/libs/chunk-T4T6GWYQ.cjs +17 -0
- package/libs/chunk-T4T6GWYQ.cjs.map +1 -0
- package/libs/chunk-TON2YGMD.cjs +9 -0
- package/libs/chunk-TON2YGMD.cjs.map +1 -0
- package/libs/chunk-UEPAWMDF.js +8 -0
- package/libs/chunk-UEPAWMDF.js.map +1 -0
- package/libs/{chunk-LT5KZ2QW.cjs → chunk-US2I5GI7.cjs} +3 -3
- package/libs/{chunk-B7F5FS6D.cjs → chunk-W2UIN7EV.cjs} +3 -3
- package/libs/{chunk-P2DC76ZZ.cjs → chunk-W5TKWBFC.cjs} +3 -3
- package/libs/chunk-WXBFBWYF.cjs +16 -0
- package/libs/chunk-WXBFBWYF.cjs.map +1 -0
- package/libs/{chunk-VUH3FXGJ.js → chunk-X3JCTEPD.js} +5 -5
- package/libs/chunk-X5LGFCWG.js +9 -0
- package/libs/chunk-X5LGFCWG.js.map +1 -0
- package/libs/{chunk-5M57K4SW.js → chunk-Y2PFDELK.js} +2 -2
- package/libs/{chunk-ETFLFC2S.js → chunk-ZFJ4U45S.js} +2 -2
- package/libs/{component-props-a8a2f97e.d.ts → component-props-67d978a2.d.ts} +4 -4
- 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/breadcrumbs/breadcrumb.cjs +6 -6
- package/libs/components/breadcrumbs/breadcrumb.d.cts +11 -11
- package/libs/components/breadcrumbs/breadcrumb.d.ts +11 -11
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +6 -4
- package/libs/components/button.d.cts +97 -4
- package/libs/components/button.d.ts +97 -4
- package/libs/components/button.js +4 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +14 -14
- package/libs/components/card.d.ts +14 -14
- package/libs/components/card.js +2 -2
- package/libs/components/dialog/dialog.cjs +9 -7
- package/libs/components/dialog/dialog.d.cts +3 -3
- package/libs/components/dialog/dialog.d.ts +3 -3
- package/libs/components/dialog/dialog.js +7 -5
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.d.cts +16 -7
- package/libs/components/form/fields.d.ts +16 -7
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/inputs.cjs +6 -4
- package/libs/components/form/inputs.d.cts +50 -2
- package/libs/components/form/inputs.d.ts +50 -2
- package/libs/components/form/inputs.js +4 -2
- package/libs/components/form/textarea.cjs +5 -4
- package/libs/components/form/textarea.d.cts +32 -23
- package/libs/components/form/textarea.d.ts +32 -23
- package/libs/components/form/textarea.js +3 -2
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.d.cts +2 -2
- package/libs/components/heading/heading.d.ts +2 -2
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +38 -38
- package/libs/components/icons/icon.d.ts +38 -38
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/link/link.cjs +4 -4
- package/libs/components/link/link.css +1 -1
- package/libs/components/link/link.css.map +1 -1
- package/libs/components/link/link.d.cts +3 -19
- package/libs/components/link/link.d.ts +3 -19
- package/libs/components/link/link.js +2 -2
- package/libs/components/link/link.min.css +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.css +1 -0
- package/libs/components/list/list.css.map +1 -0
- package/libs/components/list/list.d.cts +120 -33
- package/libs/components/list/list.d.ts +120 -33
- package/libs/components/list/list.js +2 -2
- package/libs/components/list/list.min.css +3 -0
- package/libs/components/modal.cjs +6 -4
- package/libs/components/modal.d.cts +8 -8
- package/libs/components/modal.d.ts +8 -8
- package/libs/components/modal.js +5 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.css +1 -1
- package/libs/components/nav/nav.css.map +1 -1
- package/libs/components/nav/nav.d.cts +550 -34
- package/libs/components/nav/nav.d.ts +550 -34
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/nav/nav.min.css +2 -2
- package/libs/components/popover/popover.d.cts +5 -5
- package/libs/components/popover/popover.d.ts +5 -5
- package/libs/components/tables/table.cjs +5 -5
- package/libs/components/tables/table.d.cts +8 -8
- package/libs/components/tables/table.d.ts +8 -8
- package/libs/components/tables/table.js +2 -2
- package/libs/components/tag/tag.css +1 -1
- package/libs/components/tag/tag.css.map +1 -1
- package/libs/components/tag/tag.min.css +2 -2
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.d.cts +5 -5
- package/libs/components/text/text.d.ts +5 -5
- package/libs/components/text/text.js +2 -2
- package/libs/form.types-d25ebfac.d.ts +233 -0
- package/libs/{heading-3648c538.d.ts → heading-7446cb46.d.ts} +8 -8
- package/libs/hooks.cjs +9 -4
- package/libs/hooks.d.cts +137 -3
- package/libs/hooks.d.ts +137 -3
- package/libs/hooks.js +4 -3
- package/libs/icons.cjs +3 -3
- package/libs/icons.d.cts +2 -2
- package/libs/icons.d.ts +2 -2
- package/libs/icons.js +2 -2
- package/libs/index.cjs +53 -51
- 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 +338 -49
- package/libs/index.d.ts +338 -49
- package/libs/index.js +24 -22
- package/libs/index.js.map +1 -1
- package/libs/link-5192f411.d.ts +323 -0
- package/libs/list.types-d26de310.d.ts +245 -0
- package/libs/{ui-645f95b5.d.ts → ui-d01b50d4.d.ts} +16 -12
- package/package.json +4 -6
- package/src/components/alert/alert.scss +1 -4
- package/src/components/breadcrumbs/breadcrumb.tsx +4 -1
- package/src/components/buttons/README.mdx +102 -1
- package/src/components/buttons/button.stories.tsx +106 -0
- package/src/components/buttons/button.tsx +82 -52
- package/src/components/dialog/dialog-a11y-review.md +653 -0
- package/src/components/form/README.mdx +725 -43
- package/src/components/form/WCAG-REVIEW.md +654 -0
- package/src/components/form/fields.tsx +10 -1
- package/src/components/form/form.stories.tsx +604 -23
- package/src/components/form/form.tsx +204 -63
- package/src/components/form/form.types.ts +378 -0
- package/src/components/form/input.stories.tsx +71 -3
- package/src/components/form/inputs.tsx +159 -67
- package/src/components/form/select.tsx +122 -66
- package/src/components/form/textarea.tsx +120 -73
- package/src/components/fp.tsx +86 -11
- package/src/components/link/README.mdx +923 -0
- package/src/components/link/link.scss +79 -26
- package/src/components/link/link.stories.tsx +383 -30
- package/src/components/link/link.test.tsx +677 -0
- package/src/components/link/link.tsx +163 -57
- package/src/components/link/link.types.ts +261 -0
- package/src/components/list/README.mdx +764 -0
- package/src/components/list/list.scss +285 -0
- package/src/components/list/list.stories.tsx +514 -27
- package/src/components/list/list.test.tsx +554 -0
- package/src/components/list/list.tsx +153 -51
- package/src/components/list/list.types.ts +255 -0
- package/src/components/nav/ACCESSIBILITY.md +649 -0
- package/src/components/nav/README.mdx +782 -0
- package/src/components/nav/nav.scss +32 -1
- package/src/components/nav/nav.stories.tsx +44 -6
- package/src/components/nav/nav.tsx +302 -51
- package/src/components/nav/nav.types.ts +308 -0
- package/src/components/tag/README.mdx +426 -0
- package/src/components/tag/tag.scss +101 -27
- package/src/components/tag/tag.stories.tsx +384 -10
- package/src/components/tag/tag.test.tsx +210 -0
- package/src/components/tag/tag.tsx +106 -9
- package/src/components/tag/tag.types.ts +107 -0
- package/src/components/ui.tsx +8 -3
- package/src/hooks/use-disabled-state.test.tsx +536 -0
- package/src/hooks/use-disabled-state.ts +246 -0
- package/src/hooks/useDisabledState.md +393 -0
- package/src/hooks.ts +6 -0
- package/src/index.scss +2 -0
- package/src/index.ts +2 -1
- package/src/sass/_globals.scss +2 -7
- package/src/styles/alert/alert.css +1 -3
- package/src/styles/alert/alert.css.map +1 -1
- package/src/styles/index.css +450 -76
- package/src/styles/index.css.map +1 -1
- package/src/styles/link/link.css +45 -28
- package/src/styles/link/link.css.map +1 -1
- package/src/styles/list/list.css +214 -0
- package/src/styles/list/list.css.map +1 -0
- package/src/styles/nav/nav.css +21 -1
- package/src/styles/nav/nav.css.map +1 -1
- package/src/styles/tag/tag.css +113 -35
- package/src/styles/tag/tag.css.map +1 -1
- package/src/styles/utilities/_disabled.scss +58 -0
- package/src/types/shared.ts +43 -6
- package/src/utils/accessibility.ts +109 -0
- package/libs/chunk-2LTJ7HHX.cjs +0 -18
- package/libs/chunk-2LTJ7HHX.cjs.map +0 -1
- package/libs/chunk-2Y7W75TT.js +0 -9
- package/libs/chunk-2Y7W75TT.js.map +0 -1
- package/libs/chunk-5S4ORA4C.cjs +0 -15
- package/libs/chunk-5S4ORA4C.cjs.map +0 -1
- package/libs/chunk-AHDJGCG5.cjs +0 -15
- package/libs/chunk-AHDJGCG5.cjs.map +0 -1
- package/libs/chunk-BHRQBJRY.js +0 -8
- package/libs/chunk-BHRQBJRY.js.map +0 -1
- package/libs/chunk-GZ4QFPRY.js +0 -9
- package/libs/chunk-GZ4QFPRY.js.map +0 -1
- package/libs/chunk-IYUN2EW3.cjs +0 -15
- package/libs/chunk-IYUN2EW3.cjs.map +0 -1
- package/libs/chunk-J32EZPYD.cjs +0 -15
- package/libs/chunk-J32EZPYD.cjs.map +0 -1
- package/libs/chunk-KUKIVRC2.js +0 -7
- package/libs/chunk-KUKIVRC2.js.map +0 -1
- package/libs/chunk-L75OQKEI.cjs.map +0 -1
- package/libs/chunk-M5RRNTVX.cjs +0 -15
- package/libs/chunk-M5RRNTVX.cjs.map +0 -1
- package/libs/chunk-OK5QEIMD.cjs +0 -17
- package/libs/chunk-OK5QEIMD.cjs.map +0 -1
- package/libs/chunk-P7TTEYCD.js +0 -7
- package/libs/chunk-P7TTEYCD.js.map +0 -1
- package/libs/chunk-QLZWHAMK.js +0 -8
- package/libs/chunk-QLZWHAMK.js.map +0 -1
- package/libs/chunk-RIVUMPOG.js +0 -8
- package/libs/chunk-RIVUMPOG.js.map +0 -1
- package/libs/chunk-S7BABR7Z.cjs +0 -13
- package/libs/chunk-S7BABR7Z.cjs.map +0 -1
- package/libs/chunk-SMYRLO3E.js +0 -8
- package/libs/chunk-SMYRLO3E.js.map +0 -1
- package/libs/chunk-TYRCEX2L.js +0 -8
- package/libs/chunk-TYRCEX2L.js.map +0 -1
- package/libs/chunk-XBA562WW.js +0 -8
- package/libs/chunk-XBA562WW.js.map +0 -1
- package/libs/chunk-XTQKWY7W.cjs +0 -32
- package/libs/chunk-XTQKWY7W.cjs.map +0 -1
- package/libs/inputs-f3a216db.d.ts +0 -45
- /package/libs/{chunk-PQ2K3BM6.cjs.map → chunk-2NRIP6RB.cjs.map} +0 -0
- /package/libs/{chunk-772NRB75.js.map → chunk-5QD3DWFI.js.map} +0 -0
- /package/libs/{chunk-3MKLDCKQ.cjs.map → chunk-6WTC4JXH.cjs.map} +0 -0
- /package/libs/{chunk-ZANSFMTD.js.map → chunk-7XPFW7CB.js.map} +0 -0
- /package/libs/{chunk-ROZI23GS.cjs.map → chunk-DKTHCQ5P.cjs.map} +0 -0
- /package/libs/{chunk-NGTJDDFO.js.map → chunk-IQ76HGVP.js.map} +0 -0
- /package/libs/{chunk-JJ43O4Y5.js.map → chunk-KK47SYZI.js.map} +0 -0
- /package/libs/{chunk-D4YLRWAO.cjs.map → chunk-QVW6W76L.cjs.map} +0 -0
- /package/libs/{chunk-LT5KZ2QW.cjs.map → chunk-US2I5GI7.cjs.map} +0 -0
- /package/libs/{chunk-B7F5FS6D.cjs.map → chunk-W2UIN7EV.cjs.map} +0 -0
- /package/libs/{chunk-P2DC76ZZ.cjs.map → chunk-W5TKWBFC.cjs.map} +0 -0
- /package/libs/{chunk-VUH3FXGJ.js.map → chunk-X3JCTEPD.js.map} +0 -0
- /package/libs/{chunk-5M57K4SW.js.map → chunk-Y2PFDELK.js.map} +0 -0
- /package/libs/{chunk-ETFLFC2S.js.map → chunk-ZFJ4U45S.js.map} +0 -0
|
@@ -21,7 +21,7 @@ body > nav,
|
|
|
21
21
|
padding-inline: var(--nav-px, 0.75rem);
|
|
22
22
|
|
|
23
23
|
&:hover {
|
|
24
|
-
background-color: var(--nav-hov-bg,
|
|
24
|
+
background-color: var(--nav-hov-bg, #e8e8e8);
|
|
25
25
|
}
|
|
26
26
|
&:hover:where(img) {
|
|
27
27
|
background-color: transparent;
|
|
@@ -31,6 +31,12 @@ body > nav,
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
nav {
|
|
34
|
+
// Focus Indicator Variables (WCAG 2.4.7 - 3:1 contrast minimum)
|
|
35
|
+
--nav-focus-color: currentColor;
|
|
36
|
+
--nav-focus-width: 0.125rem; // 2px
|
|
37
|
+
--nav-focus-offset: 0.125rem; // 2px
|
|
38
|
+
--nav-focus-style: solid;
|
|
39
|
+
|
|
34
40
|
display: var(--nav-dsp, flex);
|
|
35
41
|
flex-direction: var(--nav-direction, row);
|
|
36
42
|
width: var(--nav-w, auto);
|
|
@@ -87,4 +93,29 @@ nav {
|
|
|
87
93
|
> div {
|
|
88
94
|
margin-block-start: 0;
|
|
89
95
|
}
|
|
96
|
+
|
|
97
|
+
// Focus indicators for links and buttons (WCAG 2.4.7)
|
|
98
|
+
a:focus {
|
|
99
|
+
outline: var(--nav-focus-width) var(--nav-focus-style)
|
|
100
|
+
var(--nav-focus-color);
|
|
101
|
+
outline-offset: var(--nav-focus-offset);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
a:focus-visible {
|
|
105
|
+
outline: var(--nav-focus-width) var(--nav-focus-style)
|
|
106
|
+
var(--nav-focus-color);
|
|
107
|
+
outline-offset: var(--nav-focus-offset);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
button:focus {
|
|
111
|
+
outline: var(--nav-focus-width) var(--nav-focus-style)
|
|
112
|
+
var(--nav-focus-color);
|
|
113
|
+
outline-offset: var(--nav-focus-offset);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
button:focus-visible {
|
|
117
|
+
outline: var(--nav-focus-width) var(--nav-focus-style)
|
|
118
|
+
var(--nav-focus-color);
|
|
119
|
+
outline-offset: var(--nav-focus-offset);
|
|
120
|
+
}
|
|
90
121
|
}
|
|
@@ -82,17 +82,55 @@ export const MultipleNavs: Story = {
|
|
|
82
82
|
args: {
|
|
83
83
|
...NavSection.args,
|
|
84
84
|
classes: "navbar",
|
|
85
|
+
"aria-label": "Main navigation",
|
|
85
86
|
children: (
|
|
86
87
|
<>
|
|
87
|
-
<Nav.List>
|
|
88
|
-
<Nav.Item>
|
|
89
|
-
|
|
88
|
+
<Nav.List aria-label="Primary menu">
|
|
89
|
+
<Nav.Item>
|
|
90
|
+
<Link href="/">Home</Link>
|
|
91
|
+
</Nav.Item>
|
|
92
|
+
<Nav.Item>
|
|
93
|
+
<Link href="/products">Products</Link>
|
|
94
|
+
</Nav.Item>
|
|
90
95
|
</Nav.List>
|
|
91
|
-
<Nav.List>
|
|
92
|
-
<Nav.Item>
|
|
93
|
-
|
|
96
|
+
<Nav.List aria-label="User menu">
|
|
97
|
+
<Nav.Item>
|
|
98
|
+
<Link href="/login">Login</Link>
|
|
99
|
+
</Nav.Item>
|
|
100
|
+
<Nav.Item>
|
|
101
|
+
<Link href="/signup">Sign Up</Link>
|
|
102
|
+
</Nav.Item>
|
|
94
103
|
</Nav.List>
|
|
95
104
|
</>
|
|
96
105
|
),
|
|
97
106
|
},
|
|
98
107
|
} as Story;
|
|
108
|
+
|
|
109
|
+
export const WithCurrentPage: Story = {
|
|
110
|
+
args: {
|
|
111
|
+
"aria-label": "Main navigation",
|
|
112
|
+
children: (
|
|
113
|
+
<Nav.List>
|
|
114
|
+
<Nav.Item>
|
|
115
|
+
<Link href="/" aria-current="page">
|
|
116
|
+
Home
|
|
117
|
+
</Link>
|
|
118
|
+
</Nav.Item>
|
|
119
|
+
<Nav.Item>
|
|
120
|
+
<Link href="/about">About</Link>
|
|
121
|
+
</Nav.Item>
|
|
122
|
+
<Nav.Item>
|
|
123
|
+
<Link href="/products">Products</Link>
|
|
124
|
+
</Nav.Item>
|
|
125
|
+
<Nav.Item>
|
|
126
|
+
<Link href="/contact">Contact</Link>
|
|
127
|
+
</Nav.Item>
|
|
128
|
+
</Nav.List>
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
play: async ({ canvasElement }) => {
|
|
132
|
+
const canvas = within(canvasElement);
|
|
133
|
+
const homeLink = canvas.getByText(/home/i);
|
|
134
|
+
expect(homeLink).toHaveAttribute("aria-current", "page");
|
|
135
|
+
},
|
|
136
|
+
} as Story;
|
|
@@ -1,76 +1,327 @@
|
|
|
1
|
-
import UI from
|
|
2
|
-
import List from
|
|
3
|
-
import React from
|
|
1
|
+
import UI from "../ui";
|
|
2
|
+
import List from "../list/list";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import type { NavProps, NavListProps, NavItemProps } from "./nav.types";
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
export type NavItemProps = React.ComponentProps<typeof List.ListItem>
|
|
9
|
-
|
|
10
|
-
export type NavProps = React.ComponentProps<typeof UI>
|
|
6
|
+
// Re-export types for external use
|
|
7
|
+
export type { NavProps, NavListProps, NavItemProps };
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* NavList - A navigation-specific list component for grouping navigation items.
|
|
11
|
+
*
|
|
12
|
+
* Extends the List component with navigation-specific layout options through
|
|
13
|
+
* the `isBlock` prop. Automatically renders as an unstyled list to maintain
|
|
14
|
+
* clean navigation aesthetics.
|
|
15
|
+
*
|
|
16
|
+
* ## Key Features:
|
|
17
|
+
* - **Flexible Layout**: Supports both horizontal (inline) and vertical (block) layouts
|
|
18
|
+
* - **Semantic HTML**: Uses `<ul>` element for proper document structure
|
|
19
|
+
* - **Unstyled by Default**: Removes default list markers for clean navigation
|
|
20
|
+
* - **Accessible**: Works naturally with screen readers
|
|
21
|
+
*
|
|
22
|
+
* ## Accessibility:
|
|
23
|
+
* - ✅ Uses semantic `<ul>` element
|
|
24
|
+
* - ✅ Screen readers announce as "list" with item count
|
|
25
|
+
* - ✅ Supports `aria-label` for multiple lists
|
|
26
|
+
* - ✅ Keyboard navigation works naturally with focusable children
|
|
27
|
+
*
|
|
28
|
+
* ## Layout Options:
|
|
29
|
+
* - **Inline (default)**: Horizontal navigation bars, top menus
|
|
30
|
+
* - **Block**: Vertical sidebars, mobile menus, footer navigation
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // Horizontal navigation (default)
|
|
35
|
+
* <NavList>
|
|
36
|
+
* <NavItem><Link href="/">Home</Link></NavItem>
|
|
37
|
+
* <NavItem><Link href="/about">About</Link></NavItem>
|
|
38
|
+
* </NavList>
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* // Vertical sidebar navigation
|
|
44
|
+
* <NavList isBlock>
|
|
45
|
+
* <NavItem><Link href="/dashboard">Dashboard</Link></NavItem>
|
|
46
|
+
* <NavItem><Link href="/settings">Settings</Link></NavItem>
|
|
47
|
+
* </NavList>
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* // Multiple lists with labels
|
|
53
|
+
* <Nav>
|
|
54
|
+
* <NavList aria-label="Primary navigation">
|
|
55
|
+
* <NavItem><Link href="/">Home</Link></NavItem>
|
|
56
|
+
* </NavList>
|
|
57
|
+
* <NavList aria-label="User menu">
|
|
58
|
+
* <NavItem><Link href="/profile">Profile</Link></NavItem>
|
|
59
|
+
* </NavList>
|
|
60
|
+
* </Nav>
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @param {NavListProps} props - Component props
|
|
64
|
+
* @param {boolean} [props.isBlock=false] - Display items vertically (block layout)
|
|
65
|
+
* @param {React.ReactNode} props.children - Navigation items (typically NavItem components)
|
|
66
|
+
* @param {string} [props.aria-label] - Accessible label for the list
|
|
67
|
+
* @returns {React.ReactElement} A navigation list component
|
|
18
68
|
*/
|
|
19
|
-
export const NavList =
|
|
69
|
+
export const NavList = React.forwardRef<
|
|
70
|
+
HTMLUListElement,
|
|
71
|
+
NavListProps
|
|
72
|
+
>(({ isBlock, children, ...props }, ref) => {
|
|
20
73
|
return (
|
|
21
|
-
<List
|
|
74
|
+
<List
|
|
75
|
+
type="ul"
|
|
76
|
+
{...props}
|
|
77
|
+
data-list={isBlock ? "unstyled block" : "unstyled"}
|
|
78
|
+
ref={ref}
|
|
79
|
+
>
|
|
22
80
|
{children}
|
|
23
81
|
</List>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
NavList.displayName = "NavList";
|
|
26
86
|
|
|
27
87
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
88
|
+
* NavItem - An individual navigation link container (list item).
|
|
89
|
+
*
|
|
90
|
+
* Wraps navigation content (typically Link components) in a semantic list item
|
|
91
|
+
* element with consistent styling and accessibility support.
|
|
92
|
+
*
|
|
93
|
+
* ## Key Features:
|
|
94
|
+
* - **Semantic HTML**: Uses `<li>` element for proper list structure
|
|
95
|
+
* - **Flexible Content**: Accepts any React content (links, buttons, text)
|
|
96
|
+
* - **Customizable**: Supports custom styles and CSS classes
|
|
97
|
+
* - **Ref Forwarding**: Enables direct DOM access for advanced use cases
|
|
98
|
+
*
|
|
99
|
+
* ## Accessibility:
|
|
100
|
+
* - ✅ Uses semantic `<li>` element
|
|
101
|
+
* - ✅ Works with screen readers out of the box
|
|
102
|
+
* - ✅ Supports keyboard navigation naturally
|
|
103
|
+
* - ✅ Ref forwarding for programmatic focus if needed
|
|
104
|
+
*
|
|
105
|
+
* ## Best Practices:
|
|
106
|
+
* - Always wrap with NavList/Nav for proper semantics
|
|
107
|
+
* - Use `aria-current="page"` on the link inside to indicate current page
|
|
108
|
+
* - Ensure link text is descriptive and meaningful
|
|
109
|
+
* - Maintain sufficient color contrast (WCAG 2.1: 4.5:1 for normal text)
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* // Basic navigation item
|
|
114
|
+
* <NavItem>
|
|
115
|
+
* <Link href="/about">About Us</Link>
|
|
116
|
+
* </NavItem>
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```tsx
|
|
121
|
+
* // Current page with aria-current
|
|
122
|
+
* <NavItem>
|
|
123
|
+
* <Link href="/about" aria-current="page">
|
|
124
|
+
* About Us
|
|
125
|
+
* </Link>
|
|
126
|
+
* </NavItem>
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```tsx
|
|
131
|
+
* // Custom styling
|
|
132
|
+
* <NavItem
|
|
133
|
+
* classes="nav-item-featured"
|
|
134
|
+
* styles={{ fontWeight: 'bold' }}
|
|
135
|
+
* >
|
|
136
|
+
* <Link href="/special">Special Offer</Link>
|
|
137
|
+
* </NavItem>
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* // With icon
|
|
143
|
+
* <NavItem>
|
|
144
|
+
* <Link href="/settings">
|
|
145
|
+
* <SettingsIcon aria-hidden="true" />
|
|
146
|
+
* Settings
|
|
147
|
+
* </Link>
|
|
148
|
+
* </NavItem>
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @param {NavItemProps} props - Component props
|
|
152
|
+
* @param {React.Ref} ref - Forwarded ref for DOM access
|
|
153
|
+
* @returns {React.ReactElement} A navigation item component
|
|
37
154
|
*/
|
|
38
|
-
export const NavItem =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
children,
|
|
43
|
-
...props
|
|
44
|
-
}: NavItemProps) => {
|
|
155
|
+
export const NavItem = React.forwardRef<
|
|
156
|
+
HTMLLIElement,
|
|
157
|
+
NavItemProps
|
|
158
|
+
>(({ id, styles, classes, children, ...props }, ref) => {
|
|
45
159
|
return (
|
|
46
160
|
<List.ListItem
|
|
47
161
|
type="li"
|
|
48
162
|
id={id}
|
|
49
163
|
classes={classes}
|
|
50
164
|
styles={styles}
|
|
165
|
+
ref={ref}
|
|
51
166
|
{...props}
|
|
52
167
|
>
|
|
53
168
|
{children}
|
|
54
169
|
</List.ListItem>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
NavItem.displayName = "NavItem";
|
|
57
174
|
|
|
58
175
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
176
|
+
* Nav - A semantic navigation container component for site navigation.
|
|
177
|
+
*
|
|
178
|
+
* The Nav component provides a semantic `<nav>` landmark element that helps
|
|
179
|
+
* users navigate your site. It meets WCAG 2.1 AA accessibility standards and
|
|
180
|
+
* follows modern React best practices with full TypeScript support.
|
|
181
|
+
*
|
|
182
|
+
* ## Key Features:
|
|
183
|
+
* - **Semantic HTML**: Uses native `<nav>` element for accessibility
|
|
184
|
+
* - **Landmark Role**: Automatically provides navigation landmark for screen readers
|
|
185
|
+
* - **Flexible Layout**: Supports multiple navigation patterns through CSS custom properties
|
|
186
|
+
* - **Compound Components**: Use Nav.List and Nav.Item for consistent structure
|
|
187
|
+
* - **Type-Safe**: Full TypeScript support with comprehensive JSDoc documentation
|
|
188
|
+
* - **Ref Forwarding**: Direct DOM access for scroll positioning and focus management
|
|
189
|
+
*
|
|
190
|
+
* ## Accessibility (WCAG 2.1 AA Compliant):
|
|
191
|
+
* - ✅ **4.1.2 Name, Role, Value**: Uses semantic `<nav>` element (landmark role)
|
|
192
|
+
* - ✅ **2.4.1 Bypass Blocks**: Navigation landmark helps skip repeated content
|
|
193
|
+
* - ✅ **1.3.1 Info and Relationships**: Proper list structure with ul > li
|
|
194
|
+
* - ✅ **2.4.8 Location**: Supports `aria-label` for multiple navigation regions
|
|
195
|
+
* - ✅ **4.1.3 Status Messages**: Use `aria-current="page"` on links for current page
|
|
196
|
+
*
|
|
197
|
+
* ## When to Use aria-label:
|
|
198
|
+
* - ✅ **Required**: When you have multiple `<nav>` elements on the same page
|
|
199
|
+
* - ✅ **Recommended**: To distinguish navigation purpose (e.g., "Footer navigation")
|
|
200
|
+
* - ❌ **Not needed**: For single navigation regions
|
|
201
|
+
*
|
|
202
|
+
* ## CSS Custom Properties:
|
|
203
|
+
* - `--nav-dsp`: Display mode (default: flex)
|
|
204
|
+
* - `--nav-direction`: Flex direction (default: row)
|
|
205
|
+
* - `--nav-bg`: Background color
|
|
206
|
+
* - `--nav-h`: Minimum height
|
|
207
|
+
* - `--nav-px`: Horizontal padding (default: 1rem)
|
|
208
|
+
* - `--nav-gap`: Gap between items
|
|
209
|
+
* - `--nav-fs`: Font size (default: 0.9rem)
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```tsx
|
|
213
|
+
* // Simple navigation
|
|
214
|
+
* <Nav>
|
|
215
|
+
* <Nav.List>
|
|
216
|
+
* <Nav.Item><Link href="/">Home</Link></Nav.Item>
|
|
217
|
+
* <Nav.Item><Link href="/about">About</Link></Nav.Item>
|
|
218
|
+
* </Nav.List>
|
|
219
|
+
* </Nav>
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```tsx
|
|
224
|
+
* // Multiple navigation regions (requires aria-label)
|
|
225
|
+
* <Nav aria-label="Main navigation">
|
|
226
|
+
* <Nav.List>
|
|
227
|
+
* <Nav.Item><Link href="/">Home</Link></Nav.Item>
|
|
228
|
+
* </Nav.List>
|
|
229
|
+
* </Nav>
|
|
230
|
+
*
|
|
231
|
+
* <Nav aria-label="Footer navigation">
|
|
232
|
+
* <Nav.List>
|
|
233
|
+
* <Nav.Item><Link href="/privacy">Privacy</Link></Nav.Item>
|
|
234
|
+
* </Nav.List>
|
|
235
|
+
* </Nav>
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```tsx
|
|
240
|
+
* // Current page indication
|
|
241
|
+
* <Nav aria-label="Main navigation">
|
|
242
|
+
* <Nav.List>
|
|
243
|
+
* <Nav.Item>
|
|
244
|
+
* <Link href="/" aria-current="page">Home</Link>
|
|
245
|
+
* </Nav.Item>
|
|
246
|
+
* <Nav.Item>
|
|
247
|
+
* <Link href="/about">About</Link>
|
|
248
|
+
* </Nav.Item>
|
|
249
|
+
* </Nav.List>
|
|
250
|
+
* </Nav>
|
|
251
|
+
* ```
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```tsx
|
|
255
|
+
* // Vertical sidebar navigation
|
|
256
|
+
* <Nav aria-label="Sidebar navigation">
|
|
257
|
+
* <Nav.List isBlock>
|
|
258
|
+
* <Nav.Item><Link href="/dashboard">Dashboard</Link></Nav.Item>
|
|
259
|
+
* <Nav.Item><Link href="/settings">Settings</Link></Nav.Item>
|
|
260
|
+
* </Nav.List>
|
|
261
|
+
* </Nav>
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```tsx
|
|
266
|
+
* // Complex navbar with multiple sections
|
|
267
|
+
* <Nav classes="navbar" aria-label="Main navigation">
|
|
268
|
+
* <Nav.List aria-label="Primary menu">
|
|
269
|
+
* <Nav.Item><Link href="/">Home</Link></Nav.Item>
|
|
270
|
+
* <Nav.Item><Link href="/products">Products</Link></Nav.Item>
|
|
271
|
+
* </Nav.List>
|
|
272
|
+
* <Nav.List aria-label="User menu">
|
|
273
|
+
* <Nav.Item><Link href="/login">Login</Link></Nav.Item>
|
|
274
|
+
* <Nav.Item><Link href="/signup">Sign Up</Link></Nav.Item>
|
|
275
|
+
* </Nav.List>
|
|
276
|
+
* </Nav>
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```tsx
|
|
281
|
+
* // Custom theming with CSS properties
|
|
282
|
+
* <Nav
|
|
283
|
+
* aria-label="Main navigation"
|
|
284
|
+
* styles={{
|
|
285
|
+
* '--nav-bg': '#1a1a1a',
|
|
286
|
+
* '--nav-h': '4rem',
|
|
287
|
+
* '--nav-px': '2rem',
|
|
288
|
+
* }}
|
|
289
|
+
* >
|
|
290
|
+
* <Nav.List>
|
|
291
|
+
* <Nav.Item><Link href="/">Home</Link></Nav.Item>
|
|
292
|
+
* </Nav.List>
|
|
293
|
+
* </Nav>
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @param {NavProps} props - Component props
|
|
297
|
+
* @param {React.Ref} ref - Forwarded ref for DOM access
|
|
298
|
+
* @returns {React.ReactElement} A navigation element
|
|
64
299
|
*/
|
|
65
|
-
export const Nav =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
300
|
+
export const Nav = React.forwardRef<HTMLElement, NavProps>(
|
|
301
|
+
({ children, ...props }, ref) => {
|
|
302
|
+
return (
|
|
303
|
+
<UI as="nav" {...props} ref={ref}>
|
|
304
|
+
{children}
|
|
305
|
+
</UI>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
Nav.displayName = "Nav";
|
|
311
|
+
|
|
312
|
+
// Compound component pattern - attach sub-components to Nav
|
|
313
|
+
export interface NavComponent
|
|
314
|
+
extends React.ForwardRefExoticComponent<
|
|
315
|
+
NavProps & React.RefAttributes<HTMLElement>
|
|
316
|
+
> {
|
|
317
|
+
List: typeof NavList;
|
|
318
|
+
Item: typeof NavItem;
|
|
71
319
|
}
|
|
72
320
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
321
|
+
// Attach sub-components using Object.assign for better type inference
|
|
322
|
+
const NavWithSubComponents = Object.assign(Nav, {
|
|
323
|
+
List: NavList,
|
|
324
|
+
Item: NavItem,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
export default NavWithSubComponents as NavComponent;
|