@matteoaliano/forest-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FOREST_AI_GUIDELINES.md +277 -0
- package/bin/sync.mjs +91 -0
- package/dist/chunk-6MRKOL3S.mjs +1302 -0
- package/dist/chunk-6MRKOL3S.mjs.map +1 -0
- package/dist/index-DjsgXYWG.d.mts +234 -0
- package/dist/index-DjsgXYWG.d.ts +234 -0
- package/dist/index.d.mts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +1592 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +192 -0
- package/dist/index.mjs.map +1 -0
- package/dist/theme.d.mts +4 -0
- package/dist/theme.d.ts +4 -0
- package/dist/theme.js +1320 -0
- package/dist/theme.js.map +1 -0
- package/dist/theme.mjs +3 -0
- package/dist/theme.mjs.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Forest UI — AI Development Guidelines
|
|
2
|
+
|
|
3
|
+
> **This file is auto-generated from the `forest-ui` package.**
|
|
4
|
+
> Run `npx forest-ui sync` to update it.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Golden Rules
|
|
9
|
+
|
|
10
|
+
1. **NEVER import from `@mui/material` directly.** Always import from `forest-ui`.
|
|
11
|
+
2. **ALWAYS wrap your app root with `<ForestProvider>`** — it applies the theme and CSS baseline.
|
|
12
|
+
3. **NEVER use inline colors or spacing values.** Use the exported design tokens or MUI's `sx` prop with theme values.
|
|
13
|
+
4. **NEVER create custom component wrappers** for things Forest UI already provides.
|
|
14
|
+
5. **TypeScript is required.** All components export their prop types.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Installation & Setup
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @matteoaliano/forest-ui @mui/material @emotion/react @emotion/styled
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
// App root — REQUIRED
|
|
26
|
+
import { ForestProvider } from "@matteoaliano/forest-ui";
|
|
27
|
+
|
|
28
|
+
function App() {
|
|
29
|
+
return (
|
|
30
|
+
<ForestProvider>
|
|
31
|
+
{/* All app content here */}
|
|
32
|
+
</ForestProvider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Component Reference
|
|
40
|
+
|
|
41
|
+
### Import Pattern
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
// ✅ CORRECT — import from forest-ui
|
|
45
|
+
import { Button, TextField, Alert } from "@matteoaliano/forest-ui";
|
|
46
|
+
|
|
47
|
+
// ❌ WRONG — never import from @mui/material
|
|
48
|
+
import Button from "@mui/material/Button";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Available Components
|
|
52
|
+
|
|
53
|
+
#### Inputs
|
|
54
|
+
| Component | Import | Key Props |
|
|
55
|
+
|-----------|--------|-----------|
|
|
56
|
+
| `Button` | `import { Button } from "@matteoaliano/forest-ui"` | `variant: "contained" \| "outlined" \| "text"`, `size`, `color`, `disabled` |
|
|
57
|
+
| `ButtonGroup` | `import { ButtonGroup } from "@matteoaliano/forest-ui"` | `variant`, `size`, `orientation` |
|
|
58
|
+
| `TextField` | `import { TextField } from "@matteoaliano/forest-ui"` | `variant`, `size`, `label`, `error`, `helperText`, `fullWidth` |
|
|
59
|
+
| `Select` | `import { Select, MenuItem } from "@matteoaliano/forest-ui"` | `label`, `value`, `onChange` |
|
|
60
|
+
| `Checkbox` | `import { Checkbox } from "@matteoaliano/forest-ui"` | `checked`, `onChange`, `disabled` |
|
|
61
|
+
| `RadioGroup` + `Radio` | `import { RadioGroup, Radio } from "@matteoaliano/forest-ui"` | `value`, `onChange`, `row` |
|
|
62
|
+
| `Switch` | `import { Switch } from "@matteoaliano/forest-ui"` | `checked`, `onChange`, `disabled` |
|
|
63
|
+
| `ToggleButton` + `ToggleButtonGroup` | `import { ToggleButton, ToggleButtonGroup } from "@matteoaliano/forest-ui"` | `value`, `exclusive`, `onChange` |
|
|
64
|
+
| `Fab` | `import { Fab } from "@matteoaliano/forest-ui"` | `variant: "circular" \| "extended"`, `size`, `color` |
|
|
65
|
+
| `Autocomplete` | `import { Autocomplete } from "@matteoaliano/forest-ui"` | `options`, `renderInput`, `freeSolo` |
|
|
66
|
+
|
|
67
|
+
#### Data Display
|
|
68
|
+
| Component | Import | Key Props |
|
|
69
|
+
|-----------|--------|-----------|
|
|
70
|
+
| `Badge` | `import { Badge } from "@matteoaliano/forest-ui"` | `badgeContent`, `color`, `variant: "standard" \| "dot"` |
|
|
71
|
+
| `Chip` | `import { Chip } from "@matteoaliano/forest-ui"` | `label`, `variant: "filled" \| "outlined"`, `onDelete`, `size` |
|
|
72
|
+
| `Divider` | `import { Divider } from "@matteoaliano/forest-ui"` | `orientation`, `variant`, `children` (for text dividers) |
|
|
73
|
+
| `Typography` | `import { Typography } from "@matteoaliano/forest-ui"` | `variant: "h1"-"h6" \| "subtitle1" \| "body1" \| "caption" \| "overline"` |
|
|
74
|
+
| `Tooltip` | `import { Tooltip } from "@matteoaliano/forest-ui"` | `title`, `placement`, `arrow` |
|
|
75
|
+
| `Table` family | `import { Table, TableHead, TableBody, TableRow, TableCell, TableContainer } from "@matteoaliano/forest-ui"` | Standard table composition |
|
|
76
|
+
| `List` family | `import { List, ListItem, ListItemButton, ListItemIcon, ListItemText, ListItemAvatar, ListSubheader } from "@matteoaliano/forest-ui"` | Standard list composition |
|
|
77
|
+
|
|
78
|
+
#### Feedback
|
|
79
|
+
| Component | Import | Key Props |
|
|
80
|
+
|-----------|--------|-----------|
|
|
81
|
+
| `Alert` + `AlertTitle` | `import { Alert, AlertTitle } from "@matteoaliano/forest-ui"` | `severity: "success" \| "info" \| "warning" \| "error"`, `variant` |
|
|
82
|
+
| `Dialog` family | `import { Dialog, DialogTitle, DialogContent, DialogActions, DialogContentText } from "@matteoaliano/forest-ui"` | `open`, `onClose` |
|
|
83
|
+
| `Backdrop` | `import { Backdrop } from "@matteoaliano/forest-ui"` | `open`, `onClick` |
|
|
84
|
+
| `LinearProgress` | `import { LinearProgress } from "@matteoaliano/forest-ui"` | `variant: "indeterminate" \| "determinate"`, `value` |
|
|
85
|
+
| `CircularProgress` | `import { CircularProgress } from "@matteoaliano/forest-ui"` | `variant: "indeterminate" \| "determinate"`, `value` |
|
|
86
|
+
| `Skeleton` | `import { Skeleton } from "@matteoaliano/forest-ui"` | `variant: "text" \| "circular" \| "rectangular" \| "rounded"`, `width`, `height` |
|
|
87
|
+
| `Snackbar` | `import { Snackbar } from "@matteoaliano/forest-ui"` | `open`, `autoHideDuration`, `onClose`, `message` |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4. Theme & Design Tokens
|
|
92
|
+
|
|
93
|
+
### Accessing Tokens
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// Method 1: Via the sx prop (preferred)
|
|
97
|
+
<Box sx={{ p: 2, borderRadius: 2, color: "text.primary" }} />
|
|
98
|
+
|
|
99
|
+
// Method 2: Import raw token values (rare, for edge cases)
|
|
100
|
+
import { colors, spacing, radius } from "@matteoaliano/forest-ui";
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Color Palette
|
|
104
|
+
|
|
105
|
+
| Token | Value | Usage |
|
|
106
|
+
|-------|-------|-------|
|
|
107
|
+
| `primary.main` | `#7f56d9` (brand.600) | Primary actions, links, active states |
|
|
108
|
+
| `primary.light` | `#9e77ed` (brand.500) | Hover states |
|
|
109
|
+
| `primary.dark` | `#6941c6` (brand.700) | Pressed states |
|
|
110
|
+
| `secondary.main` | `#475467` (gray.600) | Secondary actions |
|
|
111
|
+
| `error.main` | `#d92d20` | Destructive actions, error states |
|
|
112
|
+
| `warning.main` | `#dc6803` | Warnings |
|
|
113
|
+
| `success.main` | `#079455` | Success states |
|
|
114
|
+
| `info.main` | `#0ba5ec` | Informational |
|
|
115
|
+
| `text.primary` | `#101828` (gray.900) | Main text |
|
|
116
|
+
| `text.secondary` | `#475467` (gray.600) | Supporting text |
|
|
117
|
+
| `background.default` | `#ffffff` | Page background |
|
|
118
|
+
| `background.paper` | `#ffffff` | Card/surface background |
|
|
119
|
+
| `divider` | `#eaecf0` (gray.200) | Borders and dividers |
|
|
120
|
+
|
|
121
|
+
### Spacing Scale
|
|
122
|
+
|
|
123
|
+
Use the MUI spacing multiplier: `theme.spacing(1) = 4px`
|
|
124
|
+
|
|
125
|
+
| Token | px | Usage |
|
|
126
|
+
|-------|-----|-------|
|
|
127
|
+
| `spacing(1)` | 4px | Tight gaps |
|
|
128
|
+
| `spacing(2)` | 8px | Component internal padding |
|
|
129
|
+
| `spacing(3)` | 12px | Small gaps |
|
|
130
|
+
| `spacing(4)` | 16px | Standard section spacing |
|
|
131
|
+
| `spacing(6)` | 24px | Medium section spacing |
|
|
132
|
+
| `spacing(8)` | 32px | Large section spacing |
|
|
133
|
+
|
|
134
|
+
### Border Radius
|
|
135
|
+
|
|
136
|
+
| Token | Value | Usage |
|
|
137
|
+
|-------|-------|-------|
|
|
138
|
+
| `borderRadius: 1` | 4px | Small elements (chips, badges) |
|
|
139
|
+
| `borderRadius: 2` | 8px | Buttons, inputs |
|
|
140
|
+
| `borderRadius: 3` | 12px | Cards, dialogs |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 5. Code Patterns
|
|
145
|
+
|
|
146
|
+
### Form Layout
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { TextField, Button, Select, MenuItem, Checkbox } from "@matteoaliano/forest-ui";
|
|
150
|
+
import Box from "@mui/material/Box"; // Box, Stack, Grid are OK from MUI
|
|
151
|
+
|
|
152
|
+
function ContactForm() {
|
|
153
|
+
return (
|
|
154
|
+
<Box sx={{ display: "flex", flexDirection: "column", gap: 3 }}>
|
|
155
|
+
<TextField label="Name" fullWidth />
|
|
156
|
+
<TextField label="Email" type="email" fullWidth />
|
|
157
|
+
<Select label="Subject" fullWidth>
|
|
158
|
+
<MenuItem value="support">Support</MenuItem>
|
|
159
|
+
<MenuItem value="sales">Sales</MenuItem>
|
|
160
|
+
</Select>
|
|
161
|
+
<Button variant="contained" type="submit">
|
|
162
|
+
Send
|
|
163
|
+
</Button>
|
|
164
|
+
</Box>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Data Table
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import {
|
|
173
|
+
Table, TableContainer, TableHead, TableBody, TableRow, TableCell,
|
|
174
|
+
} from "@matteoaliano/forest-ui";
|
|
175
|
+
|
|
176
|
+
function UsersTable({ users }) {
|
|
177
|
+
return (
|
|
178
|
+
<TableContainer>
|
|
179
|
+
<Table>
|
|
180
|
+
<TableHead>
|
|
181
|
+
<TableRow>
|
|
182
|
+
<TableCell>Name</TableCell>
|
|
183
|
+
<TableCell>Email</TableCell>
|
|
184
|
+
</TableRow>
|
|
185
|
+
</TableHead>
|
|
186
|
+
<TableBody>
|
|
187
|
+
{users.map((u) => (
|
|
188
|
+
<TableRow key={u.id}>
|
|
189
|
+
<TableCell>{u.name}</TableCell>
|
|
190
|
+
<TableCell>{u.email}</TableCell>
|
|
191
|
+
</TableRow>
|
|
192
|
+
))}
|
|
193
|
+
</TableBody>
|
|
194
|
+
</Table>
|
|
195
|
+
</TableContainer>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Feedback Pattern
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { Alert, AlertTitle, Snackbar } from "@matteoaliano/forest-ui";
|
|
204
|
+
|
|
205
|
+
// Inline feedback
|
|
206
|
+
<Alert severity="error">
|
|
207
|
+
<AlertTitle>Error</AlertTitle>
|
|
208
|
+
Something went wrong.
|
|
209
|
+
</Alert>
|
|
210
|
+
|
|
211
|
+
// Toast notification
|
|
212
|
+
<Snackbar open={open} autoHideDuration={4000} onClose={handleClose}>
|
|
213
|
+
<Alert severity="success" variant="filled">Saved!</Alert>
|
|
214
|
+
</Snackbar>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Confirmation Dialog
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { Dialog, DialogTitle, DialogContent, DialogActions, DialogContentText, Button } from "@matteoaliano/forest-ui";
|
|
221
|
+
|
|
222
|
+
function ConfirmDialog({ open, onClose, onConfirm }) {
|
|
223
|
+
return (
|
|
224
|
+
<Dialog open={open} onClose={onClose}>
|
|
225
|
+
<DialogTitle>Confirm</DialogTitle>
|
|
226
|
+
<DialogContent>
|
|
227
|
+
<DialogContentText>Are you sure?</DialogContentText>
|
|
228
|
+
</DialogContent>
|
|
229
|
+
<DialogActions>
|
|
230
|
+
<Button variant="outlined" onClick={onClose}>Cancel</Button>
|
|
231
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
232
|
+
</DialogActions>
|
|
233
|
+
</Dialog>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 6. Anti-Patterns
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
// ❌ WRONG: importing from @mui/material
|
|
244
|
+
import Button from "@mui/material/Button";
|
|
245
|
+
|
|
246
|
+
// ❌ WRONG: hardcoded colors
|
|
247
|
+
<Box sx={{ backgroundColor: "#7f56d9" }} />
|
|
248
|
+
|
|
249
|
+
// ❌ WRONG: hardcoded spacing
|
|
250
|
+
<Box sx={{ padding: "16px" }} />
|
|
251
|
+
|
|
252
|
+
// ❌ WRONG: forgetting ForestProvider
|
|
253
|
+
ReactDOM.render(<App />, root); // theme won't apply
|
|
254
|
+
|
|
255
|
+
// ✅ CORRECT: use theme tokens
|
|
256
|
+
<Box sx={{ backgroundColor: "primary.main", p: 4 }} />
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
> **Note:** Layout primitives like `Box`, `Stack`, `Grid`, and `Container` should still be imported from `@mui/material` — Forest UI does not re-export these because they have no custom styling.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 7. TypeScript Props
|
|
264
|
+
|
|
265
|
+
Every component exports its props type:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { Button, type ButtonProps } from "@matteoaliano/forest-ui";
|
|
269
|
+
|
|
270
|
+
interface MyButtonProps extends ButtonProps {
|
|
271
|
+
analyticsId: string;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
*Generated from forest-ui v0.1.0*
|
package/bin/sync.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* forest-ui sync
|
|
5
|
+
*
|
|
6
|
+
* Copies FOREST_AI_GUIDELINES.md into the AI-tool config directories
|
|
7
|
+
* of the consuming project so coding assistants automatically follow
|
|
8
|
+
* Forest UI conventions.
|
|
9
|
+
*
|
|
10
|
+
* Supported targets:
|
|
11
|
+
* .claude/forest-ui.md — Claude Code
|
|
12
|
+
* .cursor/rules/forest-ui.mdc — Cursor
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
16
|
+
import { resolve, dirname } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
|
|
22
|
+
// Locate the guidelines file shipped with the package
|
|
23
|
+
const guidelinesPath = resolve(__dirname, "..", "FOREST_AI_GUIDELINES.md");
|
|
24
|
+
|
|
25
|
+
if (!existsSync(guidelinesPath)) {
|
|
26
|
+
console.error("❌ Could not find FOREST_AI_GUIDELINES.md in the forest-ui package.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const guidelines = readFileSync(guidelinesPath, "utf-8");
|
|
31
|
+
|
|
32
|
+
// Find the consuming project root (walk up until we find package.json)
|
|
33
|
+
function findProjectRoot(startDir) {
|
|
34
|
+
let dir = startDir;
|
|
35
|
+
while (dir !== dirname(dir)) {
|
|
36
|
+
if (existsSync(resolve(dir, "package.json"))) {
|
|
37
|
+
// Skip if this is the forest-ui package itself
|
|
38
|
+
try {
|
|
39
|
+
const pkg = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf-8"));
|
|
40
|
+
if (pkg.name !== "@matteoaliano/forest-ui") return dir;
|
|
41
|
+
} catch {
|
|
42
|
+
return dir;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
dir = dirname(dir);
|
|
46
|
+
}
|
|
47
|
+
return process.cwd();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
51
|
+
|
|
52
|
+
// Define sync targets
|
|
53
|
+
const targets = [
|
|
54
|
+
{
|
|
55
|
+
name: "Claude Code",
|
|
56
|
+
dir: resolve(projectRoot, ".claude"),
|
|
57
|
+
file: "forest-ui.md",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "Cursor",
|
|
61
|
+
dir: resolve(projectRoot, ".cursor", "rules"),
|
|
62
|
+
file: "forest-ui.mdc",
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
console.log(`\n🌲 Forest UI — Syncing AI guidelines\n`);
|
|
67
|
+
console.log(` Project root: ${projectRoot}\n`);
|
|
68
|
+
|
|
69
|
+
let synced = 0;
|
|
70
|
+
|
|
71
|
+
for (const target of targets) {
|
|
72
|
+
try {
|
|
73
|
+
mkdirSync(target.dir, { recursive: true });
|
|
74
|
+
|
|
75
|
+
// For Cursor .mdc files, prepend frontmatter
|
|
76
|
+
let content = guidelines;
|
|
77
|
+
if (target.file.endsWith(".mdc")) {
|
|
78
|
+
content =
|
|
79
|
+
`---\ndescription: Forest UI Design System guidelines — components, tokens, and patterns\nglobs: **/*.{ts,tsx,js,jsx}\nalwaysApply: false\n---\n\n` +
|
|
80
|
+
guidelines;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
writeFileSync(resolve(target.dir, target.file), content, "utf-8");
|
|
84
|
+
console.log(` ✅ ${target.name} → ${target.dir}/${target.file}`);
|
|
85
|
+
synced++;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.log(` ⚠️ ${target.name} — skipped (${err.message})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(`\n Synced to ${synced}/${targets.length} targets.\n`);
|