@windforge/ui 0.1.0 → 0.1.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/LICENSE +21 -0
- package/README.md +111 -0
- package/llms.txt +592 -0
- package/package.json +10 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anthony Gorman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @windforge/ui
|
|
2
|
+
|
|
3
|
+
React components for **Windforge** — a composable, configurable, strict design
|
|
4
|
+
system built on **Radix + Tailwind** with CSS-variable tokens and zero CSS-in-JS
|
|
5
|
+
runtime.
|
|
6
|
+
|
|
7
|
+
You build with two surfaces:
|
|
8
|
+
|
|
9
|
+
- **Components** take intent-named props (`variant="primary"`, `padding="card"`,
|
|
10
|
+
`gap="md"`, `tone="muted"`) that expose only on-system options — `className` and
|
|
11
|
+
`style` are rejected at the type level, so there's no path off-system. The
|
|
12
|
+
escape hatch is the three layout primitives `Box` / `Stack` / `Grid`, which
|
|
13
|
+
**do** accept `className` (including Tailwind arbitrary values like `w-[37%]`)
|
|
14
|
+
and `style`. So the rare break-glass case stays contained to three named places.
|
|
15
|
+
- **Tokens** (`--wf-*` CSS variables, from [`@windforge/tokens`](https://www.npmjs.com/package/@windforge/tokens))
|
|
16
|
+
reskin the whole library at once. A token change can't break a layout.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @windforge/ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`@windforge/tokens` comes along as a dependency. `react` / `react-dom` (^18.3)
|
|
25
|
+
are peer dependencies you'll already have in a React app.
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
**1. Wire the Tailwind preset.** It ships the full theme, keyframes, animations,
|
|
30
|
+
and the `tailwindcss-animate` plugin, so there's no config to hand-copy:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// tailwind.config.cjs
|
|
34
|
+
module.exports = {
|
|
35
|
+
presets: [require('@windforge/ui/tailwind')],
|
|
36
|
+
content: [
|
|
37
|
+
'./src/**/*.{ts,tsx}',
|
|
38
|
+
'./node_modules/@windforge/ui/src/**/*.{ts,tsx}', // scan the library's classes
|
|
39
|
+
],
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**2. Load the token CSS and wrap your app** in `ThemeProvider`:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { ThemeProvider, Toaster } from '@windforge/ui'
|
|
47
|
+
import '@windforge/tokens/tokens.css'
|
|
48
|
+
|
|
49
|
+
export function Root() {
|
|
50
|
+
return (
|
|
51
|
+
<ThemeProvider defaultMode="system" persist>
|
|
52
|
+
<App />
|
|
53
|
+
<Toaster />
|
|
54
|
+
</ThemeProvider>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Use it
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { Stack, Card, Text, Button } from '@windforge/ui'
|
|
63
|
+
|
|
64
|
+
export function Example() {
|
|
65
|
+
return (
|
|
66
|
+
<Card padding="card">
|
|
67
|
+
<Stack gap="md">
|
|
68
|
+
<Text>Composed entirely from on-system props — no className.</Text>
|
|
69
|
+
<Button variant="primary">Continue</Button>
|
|
70
|
+
</Stack>
|
|
71
|
+
</Card>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Re-skin the brand at runtime
|
|
77
|
+
|
|
78
|
+
A brand is one **50→950 color ramp**; `themeVars` derives every role-named
|
|
79
|
+
`--wf-color-brand-*` variable from it (WCAG-aligned and mode-aware) and can swap
|
|
80
|
+
the font and radius in the same call. Because it's applied at the document root,
|
|
81
|
+
the swap reaches portaled overlays too:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { ThemeProvider, themeVars, type Ramp } from '@windforge/ui'
|
|
85
|
+
|
|
86
|
+
const brand: Ramp = {
|
|
87
|
+
50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa',
|
|
88
|
+
500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a',
|
|
89
|
+
950: '#172554',
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
<ThemeProvider tokens={(mode) => themeVars({ brand, fontSans: 'Inter, sans-serif' }, mode)}>
|
|
93
|
+
<App />
|
|
94
|
+
</ThemeProvider>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`@windforge/ui` re-exports the entire `@windforge/tokens` surface, so `themeVars`,
|
|
98
|
+
`brandVars`, `brandRamp` (the default indigo ramp), and the `wf*` token constants
|
|
99
|
+
all import from here directly.
|
|
100
|
+
|
|
101
|
+
## What's included
|
|
102
|
+
|
|
103
|
+
Primitives (`Button`, `Card`, `Input`, `Badge`, `Text`/`H1`–`H6`…), Radix-backed
|
|
104
|
+
interactive components (`Select`, `Dialog`, `Tooltip`, `Tabs`, `Popover`,
|
|
105
|
+
`DropdownMenu`, `Command`, `DatePicker`…), data display (`Table`, `DataTable`,
|
|
106
|
+
`Chart`), layout primitives (`Box`, `Stack`, `Grid`) and shells (`AppShell`,
|
|
107
|
+
`SideNav`, `AppBar`). Use `lucide-react` for icons.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
package/llms.txt
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
# Windforge (@windforge/ui)
|
|
2
|
+
|
|
3
|
+
> A composable, configurable, strict React design system built on Radix + Tailwind
|
|
4
|
+
> with CSS-variable tokens and zero CSS-in-JS runtime. You build with two surfaces:
|
|
5
|
+
> intent-named component props, and `--wf-*` design tokens.
|
|
6
|
+
|
|
7
|
+
## Rules
|
|
8
|
+
|
|
9
|
+
- Compose components and set intent-named props (e.g. `variant="primary"`,
|
|
10
|
+
`padding="card"`, `gap="md"`). Props expose only the closed set of on-system
|
|
11
|
+
values listed below — do not invent values.
|
|
12
|
+
- `className` and `style` are rejected at the type level on components. Only the
|
|
13
|
+
layout primitives `Box` / `Stack` / `Grid` accept `className` (incl. Tailwind
|
|
14
|
+
arbitrary values like `w-[37%]`) and `style`, as the escape hatch.
|
|
15
|
+
- Use `Box` / `Stack` / `Grid` for layout and `Text` / `H1`–`H6` for type instead
|
|
16
|
+
of bare `<div>` / `<span>`. Components are fluid; size them via the layout around
|
|
17
|
+
them.
|
|
18
|
+
- Re-skin by overriding `--wf-*` tokens (see `@windforge/tokens`), never by editing
|
|
19
|
+
components. A token change cannot break a layout.
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// tailwind.config.cjs
|
|
25
|
+
module.exports = {
|
|
26
|
+
presets: [require('@windforge/ui/tailwind')],
|
|
27
|
+
content: ['./src/**/*.{ts,tsx}', './node_modules/@windforge/ui/src/**/*.{ts,tsx}'],
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { ThemeProvider } from '@windforge/ui'
|
|
33
|
+
import '@windforge/tokens/tokens.css'
|
|
34
|
+
|
|
35
|
+
<ThemeProvider defaultMode="system" persist><App /></ThemeProvider>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Components
|
|
39
|
+
|
|
40
|
+
### Box
|
|
41
|
+
A padded, optionally-surfaced container — the on-system replacement for a bare <div>.
|
|
42
|
+
|
|
43
|
+
Props:
|
|
44
|
+
- `padding`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
45
|
+
- `paddingX`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
46
|
+
- `paddingY`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
47
|
+
- `background`: `none` | `surface` | `subtle` | `inset` | `inverse` | `brand`
|
|
48
|
+
- `border`: `none` | `default` | `subtle` | `strong`
|
|
49
|
+
- `borderRadius`: `none` | `sm` | `md` | `lg` | `xl` | `2xl` | `full`
|
|
50
|
+
- `boxShadow`: `none` | `sm` | `md` | `lg` | `xl`
|
|
51
|
+
- `maxWidth`: `sm` | `md` | `lg` | `xl` | `prose` | `full`
|
|
52
|
+
|
|
53
|
+
Flags:
|
|
54
|
+
- `asChild`: Render as a child element (polymorphic, replaces MUI component=)
|
|
55
|
+
|
|
56
|
+
### Stack
|
|
57
|
+
A flex Box — the primary way to arrange children in a row or column with on-scale gap.
|
|
58
|
+
|
|
59
|
+
Props:
|
|
60
|
+
- `direction`: `row` | `column`
|
|
61
|
+
- `gap`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
62
|
+
- `align`: `start` | `center` | `end` | `stretch` | `baseline`
|
|
63
|
+
- `justify`: `start` | `center` | `end` | `between` | `around`
|
|
64
|
+
- `padding`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
65
|
+
- `paddingX`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
66
|
+
- `paddingY`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
67
|
+
- `background`: `none` | `surface` | `subtle` | `inset` | `inverse` | `brand`
|
|
68
|
+
- `border`: `none` | `default` | `subtle` | `strong`
|
|
69
|
+
- `borderRadius`: `none` | `sm` | `md` | `lg` | `xl` | `2xl` | `full`
|
|
70
|
+
- `boxShadow`: `none` | `sm` | `md` | `lg` | `xl`
|
|
71
|
+
- `maxWidth`: `sm` | `md` | `lg` | `xl` | `prose` | `full`
|
|
72
|
+
|
|
73
|
+
Flags:
|
|
74
|
+
- `wrap`: flex-wrap when true
|
|
75
|
+
- `asChild`: Render as a child element (polymorphic)
|
|
76
|
+
|
|
77
|
+
### Grid
|
|
78
|
+
A closed-vocabulary CSS grid with responsive column counts and on-scale gap.
|
|
79
|
+
|
|
80
|
+
Props:
|
|
81
|
+
- `cols`: `1` | `2` | `3` | `4` | `5` | `6` | `12`
|
|
82
|
+
- `mdCols`: `1` | `2` | `3` | `4` | `5` | `6` | `12`
|
|
83
|
+
- `gap`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
84
|
+
- `align`: `start` | `center` | `end` | `stretch`
|
|
85
|
+
- `justify`: `start` | `center` | `end` | `stretch`
|
|
86
|
+
|
|
87
|
+
Flags:
|
|
88
|
+
- `asChild`: Render as a child element (polymorphic)
|
|
89
|
+
|
|
90
|
+
### Text
|
|
91
|
+
The typography primitive — renders a <p> by default; use asChild for semantics.
|
|
92
|
+
|
|
93
|
+
Props:
|
|
94
|
+
- `size`: `sm` | `base` | `lg` | `xl` | `2xl` | `3xl` | `4xl` | `5xl` | `6xl`
|
|
95
|
+
- `weight`: `light` | `regular` | `medium` | `semibold` | `bold`
|
|
96
|
+
- `tone`: `default` | `muted` | `subtle` | `disabled` | `inverse` | `brand` | `link` | `gradient`
|
|
97
|
+
- `align`: `left` | `center` | `right`
|
|
98
|
+
- `variant`: `inline-code`
|
|
99
|
+
|
|
100
|
+
Flags:
|
|
101
|
+
- `truncate`: Adds CSS truncation (single line ellipsis)
|
|
102
|
+
- `mono`: Switches to monospace font
|
|
103
|
+
- `span`: Render inline as a <span> instead of a block <p>
|
|
104
|
+
- `asChild`: Render as the wrapped child element for semantic control
|
|
105
|
+
|
|
106
|
+
### H1
|
|
107
|
+
Semantic headings: H1–H6 render the matching <h1>–<h6> with a sensible size default per level; override size freely. (H2…H6 share this API.)
|
|
108
|
+
|
|
109
|
+
Props:
|
|
110
|
+
- `size`: `sm` | `base` | `lg` | `xl` | `2xl` | `3xl` | `4xl` | `5xl` | `6xl`
|
|
111
|
+
- `weight`: `light` | `regular` | `medium` | `semibold` | `bold`
|
|
112
|
+
- `tone`: `default` | `muted` | `subtle` | `disabled` | `inverse` | `brand` | `link`
|
|
113
|
+
- `align`: `left` | `center` | `right`
|
|
114
|
+
|
|
115
|
+
Flags:
|
|
116
|
+
- `truncate`: Adds CSS truncation
|
|
117
|
+
|
|
118
|
+
### Button
|
|
119
|
+
The primary action element. Hierarchy is expressed through variant, never hue.
|
|
120
|
+
|
|
121
|
+
Props:
|
|
122
|
+
- `variant`: `primary` | `secondary` | `tertiary` | `link` | `destructive`
|
|
123
|
+
- `size`: `sm` | `md` | `lg` | `icon`
|
|
124
|
+
|
|
125
|
+
Flags:
|
|
126
|
+
- `asChild`: Render as a child element (e.g. router link)
|
|
127
|
+
|
|
128
|
+
### ButtonGroup
|
|
129
|
+
Joins a row of Buttons into a segmented control with shared edges and a single outer radius.
|
|
130
|
+
|
|
131
|
+
Flags:
|
|
132
|
+
- `children (Button elements)`: Any Button variant; the group collapses the seams
|
|
133
|
+
|
|
134
|
+
### ToggleButton
|
|
135
|
+
A pressable button inside ToggleButtonGroup that tracks selected state.
|
|
136
|
+
|
|
137
|
+
Flags:
|
|
138
|
+
- `value`: String identifier used by ToggleButtonGroup to track selection
|
|
139
|
+
|
|
140
|
+
### ToggleButtonGroup
|
|
141
|
+
Segmented control; type="single" allows one selection, type="multiple" allows many.
|
|
142
|
+
|
|
143
|
+
Props:
|
|
144
|
+
- `type`: `single` | `multiple`
|
|
145
|
+
|
|
146
|
+
### Link
|
|
147
|
+
An inline anchor styled in the link color. Use asChild to wrap a router link.
|
|
148
|
+
|
|
149
|
+
Props:
|
|
150
|
+
- `underline`: `hover` | `always` | `none`
|
|
151
|
+
|
|
152
|
+
Flags:
|
|
153
|
+
- `asChild`: Render as a child element, keeping Link styling
|
|
154
|
+
|
|
155
|
+
### Badge
|
|
156
|
+
A static label pill for status, category, or count. Non-interactive.
|
|
157
|
+
|
|
158
|
+
Props:
|
|
159
|
+
- `variant`: `brand` | `subtle` | `neutral` | `outline` | `success` | `warning` | `error` | `info`
|
|
160
|
+
- `size`: `sm` | `md`
|
|
161
|
+
|
|
162
|
+
### Chip
|
|
163
|
+
An interactive pill for filtering or selection. Distinct from Badge. Pass onDelete for a removable chip.
|
|
164
|
+
|
|
165
|
+
Props:
|
|
166
|
+
- `size`: `sm` | `md`
|
|
167
|
+
|
|
168
|
+
Flags:
|
|
169
|
+
- `selected`: Boolean — shows the neutral selected (inverse) fill when true
|
|
170
|
+
- `clickable`: Set implicitly when onClick is provided
|
|
171
|
+
- `onDelete`: Renders a trailing ✕ dismiss button
|
|
172
|
+
- `disabled`: Dims and disables interaction
|
|
173
|
+
- `icon`: Optional leading ReactNode (e.g. an icon)
|
|
174
|
+
|
|
175
|
+
### Alert
|
|
176
|
+
An inline status message. Icon carries the hue; body text stays neutral.
|
|
177
|
+
|
|
178
|
+
Props:
|
|
179
|
+
- `variant`: `neutral` | `info` | `success` | `warning` | `error`
|
|
180
|
+
|
|
181
|
+
Flags:
|
|
182
|
+
- `title`: Heading (ReactNode)
|
|
183
|
+
- `description`: Body text (ReactNode)
|
|
184
|
+
- `icon`: Leading status icon (a lucide element)
|
|
185
|
+
- `actions`: Trailing actions, typically buttons (ReactNode)
|
|
186
|
+
|
|
187
|
+
### Avatar
|
|
188
|
+
A circular image or fallback initials display. The fallback text scales with size.
|
|
189
|
+
|
|
190
|
+
Props:
|
|
191
|
+
- `size`: `sm` | `md` | `lg` | `xl`
|
|
192
|
+
|
|
193
|
+
Flags:
|
|
194
|
+
- `AvatarImage src`: Image URL; falls back to AvatarFallback when unresolvable
|
|
195
|
+
- `AvatarFallback`: Text shown while image loads or if it fails
|
|
196
|
+
|
|
197
|
+
### Card
|
|
198
|
+
A Box preset with surface background, default border, 2xl radius, and sm shadow. Accepts all Box props except className/style.
|
|
199
|
+
|
|
200
|
+
Props:
|
|
201
|
+
- `padding`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
202
|
+
- `paddingX`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
203
|
+
- `paddingY`: `none` | `xs` | `sm` | `md` | `lg` | `xl` | `2xl` | `nbsp` | `card` | `gutter` | `section` | `page`
|
|
204
|
+
- `background`: `none` | `surface` | `subtle` | `inset` | `inverse` | `brand`
|
|
205
|
+
- `border`: `none` | `default` | `subtle` | `strong`
|
|
206
|
+
- `borderRadius`: `none` | `sm` | `md` | `lg` | `xl` | `2xl` | `full`
|
|
207
|
+
- `boxShadow`: `none` | `sm` | `md` | `lg` | `xl`
|
|
208
|
+
|
|
209
|
+
Flags:
|
|
210
|
+
- `title`: Heading (ReactNode)
|
|
211
|
+
- `description`: Sub-heading body text (ReactNode)
|
|
212
|
+
- `headerAction`: Trailing header content opposite the title, e.g. a Badge (ReactNode)
|
|
213
|
+
- `footer`: Footer content, typically buttons (ReactNode)
|
|
214
|
+
- `children`: Card body content
|
|
215
|
+
|
|
216
|
+
### Input
|
|
217
|
+
A styled text input. Pass standard HTML input props (type, placeholder, disabled, …).
|
|
218
|
+
|
|
219
|
+
Flags:
|
|
220
|
+
- `type`: HTML input type (text, email, password, number, …)
|
|
221
|
+
- `placeholder`: Placeholder text
|
|
222
|
+
- `disabled`: Dims and disables the field
|
|
223
|
+
- `invalid`: Error state — red outline + aria-invalid (usually set by FormField)
|
|
224
|
+
|
|
225
|
+
### Textarea
|
|
226
|
+
A styled multiline text input. Pass standard HTML textarea props.
|
|
227
|
+
|
|
228
|
+
Flags:
|
|
229
|
+
- `placeholder`: Placeholder text
|
|
230
|
+
- `disabled`: Dims and disables the field
|
|
231
|
+
- `rows`: Number of visible text rows
|
|
232
|
+
- `invalid`: Error state — red outline + aria-invalid (usually set by FormField)
|
|
233
|
+
|
|
234
|
+
### Label
|
|
235
|
+
A form label that connects to a field via htmlFor (Radix Label under the hood).
|
|
236
|
+
|
|
237
|
+
### FormField
|
|
238
|
+
Renders a labelled control and wires the label, description, required marker, and error state with correct ids and ARIA. Renders an Input by default; pass a child to wrap Textarea/Select/Autocomplete. The on-system way to build accessible forms.
|
|
239
|
+
|
|
240
|
+
Flags:
|
|
241
|
+
- `label`: Field label (ReactNode); rendered as a <Label> bound to the control
|
|
242
|
+
- `description`: Helper text below the label — normal foreground, meant to be read
|
|
243
|
+
- `placeholder`: Placeholder forwarded onto the control (a prop on the child wins)
|
|
244
|
+
- `type`: Input type for the default control (text, email, password, …)
|
|
245
|
+
- `error`: Error message (ReactNode); its presence sets the control to invalid
|
|
246
|
+
- `required`: Adds a required marker and aria-required
|
|
247
|
+
- `children`: Escape hatch — a non-Input control to wrap; omit to render an Input
|
|
248
|
+
|
|
249
|
+
### Select
|
|
250
|
+
A styled dropdown select. Pass an options array, or compose the primitives for groups/labels/custom triggers.
|
|
251
|
+
|
|
252
|
+
Flags:
|
|
253
|
+
- `options`: Array of { value: string; label: ReactNode; disabled?: boolean } — the props API
|
|
254
|
+
- `placeholder`: Placeholder shown before a value is chosen
|
|
255
|
+
- `value`: Controlled value
|
|
256
|
+
- `onValueChange`: Callback with the selected value
|
|
257
|
+
- `disabled`: Disables the trigger
|
|
258
|
+
- `invalid`: Error state — red trigger outline + aria-invalid (usually set by FormField)
|
|
259
|
+
- `SelectTrigger`: Escape hatch: the visible trigger element
|
|
260
|
+
- `SelectContent`: Escape hatch: the dropdown panel
|
|
261
|
+
- `SelectItem`: Escape hatch: an individual option (value, children)
|
|
262
|
+
- `SelectGroup`: Escape hatch: groups items under a SelectLabel
|
|
263
|
+
- `SelectLabel`: Escape hatch: a non-selectable group heading
|
|
264
|
+
- `SelectValue`: Escape hatch: renders the current value inside SelectTrigger
|
|
265
|
+
|
|
266
|
+
### Autocomplete
|
|
267
|
+
A searchable single-select combobox with keyboard navigation.
|
|
268
|
+
|
|
269
|
+
Flags:
|
|
270
|
+
- `options`: Array of { value: string; label: string }
|
|
271
|
+
- `value`: Controlled current value (string | null)
|
|
272
|
+
- `onValueChange`: Called with the new value or null on clear
|
|
273
|
+
- `placeholder`: Input placeholder text
|
|
274
|
+
- `emptyText`: Message shown when filter yields no results
|
|
275
|
+
- `disabled`: Dims and disables the field
|
|
276
|
+
- `invalid`: Error state — red field outline + aria-invalid (usually set by FormField)
|
|
277
|
+
|
|
278
|
+
### MultiSelect
|
|
279
|
+
A searchable multi-value combobox. Selected values render as removable tags; the dropdown filters as you type. Controlled via value/onValueChange.
|
|
280
|
+
|
|
281
|
+
Flags:
|
|
282
|
+
- `options`: Array of { value: string; label: string }
|
|
283
|
+
- `value`: Controlled selected values (string[])
|
|
284
|
+
- `onValueChange`: Called with the new value array
|
|
285
|
+
- `placeholder`: Field placeholder when nothing is selected
|
|
286
|
+
- `emptyText`: Message shown when the filter yields no results
|
|
287
|
+
- `disabled`: Dims and disables the field
|
|
288
|
+
- `invalid`: Error state — red outline + aria-invalid (usually set by FormField)
|
|
289
|
+
|
|
290
|
+
### DatePicker
|
|
291
|
+
A single-date field: a styled trigger that opens the Calendar in a Popover. Controlled via value/onValueChange. For ranges, use Calendar directly.
|
|
292
|
+
|
|
293
|
+
Flags:
|
|
294
|
+
- `value`: Selected Date (or undefined)
|
|
295
|
+
- `onValueChange`: Called with the chosen Date (or undefined)
|
|
296
|
+
- `placeholder`: Trigger text before a date is chosen
|
|
297
|
+
- `disabled`: Dims and disables the trigger
|
|
298
|
+
- `invalid`: Error state — red outline + aria-invalid (usually set by FormField)
|
|
299
|
+
- `formatOptions`: Intl.DateTimeFormatOptions for the displayed value
|
|
300
|
+
|
|
301
|
+
### Calendar
|
|
302
|
+
The date grid (react-day-picker), token-styled with no vendor CSS. Pass any react-day-picker prop — mode="single|range|multiple", selected, onSelect.
|
|
303
|
+
|
|
304
|
+
Props:
|
|
305
|
+
- `mode`: `single` | `multiple` | `range`
|
|
306
|
+
|
|
307
|
+
Flags:
|
|
308
|
+
- `selected`: Selected date(s) — shape depends on mode
|
|
309
|
+
- `onSelect`: Selection callback — shape depends on mode
|
|
310
|
+
- `showOutsideDays`: Render days from adjacent months (default true)
|
|
311
|
+
|
|
312
|
+
### Command
|
|
313
|
+
A fast, filterable command menu / palette (cmdk). Compose Command > CommandInput + CommandList (CommandEmpty/CommandGroup/CommandItem/CommandSeparator), or use CommandDialog for a ⌘K palette.
|
|
314
|
+
|
|
315
|
+
Flags:
|
|
316
|
+
- `CommandInput`: The search field
|
|
317
|
+
- `CommandList`: Scrollable results region
|
|
318
|
+
- `CommandEmpty`: Shown when nothing matches
|
|
319
|
+
- `CommandGroup`: A labelled group (heading prop)
|
|
320
|
+
- `CommandItem`: A selectable row (onSelect, value)
|
|
321
|
+
- `CommandSeparator`: A divider between groups
|
|
322
|
+
- `CommandShortcut`: Trailing keyboard hint
|
|
323
|
+
- `CommandDialog`: The palette in a centered overlay (open, onOpenChange)
|
|
324
|
+
|
|
325
|
+
### Checkbox
|
|
326
|
+
A binary toggle. Pair with Label for an accessible label.
|
|
327
|
+
|
|
328
|
+
Flags:
|
|
329
|
+
- `checked`: Controlled checked state
|
|
330
|
+
- `onCheckedChange`: Callback with the new boolean
|
|
331
|
+
- `disabled`: Disables the checkbox
|
|
332
|
+
|
|
333
|
+
### RadioGroup
|
|
334
|
+
A group of mutually exclusive RadioGroupItem options.
|
|
335
|
+
|
|
336
|
+
Flags:
|
|
337
|
+
- `value`: Controlled value
|
|
338
|
+
- `onValueChange`: Callback with the selected value
|
|
339
|
+
- `RadioGroupItem value`: The string value for each item; pair with Label
|
|
340
|
+
|
|
341
|
+
### Switch
|
|
342
|
+
A toggle switch (on/off). Pair with Label for an accessible label.
|
|
343
|
+
|
|
344
|
+
Flags:
|
|
345
|
+
- `checked`: Controlled checked state
|
|
346
|
+
- `onCheckedChange`: Callback with the new boolean
|
|
347
|
+
- `disabled`: Disables the switch
|
|
348
|
+
|
|
349
|
+
### Slider
|
|
350
|
+
A range slider for numeric input.
|
|
351
|
+
|
|
352
|
+
Flags:
|
|
353
|
+
- `value`: Controlled value array e.g. [50]
|
|
354
|
+
- `onValueChange`: Callback with the new value array
|
|
355
|
+
- `min`: Minimum value (default 0)
|
|
356
|
+
- `max`: Maximum value (default 100)
|
|
357
|
+
- `step`: Step increment
|
|
358
|
+
- `disabled`: Disables the slider
|
|
359
|
+
|
|
360
|
+
### Tabs
|
|
361
|
+
Tabbed content switcher. Pass an items array, or compose TabsList/TabsTrigger/TabsContent by hand.
|
|
362
|
+
|
|
363
|
+
Flags:
|
|
364
|
+
- `items`: Array of { value, label, content, disabled? } — the props API
|
|
365
|
+
- `defaultValue`: Initially active tab value
|
|
366
|
+
- `value`: Controlled active tab value
|
|
367
|
+
- `onValueChange`: Callback with new tab value
|
|
368
|
+
- `TabsList / TabsTrigger / TabsContent`: Escape hatch primitives for hand-composition
|
|
369
|
+
|
|
370
|
+
### Accordion
|
|
371
|
+
Vertically stacked collapsible sections. Pass an items array, or compose the primitives. type="single" or "multiple".
|
|
372
|
+
|
|
373
|
+
Props:
|
|
374
|
+
- `type`: `single` | `multiple`
|
|
375
|
+
|
|
376
|
+
Flags:
|
|
377
|
+
- `items`: Array of { value, trigger, content, disabled? } — the props API
|
|
378
|
+
- `collapsible`: When type="single", allows deselecting the open item
|
|
379
|
+
- `AccordionItem / AccordionTrigger / AccordionContent`: Escape hatch primitives for hand-composition
|
|
380
|
+
|
|
381
|
+
### Breadcrumb
|
|
382
|
+
Navigation trail. Pass an items array; the last item is the current (non-link) page.
|
|
383
|
+
|
|
384
|
+
Flags:
|
|
385
|
+
- `items`: Array of { label: ReactNode, href?: string }; the final item renders as the current page
|
|
386
|
+
- `separator`: Divider between crumbs; defaults to a ChevronRight (ReactNode)
|
|
387
|
+
|
|
388
|
+
### Pagination
|
|
389
|
+
Controlled page navigation with previous/next buttons, a window of sibling pages, and ellipses.
|
|
390
|
+
|
|
391
|
+
Flags:
|
|
392
|
+
- `page`: 1-based current page number
|
|
393
|
+
- `count`: Total number of pages
|
|
394
|
+
- `onPageChange`: Callback with the new page number
|
|
395
|
+
- `siblingCount`: Number of pages to show on each side of the current (default 1)
|
|
396
|
+
|
|
397
|
+
### Stepper
|
|
398
|
+
Linear progress through a sequence of named steps.
|
|
399
|
+
|
|
400
|
+
Props:
|
|
401
|
+
- `orientation`: `horizontal` | `vertical`
|
|
402
|
+
|
|
403
|
+
Flags:
|
|
404
|
+
- `steps`: Array of { label: string; description?: string }
|
|
405
|
+
- `activeStep`: 0-based index of the current step
|
|
406
|
+
|
|
407
|
+
### Dialog
|
|
408
|
+
A convenience overlay with title, description, and footer actions — built on Modal.
|
|
409
|
+
|
|
410
|
+
Props:
|
|
411
|
+
- `size`: `sm` | `md` | `lg` | `xl`
|
|
412
|
+
|
|
413
|
+
Flags:
|
|
414
|
+
- `trigger`: The element that opens the dialog
|
|
415
|
+
- `title`: Heading text (required)
|
|
416
|
+
- `description`: Body text below the title
|
|
417
|
+
- `actions`: ReactNode for the footer, typically buttons
|
|
418
|
+
- `open`: Controlled open state
|
|
419
|
+
- `onOpenChange`: Callback when open state changes
|
|
420
|
+
|
|
421
|
+
### Modal
|
|
422
|
+
Low-level composable overlay (Radix Dialog). Use Dialog for the common case.
|
|
423
|
+
|
|
424
|
+
Props:
|
|
425
|
+
- `size`: `sm` | `md` | `lg` | `xl`
|
|
426
|
+
|
|
427
|
+
Flags:
|
|
428
|
+
- `ModalTrigger`: The trigger element; use asChild to keep your button
|
|
429
|
+
- `ModalContent`: The panel; size prop sets max-width
|
|
430
|
+
- `ModalClose`: Programmatic close trigger; use asChild
|
|
431
|
+
- `hideClose`: Hide the default corner ✕ on ModalContent
|
|
432
|
+
|
|
433
|
+
### Popover
|
|
434
|
+
A small floating panel anchored to a trigger. Compose: Popover > PopoverTrigger + PopoverContent.
|
|
435
|
+
|
|
436
|
+
Flags:
|
|
437
|
+
- `PopoverTrigger asChild`: Attach the trigger to your own element
|
|
438
|
+
- `PopoverContent align`: Alignment relative to the trigger (start|center|end)
|
|
439
|
+
- `PopoverContent sideOffset`: Distance from the trigger in px (default 6)
|
|
440
|
+
|
|
441
|
+
### Sheet
|
|
442
|
+
A slide-in drawer anchored to a screen edge.
|
|
443
|
+
|
|
444
|
+
Props:
|
|
445
|
+
- `side`: `top` | `bottom` | `left` | `right`
|
|
446
|
+
|
|
447
|
+
Flags:
|
|
448
|
+
- `SheetTrigger asChild`: Attach the trigger to your own element
|
|
449
|
+
- `SheetContent title`: Accessible sheet title (ReactNode)
|
|
450
|
+
- `SheetContent description`: Supporting body text (ReactNode)
|
|
451
|
+
- `SheetContent footer`: Action row pinned to the bottom (ReactNode)
|
|
452
|
+
- `SheetClose asChild`: Programmatic close trigger
|
|
453
|
+
|
|
454
|
+
### DropdownMenu
|
|
455
|
+
A contextual menu anchored to a trigger. Pass trigger + items for the common case, or compose the primitives for checkboxes, radios, and sub-menus.
|
|
456
|
+
|
|
457
|
+
Flags:
|
|
458
|
+
- `trigger`: The element that opens the menu (rendered via asChild)
|
|
459
|
+
- `items`: Array of { label, icon?, shortcut?, onSelect?, disabled? } | { type: "separator" } | { type: "label", label } — the props API
|
|
460
|
+
- `DropdownMenuTrigger asChild`: Escape hatch: use your own element as the trigger
|
|
461
|
+
- `DropdownMenuContent`: Escape hatch: the floating menu panel
|
|
462
|
+
- `DropdownMenuItem`: Escape hatch: a clickable item; inset adds left padding
|
|
463
|
+
- `DropdownMenuCheckboxItem`: Escape hatch: a checkable item
|
|
464
|
+
- `DropdownMenuRadioGroup / DropdownMenuRadioItem`: Escape hatch: radio group + items
|
|
465
|
+
- `DropdownMenuLabel / DropdownMenuSeparator / DropdownMenuShortcut`: Escape hatch: label, divider, shortcut
|
|
466
|
+
- `DropdownMenuSub / DropdownMenuSubTrigger / DropdownMenuSubContent`: Escape hatch: nested sub-menus
|
|
467
|
+
|
|
468
|
+
### Tooltip
|
|
469
|
+
A floating label on hover/focus. Wrap with TooltipProvider at the app root.
|
|
470
|
+
|
|
471
|
+
Flags:
|
|
472
|
+
- `TooltipProvider`: Render once near the app root to share delay
|
|
473
|
+
- `TooltipTrigger asChild`: Attach the tooltip to your own element
|
|
474
|
+
- `TooltipContent`: The tooltip bubble; sideOffset adjusts distance
|
|
475
|
+
|
|
476
|
+
### Progress
|
|
477
|
+
A horizontal progress bar. value is 0–100.
|
|
478
|
+
|
|
479
|
+
Flags:
|
|
480
|
+
- `value`: Number 0–100 representing completion percentage
|
|
481
|
+
- `indeterminate`: Looping animation for work of unknown duration; ignores value
|
|
482
|
+
|
|
483
|
+
### Skeleton
|
|
484
|
+
An animated placeholder while content loads. Size with width/height via className on Box.
|
|
485
|
+
|
|
486
|
+
Flags:
|
|
487
|
+
- `className (via Box wrapper)`: Use Box className to size the skeleton
|
|
488
|
+
|
|
489
|
+
### Toast
|
|
490
|
+
Transient status notifications. Call toast({...}) imperatively; render <Toaster /> once near root.
|
|
491
|
+
|
|
492
|
+
Props:
|
|
493
|
+
- `variant`: `neutral` | `success` | `error` | `warning` | `info`
|
|
494
|
+
|
|
495
|
+
Flags:
|
|
496
|
+
- `title`: Heading text
|
|
497
|
+
- `description`: Body text
|
|
498
|
+
- `duration`: Auto-dismiss time in ms (default 5000; 0 = permanent)
|
|
499
|
+
- `action`: ReactNode action slot inside the toast
|
|
500
|
+
- `Toaster`: Place once near the app root to render active toasts
|
|
501
|
+
|
|
502
|
+
### Table
|
|
503
|
+
A semantic table. Pass columns + data for the common case, or compose the primitives for full control.
|
|
504
|
+
|
|
505
|
+
Flags:
|
|
506
|
+
- `columns`: Array of { header, accessor: key | (row) => ReactNode, align? } — the props API
|
|
507
|
+
- `data`: Array of row objects rendered against columns
|
|
508
|
+
- `caption`: Caption rendered below the table (ReactNode)
|
|
509
|
+
- `TableHeader / TableBody / TableFooter`: Escape hatch: thead / tbody / tfoot wrappers
|
|
510
|
+
- `TableRow`: Escape hatch: tr with hover and selected states
|
|
511
|
+
- `TableHead / TableCell`: Escape hatch: th / td; both take align="left|center|right"
|
|
512
|
+
- `TableCaption`: Escape hatch: caption rendered below the table
|
|
513
|
+
|
|
514
|
+
### DataTable
|
|
515
|
+
The batteries-included table: column sorting, row selection, and optional client-side pagination, built on the Table primitives. Pass columns + data + rowKey.
|
|
516
|
+
|
|
517
|
+
Flags:
|
|
518
|
+
- `columns`: Array of { header, accessor, align?, sortable?, sortAccessor? }
|
|
519
|
+
- `data`: Array of row objects
|
|
520
|
+
- `rowKey`: (row) => string — stable id for selection and keys (required)
|
|
521
|
+
- `selectable`: Adds a select-all + per-row checkbox column
|
|
522
|
+
- `selected`: Controlled selection (array of ids); omit for uncontrolled
|
|
523
|
+
- `onSelectedChange`: Callback with the new selected id array
|
|
524
|
+
- `pageSize`: Enable client-side pagination at this page size
|
|
525
|
+
- `caption`: Caption rendered below the table
|
|
526
|
+
- `emptyState`: Content shown when there are no rows
|
|
527
|
+
|
|
528
|
+
### Chart
|
|
529
|
+
A token-driven ECharts surface. Pass a standard ECharts option; palette, axes, tooltip, and fonts are themed from the live --wf-* tokens and re-skin with the brand and color mode automatically. Auto-resizes.
|
|
530
|
+
|
|
531
|
+
Flags:
|
|
532
|
+
- `option`: A standard ECharts option object (xAxis/yAxis/series/…)
|
|
533
|
+
- `height`: Pixel or CSS height; width fills the container (default 320)
|
|
534
|
+
- `notMerge`: Replace the previous option wholesale instead of merging (default true)
|
|
535
|
+
- `onEvents`: Map of ECharts event name → handler
|
|
536
|
+
|
|
537
|
+
### Separator
|
|
538
|
+
A horizontal or vertical visual divider.
|
|
539
|
+
|
|
540
|
+
Props:
|
|
541
|
+
- `orientation`: `horizontal` | `vertical`
|
|
542
|
+
|
|
543
|
+
### CodeBlock
|
|
544
|
+
A syntax-highlighted code surface. Highlighting runs locally (prism-react-renderer, no API); the theme is built from --wf-* tokens, so it re-skins with mode and brand automatically.
|
|
545
|
+
|
|
546
|
+
Flags:
|
|
547
|
+
- `code`: The source string to render
|
|
548
|
+
- `language`: Prism language id (default 'tsx')
|
|
549
|
+
- `filename`: Optional header filename, shown with the language label
|
|
550
|
+
- `showLineNumbers`: Render a 1-based line-number gutter
|
|
551
|
+
- `highlightLines`: Array of 1-based line numbers to emphasize
|
|
552
|
+
- `wrap`: Soft-wrap long lines instead of scrolling
|
|
553
|
+
- `maxHeight`: Max height before vertical scroll, e.g. '24rem'
|
|
554
|
+
- `copyable`: Show the copy button (default true)
|
|
555
|
+
|
|
556
|
+
### AppShell
|
|
557
|
+
The guaranteed-layout frame: fixed sidebar on desktop, Sheet drawer on mobile, sticky header, scrollable main. Pass header and sidebar as slots.
|
|
558
|
+
|
|
559
|
+
Flags:
|
|
560
|
+
- `header`: Top-bar slot, typically an <AppBar>
|
|
561
|
+
- `sidebar`: Side-navigation slot, typically a <SideNav>
|
|
562
|
+
- `fullBleed`: Drop the default main padding for edge-to-edge content
|
|
563
|
+
|
|
564
|
+
### AppBar
|
|
565
|
+
The sticky top bar slot for AppShell: menu toggle, logo, title, and actions. Drop a <ModeToggle/> into actions for the light/dark/system switch.
|
|
566
|
+
|
|
567
|
+
Props:
|
|
568
|
+
- `variant`: `glass` | `solid`
|
|
569
|
+
- `color`: `surface` | `subtle` | `inset`
|
|
570
|
+
|
|
571
|
+
Flags:
|
|
572
|
+
- `title`: Optional title text
|
|
573
|
+
- `logo`: Optional logo ReactNode
|
|
574
|
+
- `actions`: Right-aligned action ReactNode(s) — e.g. a <ModeToggle/>
|
|
575
|
+
- `onMenuClick`: Override the menu-button handler (defaults to toggling the nav)
|
|
576
|
+
|
|
577
|
+
### SideNav
|
|
578
|
+
The data-driven side navigation: pass a typed items tree (item | group | section | divider); renders active state, nesting, and mobile drawer integration.
|
|
579
|
+
|
|
580
|
+
Flags:
|
|
581
|
+
- `items`: NavItem[] — the navigation tree (item/group/section/divider)
|
|
582
|
+
- `activePath`: The currently active path, highlighted in the tree
|
|
583
|
+
- `onNavigate`: Called with a path when a leaf is activated
|
|
584
|
+
- `logo`: Optional logo ReactNode pinned to the nav header
|
|
585
|
+
|
|
586
|
+
### ThemeProvider
|
|
587
|
+
Provides color mode (light/dark/system) and optional runtime --wf-* token overrides for live re-skinning. Wrap the app once near the root.
|
|
588
|
+
|
|
589
|
+
Flags:
|
|
590
|
+
- `defaultMode`: Initial color mode: light | dark | system (default system)
|
|
591
|
+
- `tokens`: A --wf-* override map, or (mode) => map for mode-aware brand swaps
|
|
592
|
+
- `persist`: Persist the chosen mode to localStorage (default true)
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windforge/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"sideEffects": false,
|
|
6
7
|
"main": "./dist/index.js",
|
|
@@ -9,22 +10,26 @@
|
|
|
9
10
|
"files": [
|
|
10
11
|
"dist",
|
|
11
12
|
"src",
|
|
12
|
-
"tailwind-preset.cjs"
|
|
13
|
+
"tailwind-preset.cjs",
|
|
14
|
+
"llms.txt"
|
|
13
15
|
],
|
|
14
16
|
"publishConfig": {
|
|
15
17
|
"access": "public"
|
|
16
18
|
},
|
|
17
19
|
"scripts": {
|
|
18
20
|
"check:catalog": "tsx scripts/check-catalog.ts",
|
|
21
|
+
"generate:llms": "tsx scripts/generate-llms.ts",
|
|
22
|
+
"test": "tsx --test scripts/*.test.ts",
|
|
19
23
|
"build": "tsup",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
24
|
+
"prepublishOnly": "npm run generate:llms && npm run build"
|
|
21
25
|
},
|
|
22
26
|
"exports": {
|
|
23
27
|
".": {
|
|
24
28
|
"types": "./dist/index.d.ts",
|
|
25
29
|
"import": "./dist/index.js"
|
|
26
30
|
},
|
|
27
|
-
"./tailwind": "./tailwind-preset.cjs"
|
|
31
|
+
"./tailwind": "./tailwind-preset.cjs",
|
|
32
|
+
"./package.json": "./package.json"
|
|
28
33
|
},
|
|
29
34
|
"dependencies": {
|
|
30
35
|
"@radix-ui/react-accordion": "^1.2.2",
|
|
@@ -43,7 +48,7 @@
|
|
|
43
48
|
"@radix-ui/react-switch": "^1.1.2",
|
|
44
49
|
"@radix-ui/react-tabs": "^1.1.2",
|
|
45
50
|
"@radix-ui/react-tooltip": "^1.1.6",
|
|
46
|
-
"@windforge/tokens": "^0.1.
|
|
51
|
+
"@windforge/tokens": "^0.1.1",
|
|
47
52
|
"class-variance-authority": "^0.7.1",
|
|
48
53
|
"clsx": "^2.1.1",
|
|
49
54
|
"cmdk": "^1.0.4",
|