@fpkit/acss 3.1.1 → 3.2.1
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-2NRIP6RB.cjs → chunk-2C3YLBWP.cjs} +3 -3
- package/libs/{chunk-NWJDAHP6.cjs → chunk-2GJHKWEK.cjs} +3 -3
- package/libs/{chunk-FVROL3V5.js → chunk-2JCDEC32.js} +3 -3
- package/libs/{chunk-IRLFZ3OL.js → chunk-3XJC4XUG.js} +2 -2
- package/libs/{chunk-L6PRDL6F.cjs → chunk-5CJPTDK3.cjs} +3 -3
- package/libs/{chunk-E4OSROCA.cjs → chunk-5QSNJQVH.cjs} +3 -3
- package/libs/{chunk-O3JIHC5M.cjs → chunk-6BUJZ4DJ.cjs} +3 -3
- package/libs/{chunk-WXBFBWYF.cjs → chunk-AFINOD2L.cjs} +3 -3
- package/libs/{chunk-HRRHPLER.js → chunk-AWZLSWDO.js} +2 -2
- package/libs/chunk-DDSXKOUB.js +7 -0
- package/libs/chunk-DDSXKOUB.js.map +1 -0
- package/libs/{chunk-CWRNJA4P.js → chunk-DIJBIOFE.js} +3 -3
- package/libs/chunk-EJ6KYBFE.cjs +13 -0
- package/libs/chunk-EJ6KYBFE.cjs.map +1 -0
- package/libs/{chunk-GUJSMQ3V.cjs → chunk-EKJYOCLY.cjs} +3 -3
- package/libs/{chunk-X5RKCLDC.cjs → chunk-F64GE6RG.cjs} +4 -4
- package/libs/{chunk-5RAWNUVD.js → chunk-IBUTNPTQ.js} +2 -2
- package/libs/{chunk-ZFJ4U45S.js → chunk-KDMX3FAW.js} +2 -2
- package/libs/{chunk-DYFAUAB7.cjs → chunk-LXODKKA3.cjs} +4 -4
- package/libs/{chunk-MPTMPBFT.js → chunk-M7JLT62Q.js} +2 -2
- package/libs/{chunk-IQ76HGVP.js → chunk-MBWI67UT.js} +2 -2
- package/libs/{chunk-O5XAJ7BY.cjs → chunk-NCGVF2QS.cjs} +4 -4
- package/libs/{chunk-W2UIN7EV.cjs → chunk-NPWHQVYB.cjs} +3 -3
- package/libs/chunk-OU52NIKA.js +8 -0
- package/libs/chunk-OU52NIKA.js.map +1 -0
- package/libs/{chunk-43TK2ICH.js → chunk-PMWL5XZ4.js} +3 -3
- package/libs/{chunk-KVKQLRJG.js → chunk-TF3GQKOY.js} +2 -2
- package/libs/{chunk-IEB64SWY.js → chunk-U5VA34SU.js} +2 -2
- package/libs/{chunk-EE3ZWSBY.cjs → chunk-URBGDUFN.cjs} +6 -6
- package/libs/chunk-WWPLBWCQ.cjs +18 -0
- package/libs/chunk-WWPLBWCQ.cjs.map +1 -0
- package/libs/{chunk-TPIB3RQP.js → chunk-ZF6Y7W57.js} +5 -5
- package/libs/component-props-50e69975.d.ts +66 -0
- package/libs/components/box/box.css +1 -0
- package/libs/components/box/box.css.map +1 -0
- package/libs/components/box/box.min.css +3 -0
- package/libs/components/breadcrumbs/breadcrumb.cjs +6 -6
- package/libs/components/breadcrumbs/breadcrumb.js +3 -3
- package/libs/components/button.cjs +4 -4
- package/libs/components/button.d.cts +10 -3
- package/libs/components/button.d.ts +10 -3
- package/libs/components/button.js +2 -2
- package/libs/components/card.cjs +7 -7
- package/libs/components/card.d.cts +13 -85
- package/libs/components/card.d.ts +13 -85
- package/libs/components/card.js +2 -2
- package/libs/components/cards/card.css +1 -1
- package/libs/components/cards/card.css.map +1 -1
- package/libs/components/cards/card.min.css +2 -2
- package/libs/components/cluster/cluster.css +1 -0
- package/libs/components/cluster/cluster.css.map +1 -0
- package/libs/components/cluster/cluster.min.css +3 -0
- package/libs/components/dialog/dialog.cjs +7 -7
- package/libs/components/dialog/dialog.js +5 -5
- package/libs/components/form/fields.cjs +4 -4
- package/libs/components/form/fields.js +2 -2
- package/libs/components/form/textarea.cjs +4 -4
- package/libs/components/form/textarea.js +2 -2
- package/libs/components/grid/grid.css +1 -0
- package/libs/components/grid/grid.css.map +1 -0
- package/libs/components/grid/grid.min.css +3 -0
- package/libs/components/heading/heading.cjs +3 -3
- package/libs/components/heading/heading.js +2 -2
- package/libs/components/icons/icon.cjs +4 -4
- package/libs/components/icons/icon.d.cts +2 -2
- package/libs/components/icons/icon.d.ts +2 -2
- package/libs/components/icons/icon.js +2 -2
- package/libs/components/link/link.cjs +6 -6
- package/libs/components/link/link.js +2 -2
- package/libs/components/list/list.cjs +5 -5
- package/libs/components/list/list.js +2 -2
- package/libs/components/modal.cjs +4 -4
- package/libs/components/modal.d.cts +1 -1
- package/libs/components/modal.d.ts +1 -1
- package/libs/components/modal.js +3 -3
- package/libs/components/nav/nav.cjs +7 -7
- package/libs/components/nav/nav.js +3 -3
- package/libs/components/stack/stack.css +1 -0
- package/libs/components/stack/stack.css.map +1 -0
- package/libs/components/stack/stack.min.css +3 -0
- package/libs/components/tables/table.d.cts +1 -1
- package/libs/components/tables/table.d.ts +1 -1
- package/libs/components/text/text.cjs +5 -5
- package/libs/components/text/text.js +2 -2
- package/libs/hooks.cjs +4 -4
- package/libs/hooks.js +3 -3
- package/libs/{icons-287fce3a.d.ts → icons-df8e744f.d.ts} +1 -1
- 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 +64 -63
- 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 +923 -4
- package/libs/index.d.ts +923 -4
- package/libs/index.js +28 -28
- package/libs/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/alert/STYLES.mdx +790 -0
- package/src/components/badge/STYLES.mdx +610 -0
- package/src/components/box/README.mdx +401 -0
- package/src/components/box/STYLES.mdx +360 -0
- package/src/components/box/box.scss +245 -0
- package/src/components/box/box.stories.tsx +395 -0
- package/src/components/box/box.test.tsx +425 -0
- package/src/components/box/box.tsx +170 -0
- package/src/components/box/box.types.ts +166 -0
- package/src/components/breadcrumbs/STYLES.mdx +99 -0
- package/src/components/buttons/STYLES.mdx +766 -0
- package/src/components/cards/STYLES.mdx +835 -0
- package/src/components/cards/card.scss +30 -21
- package/src/components/cards/card.stories.tsx +120 -80
- package/src/components/cards/card.tsx +14 -4
- package/src/components/cards/card.types.ts +13 -0
- package/src/components/cluster/README.mdx +595 -0
- package/src/components/cluster/STYLES.mdx +626 -0
- package/src/components/cluster/cluster.scss +86 -0
- package/src/components/cluster/cluster.stories.tsx +385 -0
- package/src/components/cluster/cluster.test.tsx +655 -0
- package/src/components/cluster/cluster.tsx +94 -0
- package/src/components/cluster/cluster.types.ts +75 -0
- package/src/components/details/STYLES.mdx +445 -0
- package/src/components/dialog/STYLES.mdx +888 -0
- package/src/components/flexbox/STYLES.mdx +1 -1
- package/src/components/form/STYLES.mdx +821 -0
- package/src/components/grid/README.mdx +709 -0
- package/src/components/grid/STYLES.mdx +785 -0
- package/src/components/grid/grid.scss +287 -0
- package/src/components/grid/grid.stories.tsx +486 -0
- package/src/components/grid/grid.test.tsx +981 -0
- package/src/components/grid/grid.tsx +222 -0
- package/src/components/grid/grid.types.ts +344 -0
- package/src/components/icons/STYLES.mdx +56 -0
- package/src/components/images/STYLES.mdx +75 -0
- package/src/components/layout/STYLES.mdx +556 -0
- package/src/components/link/STYLES.mdx +75 -0
- package/src/components/list/STYLES.mdx +631 -0
- package/src/components/nav/STYLES.mdx +460 -0
- package/src/components/progress/STYLES.mdx +64 -0
- package/src/components/stack/README.mdx +400 -0
- package/src/components/stack/STYLES.mdx +414 -0
- package/src/components/stack/stack.scss +109 -0
- package/src/components/stack/stack.stories.tsx +559 -0
- package/src/components/stack/stack.test.tsx +426 -0
- package/src/components/stack/stack.tsx +141 -0
- package/src/components/stack/stack.types.ts +133 -0
- package/src/components/tag/STYLES.mdx +105 -0
- package/src/components/text-to-speech/STYLES.mdx +80 -0
- package/src/components/ui.tsx +3 -3
- package/src/index.scss +7 -2
- package/src/index.ts +305 -12
- package/src/sass/GLOBALS-STYLES.md +631 -0
- package/src/sass/_globals.scss +45 -24
- package/src/sass/_styles.scss +2 -2
- package/src/styles/box/box.css +220 -0
- package/src/styles/box/box.css.map +1 -0
- package/src/styles/cards/card.css +23 -17
- package/src/styles/cards/card.css.map +1 -1
- package/src/styles/cluster/cluster.css +71 -0
- package/src/styles/cluster/cluster.css.map +1 -0
- package/src/styles/grid/grid.css +238 -0
- package/src/styles/grid/grid.css.map +1 -0
- package/src/styles/index.css +668 -49
- package/src/styles/index.css.map +1 -1
- package/src/styles/stack/stack.css +86 -0
- package/src/styles/stack/stack.css.map +1 -0
- package/src/types/component-props.ts +42 -14
- package/src/types/layout-primitives.ts +48 -0
- package/src/types/shared.ts +10 -26
- package/libs/chunk-ENTCUJ3A.cjs +0 -13
- package/libs/chunk-ENTCUJ3A.cjs.map +0 -1
- package/libs/chunk-HHLNOC5T.js +0 -7
- package/libs/chunk-HHLNOC5T.js.map +0 -1
- package/libs/chunk-KK47SYZI.js +0 -8
- package/libs/chunk-KK47SYZI.js.map +0 -1
- package/libs/chunk-W5TKWBFC.cjs +0 -18
- package/libs/chunk-W5TKWBFC.cjs.map +0 -1
- package/libs/component-props-67d978a2.d.ts +0 -38
- /package/libs/{chunk-2NRIP6RB.cjs.map → chunk-2C3YLBWP.cjs.map} +0 -0
- /package/libs/{chunk-NWJDAHP6.cjs.map → chunk-2GJHKWEK.cjs.map} +0 -0
- /package/libs/{chunk-FVROL3V5.js.map → chunk-2JCDEC32.js.map} +0 -0
- /package/libs/{chunk-IRLFZ3OL.js.map → chunk-3XJC4XUG.js.map} +0 -0
- /package/libs/{chunk-L6PRDL6F.cjs.map → chunk-5CJPTDK3.cjs.map} +0 -0
- /package/libs/{chunk-E4OSROCA.cjs.map → chunk-5QSNJQVH.cjs.map} +0 -0
- /package/libs/{chunk-O3JIHC5M.cjs.map → chunk-6BUJZ4DJ.cjs.map} +0 -0
- /package/libs/{chunk-WXBFBWYF.cjs.map → chunk-AFINOD2L.cjs.map} +0 -0
- /package/libs/{chunk-HRRHPLER.js.map → chunk-AWZLSWDO.js.map} +0 -0
- /package/libs/{chunk-CWRNJA4P.js.map → chunk-DIJBIOFE.js.map} +0 -0
- /package/libs/{chunk-GUJSMQ3V.cjs.map → chunk-EKJYOCLY.cjs.map} +0 -0
- /package/libs/{chunk-X5RKCLDC.cjs.map → chunk-F64GE6RG.cjs.map} +0 -0
- /package/libs/{chunk-5RAWNUVD.js.map → chunk-IBUTNPTQ.js.map} +0 -0
- /package/libs/{chunk-ZFJ4U45S.js.map → chunk-KDMX3FAW.js.map} +0 -0
- /package/libs/{chunk-DYFAUAB7.cjs.map → chunk-LXODKKA3.cjs.map} +0 -0
- /package/libs/{chunk-MPTMPBFT.js.map → chunk-M7JLT62Q.js.map} +0 -0
- /package/libs/{chunk-IQ76HGVP.js.map → chunk-MBWI67UT.js.map} +0 -0
- /package/libs/{chunk-O5XAJ7BY.cjs.map → chunk-NCGVF2QS.cjs.map} +0 -0
- /package/libs/{chunk-W2UIN7EV.cjs.map → chunk-NPWHQVYB.cjs.map} +0 -0
- /package/libs/{chunk-43TK2ICH.js.map → chunk-PMWL5XZ4.js.map} +0 -0
- /package/libs/{chunk-KVKQLRJG.js.map → chunk-TF3GQKOY.js.map} +0 -0
- /package/libs/{chunk-IEB64SWY.js.map → chunk-U5VA34SU.js.map} +0 -0
- /package/libs/{chunk-EE3ZWSBY.cjs.map → chunk-URBGDUFN.cjs.map} +0 -0
- /package/libs/{chunk-TPIB3RQP.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
|