@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.
@@ -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`);