@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.
Files changed (264) hide show
  1. package/README.md +89 -0
  2. package/libs/{chunk-DV56L5YX.cjs → chunk-2LTJ7HHX.cjs} +4 -4
  3. package/libs/{chunk-EQ67LF46.js → chunk-2Y7W75TT.js} +3 -3
  4. package/libs/{chunk-KKLTUJFB.cjs → chunk-3MKLDCKQ.cjs} +5 -5
  5. package/libs/chunk-3MKLDCKQ.cjs.map +1 -0
  6. package/libs/{chunk-X3EVB7VS.cjs → chunk-5S4ORA4C.cjs} +3 -3
  7. package/libs/{chunk-O6QZBB6G.js → chunk-772NRB75.js} +5 -5
  8. package/libs/chunk-772NRB75.js.map +1 -0
  9. package/libs/{chunk-6BVXFW7U.cjs → chunk-AHDJGCG5.cjs} +3 -3
  10. package/libs/{chunk-E3XP6BEX.cjs → chunk-B7F5FS6D.cjs} +3 -3
  11. package/libs/chunk-D4YLRWAO.cjs +18 -0
  12. package/libs/chunk-D4YLRWAO.cjs.map +1 -0
  13. package/libs/chunk-ETFLFC2S.js +10 -0
  14. package/libs/chunk-ETFLFC2S.js.map +1 -0
  15. package/libs/chunk-GZ4QFPRY.js +9 -0
  16. package/libs/chunk-GZ4QFPRY.js.map +1 -0
  17. package/libs/{chunk-LHVJKDMA.cjs → chunk-J32EZPYD.cjs} +3 -3
  18. package/libs/chunk-JJ43O4Y5.js +8 -0
  19. package/libs/chunk-JJ43O4Y5.js.map +1 -0
  20. package/libs/chunk-KUKIVRC2.js +7 -0
  21. package/libs/chunk-KUKIVRC2.js.map +1 -0
  22. package/libs/chunk-L75OQKEI.cjs +13 -0
  23. package/libs/chunk-L75OQKEI.cjs.map +1 -0
  24. package/libs/{chunk-LL7HTLMS.cjs → chunk-M5RRNTVX.cjs} +3 -3
  25. package/libs/{chunk-LIQJ7ZZR.js → chunk-NGTJDDFO.js} +2 -2
  26. package/libs/chunk-OK5QEIMD.cjs +17 -0
  27. package/libs/chunk-OK5QEIMD.cjs.map +1 -0
  28. package/libs/chunk-P2DC76ZZ.cjs +18 -0
  29. package/libs/chunk-P2DC76ZZ.cjs.map +1 -0
  30. package/libs/chunk-PQ2K3BM6.cjs +17 -0
  31. package/libs/chunk-PQ2K3BM6.cjs.map +1 -0
  32. package/libs/{chunk-QCMV4VQZ.js → chunk-QLZWHAMK.js} +2 -2
  33. package/libs/{chunk-BIP2NY53.js → chunk-RIVUMPOG.js} +2 -2
  34. package/libs/{chunk-ICCKQ2GC.cjs → chunk-ROZI23GS.cjs} +4 -4
  35. package/libs/{chunk-NHYXGV3L.js → chunk-SMYRLO3E.js} +2 -2
  36. package/libs/{chunk-5ZM4XL44.js → chunk-TYRCEX2L.js} +2 -2
  37. package/libs/chunk-VUH3FXGJ.js +11 -0
  38. package/libs/chunk-VUH3FXGJ.js.map +1 -0
  39. package/libs/{chunk-PPOOBUOS.js → chunk-XBA562WW.js} +2 -2
  40. package/libs/{chunk-QVV34QEH.cjs → chunk-XTQKWY7W.cjs} +3 -3
  41. package/libs/{chunk-YWOYVRFT.js → chunk-ZANSFMTD.js} +3 -3
  42. package/libs/components/alert/alert.css +1 -1
  43. package/libs/components/alert/alert.css.map +1 -1
  44. package/libs/components/alert/alert.min.css +2 -2
  45. package/libs/components/badge/badge.css +1 -1
  46. package/libs/components/badge/badge.css.map +1 -1
  47. package/libs/components/badge/badge.min.css +2 -2
  48. package/libs/components/breadcrumbs/breadcrumb.cjs +9 -5
  49. package/libs/components/breadcrumbs/breadcrumb.d.cts +271 -32
  50. package/libs/components/breadcrumbs/breadcrumb.d.ts +271 -32
  51. package/libs/components/breadcrumbs/breadcrumb.js +3 -3
  52. package/libs/components/button.cjs +4 -4
  53. package/libs/components/button.d.cts +2 -2
  54. package/libs/components/button.d.ts +2 -2
  55. package/libs/components/button.js +2 -2
  56. package/libs/components/buttons/button.css +1 -1
  57. package/libs/components/buttons/button.css.map +1 -1
  58. package/libs/components/buttons/button.min.css +2 -2
  59. package/libs/components/card.cjs +7 -7
  60. package/libs/components/card.d.cts +277 -33
  61. package/libs/components/card.d.ts +277 -33
  62. package/libs/components/card.js +2 -2
  63. package/libs/components/cards/card.css +1 -1
  64. package/libs/components/cards/card.css.map +1 -1
  65. package/libs/components/cards/card.min.css +2 -2
  66. package/libs/components/details/details.css +1 -1
  67. package/libs/components/details/details.css.map +1 -1
  68. package/libs/components/details/details.min.css +2 -2
  69. package/libs/components/dialog/dialog.cjs +7 -7
  70. package/libs/components/dialog/dialog.css +1 -1
  71. package/libs/components/dialog/dialog.css.map +1 -1
  72. package/libs/components/dialog/dialog.d.cts +88 -34
  73. package/libs/components/dialog/dialog.d.ts +88 -34
  74. package/libs/components/dialog/dialog.js +5 -5
  75. package/libs/components/dialog/dialog.min.css +2 -2
  76. package/libs/components/form/fields.cjs +4 -4
  77. package/libs/components/form/fields.d.cts +2 -2
  78. package/libs/components/form/fields.d.ts +2 -2
  79. package/libs/components/form/fields.js +2 -2
  80. package/libs/components/form/textarea.cjs +4 -4
  81. package/libs/components/form/textarea.d.cts +2 -2
  82. package/libs/components/form/textarea.d.ts +2 -2
  83. package/libs/components/form/textarea.js +2 -2
  84. package/libs/components/heading/heading.cjs +3 -3
  85. package/libs/components/heading/heading.d.cts +3 -14
  86. package/libs/components/heading/heading.d.ts +3 -14
  87. package/libs/components/heading/heading.js +2 -2
  88. package/libs/components/icons/icon.cjs +4 -4
  89. package/libs/components/icons/icon.d.cts +148 -4
  90. package/libs/components/icons/icon.d.ts +148 -4
  91. package/libs/components/icons/icon.js +2 -2
  92. package/libs/components/images/img.css +1 -1
  93. package/libs/components/images/img.css.map +1 -1
  94. package/libs/components/images/img.min.css +2 -2
  95. package/libs/components/link/link.cjs +4 -4
  96. package/libs/components/link/link.d.cts +2 -2
  97. package/libs/components/link/link.d.ts +2 -2
  98. package/libs/components/link/link.js +2 -2
  99. package/libs/components/list/list.cjs +5 -5
  100. package/libs/components/list/list.d.cts +3 -3
  101. package/libs/components/list/list.d.ts +3 -3
  102. package/libs/components/list/list.js +2 -2
  103. package/libs/components/modal.cjs +4 -4
  104. package/libs/components/modal.js +3 -3
  105. package/libs/components/nav/nav.cjs +7 -7
  106. package/libs/components/nav/nav.d.cts +2 -2
  107. package/libs/components/nav/nav.d.ts +2 -2
  108. package/libs/components/nav/nav.js +3 -3
  109. package/libs/components/text/text.cjs +5 -5
  110. package/libs/components/text/text.d.cts +2 -2
  111. package/libs/components/text/text.d.ts +2 -2
  112. package/libs/components/text/text.js +2 -2
  113. package/libs/heading-3648c538.d.ts +250 -0
  114. package/libs/hooks.cjs +7 -0
  115. package/libs/hooks.d.cts +5 -0
  116. package/libs/hooks.d.ts +5 -0
  117. package/libs/hooks.js +3 -0
  118. package/libs/icons.cjs +3 -3
  119. package/libs/icons.d.cts +1 -1
  120. package/libs/icons.d.ts +1 -1
  121. package/libs/icons.js +2 -2
  122. package/libs/index.cjs +112 -91
  123. package/libs/index.cjs.map +1 -1
  124. package/libs/index.css +1 -1
  125. package/libs/index.css.map +1 -1
  126. package/libs/index.d.cts +515 -31
  127. package/libs/index.d.ts +515 -31
  128. package/libs/index.js +31 -19
  129. package/libs/index.js.map +1 -1
  130. package/libs/ui-645f95b5.d.ts +285 -0
  131. package/package.json +2 -83
  132. package/src/components/README-UI.mdx +416 -0
  133. package/src/components/alert/ACCESSIBILITY.md +319 -0
  134. package/src/components/alert/README.mdx +475 -19
  135. package/src/components/alert/alert.scss +113 -6
  136. package/src/components/alert/alert.stories.tsx +372 -0
  137. package/src/components/alert/alert.test.tsx +762 -0
  138. package/src/components/alert/alert.tsx +331 -66
  139. package/src/components/alert/views/alert-actions.tsx +13 -0
  140. package/src/components/alert/views/alert-content.tsx +17 -0
  141. package/src/components/alert/views/alert-icon.tsx +53 -0
  142. package/src/components/alert/views/alert-screen-reader-text.tsx +30 -0
  143. package/src/components/alert/views/alert-title.tsx +23 -0
  144. package/src/components/alert/views/alert-view.tsx +158 -0
  145. package/src/components/alert/views/index.ts +12 -0
  146. package/src/components/badge/badge.mdx +186 -49
  147. package/src/components/badge/badge.scss +20 -2
  148. package/src/components/badge/badge.stories.tsx +160 -14
  149. package/src/components/badge/badge.test.tsx +179 -0
  150. package/src/components/badge/badge.tsx +97 -4
  151. package/src/components/breadcrumbs/README.mdx +364 -45
  152. package/src/components/breadcrumbs/__snapshots__/breadcrumb.test.tsx.snap +152 -0
  153. package/src/components/breadcrumbs/breadcrumb.stories.tsx +7 -3
  154. package/src/components/breadcrumbs/breadcrumb.test.tsx +490 -0
  155. package/src/components/breadcrumbs/breadcrumb.tsx +427 -170
  156. package/src/components/buttons/button.scss +34 -31
  157. package/src/components/buttons/button.stories.tsx +35 -0
  158. package/src/components/cards/README.mdx +657 -0
  159. package/src/components/cards/card.scss +22 -0
  160. package/src/components/cards/card.stories.tsx +167 -5
  161. package/src/components/cards/card.test.tsx +360 -20
  162. package/src/components/cards/card.tsx +200 -79
  163. package/src/components/cards/card.types.ts +135 -0
  164. package/src/components/cards/card.utils.ts +79 -0
  165. package/src/components/details/ACCESSIBILITY-REVIEW-LIVE.md +1050 -0
  166. package/src/components/details/ACCESSIBILITY-REVIEW.md +502 -0
  167. package/src/components/details/README.mdx +437 -69
  168. package/src/components/details/details.scss +16 -7
  169. package/src/components/details/details.test.tsx +385 -0
  170. package/src/components/details/details.tsx +101 -69
  171. package/src/components/details/details.types.ts +76 -0
  172. package/src/components/dialog/README.mdx +513 -110
  173. package/src/components/dialog/dialog-modal.tsx +79 -56
  174. package/src/components/dialog/dialog.scss +53 -3
  175. package/src/components/dialog/dialog.stories.tsx +10 -7
  176. package/src/components/dialog/dialog.test.tsx +450 -0
  177. package/src/components/dialog/dialog.tsx +69 -59
  178. package/src/components/dialog/dialog.types.ts +133 -0
  179. package/src/components/dialog/views/dialog-footer.tsx +54 -11
  180. package/src/components/dialog/views/dialog-header.tsx +20 -15
  181. package/src/components/heading/heading.stories.tsx +44 -4
  182. package/src/components/heading/heading.tsx +89 -23
  183. package/src/components/icons/README.mdx +332 -0
  184. package/src/components/icons/icon.stories.tsx +74 -1
  185. package/src/components/icons/icon.tsx +89 -1
  186. package/src/components/icons/types.ts +47 -0
  187. package/src/components/images/README.mdx +340 -24
  188. package/src/components/images/img.scss +19 -3
  189. package/src/components/images/img.stories.tsx +424 -15
  190. package/src/components/images/img.test.tsx +354 -25
  191. package/src/components/images/img.tsx +186 -63
  192. package/src/components/images/img.types.ts +211 -0
  193. package/src/components/title/MIGRATION.md +199 -0
  194. package/src/components/title/README.md +326 -0
  195. package/src/components/title/README.mdx +452 -0
  196. package/src/components/title/title.stories.tsx +393 -0
  197. package/src/components/title/title.test.tsx +251 -0
  198. package/src/components/title/title.tsx +219 -0
  199. package/src/components/ui.stories.tsx +894 -0
  200. package/src/components/ui.test.tsx +559 -0
  201. package/src/components/ui.tsx +266 -15
  202. package/src/components/word-count/README.md +240 -0
  203. package/src/hooks.ts +1 -0
  204. package/src/index.ts +10 -2
  205. package/src/sass/_properties.scss +1 -0
  206. package/src/styles/alert/alert.css +94 -4
  207. package/src/styles/alert/alert.css.map +1 -1
  208. package/src/styles/badge/badge.css +20 -2
  209. package/src/styles/badge/badge.css.map +1 -1
  210. package/src/styles/buttons/button.css +31 -31
  211. package/src/styles/buttons/button.css.map +1 -1
  212. package/src/styles/cards/card.css +16 -0
  213. package/src/styles/cards/card.css.map +1 -1
  214. package/src/styles/details/details.css +19 -8
  215. package/src/styles/details/details.css.map +1 -1
  216. package/src/styles/dialog/dialog.css +43 -2
  217. package/src/styles/dialog/dialog.css.map +1 -1
  218. package/src/styles/images/img.css +15 -3
  219. package/src/styles/images/img.css.map +1 -1
  220. package/src/styles/index.css +240 -51
  221. package/src/styles/index.css.map +1 -1
  222. package/src/test/setup.d.ts +9 -0
  223. package/src/test/setup.ts +53 -1
  224. package/libs/chunk-6TE5QEVE.cjs +0 -13
  225. package/libs/chunk-6TE5QEVE.cjs.map +0 -1
  226. package/libs/chunk-7K76RW2A.cjs +0 -18
  227. package/libs/chunk-7K76RW2A.cjs.map +0 -1
  228. package/libs/chunk-BSPKFLO4.js +0 -8
  229. package/libs/chunk-BSPKFLO4.js.map +0 -1
  230. package/libs/chunk-BV5CLH44.cjs +0 -18
  231. package/libs/chunk-BV5CLH44.cjs.map +0 -1
  232. package/libs/chunk-DKGJHKGW.js +0 -9
  233. package/libs/chunk-DKGJHKGW.js.map +0 -1
  234. package/libs/chunk-ECLD37WN.cjs +0 -16
  235. package/libs/chunk-ECLD37WN.cjs.map +0 -1
  236. package/libs/chunk-HYBZBN4G.js +0 -8
  237. package/libs/chunk-HYBZBN4G.js.map +0 -1
  238. package/libs/chunk-KKLTUJFB.cjs.map +0 -1
  239. package/libs/chunk-M5QL5TAE.cjs +0 -14
  240. package/libs/chunk-M5QL5TAE.cjs.map +0 -1
  241. package/libs/chunk-NE6YXTMC.js +0 -7
  242. package/libs/chunk-NE6YXTMC.js.map +0 -1
  243. package/libs/chunk-O6QZBB6G.js.map +0 -1
  244. package/libs/chunk-SXVZSWX6.js +0 -11
  245. package/libs/chunk-SXVZSWX6.js.map +0 -1
  246. package/libs/ui-9a6f9f8d.d.ts +0 -24
  247. package/src/components/cards/README.md +0 -80
  248. package/src/components/dialog/hooks/useClickOutside.ts +0 -33
  249. /package/libs/{chunk-DV56L5YX.cjs.map → chunk-2LTJ7HHX.cjs.map} +0 -0
  250. /package/libs/{chunk-EQ67LF46.js.map → chunk-2Y7W75TT.js.map} +0 -0
  251. /package/libs/{chunk-X3EVB7VS.cjs.map → chunk-5S4ORA4C.cjs.map} +0 -0
  252. /package/libs/{chunk-6BVXFW7U.cjs.map → chunk-AHDJGCG5.cjs.map} +0 -0
  253. /package/libs/{chunk-E3XP6BEX.cjs.map → chunk-B7F5FS6D.cjs.map} +0 -0
  254. /package/libs/{chunk-LHVJKDMA.cjs.map → chunk-J32EZPYD.cjs.map} +0 -0
  255. /package/libs/{chunk-LL7HTLMS.cjs.map → chunk-M5RRNTVX.cjs.map} +0 -0
  256. /package/libs/{chunk-LIQJ7ZZR.js.map → chunk-NGTJDDFO.js.map} +0 -0
  257. /package/libs/{chunk-QCMV4VQZ.js.map → chunk-QLZWHAMK.js.map} +0 -0
  258. /package/libs/{chunk-BIP2NY53.js.map → chunk-RIVUMPOG.js.map} +0 -0
  259. /package/libs/{chunk-ICCKQ2GC.cjs.map → chunk-ROZI23GS.cjs.map} +0 -0
  260. /package/libs/{chunk-NHYXGV3L.js.map → chunk-SMYRLO3E.js.map} +0 -0
  261. /package/libs/{chunk-5ZM4XL44.js.map → chunk-TYRCEX2L.js.map} +0 -0
  262. /package/libs/{chunk-PPOOBUOS.js.map → chunk-XBA562WW.js.map} +0 -0
  263. /package/libs/{chunk-QVV34QEH.cjs.map → chunk-XTQKWY7W.cjs.map} +0 -0
  264. /package/libs/{chunk-YWOYVRFT.js.map → chunk-ZANSFMTD.js.map} +0 -0
