@fpkit/acss 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/libs/{chunk-2NRIP6RB.cjs → chunk-2C3YLBWP.cjs} +3 -3
  2. package/libs/{chunk-NWJDAHP6.cjs → chunk-2GJHKWEK.cjs} +3 -3
  3. package/libs/{chunk-FVROL3V5.js → chunk-2JCDEC32.js} +3 -3
  4. package/libs/{chunk-IRLFZ3OL.js → chunk-3XJC4XUG.js} +2 -2
  5. package/libs/{chunk-23ANBDCR.js → chunk-4I5MF54P.js} +3 -3
  6. package/libs/chunk-4I5MF54P.js.map +1 -0
  7. package/libs/chunk-5CJPTDK3.cjs +31 -0
  8. package/libs/chunk-5CJPTDK3.cjs.map +1 -0
  9. package/libs/{chunk-E4OSROCA.cjs → chunk-5QSNJQVH.cjs} +3 -3
  10. package/libs/{chunk-O3JIHC5M.cjs → chunk-6BUJZ4DJ.cjs} +3 -3
  11. package/libs/{chunk-WXBFBWYF.cjs → chunk-AFINOD2L.cjs} +3 -3
  12. package/libs/{chunk-HRRHPLER.js → chunk-AWZLSWDO.js} +2 -2
  13. package/libs/chunk-DDSXKOUB.js +7 -0
  14. package/libs/chunk-DDSXKOUB.js.map +1 -0
  15. package/libs/{chunk-CWRNJA4P.js → chunk-DIJBIOFE.js} +3 -3
  16. package/libs/chunk-EJ6KYBFE.cjs +13 -0
  17. package/libs/chunk-EJ6KYBFE.cjs.map +1 -0
  18. package/libs/{chunk-GUJSMQ3V.cjs → chunk-EKJYOCLY.cjs} +3 -3
  19. package/libs/{chunk-X5RKCLDC.cjs → chunk-F64GE6RG.cjs} +4 -4
  20. package/libs/chunk-FMIM3332.js +8 -0
  21. package/libs/chunk-FMIM3332.js.map +1 -0
  22. package/libs/{chunk-5RAWNUVD.js → chunk-IBUTNPTQ.js} +2 -2
  23. package/libs/chunk-IWP4VJEP.cjs +18 -0
  24. package/libs/chunk-IWP4VJEP.cjs.map +1 -0
  25. package/libs/{chunk-ZFJ4U45S.js → chunk-KDMX3FAW.js} +2 -2
  26. package/libs/{chunk-DYFAUAB7.cjs → chunk-LXODKKA3.cjs} +4 -4
  27. package/libs/chunk-M7JLT62Q.js +9 -0
  28. package/libs/chunk-M7JLT62Q.js.map +1 -0
  29. package/libs/{chunk-IQ76HGVP.js → chunk-MBWI67UT.js} +2 -2
  30. package/libs/{chunk-O5XAJ7BY.cjs → chunk-NCGVF2QS.cjs} +4 -4
  31. package/libs/{chunk-W2UIN7EV.cjs → chunk-NPWHQVYB.cjs} +3 -3
  32. package/libs/{chunk-G55UJ53G.cjs → chunk-NZVSXRTB.cjs} +3 -3
  33. package/libs/chunk-NZVSXRTB.cjs.map +1 -0
  34. package/libs/{chunk-43TK2ICH.js → chunk-PMWL5XZ4.js} +3 -3
  35. package/libs/{chunk-KVKQLRJG.js → chunk-TF3GQKOY.js} +2 -2
  36. package/libs/chunk-TNEJXNZA.cjs +22 -0
  37. package/libs/chunk-TNEJXNZA.cjs.map +1 -0
  38. package/libs/{chunk-IEB64SWY.js → chunk-U5VA34SU.js} +2 -2
  39. package/libs/chunk-UGMP72J2.js +8 -0
  40. package/libs/chunk-UGMP72J2.js.map +1 -0
  41. package/libs/{chunk-MGPWZRBX.cjs → chunk-URBGDUFN.cjs} +6 -6
  42. package/libs/{chunk-QKHPHMG2.js → chunk-ZF6Y7W57.js} +5 -5
  43. package/libs/component-props-50e69975.d.ts +66 -0
  44. package/libs/components/box/box.css +1 -0
  45. package/libs/components/box/box.css.map +1 -0
  46. package/libs/components/box/box.min.css +3 -0
  47. package/libs/components/breadcrumbs/breadcrumb.cjs +6 -6
  48. package/libs/components/breadcrumbs/breadcrumb.js +3 -3
  49. package/libs/components/button.cjs +4 -4
  50. package/libs/components/button.d.cts +10 -3
  51. package/libs/components/button.d.ts +10 -3
  52. package/libs/components/button.js +2 -2
  53. package/libs/components/card.cjs +7 -7
  54. package/libs/components/card.d.cts +13 -85
  55. package/libs/components/card.d.ts +13 -85
  56. package/libs/components/card.js +2 -2
  57. package/libs/components/cards/card.css +1 -1
  58. package/libs/components/cards/card.css.map +1 -1
  59. package/libs/components/cards/card.min.css +2 -2
  60. package/libs/components/cluster/cluster.css +1 -0
  61. package/libs/components/cluster/cluster.css.map +1 -0
  62. package/libs/components/cluster/cluster.min.css +3 -0
  63. package/libs/components/dialog/dialog.cjs +7 -7
  64. package/libs/components/dialog/dialog.js +5 -5
  65. package/libs/components/form/fields.cjs +4 -4
  66. package/libs/components/form/fields.js +2 -2
  67. package/libs/components/form/textarea.cjs +4 -4
  68. package/libs/components/form/textarea.js +2 -2
  69. package/libs/components/grid/grid.css +1 -0
  70. package/libs/components/grid/grid.css.map +1 -0
  71. package/libs/components/grid/grid.min.css +3 -0
  72. package/libs/components/heading/heading.cjs +3 -3
  73. package/libs/components/heading/heading.js +2 -2
  74. package/libs/components/icons/icon.cjs +4 -4
  75. package/libs/components/icons/icon.d.cts +2 -2
  76. package/libs/components/icons/icon.d.ts +2 -2
  77. package/libs/components/icons/icon.js +2 -2
  78. package/libs/components/link/link.cjs +6 -6
  79. package/libs/components/link/link.js +2 -2
  80. package/libs/components/list/list.cjs +5 -5
  81. package/libs/components/list/list.js +2 -2
  82. package/libs/components/modal.cjs +4 -4
  83. package/libs/components/modal.d.cts +1 -1
  84. package/libs/components/modal.d.ts +1 -1
  85. package/libs/components/modal.js +3 -3
  86. package/libs/components/nav/nav.cjs +7 -7
  87. package/libs/components/nav/nav.js +3 -3
  88. package/libs/components/popover/popover.cjs +4 -4
  89. package/libs/components/popover/popover.d.cts +1 -1
  90. package/libs/components/popover/popover.d.ts +1 -1
  91. package/libs/components/popover/popover.js +1 -1
  92. package/libs/components/stack/stack.css +1 -0
  93. package/libs/components/stack/stack.css.map +1 -0
  94. package/libs/components/stack/stack.min.css +3 -0
  95. package/libs/components/tables/table.cjs +4 -4
  96. package/libs/components/tables/table.d.cts +2 -2
  97. package/libs/components/tables/table.d.ts +2 -2
  98. package/libs/components/tables/table.js +1 -1
  99. package/libs/components/text/text.cjs +5 -5
  100. package/libs/components/text/text.js +2 -2
  101. package/libs/hooks.cjs +4 -4
  102. package/libs/hooks.js +3 -3
  103. package/libs/{icons-287fce3a.d.ts → icons-df8e744f.d.ts} +1 -1
  104. package/libs/icons.cjs +3 -3
  105. package/libs/icons.d.cts +2 -2
  106. package/libs/icons.d.ts +2 -2
  107. package/libs/icons.js +2 -2
  108. package/libs/index.cjs +74 -73
  109. package/libs/index.cjs.map +1 -1
  110. package/libs/index.css +1 -1
  111. package/libs/index.css.map +1 -1
  112. package/libs/index.d.cts +925 -6
  113. package/libs/index.d.ts +925 -6
  114. package/libs/index.js +30 -30
  115. package/libs/index.js.map +1 -1
  116. package/package.json +2 -2
  117. package/src/App.tsx +1 -3
  118. package/src/components/alert/STYLES.mdx +790 -0
  119. package/src/components/badge/STYLES.mdx +610 -0
  120. package/src/components/box/README.mdx +401 -0
  121. package/src/components/box/STYLES.mdx +360 -0
  122. package/src/components/box/box.scss +245 -0
  123. package/src/components/box/box.stories.tsx +395 -0
  124. package/src/components/box/box.test.tsx +425 -0
  125. package/src/components/box/box.tsx +170 -0
  126. package/src/components/box/box.types.ts +166 -0
  127. package/src/components/breadcrumbs/STYLES.mdx +99 -0
  128. package/src/components/breadcrumbs/bc-item.tsx +0 -1
  129. package/src/components/buttons/STYLES.mdx +766 -0
  130. package/src/components/cards/STYLES.mdx +835 -0
  131. package/src/components/cards/card.scss +29 -21
  132. package/src/components/cards/card.tsx +13 -3
  133. package/src/components/cards/card.types.ts +13 -0
  134. package/src/components/cluster/README.mdx +595 -0
  135. package/src/components/cluster/STYLES.mdx +626 -0
  136. package/src/components/cluster/cluster.scss +86 -0
  137. package/src/components/cluster/cluster.stories.tsx +385 -0
  138. package/src/components/cluster/cluster.test.tsx +655 -0
  139. package/src/components/cluster/cluster.tsx +94 -0
  140. package/src/components/cluster/cluster.types.ts +75 -0
  141. package/src/components/details/STYLES.mdx +445 -0
  142. package/src/components/dialog/STYLES.mdx +888 -0
  143. package/src/components/flexbox/STYLES.mdx +857 -0
  144. package/src/components/flexbox/flex.stories.tsx +842 -141
  145. package/src/components/flexbox/flex.types.ts +25 -6
  146. package/src/components/form/STYLES.mdx +821 -0
  147. package/src/components/grid/README.mdx +709 -0
  148. package/src/components/grid/STYLES.mdx +785 -0
  149. package/src/components/grid/grid.scss +287 -0
  150. package/src/components/grid/grid.stories.tsx +486 -0
  151. package/src/components/grid/grid.test.tsx +981 -0
  152. package/src/components/grid/grid.tsx +222 -0
  153. package/src/components/grid/grid.types.ts +344 -0
  154. package/src/components/icons/STYLES.mdx +56 -0
  155. package/src/components/icons/components/arrow-right.tsx +0 -5
  156. package/src/components/images/STYLES.mdx +75 -0
  157. package/src/components/kit.tsx +8 -4
  158. package/src/components/layout/STYLES.mdx +556 -0
  159. package/src/components/link/STYLES.mdx +75 -0
  160. package/src/components/list/STYLES.mdx +631 -0
  161. package/src/components/nav/STYLES.mdx +460 -0
  162. package/src/components/popover/popover.tsx +1 -1
  163. package/src/components/progress/STYLES.mdx +64 -0
  164. package/src/components/stack/README.mdx +400 -0
  165. package/src/components/stack/STYLES.mdx +414 -0
  166. package/src/components/stack/stack.scss +109 -0
  167. package/src/components/stack/stack.stories.tsx +559 -0
  168. package/src/components/stack/stack.test.tsx +426 -0
  169. package/src/components/stack/stack.tsx +141 -0
  170. package/src/components/stack/stack.types.ts +133 -0
  171. package/src/components/tables/table-elements.tsx +1 -1
  172. package/src/components/tables/table.tsx +2 -2
  173. package/src/components/tag/STYLES.mdx +105 -0
  174. package/src/components/text-to-speech/STYLES.mdx +80 -0
  175. package/src/components/text-to-speech/TextToSpeech.tsx +0 -4
  176. package/src/components/text-to-speech/useTextToSpeech.tsx +2 -6
  177. package/src/components/ui.tsx +3 -3
  178. package/src/decorators/instructions.tsx +22 -18
  179. package/src/hooks/popover/popover.tsx +1 -1
  180. package/src/index.scss +5 -1
  181. package/src/index.ts +305 -12
  182. package/src/sass/GLOBALS-STYLES.md +631 -0
  183. package/src/sass/_globals.scss +45 -24
  184. package/src/styles/box/box.css +220 -0
  185. package/src/styles/box/box.css.map +1 -0
  186. package/src/styles/cards/card.css +22 -17
  187. package/src/styles/cards/card.css.map +1 -1
  188. package/src/styles/cluster/cluster.css +71 -0
  189. package/src/styles/cluster/cluster.css.map +1 -0
  190. package/src/styles/grid/grid.css +238 -0
  191. package/src/styles/grid/grid.css.map +1 -0
  192. package/src/styles/index.css +667 -49
  193. package/src/styles/index.css.map +1 -1
  194. package/src/styles/stack/stack.css +86 -0
  195. package/src/styles/stack/stack.css.map +1 -0
  196. package/src/types/component-props.ts +42 -13
  197. package/src/types/layout-primitives.ts +48 -0
  198. package/src/types/shared.ts +10 -26
  199. package/libs/chunk-23ANBDCR.js.map +0 -1
  200. package/libs/chunk-5QD3DWFI.js +0 -9
  201. package/libs/chunk-5QD3DWFI.js.map +0 -1
  202. package/libs/chunk-6WTC4JXH.cjs +0 -31
  203. package/libs/chunk-6WTC4JXH.cjs.map +0 -1
  204. package/libs/chunk-ENTCUJ3A.cjs +0 -13
  205. package/libs/chunk-ENTCUJ3A.cjs.map +0 -1
  206. package/libs/chunk-G55UJ53G.cjs.map +0 -1
  207. package/libs/chunk-HHLNOC5T.js +0 -7
  208. package/libs/chunk-HHLNOC5T.js.map +0 -1
  209. package/libs/chunk-KK47SYZI.js +0 -8
  210. package/libs/chunk-KK47SYZI.js.map +0 -1
  211. package/libs/chunk-US2I5GI7.cjs +0 -22
  212. package/libs/chunk-US2I5GI7.cjs.map +0 -1
  213. package/libs/chunk-W5TKWBFC.cjs +0 -18
  214. package/libs/chunk-W5TKWBFC.cjs.map +0 -1
  215. package/libs/chunk-Y2PFDELK.js +0 -8
  216. package/libs/chunk-Y2PFDELK.js.map +0 -1
  217. package/libs/component-props-67d978a2.d.ts +0 -38
  218. /package/libs/{chunk-2NRIP6RB.cjs.map → chunk-2C3YLBWP.cjs.map} +0 -0
  219. /package/libs/{chunk-NWJDAHP6.cjs.map → chunk-2GJHKWEK.cjs.map} +0 -0
  220. /package/libs/{chunk-FVROL3V5.js.map → chunk-2JCDEC32.js.map} +0 -0
  221. /package/libs/{chunk-IRLFZ3OL.js.map → chunk-3XJC4XUG.js.map} +0 -0
  222. /package/libs/{chunk-E4OSROCA.cjs.map → chunk-5QSNJQVH.cjs.map} +0 -0
  223. /package/libs/{chunk-O3JIHC5M.cjs.map → chunk-6BUJZ4DJ.cjs.map} +0 -0
  224. /package/libs/{chunk-WXBFBWYF.cjs.map → chunk-AFINOD2L.cjs.map} +0 -0
  225. /package/libs/{chunk-HRRHPLER.js.map → chunk-AWZLSWDO.js.map} +0 -0
  226. /package/libs/{chunk-CWRNJA4P.js.map → chunk-DIJBIOFE.js.map} +0 -0
  227. /package/libs/{chunk-GUJSMQ3V.cjs.map → chunk-EKJYOCLY.cjs.map} +0 -0
  228. /package/libs/{chunk-X5RKCLDC.cjs.map → chunk-F64GE6RG.cjs.map} +0 -0
  229. /package/libs/{chunk-5RAWNUVD.js.map → chunk-IBUTNPTQ.js.map} +0 -0
  230. /package/libs/{chunk-ZFJ4U45S.js.map → chunk-KDMX3FAW.js.map} +0 -0
  231. /package/libs/{chunk-DYFAUAB7.cjs.map → chunk-LXODKKA3.cjs.map} +0 -0
  232. /package/libs/{chunk-IQ76HGVP.js.map → chunk-MBWI67UT.js.map} +0 -0
  233. /package/libs/{chunk-O5XAJ7BY.cjs.map → chunk-NCGVF2QS.cjs.map} +0 -0
  234. /package/libs/{chunk-W2UIN7EV.cjs.map → chunk-NPWHQVYB.cjs.map} +0 -0
  235. /package/libs/{chunk-43TK2ICH.js.map → chunk-PMWL5XZ4.js.map} +0 -0
  236. /package/libs/{chunk-KVKQLRJG.js.map → chunk-TF3GQKOY.js.map} +0 -0
  237. /package/libs/{chunk-IEB64SWY.js.map → chunk-U5VA34SU.js.map} +0 -0
  238. /package/libs/{chunk-MGPWZRBX.cjs.map → chunk-URBGDUFN.cjs.map} +0 -0
  239. /package/libs/{chunk-QKHPHMG2.js.map → chunk-ZF6Y7W57.js.map} +0 -0
