create-deesse-app 0.2.3 → 0.4.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/dist/src/copy.d.ts +1 -1
- package/dist/src/copy.d.ts.map +1 -1
- package/dist/src/copy.js +50 -71
- package/dist/src/copy.js.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -5
- package/templates/default/.agents/skills/shadcn/SKILL.md +242 -0
- package/templates/default/.agents/skills/shadcn/agents/openai.yml +5 -0
- package/templates/default/.agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/templates/default/.agents/skills/shadcn/assets/shadcn.png +0 -0
- package/templates/default/.agents/skills/shadcn/cli.md +257 -0
- package/templates/default/.agents/skills/shadcn/customization.md +202 -0
- package/templates/default/.agents/skills/shadcn/evals/evals.json +47 -0
- package/templates/default/.agents/skills/shadcn/mcp.md +94 -0
- package/templates/default/.agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/templates/default/.agents/skills/shadcn/rules/composition.md +195 -0
- package/templates/default/.agents/skills/shadcn/rules/forms.md +192 -0
- package/templates/default/.agents/skills/shadcn/rules/icons.md +101 -0
- package/templates/default/.agents/skills/shadcn/rules/styling.md +162 -0
- package/templates/default/AGENTS.md +5 -0
- package/templates/default/CLAUDE.md +1 -0
- package/templates/default/README.md +28 -0
- package/templates/default/components.json +25 -0
- package/templates/default/eslint.config.mjs +18 -0
- package/templates/default/next.config.ts +7 -0
- package/templates/default/package.json +50 -0
- package/templates/default/postcss.config.mjs +7 -0
- package/templates/default/public/file.svg +1 -0
- package/templates/default/public/globe.svg +1 -0
- package/templates/default/public/nesalia.svg +50 -0
- package/templates/default/public/window.svg +1 -0
- package/templates/default/skills-lock.json +10 -0
- package/templates/default/src/app/(deesse)/admin/[[...slug]]/page.tsx +20 -0
- package/templates/default/src/app/(deesse)/admin/layout.tsx +7 -0
- package/templates/default/src/app/(frontend)/page.tsx +50 -0
- package/templates/default/src/app/globals.css +130 -0
- package/templates/default/src/app/icon.svg +109 -0
- package/templates/default/src/app/layout.tsx +33 -0
- package/templates/default/src/app/page.tsx +50 -0
- package/templates/default/src/components/providers/index.tsx +9 -0
- package/templates/default/src/components/providers/theme-provider.tsx +11 -0
- package/templates/default/src/components/ui/accordion.tsx +81 -0
- package/templates/default/src/components/ui/alert-dialog.tsx +199 -0
- package/templates/default/src/components/ui/alert.tsx +76 -0
- package/templates/default/src/components/ui/aspect-ratio.tsx +11 -0
- package/templates/default/src/components/ui/avatar.tsx +112 -0
- package/templates/default/src/components/ui/badge.tsx +49 -0
- package/templates/default/src/components/ui/breadcrumb.tsx +122 -0
- package/templates/default/src/components/ui/button-group.tsx +83 -0
- package/templates/default/src/components/ui/button.tsx +67 -0
- package/templates/default/src/components/ui/calendar.tsx +222 -0
- package/templates/default/src/components/ui/card.tsx +103 -0
- package/templates/default/src/components/ui/carousel.tsx +242 -0
- package/templates/default/src/components/ui/chart.tsx +373 -0
- package/templates/default/src/components/ui/checkbox.tsx +33 -0
- package/templates/default/src/components/ui/collapsible.tsx +33 -0
- package/templates/default/src/components/ui/combobox.tsx +299 -0
- package/templates/default/src/components/ui/command.tsx +195 -0
- package/templates/default/src/components/ui/context-menu.tsx +263 -0
- package/templates/default/src/components/ui/dialog.tsx +168 -0
- package/templates/default/src/components/ui/direction.tsx +22 -0
- package/templates/default/src/components/ui/drawer.tsx +134 -0
- package/templates/default/src/components/ui/dropdown-menu.tsx +269 -0
- package/templates/default/src/components/ui/empty.tsx +104 -0
- package/templates/default/src/components/ui/field.tsx +238 -0
- package/templates/default/src/components/ui/hover-card.tsx +44 -0
- package/templates/default/src/components/ui/input-group.tsx +156 -0
- package/templates/default/src/components/ui/input-otp.tsx +87 -0
- package/templates/default/src/components/ui/input.tsx +19 -0
- package/templates/default/src/components/ui/item.tsx +196 -0
- package/templates/default/src/components/ui/kbd.tsx +26 -0
- package/templates/default/src/components/ui/label.tsx +24 -0
- package/templates/default/src/components/ui/menubar.tsx +280 -0
- package/templates/default/src/components/ui/native-select.tsx +52 -0
- package/templates/default/src/components/ui/navigation-menu.tsx +164 -0
- package/templates/default/src/components/ui/pagination.tsx +129 -0
- package/templates/default/src/components/ui/popover.tsx +89 -0
- package/templates/default/src/components/ui/progress.tsx +31 -0
- package/templates/default/src/components/ui/radio-group.tsx +44 -0
- package/templates/default/src/components/ui/resizable.tsx +50 -0
- package/templates/default/src/components/ui/scroll-area.tsx +55 -0
- package/templates/default/src/components/ui/select.tsx +192 -0
- package/templates/default/src/components/ui/separator.tsx +28 -0
- package/templates/default/src/components/ui/sheet.tsx +147 -0
- package/templates/default/src/components/ui/sidebar.tsx +702 -0
- package/templates/default/src/components/ui/skeleton.tsx +13 -0
- package/templates/default/src/components/ui/slider.tsx +59 -0
- package/templates/default/src/components/ui/sonner.tsx +49 -0
- package/templates/default/src/components/ui/spinner.tsx +10 -0
- package/templates/default/src/components/ui/switch.tsx +33 -0
- package/templates/default/src/components/ui/table.tsx +116 -0
- package/templates/default/src/components/ui/tabs.tsx +90 -0
- package/templates/default/src/components/ui/textarea.tsx +18 -0
- package/templates/default/src/components/ui/toggle-group.tsx +89 -0
- package/templates/default/src/components/ui/toggle.tsx +46 -0
- package/templates/default/src/components/ui/tooltip.tsx +57 -0
- package/templates/default/src/deesse.config.ts +11 -0
- package/templates/default/src/hooks/use-mobile.ts +19 -0
- package/templates/default/src/lib/utils.ts +6 -0
- package/templates/default/tsconfig.json +35 -0
- package/templates/minimal/.gitkeep +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Component Composition
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Items always inside their Group component
|
|
6
|
+
- Callouts use Alert
|
|
7
|
+
- Empty states use Empty component
|
|
8
|
+
- Toast notifications use sonner
|
|
9
|
+
- Choosing between overlay components
|
|
10
|
+
- Dialog, Sheet, and Drawer always need a Title
|
|
11
|
+
- Card structure
|
|
12
|
+
- Button has no isPending or isLoading prop
|
|
13
|
+
- TabsTrigger must be inside TabsList
|
|
14
|
+
- Avatar always needs AvatarFallback
|
|
15
|
+
- Use Separator instead of raw hr or border divs
|
|
16
|
+
- Use Skeleton for loading placeholders
|
|
17
|
+
- Use Badge instead of custom styled spans
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Items always inside their Group component
|
|
22
|
+
|
|
23
|
+
Never render items directly inside the content container.
|
|
24
|
+
|
|
25
|
+
**Incorrect:**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<SelectContent>
|
|
29
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
30
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
31
|
+
</SelectContent>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Correct:**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
<SelectContent>
|
|
38
|
+
<SelectGroup>
|
|
39
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
40
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
41
|
+
</SelectGroup>
|
|
42
|
+
</SelectContent>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This applies to all group-based components:
|
|
46
|
+
|
|
47
|
+
| Item | Group |
|
|
48
|
+
|------|-------|
|
|
49
|
+
| `SelectItem`, `SelectLabel` | `SelectGroup` |
|
|
50
|
+
| `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSub` | `DropdownMenuGroup` |
|
|
51
|
+
| `MenubarItem` | `MenubarGroup` |
|
|
52
|
+
| `ContextMenuItem` | `ContextMenuGroup` |
|
|
53
|
+
| `CommandItem` | `CommandGroup` |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Callouts use Alert
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<Alert>
|
|
61
|
+
<AlertTitle>Warning</AlertTitle>
|
|
62
|
+
<AlertDescription>Something needs attention.</AlertDescription>
|
|
63
|
+
</Alert>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Empty states use Empty component
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<Empty>
|
|
72
|
+
<EmptyHeader>
|
|
73
|
+
<EmptyMedia variant="icon"><FolderIcon /></EmptyMedia>
|
|
74
|
+
<EmptyTitle>No projects yet</EmptyTitle>
|
|
75
|
+
<EmptyDescription>Get started by creating a new project.</EmptyDescription>
|
|
76
|
+
</EmptyHeader>
|
|
77
|
+
<EmptyContent>
|
|
78
|
+
<Button>Create Project</Button>
|
|
79
|
+
</EmptyContent>
|
|
80
|
+
</Empty>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Toast notifications use sonner
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { toast } from "sonner"
|
|
89
|
+
|
|
90
|
+
toast.success("Changes saved.")
|
|
91
|
+
toast.error("Something went wrong.")
|
|
92
|
+
toast("File deleted.", {
|
|
93
|
+
action: { label: "Undo", onClick: () => undoDelete() },
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Choosing between overlay components
|
|
100
|
+
|
|
101
|
+
| Use case | Component |
|
|
102
|
+
|----------|-----------|
|
|
103
|
+
| Focused task that requires input | `Dialog` |
|
|
104
|
+
| Destructive action confirmation | `AlertDialog` |
|
|
105
|
+
| Side panel with details or filters | `Sheet` |
|
|
106
|
+
| Mobile-first bottom panel | `Drawer` |
|
|
107
|
+
| Quick info on hover | `HoverCard` |
|
|
108
|
+
| Small contextual content on click | `Popover` |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Dialog, Sheet, and Drawer always need a Title
|
|
113
|
+
|
|
114
|
+
`DialogTitle`, `SheetTitle`, `DrawerTitle` are required for accessibility. Use `className="sr-only"` if visually hidden.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<DialogContent>
|
|
118
|
+
<DialogHeader>
|
|
119
|
+
<DialogTitle>Edit Profile</DialogTitle>
|
|
120
|
+
<DialogDescription>Update your profile.</DialogDescription>
|
|
121
|
+
</DialogHeader>
|
|
122
|
+
...
|
|
123
|
+
</DialogContent>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Card structure
|
|
129
|
+
|
|
130
|
+
Use full composition — don't dump everything into `CardContent`:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<Card>
|
|
134
|
+
<CardHeader>
|
|
135
|
+
<CardTitle>Team Members</CardTitle>
|
|
136
|
+
<CardDescription>Manage your team.</CardDescription>
|
|
137
|
+
</CardHeader>
|
|
138
|
+
<CardContent>...</CardContent>
|
|
139
|
+
<CardFooter>
|
|
140
|
+
<Button>Invite</Button>
|
|
141
|
+
</CardFooter>
|
|
142
|
+
</Card>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Button has no isPending or isLoading prop
|
|
148
|
+
|
|
149
|
+
Compose with `Spinner` + `data-icon` + `disabled`:
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<Button disabled>
|
|
153
|
+
<Spinner data-icon="inline-start" />
|
|
154
|
+
Saving...
|
|
155
|
+
</Button>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## TabsTrigger must be inside TabsList
|
|
161
|
+
|
|
162
|
+
Never render `TabsTrigger` directly inside `Tabs` — always wrap in `TabsList`:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
<Tabs defaultValue="account">
|
|
166
|
+
<TabsList>
|
|
167
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
168
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
169
|
+
</TabsList>
|
|
170
|
+
<TabsContent value="account">...</TabsContent>
|
|
171
|
+
</Tabs>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Avatar always needs AvatarFallback
|
|
177
|
+
|
|
178
|
+
Always include `AvatarFallback` for when the image fails to load:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<Avatar>
|
|
182
|
+
<AvatarImage src="/avatar.png" alt="User" />
|
|
183
|
+
<AvatarFallback>JD</AvatarFallback>
|
|
184
|
+
</Avatar>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Use existing components instead of custom markup
|
|
190
|
+
|
|
191
|
+
| Instead of | Use |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `<hr>` or `<div className="border-t">` | `<Separator />` |
|
|
194
|
+
| `<div className="animate-pulse">` with styled divs | `<Skeleton className="h-4 w-3/4" />` |
|
|
195
|
+
| `<span className="rounded-full bg-green-100 ...">` | `<Badge variant="secondary">` |
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Forms & Inputs
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Forms use FieldGroup + Field
|
|
6
|
+
- InputGroup requires InputGroupInput/InputGroupTextarea
|
|
7
|
+
- Buttons inside inputs use InputGroup + InputGroupAddon
|
|
8
|
+
- Option sets (2–7 choices) use ToggleGroup
|
|
9
|
+
- FieldSet + FieldLegend for grouping related fields
|
|
10
|
+
- Field validation and disabled states
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Forms use FieldGroup + Field
|
|
15
|
+
|
|
16
|
+
Always use `FieldGroup` + `Field` — never raw `div` with `space-y-*`:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<FieldGroup>
|
|
20
|
+
<Field>
|
|
21
|
+
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
22
|
+
<Input id="email" type="email" />
|
|
23
|
+
</Field>
|
|
24
|
+
<Field>
|
|
25
|
+
<FieldLabel htmlFor="password">Password</FieldLabel>
|
|
26
|
+
<Input id="password" type="password" />
|
|
27
|
+
</Field>
|
|
28
|
+
</FieldGroup>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use `Field orientation="horizontal"` for settings pages. Use `FieldLabel className="sr-only"` for visually hidden labels.
|
|
32
|
+
|
|
33
|
+
**Choosing form controls:**
|
|
34
|
+
|
|
35
|
+
- Simple text input → `Input`
|
|
36
|
+
- Dropdown with predefined options → `Select`
|
|
37
|
+
- Searchable dropdown → `Combobox`
|
|
38
|
+
- Native HTML select (no JS) → `native-select`
|
|
39
|
+
- Boolean toggle → `Switch` (for settings) or `Checkbox` (for forms)
|
|
40
|
+
- Single choice from few options → `RadioGroup`
|
|
41
|
+
- Toggle between 2–5 options → `ToggleGroup` + `ToggleGroupItem`
|
|
42
|
+
- OTP/verification code → `InputOTP`
|
|
43
|
+
- Multi-line text → `Textarea`
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## InputGroup requires InputGroupInput/InputGroupTextarea
|
|
48
|
+
|
|
49
|
+
Never use raw `Input` or `Textarea` inside an `InputGroup`.
|
|
50
|
+
|
|
51
|
+
**Incorrect:**
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<InputGroup>
|
|
55
|
+
<Input placeholder="Search..." />
|
|
56
|
+
</InputGroup>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Correct:**
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { InputGroup, InputGroupInput } from "@/components/ui/input-group"
|
|
63
|
+
|
|
64
|
+
<InputGroup>
|
|
65
|
+
<InputGroupInput placeholder="Search..." />
|
|
66
|
+
</InputGroup>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Buttons inside inputs use InputGroup + InputGroupAddon
|
|
72
|
+
|
|
73
|
+
Never place a `Button` directly inside or adjacent to an `Input` with custom positioning.
|
|
74
|
+
|
|
75
|
+
**Incorrect:**
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<div className="relative">
|
|
79
|
+
<Input placeholder="Search..." className="pr-10" />
|
|
80
|
+
<Button className="absolute right-0 top-0" size="icon">
|
|
81
|
+
<SearchIcon />
|
|
82
|
+
</Button>
|
|
83
|
+
</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Correct:**
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group"
|
|
90
|
+
|
|
91
|
+
<InputGroup>
|
|
92
|
+
<InputGroupInput placeholder="Search..." />
|
|
93
|
+
<InputGroupAddon>
|
|
94
|
+
<Button size="icon">
|
|
95
|
+
<SearchIcon data-icon="inline-start" />
|
|
96
|
+
</Button>
|
|
97
|
+
</InputGroupAddon>
|
|
98
|
+
</InputGroup>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Option sets (2–7 choices) use ToggleGroup
|
|
104
|
+
|
|
105
|
+
Don't manually loop `Button` components with active state.
|
|
106
|
+
|
|
107
|
+
**Incorrect:**
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
const [selected, setSelected] = useState("daily")
|
|
111
|
+
|
|
112
|
+
<div className="flex gap-2">
|
|
113
|
+
{["daily", "weekly", "monthly"].map((option) => (
|
|
114
|
+
<Button
|
|
115
|
+
key={option}
|
|
116
|
+
variant={selected === option ? "default" : "outline"}
|
|
117
|
+
onClick={() => setSelected(option)}
|
|
118
|
+
>
|
|
119
|
+
{option}
|
|
120
|
+
</Button>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Correct:**
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|
129
|
+
|
|
130
|
+
<ToggleGroup spacing={2}>
|
|
131
|
+
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
|
|
132
|
+
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
|
|
133
|
+
<ToggleGroupItem value="monthly">Monthly</ToggleGroupItem>
|
|
134
|
+
</ToggleGroup>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Combine with `Field` for labelled toggle groups:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<Field orientation="horizontal">
|
|
141
|
+
<FieldTitle id="theme-label">Theme</FieldTitle>
|
|
142
|
+
<ToggleGroup aria-labelledby="theme-label" spacing={2}>
|
|
143
|
+
<ToggleGroupItem value="light">Light</ToggleGroupItem>
|
|
144
|
+
<ToggleGroupItem value="dark">Dark</ToggleGroupItem>
|
|
145
|
+
<ToggleGroupItem value="system">System</ToggleGroupItem>
|
|
146
|
+
</ToggleGroup>
|
|
147
|
+
</Field>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> **Note:** `defaultValue` and `type`/`multiple` props differ between base and radix. See [base-vs-radix.md](./base-vs-radix.md#togglegroup).
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## FieldSet + FieldLegend for grouping related fields
|
|
155
|
+
|
|
156
|
+
Use `FieldSet` + `FieldLegend` for related checkboxes, radios, or switches — not `div` with a heading:
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<FieldSet>
|
|
160
|
+
<FieldLegend variant="label">Preferences</FieldLegend>
|
|
161
|
+
<FieldDescription>Select all that apply.</FieldDescription>
|
|
162
|
+
<FieldGroup className="gap-3">
|
|
163
|
+
<Field orientation="horizontal">
|
|
164
|
+
<Checkbox id="dark" />
|
|
165
|
+
<FieldLabel htmlFor="dark" className="font-normal">Dark mode</FieldLabel>
|
|
166
|
+
</Field>
|
|
167
|
+
</FieldGroup>
|
|
168
|
+
</FieldSet>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Field validation and disabled states
|
|
174
|
+
|
|
175
|
+
Both attributes are needed — `data-invalid`/`data-disabled` styles the field (label, description), while `aria-invalid`/`disabled` styles the control.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
// Invalid.
|
|
179
|
+
<Field data-invalid>
|
|
180
|
+
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
181
|
+
<Input id="email" aria-invalid />
|
|
182
|
+
<FieldDescription>Invalid email address.</FieldDescription>
|
|
183
|
+
</Field>
|
|
184
|
+
|
|
185
|
+
// Disabled.
|
|
186
|
+
<Field data-disabled>
|
|
187
|
+
<FieldLabel htmlFor="email">Email</FieldLabel>
|
|
188
|
+
<Input id="email" disabled />
|
|
189
|
+
</Field>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Works for all controls: `Input`, `Textarea`, `Select`, `Checkbox`, `RadioGroupItem`, `Switch`, `Slider`, `NativeSelect`, `InputOTP`.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Icons
|
|
2
|
+
|
|
3
|
+
**Always use the project's configured `iconLibrary` for imports.** Check the `iconLibrary` field from project context: `lucide` → `lucide-react`, `tabler` → `@tabler/icons-react`, etc. Never assume `lucide-react`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Icons in Button use data-icon attribute
|
|
8
|
+
|
|
9
|
+
Add `data-icon="inline-start"` (prefix) or `data-icon="inline-end"` (suffix) to the icon. No sizing classes on the icon.
|
|
10
|
+
|
|
11
|
+
**Incorrect:**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Button>
|
|
15
|
+
<SearchIcon className="mr-2 size-4" />
|
|
16
|
+
Search
|
|
17
|
+
</Button>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct:**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<Button>
|
|
24
|
+
<SearchIcon data-icon="inline-start"/>
|
|
25
|
+
Search
|
|
26
|
+
</Button>
|
|
27
|
+
|
|
28
|
+
<Button>
|
|
29
|
+
Next
|
|
30
|
+
<ArrowRightIcon data-icon="inline-end"/>
|
|
31
|
+
</Button>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## No sizing classes on icons inside components
|
|
37
|
+
|
|
38
|
+
Components handle icon sizing via CSS. Don't add `size-4`, `w-4 h-4`, or other sizing classes to icons inside `Button`, `DropdownMenuItem`, `Alert`, `Sidebar*`, or other shadcn components. Unless the user explicitly asks for custom icon sizes.
|
|
39
|
+
|
|
40
|
+
**Incorrect:**
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Button>
|
|
44
|
+
<SearchIcon className="size-4" data-icon="inline-start" />
|
|
45
|
+
Search
|
|
46
|
+
</Button>
|
|
47
|
+
|
|
48
|
+
<DropdownMenuItem>
|
|
49
|
+
<SettingsIcon className="mr-2 size-4" />
|
|
50
|
+
Settings
|
|
51
|
+
</DropdownMenuItem>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Correct:**
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Button>
|
|
58
|
+
<SearchIcon data-icon="inline-start" />
|
|
59
|
+
Search
|
|
60
|
+
</Button>
|
|
61
|
+
|
|
62
|
+
<DropdownMenuItem>
|
|
63
|
+
<SettingsIcon />
|
|
64
|
+
Settings
|
|
65
|
+
</DropdownMenuItem>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Pass icons as component objects, not string keys
|
|
71
|
+
|
|
72
|
+
Use `icon={CheckIcon}`, not a string key to a lookup map.
|
|
73
|
+
|
|
74
|
+
**Incorrect:**
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
const iconMap = {
|
|
78
|
+
check: CheckIcon,
|
|
79
|
+
alert: AlertIcon,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function StatusBadge({ icon }: { icon: string }) {
|
|
83
|
+
const Icon = iconMap[icon]
|
|
84
|
+
return <Icon />
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
<StatusBadge icon="check" />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Correct:**
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// Import from the project's configured iconLibrary (e.g. lucide-react, @tabler/icons-react).
|
|
94
|
+
import { CheckIcon } from "lucide-react"
|
|
95
|
+
|
|
96
|
+
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
|
|
97
|
+
return <Icon />
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
<StatusBadge icon={CheckIcon} />
|
|
101
|
+
```
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Styling & Customization
|
|
2
|
+
|
|
3
|
+
See [customization.md](../customization.md) for theming, CSS variables, and adding custom colors.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- Semantic colors
|
|
8
|
+
- Built-in variants first
|
|
9
|
+
- className for layout only
|
|
10
|
+
- No space-x-* / space-y-*
|
|
11
|
+
- Prefer size-* over w-* h-* when equal
|
|
12
|
+
- Prefer truncate shorthand
|
|
13
|
+
- No manual dark: color overrides
|
|
14
|
+
- Use cn() for conditional classes
|
|
15
|
+
- No manual z-index on overlay components
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Semantic colors
|
|
20
|
+
|
|
21
|
+
**Incorrect:**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
<div className="bg-blue-500 text-white">
|
|
25
|
+
<p className="text-gray-600">Secondary text</p>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct:**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<div className="bg-primary text-primary-foreground">
|
|
33
|
+
<p className="text-muted-foreground">Secondary text</p>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## No raw color values for status/state indicators
|
|
40
|
+
|
|
41
|
+
For positive, negative, or status indicators, use Badge variants, semantic tokens like `text-destructive`, or define custom CSS variables — don't reach for raw Tailwind colors.
|
|
42
|
+
|
|
43
|
+
**Incorrect:**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<span className="text-emerald-600">+20.1%</span>
|
|
47
|
+
<span className="text-green-500">Active</span>
|
|
48
|
+
<span className="text-red-600">-3.2%</span>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Correct:**
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<Badge variant="secondary">+20.1%</Badge>
|
|
55
|
+
<Badge>Active</Badge>
|
|
56
|
+
<span className="text-destructive">-3.2%</span>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If you need a success/positive color that doesn't exist as a semantic token, use a Badge variant or ask the user about adding a custom CSS variable to the theme (see [customization.md](../customization.md)).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Built-in variants first
|
|
64
|
+
|
|
65
|
+
**Incorrect:**
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<Button className="border border-input bg-transparent hover:bg-accent">
|
|
69
|
+
Click me
|
|
70
|
+
</Button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Correct:**
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Button variant="outline">Click me</Button>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## className for layout only
|
|
82
|
+
|
|
83
|
+
Use `className` for layout (e.g. `max-w-md`, `mx-auto`, `mt-4`), **not** for overriding component colors or typography. To change colors, use semantic tokens, built-in variants, or CSS variables.
|
|
84
|
+
|
|
85
|
+
**Incorrect:**
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<Card className="bg-blue-100 text-blue-900 font-bold">
|
|
89
|
+
<CardContent>Dashboard</CardContent>
|
|
90
|
+
</Card>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Correct:**
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<Card className="max-w-md mx-auto">
|
|
97
|
+
<CardContent>Dashboard</CardContent>
|
|
98
|
+
</Card>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To customize a component's appearance, prefer these approaches in order:
|
|
102
|
+
1. **Built-in variants** — `variant="outline"`, `variant="destructive"`, etc.
|
|
103
|
+
2. **Semantic color tokens** — `bg-primary`, `text-muted-foreground`.
|
|
104
|
+
3. **CSS variables** — define custom colors in the global CSS file (see [customization.md](../customization.md)).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## No space-x-* / space-y-*
|
|
109
|
+
|
|
110
|
+
Use `gap-*` instead. `space-y-4` → `flex flex-col gap-4`. `space-x-2` → `flex gap-2`.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<div className="flex flex-col gap-4">
|
|
114
|
+
<Input />
|
|
115
|
+
<Input />
|
|
116
|
+
<Button>Submit</Button>
|
|
117
|
+
</div>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Prefer size-* over w-* h-* when equal
|
|
123
|
+
|
|
124
|
+
`size-10` not `w-10 h-10`. Applies to icons, avatars, skeletons, etc.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Prefer truncate shorthand
|
|
129
|
+
|
|
130
|
+
`truncate` not `overflow-hidden text-ellipsis whitespace-nowrap`.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## No manual dark: color overrides
|
|
135
|
+
|
|
136
|
+
Use semantic tokens — they handle light/dark via CSS variables. `bg-background text-foreground` not `bg-white dark:bg-gray-950`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Use cn() for conditional classes
|
|
141
|
+
|
|
142
|
+
Use the `cn()` utility from the project for conditional or merged class names. Don't write manual ternaries in className strings.
|
|
143
|
+
|
|
144
|
+
**Incorrect:**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Correct:**
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { cn } from "@/lib/utils"
|
|
154
|
+
|
|
155
|
+
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## No manual z-index on overlay components
|
|
161
|
+
|
|
162
|
+
`Dialog`, `Sheet`, `Drawer`, `AlertDialog`, `DropdownMenu`, `Popover`, `Tooltip`, `HoverCard` handle their own stacking. Never add `z-50` or `z-[999]`.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- BEGIN:nextjs-agent-rules -->
|
|
2
|
+
# This is NOT the Next.js you know
|
|
3
|
+
|
|
4
|
+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
|
5
|
+
<!-- END:nextjs-agent-rules -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
This is a [DeesseJS](https://deessejs.com) project bootstrapped with [`create-deesse-app`](https://deessejs.com).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
pnpm dev
|
|
11
|
+
# or
|
|
12
|
+
bun dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
16
|
+
|
|
17
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
18
|
+
|
|
19
|
+
## Learn More
|
|
20
|
+
|
|
21
|
+
To learn more about DeesseJS, take a look at the following resources:
|
|
22
|
+
|
|
23
|
+
- [DeesseJS Documentation](https://deessejs.com) - learn about DeesseJS features and API.
|
|
24
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features (DeesseJS is built on Next.js).
|
|
25
|
+
|
|
26
|
+
## Deploy
|
|
27
|
+
|
|
28
|
+
Check out our [DeesseJS deployment documentation](https://deessejs.com) for more details.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "radix-nova",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"rtl": false,
|
|
15
|
+
"aliases": {
|
|
16
|
+
"components": "@/components",
|
|
17
|
+
"utils": "@/lib/utils",
|
|
18
|
+
"ui": "@/components/ui",
|
|
19
|
+
"lib": "@/lib",
|
|
20
|
+
"hooks": "@/hooks"
|
|
21
|
+
},
|
|
22
|
+
"menuColor": "default",
|
|
23
|
+
"menuAccent": "subtle",
|
|
24
|
+
"registries": {}
|
|
25
|
+
}
|