@@ -0,0 +1,158 @@
1
+ import React from "react";
2
+ import UI from "#components/ui";
3
+ import { IconProps } from "#components/icons/types";
4
+ import DismissButton from "../elements/dismiss-button";
5
+ import {
6
+ AlertScreenReaderText,
7
+ AlertIcon,
8
+ AlertTitle,
9
+ AlertContent,
10
+ AlertActions,
11
+ } from ".";
12
+
13
+ /**
14
+ * Valid severity levels for alerts.
15
+ */
16
+ type Severity = "default" | "info" | "success" | "warning" | "error";
17
+
18
+ /**
19
+ * Maps severity levels to ARIA live region types.
20
+ */
21
+ const SEVERITY_ARIA_LIVE: Record<Severity, "polite" | "assertive"> = {
22
+ default: "polite",
23
+ info: "polite",
24
+ success: "polite",
25
+ warning: "polite",
26
+ error: "assertive",
27
+ } as const;
28
+
29
+ /**
30
+ * Props for the AlertView presentation component.
31
+ */
32
+ export type AlertViewProps = {
33
+ /**
34
+ * The severity level of the alert.
35
+ */
36
+ severity: Severity;
37
+ /**
38
+ * Visual variant of the alert.
39
+ */
40
+ variant: "outlined" | "filled" | "soft";
41
+ /**
42
+ * Whether the alert is currently visible (for animations).
43
+ */
44
+ isVisible: boolean;
45
+ /**
46
+ * Whether the alert can be dismissed.
47
+ */
48
+ dismissible: boolean;
49
+ /**
50
+ * Callback when dismiss button is clicked.
51
+ */
52
+ onDismiss: () => void;
53
+ /**
54
+ * Handler for interaction start (hover/focus).
55
+ */
56
+ onInteractionStart: () => void;
57
+ /**
58
+ * Handler for interaction end (leave/blur).
59
+ */
60
+ onInteractionEnd: () => void;
61
+ /**
62
+ * Whether to automatically focus the alert.
63
+ */
64
+ autoFocus: boolean;
65
+ /**
66
+ * Optional title for the alert.
67
+ */
68
+ title?: string;
69
+ /**
70
+ * Semantic heading level for the title (2-6).
71
+ */
72
+ titleLevel: 2 | 3 | 4 | 5 | 6;
73
+ /**
74
+ * The main message content.
75
+ */
76
+ children: React.ReactNode;
77
+ /**
78
+ * Content rendering mode for alert children.
79
+ */
80
+ contentType: "text" | "node";
81
+ /**
82
+ * Custom action buttons to display in the alert.
83
+ */
84
+ actions?: React.ReactNode;
85
+ /**
86
+ * Whether to hide the severity icon.
87
+ */
88
+ hideIcon?: boolean;
89
+ /**
90
+ * Merged props for the Icon component.
91
+ */
92
+ iconProps: IconProps;
93
+ } & React.ComponentProps<typeof UI>;
94
+
95
+ /**
96
+ * AlertView is a pure presentation component that renders the Alert UI structure.
97
+ * It receives all presentation props and behavior handlers from the parent Alert component.
98
+ *
99
+ * This component is focused solely on rendering the visual structure and does not contain
100
+ * any business logic or state management. All behavior is delegated to the parent.
101
+ *
102
+ * @param props - AlertView props including severity, variant, handlers, and content
103
+ * @returns The rendered alert UI structure
104
+ */
105
+ export const AlertView = React.forwardRef<HTMLDivElement, AlertViewProps>(
106
+ (
107
+ {
108
+ severity,
109
+ variant,
110
+ isVisible,
111
+ dismissible,
112
+ onDismiss,
113
+ onInteractionStart,
114
+ onInteractionEnd,
115
+ autoFocus,
116
+ title,
117
+ titleLevel,
118
+ children,
119
+ contentType,
120
+ actions,
121
+ hideIcon,
122
+ iconProps,
123
+ ...props
124
+ },
125
+ ref
126
+ ) => {
127
+ return (
128
+ <UI
129
+ as="div"
130
+ ref={ref}
131
+ role="alert"
132
+ aria-live={SEVERITY_ARIA_LIVE[severity]}
133
+ aria-atomic="true"
134
+ className={`alert alert-${severity}`}
135
+ data-alert={severity}
136
+ data-visible={isVisible}
137
+ data-variant={variant}
138
+ tabIndex={autoFocus ? -1 : undefined}
139
+ onMouseEnter={onInteractionStart}
140
+ onMouseLeave={onInteractionEnd}
141
+ onFocus={onInteractionStart}
142
+ onBlur={onInteractionEnd}
143
+ {...props}
144
+ >
145
+ <AlertScreenReaderText severity={severity} />
146
+ <AlertIcon severity={severity} iconProps={iconProps} hideIcon={hideIcon} />
147
+ <UI as="div" className="alert-message">
148
+ <AlertTitle title={title} titleLevel={titleLevel} />
149
+ <AlertContent contentType={contentType}>{children}</AlertContent>
150
+ <AlertActions actions={actions} />
151
+ </UI>
152
+ {dismissible && <DismissButton onDismiss={onDismiss} />}
153
+ </UI>
154
+ );
155
+ }
156
+ );
157
+
158
+ AlertView.displayName = "AlertView";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Alert view components - focused UI presentation components.
3
+ * Each component handles a single visual concern of the Alert.
4
+ */
5
+
6
+ export { AlertScreenReaderText } from "./alert-screen-reader-text";
7
+ export { AlertIcon } from "./alert-icon";
8
+ export { AlertTitle } from "./alert-title";
9
+ export { AlertContent } from "./alert-content";
10
+ export { AlertActions } from "./alert-actions";
11
+ export { AlertView } from "./alert-view";
12
+ export type { AlertViewProps } from "./alert-view";
@@ -6,59 +6,103 @@ import { Meta } from "@storybook/addon-docs/blocks";
6
6
 