@@ -0,0 +1,595 @@
1
+ import { Meta, Story, Canvas } from '@storybook/addon-docs/blocks';
2
+ import * as ClusterStories from './cluster.stories';
3
+
4
+ <Meta of={ClusterStories} />
5
+
6
+ # Cluster
7
+
8
+ A wrapping flex layout primitive for inline groups that need to flow and wrap naturally.
9
+
10
+ ## Overview
11
+
12
+ Cluster provides a flexible, wrapping layout for inline content like tags, badges, button groups, navigation links, or any inline content that needs to wrap responsively. Items automatically wrap to the next line when the container width is exceeded.
13
+
14
+ **Key Characteristics:**
15
+ - **Auto-Wrapping:** Uses `flex-wrap: wrap` for natural content flow
16
+ - **Fluid Spacing:** Gap spacing using CSS custom properties with clamp()
17
+ - **Inline Groups:** Perfect for tags, badges, buttons, links
18
+ - **Semantic Naming:** Clear intent for grouped inline content
19
+ - **Zero Runtime:** All styling via utility classes (no JavaScript)
20
+
21
+ ## When to Use
22
+
23
+ ### ✅ Use Cluster for:
24
+ - **Tag Clouds:** Multiple keyword tags that wrap
25
+ - **Button Groups:** Action buttons that wrap on smaller screens
26
+ - **Navigation Links:** Horizontal nav that wraps responsively
27
+ - **Badge Clusters:** Multiple status badges or labels
28
+ - **Filter Pills:** Interactive filter buttons
29
+ - **Inline Lists:** List items displayed inline with wrapping
30
+
31
+ ### ❌ Don't Use Cluster for:
32
+ - **Vertical Layouts:** Use `Stack` for non-wrapping vertical layouts
33
+ - **Horizontal Layouts without Wrapping:** Use `Stack direction="horizontal" wrap="nowrap"`
34
+ - **Grid Layouts:** Use `Grid` for explicit column/row layouts
35
+ - **Containers with Padding:** Use `Box` for padding/margin control
36
+
37
+ ## Usage
38
+
39
+ ### Basic Tag Cloud
40
+
41
+ ```tsx
42
+ import { Cluster } from '@fpkit/acss';
43
+
44
+ <Cluster gap="sm">
45
+ <Tag>React</Tag>
46
+ <Tag>TypeScript</Tag>
47
+ <Tag>CSS</Tag>
48
+ <Tag>Accessibility</Tag>
49
+ <Tag>Performance</Tag>
50
+ </Cluster>
51
+ ```
52
+
53
+ ### Button Group
54
+
55
+ ```tsx
56
+ <Cluster gap="md">
57
+ <Button>Save</Button>
58
+ <Button>Cancel</Button>
59
+ <Button>Delete</Button>
60
+ </Cluster>
61
+ ```
62
+
63
+ ### Navigation with Baseline Alignment
64
+
65
+ ```tsx
66
+ <Cluster as="nav" gap="lg" align="baseline" justify="center">
67
+ <a href="#home">Home</a>
68
+ <a href="#products">Products</a>
69
+ <a href="#about">About</a>
70
+ <a href="#contact">Contact</a>
71
+ </Cluster>
72
+ ```
73
+
74
+ ### Centered Tag Cloud
75
+
76
+ ```tsx
77
+ <Cluster gap="sm" justify="center">
78
+ <Tag>HTML</Tag>
79
+ <Tag>CSS</Tag>
80
+ <Tag>JavaScript</Tag>
81
+ <Tag>React</Tag>
82
+ </Cluster>
83
+ ```
84
+
85
+ ### Filter Pills
86
+
87
+ ```tsx
88
+ <Cluster gap="sm">
89
+ <button className="pill active">All</button>
90
+ <button className="pill">Active</button>
91
+ <button className="pill">Completed</button>
92
+ <button className="pill">Pending</button>
93
+ </Cluster>
94
+ ```
95
+
96
+ ### Semantic List
97
+
98
+ ```tsx
99
+ <Cluster as="ul" gap="md" styles={{ listStyle: 'none', padding: 0 }}>
100
+ <li>Item 1</li>
101
+ <li>Item 2</li>
102
+ <li>Item 3</li>
103
+ </Cluster>
104
+ ```
105
+
106
+ ## Props
107
+
108
+ ### `gap`
109
+ **Type:** `SpacingScale` (`"0" | "xs" | "sm" | "md" | "lg" | "xl"`)
110
+ **Default:** `undefined` (uses default `.cluster` gap: `--spacing-sm`)
111
+
112
+ Controls the spacing between items using the unified spacing scale.
113
+
114
+ **CSS Variable Mapping:**
115
+ - `0` → `--spacing-0` (0)
116
+ - `xs` → `--spacing-xs` (clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem))
117
+ - `sm` → `--spacing-sm` (clamp(0.5rem, 0.45rem + 0.35vw, 0.75rem))
118
+ - `md` → `--spacing-md` (clamp(0.75rem, 0.65rem + 0.45vw, 1.125rem))
119
+ - `lg` → `--spacing-lg` (clamp(1rem, 0.85rem + 0.6vw, 1.5rem))
120
+ - `xl` → `--spacing-xl` (clamp(1.5rem, 1.25rem + 0.75vw, 2rem))
121
+
122
+ **Usage:**
123
+
124
+ ```tsx
125
+ <Cluster gap="xs">Tight spacing</Cluster>
126
+ <Cluster gap="md">Medium spacing</Cluster>
127
+ <Cluster gap="xl">Large spacing</Cluster>
128
+ ```
129
+
130
+ ### `justify`
131
+ **Type:** `"start" | "center" | "end" | "between"`
132
+ **Default:** `undefined` (defaults to `flex-start`)
133
+
134
+ Controls horizontal alignment of items (justify-content).
135
+
136
+ **Values:**
137
+ - `start` → `justify-content: flex-start` (left-aligned)
138
+ - `center` → `justify-content: center` (centered)
139
+ - `end` → `justify-content: flex-end` (right-aligned)
140
+ - `between` → `justify-content: space-between` (distributed)
141
+
142
+ **Usage:**
143
+
144
+ ```tsx
145
+ <Cluster justify="start">Left aligned</Cluster>
146
+ <Cluster justify="center">Centered</Cluster>
147
+ <Cluster justify="end">Right aligned</Cluster>
148
+ <Cluster justify="between">Space between items</Cluster>
149
+ ```
150
+
151
+ ### `align`
152
+ **Type:** `"start" | "center" | "end" | "baseline"`
153
+ **Default:** `undefined` (defaults to `stretch`)
154
+
155
+ Controls vertical alignment of items within each row (align-items).
156
+
157
+ **Values:**
158
+ - `start` → `align-items: flex-start` (top-aligned)
159
+ - `center` → `align-items: center` (vertically centered)
160
+ - `end` → `align-items: flex-end` (bottom-aligned)
161
+ - `baseline` → `align-items: baseline` (text baseline alignment)
162
+
163
+ **Usage:**
164
+
165
+ ```tsx
166
+ <Cluster align="start">Top aligned</Cluster>
167
+ <Cluster align="center">Vertically centered</Cluster>
168
+ <Cluster align="baseline">Baseline aligned (for text)</Cluster>
169
+ ```
170
+
171
+ ### `as`
172
+ **Type:** `ClusterElement` (`"div" | "ul" | "ol" | "nav" | "section"`)
173
+ **Default:** `"div"`
174
+
175
+ Polymorphic prop to render the Cluster as any semantic HTML element.
176
+
177
+ **Usage:**
178
+
179
+ ```tsx
180
+ <Cluster as="div">Default div container</Cluster>
181
+ <Cluster as="ul">Unordered list</Cluster>
182
+ <Cluster as="ol">Ordered list</Cluster>
183
+ <Cluster as="nav">Navigation element</Cluster>
184
+ <Cluster as="section">Section element</Cluster>
185
+ ```
186
+
187
+ ### `className` / `classes`
188
+ **Type:** `string`
189
+ **Default:** `undefined`
190
+
191
+ Additional CSS classes to merge with utility classes.
192
+
193
+ **Usage:**
194
+
195
+ ```tsx
196
+ <Cluster className="custom-class">Content</Cluster>
197
+ <Cluster classes="utility-class">Content</Cluster>
198
+ ```
199
+
200
+ ### `styles` / `style`
201
+ **Type:** `React.CSSProperties`
202
+ **Default:** `undefined`
203
+
204
+ Inline styles for custom styling or CSS custom property overrides.
205
+
206
+ **Usage:**
207
+
208
+ ```tsx
209
+ <Cluster styles={{ maxWidth: '600px' }}>
210
+ Content
211
+ </Cluster>
212
+
213
+ <Cluster styles={{ '--spacing-md': '2rem' } as React.CSSProperties}>
214
+ Custom gap spacing
215
+ </Cluster>
216
+ ```
217
+
218
+ ### `children`
219
+ **Type:** `React.ReactNode`
220
+ **Required:** Yes
221
+
222
+ Content to display in the Cluster.
223
+
224
+ **Usage:**
225
+
226
+ ```tsx
227
+ <Cluster>
228
+ <Tag>Tag 1</Tag>
229
+ <Tag>Tag 2</Tag>
230
+ <Tag>Tag 3</Tag>
231
+ </Cluster>
232
+ ```
233
+
234
+ ### Standard HTML Attributes
235
+
236
+ Cluster accepts all standard HTML attributes including:
237
+ - **ARIA:** `aria-label`, `aria-labelledby`, `aria-describedby`, etc.
238
+ - **Data Attributes:** `data-*` for custom data
239
+ - **Event Handlers:** `onClick`, `onFocus`, etc.
240
+ - **ID:** `id` attribute
241
+
242
+ **Usage:**
243
+
244
+ ```tsx
245
+ <Cluster
246
+ aria-label="Technology tags"
247
+ data-component="tag-cloud"
248
+ id="tags"
249
+ >
250
+ {tags.map(tag => <Tag key={tag.id}>{tag.name}</Tag>)}
251
+ </Cluster>
252
+ ```
253
+
254
+ ## CSS Classes
255
+
256
+ Cluster generates utility classes based on props:
257
+
258
+ ### Base Class
259
+ - `.cluster` - Base flexbox container with `flex-wrap: wrap`
260
+
261
+ ### Gap Classes
262
+ - `.cluster-gap-0` - No gap
263
+ - `.cluster-gap-xs` - Extra small gap
264
+ - `.cluster-gap-sm` - Small gap
265
+ - `.cluster-gap-md` - Medium gap
266
+ - `.cluster-gap-lg` - Large gap
267
+ - `.cluster-gap-xl` - Extra large gap
268
+
269
+ ### Justify Classes
270
+ - `.cluster-justify-start` - Left align
271
+ - `.cluster-justify-center` - Center align
272
+ - `.cluster-justify-end` - Right align
273
+ - `.cluster-justify-between` - Space between
274
+
275
+ ### Align Classes
276
+ - `.cluster-align-start` - Top align
277
+ - `.cluster-align-center` - Center align
278
+ - `.cluster-align-end` - Bottom align
279
+ - `.cluster-align-baseline` - Baseline align
280
+
281
+ ## Composition Patterns
282
+
283
+ ### Nested Layout Primitives
284
+
285
+ Combine Cluster with other layout primitives:
286
+
287
+ ```tsx
288
+ <Stack gap="lg">
289
+ <Title level={2}>Filter by Tags</Title>
290
+ <Cluster gap="sm" justify="center">
291
+ <Tag>React</Tag>
292
+ <Tag>TypeScript</Tag>
293
+ <Tag>CSS</Tag>
294
+ </Cluster>
295
+ </Stack>
296
+ ```
297
+
298
+ ### With Box Container
299
+
300
+ ```tsx
301
+ <Box padding="lg" maxWidth="container">
302
+ <Cluster gap="md" justify="center">
303
+ <Button>Action 1</Button>
304
+ <Button>Action 2</Button>
305
+ <Button>Action 3</Button>
306
+ </Cluster>
307
+ </Box>
308
+ ```
309
+
310
+ ### Responsive Button Group
311
+
312
+ ```tsx
313
+ <Cluster gap="md" styles={{ maxWidth: '600px' }}>
314
+ {actions.map(action => (
315
+ <Button key={action.id} onClick={action.handler}>
316
+ {action.label}
317
+ </Button>
318
+ ))}
319
+ </Cluster>
320
+ ```
321
+
322
+ ## Accessibility
323
+
324
+ ### Semantic Elements
325
+
326
+ Use appropriate semantic elements via the `as` prop:
327
+
328
+ ```tsx
329
+ {/* Navigation */}
330
+ <Cluster as="nav" aria-label="Main navigation">
331
+ <a href="#home">Home</a>
332
+ <a href="#about">About</a>
333
+ </Cluster>
334
+
335
+ {/* List of tags */}
336
+ <Cluster as="ul" aria-label="Article tags">
337
+ <li><Tag>React</Tag></li>
338
+ <li><Tag>CSS</Tag></li>
339
+ </Cluster>
340
+ ```
341
+
342
+ ### ARIA Labels
343
+
344
+ Provide descriptive labels for screen readers:
345
+
346
+ ```tsx
347
+ <Cluster aria-label="Technology stack">
348
+ <Tag>React</Tag>
349
+ <Tag>Node.js</Tag>
350
+ <Tag>PostgreSQL</Tag>
351
+ </Cluster>
352
+ ```
353
+
354
+ ### Keyboard Navigation
355
+
356
+ Ensure interactive items within Cluster are keyboard accessible:
357
+
358
+ ```tsx
359
+ <Cluster gap="sm">
360
+ <button type="button" onClick={handleFilter}>All</button>
361
+ <button type="button" onClick={handleFilter}>Active</button>
362
+ <button type="button" onClick={handleFilter}>Completed</button>
363
+ </Cluster>
364
+ ```
365
+
366
+ ## Comparison with Other Primitives
367
+
368
+ ### Cluster vs Stack
369
+
370
+ | Feature | Cluster | Stack |
371
+ |---------|---------|-------|
372
+ | **Direction** | Horizontal with wrapping | Vertical or horizontal |
373
+ | **Wrapping** | Always wraps (`flex-wrap: wrap`) | Optional (`wrap` prop) |
374
+ | **Use Case** | Inline groups (tags, buttons) | Linear layouts (cards, sections) |
375
+ | **Gap** | Unified spacing scale | Unified spacing scale |
376
+
377
+ **When to use Cluster:**
378
+ - Content needs to wrap naturally
379
+ - Inline groups like tags, badges, buttons
380
+ - Horizontal navigation that wraps on small screens
381
+
382
+ **When to use Stack:**
383
+ - Vertical or horizontal linear layouts
384
+ - Content that should NOT wrap by default
385
+ - Card stacks, form fields, page sections
386
+
387
+ ### Cluster vs Grid
388
+
389
+ | Feature | Cluster | Grid |
390
+ |---------|---------|------|
391
+ | **Layout** | Flexbox with wrapping | CSS Grid |
392
+ | **Columns** | Auto (based on content width) | Explicit column control |
393
+ | **Use Case** | Inline groups | Multi-column layouts |
394
+
395
+ **When to use Cluster:**
396
+ - Content width varies (tags, buttons)
397
+ - Natural wrapping based on container width
398
+ - Simpler, one-dimensional inline layouts
399
+
400
+ **When to use Grid:**
401
+ - Fixed column layouts (2-col, 3-col, etc.)
402
+ - Equal-width items
403
+ - Complex two-dimensional layouts
404
+
405
+ ### Cluster vs Box
406
+
407
+ | Feature | Cluster | Box |
408
+ |---------|---------|-----|
409
+ | **Purpose** | Layout inline groups | Control spacing/sizing |
410
+ | **Gap** | Between items | N/A |
411
+ | **Padding** | N/A | Full padding control |
412
+
413
+ **When to use Cluster:**
414
+ - Grouping inline items with spacing
415
+
416
+ **When to use Box:**
417
+ - Adding padding/margin to containers
418
+ - Controlling width and border radius
419
+
420
+ **Combine both:**
421
+
422
+ ```tsx
423
+ <Box padding="lg" radius="md">
424
+ <Cluster gap="sm" justify="center">
425
+ <Tag>React</Tag>
426
+ <Tag>TypeScript</Tag>
427
+ </Cluster>
428
+ </Box>
429
+ ```
430
+
431
+ ## Design Patterns
432
+
433
+ ### Tag Cloud
434
+
435
+ ```tsx
436
+ <Cluster gap="sm" justify="center">
437
+ {tags.map(tag => (
438
+ <Tag key={tag.id} onRemove={() => handleRemove(tag.id)}>
439
+ {tag.name}
440
+ </Tag>
441
+ ))}
442
+ </Cluster>
443
+ ```
444
+
445
+ ### Action Button Group
446
+
447
+ ```tsx
448
+ <Cluster gap="md" justify="end">
449
+ <Button variant="secondary" onClick={handleCancel}>Cancel</Button>
450
+ <Button variant="primary" onClick={handleSave}>Save</Button>
451
+ </Cluster>
452
+ ```
453
+
454
+ ### Navigation Breadcrumbs
455
+
456
+ ```tsx
457
+ <Cluster as="nav" gap="xs" align="center" aria-label="Breadcrumb">
458
+ <a href="/">Home</a>
459
+ <span>/</span>
460
+ <a href="/products">Products</a>
461
+ <span>/</span>
462
+ <span aria-current="page">Product Name</span>
463
+ </Cluster>
464
+ ```
465
+
466
+ ### Filter Pills with Active State
467
+
468
+ ```tsx
469
+ const [activeFilter, setActiveFilter] = useState('all');
470
+
471
+ <Cluster gap="sm">
472
+ {filters.map(filter => (
473
+ <button
474
+ key={filter.id}
475
+ className={activeFilter === filter.id ? 'pill active' : 'pill'}
476
+ onClick={() => setActiveFilter(filter.id)}
477
+ >
478
+ {filter.label}
479
+ </button>
480
+ ))}
481
+ </Cluster>
482
+ ```
483
+
484
+ ### Badge Cluster
485
+
486
+ ```tsx
487
+ <Cluster gap="xs">
488
+ <Badge variant="success">Active</Badge>
489
+ <Badge variant="info">New</Badge>
490
+ <Badge variant="warning">Beta</Badge>
491
+ </Cluster>
492
+ ```
493
+
494
+ ## Customization
495
+
496
+ ### CSS Custom Properties
497
+
498
+ Override spacing via CSS custom properties:
499
+
500
+ ```tsx
501
+ <Cluster
502
+ gap="md"
503
+ styles={{
504
+ '--spacing-md': '2rem', // Override default medium gap
505
+ } as React.CSSProperties}
506
+ >
507
+ <Tag>React</Tag>
508
+ <Tag>TypeScript</Tag>
509
+ </Cluster>
510
+ ```
511
+
512
+ ### Custom Styling
513
+
514
+ Add custom styles via className:
515
+
516
+ ```tsx
517
+ <Cluster className="custom-cluster" gap="sm">
518
+ <Tag>React</Tag>
519
+ </Cluster>
520
+
521
+ <style>
522
+ .custom-cluster {
523
+ max-width: 600px;
524
+ margin-inline: auto;
525
+ }
526
+ </style>
527
+ ```
528
+
529
+ ### Responsive Behavior
530
+
531
+ Use container queries or media queries for responsive control:
532
+
533
+ ```tsx
534
+ <Cluster
535
+ gap="sm"
536
+ justify="center"
537
+ className="responsive-cluster"
538
+ >
539
+ <Button>Action 1</Button>
540
+ <Button>Action 2</Button>
541
+ </Cluster>
542
+
543
+ <style>
544
+ .responsive-cluster {
545
+ justify-content: flex-start;
546
+ }
547
+
548
+ @media (min-width: 768px) {
549
+ .responsive-cluster {
550
+ justify-content: center;
551
+ }
552
+ }
553
+ </style>
554
+ ```
555
+
556
+ ## Performance
557
+
558
+ - **Zero Runtime:** All layout via CSS classes (no JavaScript calculations)
559
+ - **Fluid Spacing:** CSS clamp() for responsive gaps without media queries
560
+ - **Minimal Footprint:** Lightweight utility classes (~200 bytes gzipped)
561
+ - **No Re-renders:** Static utility classes don't cause re-renders
562
+
563
+ ## Browser Support
564
+
565
+ - **Modern Browsers:** Chrome, Firefox, Safari, Edge (latest versions)
566
+ - **Flexbox:** Fully supported in all modern browsers
567
+ - **CSS clamp():** Supported in Chrome 79+, Firefox 75+, Safari 13.1+
568
+ - **Logical Properties:** Supported in all modern browsers
569
+
570
+ ## Related Components
571
+
572
+ - **Stack:** Vertical/horizontal layouts without wrapping
573
+ - **Box:** Container with padding/margin/sizing control
574
+ - **Grid:** Multi-column layouts with explicit column control
575
+ - **Flex:** Advanced flexbox layouts with all flex properties
576
+
577
+ ## Examples
578
+
579
+ <Canvas of={ClusterStories.Default} />
580
+ <Canvas of={ClusterStories.ButtonGroup} />
581
+ <Canvas of={ClusterStories.CenteredTags} />
582
+ <Canvas of={ClusterStories.Navigation} />
583
+ <Canvas of={ClusterStories.FilterPills} />
584
+
585
+ ## API Reference
586
+
587
+ See the [STYLES.mdx](./STYLES.mdx) documentation for complete CSS custom properties reference.
588
+
589
+ ## Version
590
+
591
+ Available since `@fpkit/acss` v0.5.11
592
+
593
+ ## License
594
+
595
+ MIT