7
7
  A lightweight and flexible badge component for displaying small counts, labels,
8
8
  or status indicators. The Badge component renders as a superscript element with
9
- customizable styling.
9
+ customizable styling and comprehensive accessibility features.
10
10
 
11
11
  ## Features
12
12
 
13
- - Renders as a `<sup>` element with nested `<span>`
13
+ - Renders as a `<sup>` element with nested `<span>` for proper styling architecture
14
14
  - Fully customizable via CSS custom properties
15
- - Supports rounded and standard variants
16
- - ARIA-friendly with proper accessibility attributes
15
+ - Supports `rounded` variant for perfect circular badges
16
+ - Auto-scaling with fixed dimensions for rounded badges (1.5625rem)
17
+ - Content truncation with ellipsis for overflow text
18
+ - ARIA-friendly with `role="status"` for live region announcements
19
+ - Built-in accessibility support with aria-label prop
17
20
  - Integrates with FPKit UI component system
21
+ - Comprehensive TypeScript support with full JSDoc documentation
18
22
 
19
23
  ## Props
20
24
 
21
- The Badge component accepts the following props:
25
+ The Badge component accepts the following props (extends `BadgeProps`):
22
26
 
23
- - `children: React.ReactNode` - Content to display inside the badge
24
- - `id?: string` - Optional ID for the badge element
25
- - `styles?: object` - Custom styles object
26
- - `classes?: string` - Additional CSS classes
27
- - `data-badge?: string` - Data attribute for badge variants (e.g. "rounded")
27
+ - `children?: React.ReactNode` - Content to display inside the badge (typically numbers or short text)
28
+ - `id?: string` - Optional HTML id attribute for the badge element
29
+ - `styles?: React.CSSProperties` - Inline styles to apply to the badge
30
+ - `classes?: string` - CSS class names to apply to the badge
31
+ - `variant?: 'rounded'` - Visual variant of the badge. Use 'rounded' for circular badges with fixed size
32
+ - `aria-label?: string` - Accessible label for screen readers (required for icon-only or number-only badges)
33
+ - `role?: 'status' | 'note'` - ARIA role for the badge (defaults to 'status' for dynamic content)
28
34
  - All standard HTML attributes supported by `<sup>` element
29
35
 
30
36
  ## Technical Details
31
37
 
32
- - Built using React functional components
33
- - Uses TypeScript for type safety
34
- - Styled with CSS custom properties
38
+ - Built using React functional components with TypeScript
39
+ - Uses TypeScript with comprehensive JSDoc comments for enhanced IDE support
40
+ - Styled with SCSS/CSS custom properties (units in rem)
35
41
  - Leverages base UI component from FPKit
42
+ - Exports `BadgeProps` type for external usage
43
+ - Component display name: 'Badge'
36
44
 
37
- ## Usage Example
45
+ ## Styling Architecture
38
46
 
39
- ### Basic Usage
47
+ The Badge uses a **nested structure** (`<sup><span>content</span></sup>`) which is required
48
+ for the SCSS styling system:
49
+
50
+ - **Outer `<sup>` element**: Provides positioning context and base styling
51
+ - **Inner `<span>` element**: Receives visual styling (background, padding, border-radius)
52
+
53
+ This architecture ensures proper styling isolation and allows the badge to work correctly
54
+ when positioned alongside other content.
55
+
56
+ ## Rounded Variant Behavior
57
+
58
+ The `rounded` variant creates a **perfect circular badge** with fixed dimensions (1.5625rem).
59
+ Key characteristics:
60
+
61
+ - **Fixed size**: 1.5625rem × 1.5625rem (25px × 25px at base font size)
62
+ - **Perfect circle**: 50% border-radius
63
+ - **Content overflow**: Text exceeding available space is truncated with clip (no ellipsis visible)
64
+ - **Font weight**: Bold (700) for better readability
65
+ - **Font size**: 0.6875rem (11px at base font size)
66
+
67
+ ### Best Practices for Rounded Badges
68
+
69
+ ✅ **GOOD**: Format large numbers yourself
70
+ ```tsx
71
+ <Badge variant="rounded" aria-label="More than 99 notifications">99+</Badge>
72
+ ```
73
+
74
+ ❌ **BAD**: Let unformatted numbers overflow
75
+ ```tsx
76
+ <Badge variant="rounded">999</Badge> {/* Will be truncated */}
77
+ ```
78
+
79
+ ## Usage Examples
80
+
81
+ ### Basic Usage with Accessibility
40
82
 
41
83
  ```tsx
42
- import Badge from "./badge";
84
+ import { Badge } from "@fpkit/acss";
43
85
 
44
86
  function Example() {
45
87
  return (
46
88
  <p>
47
- Messages <Badge>3</Badge>
89
+ Messages
90
+ <Badge aria-label="3 unread messages">3</Badge>
48
91
  </p>
49
92
  );
50
93
  }
51
94
  ```
52
95
 
53
- ### Rounded Badge
96
+ ### Rounded Badge Variant
54
97
 
55
98
  ```tsx
56
- import Badge from "./badge";
99
+ import { Badge } from "@fpkit/acss";
57
100
 
58
101
  function Example() {
59
102
  return (
60
103
  <p>
61
- Status <Badge data-badge="rounded">21</Badge>
104
+ Notifications
105
+ <Badge variant="rounded" aria-label="99 or more notifications">99+</Badge>
62
106
  </p>
63
107
  );
64
108
  }
@@ -67,58 +111,151 @@ function Example() {
67
111
  ### Custom Styled Badge
68
112
 
69
113
  ```tsx
70
- import Badge from "./badge";
114
+ import { Badge } from "@fpkit/acss";
71
115
 
72
116
  function Example() {
73
117
  return (
74
118
  <p>
75
- Count{" "}
119
+ Active Users
76
120
  <Badge
77
- styles={{
78
- "--badge-bg": "blue",
79
- "--badge-color": "white",
80
- }}
81
- aria-label="badge count"
121
+ styles={{ backgroundColor: "green", color: "white" }}
122
+ aria-label="21 active users"
82
123
  >
83
- 5
124
+ 21
84
125
  </Badge>
85
126
  </p>
86
127
  );
87
128
  }
88
129
  ```
89
130
 
131
+ ### Multiple Badges
132
+
133
+ ```tsx
134
+ import { Badge } from "@fpkit/acss";
135
+
136
+ function Dashboard() {
137
+ return (
138
+ <div>
139
+ <p>
140
+ Inbox
141
+ <Badge variant="rounded" aria-label="12 unread emails">12</Badge>
142
+ </p>
143
+ <p>
144
+ Tasks
145
+ <Badge variant="rounded" aria-label="5 pending tasks">5</Badge>
146
+ </p>
147
+ <p>
148
+ Alerts
149
+ <Badge variant="rounded" aria-label="1 new alert">1</Badge>
150
+ </p>
151
+ </div>
152
+ );
153
+ }
154
+ ```
155
+
90
156
  ## Styling
91
157
 
92
- The component uses CSS custom properties for styling:
158
+ The component uses CSS custom properties for full customization:
159
+
160
+ ### Standard Badge CSS Variables
93
161
 
94
162
  ```css
95
- --badge-bg: lightgray; /* Background color */
96
- --badge-color: currentColor; /* Text color */
97
- --badge-radius: 0.5rem; /* Border radius */
98
- --badge-padding: 0.3rem; /* Inner padding */
99
- --badge-v-align: 0.5rem; /* Vertical alignment */
100
- --badge-fs: var(--fs-1); /* Font size */
163
+ --badge-bg: lightgray; /* Background color */
164
+ --badge-color: currentColor; /* Text color */
165
+ --badge-radius: 0.5rem; /* Border radius */
166
+ --badge-padding: 0.3rem; /* Inner padding */
167
+ --badge-v-align: 0.5rem; /* Vertical alignment */
168
+ --badge-fs: var(--fs-1); /* Font size */
101
169
  ```
102
170
 
103
- For rounded badges, use the `data-badge="rounded"` attribute which sets:
171
+ ### Rounded Badge CSS Variables
172
+
173
+ When using `variant="rounded"`, these additional variables are set:
104
174
 
105
175
  ```css
106
- --badge-radius: 100%;
107
- --badge-padding: 0.2rem;
176
+ --badge-radius: 50%; /* Perfect circle */
177
+ --badge-padding: 0; /* No padding (uses width/height instead) */
178
+ --badge-size: 1.5625rem; /* Fixed dimensions for circle */
179
+ ```
180
+
181
+ ### Customizing Badge Styles
182
+
183
+ You can override CSS variables using inline styles:
184
+
185
+ ```tsx
186
+ <Badge
187
+ styles={{
188
+ "--badge-bg": "#3b82f6",
189
+ "--badge-color": "white",
190
+ "--badge-padding": "0.5rem",
191
+ } as React.CSSProperties}
192
+ >
193
+ Custom
194
+ </Badge>
195
+ ```
196
+
197
+ Or using standard CSS properties:
198
+
199
+ ```tsx
200
+ <Badge styles={{ backgroundColor: "#10b981", color: "white" }}>
201
+ NEW
202
+ </Badge>
203
+ ```
204
+
205
+ ## Accessibility Features
206
+
207
+ The Badge component follows WCAG 2.1 Level AA guidelines and includes:
208
+
209
+ ### Built-in Accessibility
210
+
211
+ - **Semantic HTML**: Uses `<sup>` element for proper positioning context
212
+ - **ARIA Role**: Defaults to `role="status"` for dynamic content, making it a live region
213
+ - **Live Regions**: Screen readers automatically announce changes to badge content
214
+ - **Accessible Names**: `aria-label` prop provides context for screen readers
215
+
216
+ ### WCAG 2.1 Compliance
217
+
218
+ - **1.4.3 Contrast (Minimum)**: Ensures sufficient color contrast with customizable colors
219
+ - **1.4.4 Resize Text**: Supports text resizing up to 200% using rem units
220
+ - **1.4.12 Text Spacing**: Maintains proper spacing on zoom and text spacing adjustments
221
+ - **4.1.1 Parsing**: Uses semantic HTML elements (`<sup>`, `<span>`)
222
+ - **4.1.2 Name, Role, Value**: Provides meaningful ARIA labels and roles
223
+
224
+ ### Accessibility Best Practices
225
+
226
+ ✅ **GOOD**: Descriptive aria-label with context
227
+ ```tsx
228
+ <Badge variant="rounded" aria-label="12 items in cart">12</Badge>
229
+ ```
230
+
231
+ ✅ **GOOD**: Format numbers and provide full context
232
+ ```tsx
233
+ <Badge variant="rounded" aria-label="More than 99 notifications">99+</Badge>
234
+ ```
235
+
236
+ ❌ **BAD**: Number-only badge without context
237
+ ```tsx
238
+ <Badge>12</Badge> {/* Screen reader only hears "12" without context */}
239
+ ```
240
+
241
+ ❌ **BAD**: Missing aria-label for icon-only badges
242
+ ```tsx
243
+ <Badge variant="rounded">!</Badge> {/* What does "!" mean? */}
108
244
  ```
109
245
 
110
- ## WCAG 2.1 Guidelines
246
+ ### Testing Recommendations
111
247
 
112
- - Ensures sufficient color contrast (1.4.3)
113
- - Supports text resizing (1.4.4)
114
- - Maintains spacing on zoom (1.4.12)
115
- - Uses semantic HTML elements (4.1.1)
116
- - Provides meaningful content (4.1.2)
248
+ - Test with screen readers (NVDA, JAWS, VoiceOver)
249
+ - Verify keyboard navigation and focus management
250
+ - Check color contrast ratios (minimum 4.5:1 for normal text)
251
+ - Test with 200% zoom and custom text spacing
252
+ - Validate that badge updates are announced by screen readers
117
253
 
118
254
  ## Additional Notes
119
255
 
120
- - Use semantic values for badge content
121
- - Ensure adequate color contrast when customizing
122
- - Keep badge content concise
123
- - Include ARIA labels when needed
124
- - Test with screen readers and keyboard navigation
256
+ - **Keep content concise**: Badges work best with 1-3 characters
257
+ - **Format large numbers**: Use "99+" instead of "999" for better UX
258
+ - **Color contrast**: Ensure adequate contrast when customizing colors
259
+ - **Semantic values**: Use meaningful, descriptive content
260
+ - **ARIA labels**: Always include aria-label for number-only or icon-only badges
261
+ - **Units**: All sizing uses rem units (base 16px = 1rem) for better accessibility
@@ -15,7 +15,25 @@ sup:has(> span) {
15
15
  color: inherit;
16
16
  }
17
17
  &[data-badge~="rounded"] {
18
- --badge-radius: 100%;
19
- --badge-padding: 0.2rem;
18
+ --badge-radius: 50%;
19
+ --badge-padding: 0;
20
+ --badge-size: 1.5625rem;
21
+ width: var(--badge-size);
22
+ height: var(--badge-size);
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ line-height: 1;
27
+ font-size: 0.6875rem;
28
+ font-weight: 700;
29
+ overflow: hidden;
30
+ box-sizing: border-box;
31
+ span {
32
+ max-width: 100%;
33
+ overflow: hidden;
34
+ text-overflow: clip;
35
+ white-space: nowrap;
36
+ padding: 0 0.125rem;
37
+ }
20
38
  }
21
39
  }