@devmunna/agent-skillkit 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/LICENSE +21 -0
- package/README.md +147 -0
- package/bin/ai-skills.js +5 -0
- package/dist/cli/commands/add.d.ts +2 -0
- package/dist/cli/commands/add.d.ts.map +1 -0
- package/dist/cli/commands/add.js +66 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +33 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +10 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +145 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/list.d.ts +5 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +55 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/update.d.ts +2 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +49 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +2 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +22 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +49 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts/agent-selector.d.ts +3 -0
- package/dist/cli/prompts/agent-selector.d.ts.map +1 -0
- package/dist/cli/prompts/agent-selector.js +23 -0
- package/dist/cli/prompts/agent-selector.js.map +1 -0
- package/dist/cli/prompts/stack-selector.d.ts +3 -0
- package/dist/cli/prompts/stack-selector.d.ts.map +1 -0
- package/dist/cli/prompts/stack-selector.js +60 -0
- package/dist/cli/prompts/stack-selector.js.map +1 -0
- package/dist/core/config-manager.d.ts +20 -0
- package/dist/core/config-manager.d.ts.map +1 -0
- package/dist/core/config-manager.js +107 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/detector.d.ts +3 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +50 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/doctor.d.ts +12 -0
- package/dist/core/doctor.d.ts.map +1 -0
- package/dist/core/doctor.js +102 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/skill-registry.d.ts +11 -0
- package/dist/core/skill-registry.d.ts.map +1 -0
- package/dist/core/skill-registry.js +174 -0
- package/dist/core/skill-registry.js.map +1 -0
- package/dist/core/skill-resolver.d.ts +3 -0
- package/dist/core/skill-resolver.d.ts.map +1 -0
- package/dist/core/skill-resolver.js +36 -0
- package/dist/core/skill-resolver.js.map +1 -0
- package/dist/core/validator.d.ts +13 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +99 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/generators/agent-installer.d.ts +5 -0
- package/dist/generators/agent-installer.d.ts.map +1 -0
- package/dist/generators/agent-installer.js +20 -0
- package/dist/generators/agent-installer.js.map +1 -0
- package/dist/generators/agents-md.d.ts +3 -0
- package/dist/generators/agents-md.d.ts.map +1 -0
- package/dist/generators/agents-md.js +70 -0
- package/dist/generators/agents-md.js.map +1 -0
- package/dist/generators/claude-md.d.ts +3 -0
- package/dist/generators/claude-md.d.ts.map +1 -0
- package/dist/generators/claude-md.js +47 -0
- package/dist/generators/claude-md.js.map +1 -0
- package/dist/generators/skill-generator.d.ts +5 -0
- package/dist/generators/skill-generator.d.ts.map +1 -0
- package/dist/generators/skill-generator.js +34 -0
- package/dist/generators/skill-generator.js.map +1 -0
- package/dist/generators/workflows.d.ts +3 -0
- package/dist/generators/workflows.d.ts.map +1 -0
- package/dist/generators/workflows.js +57 -0
- package/dist/generators/workflows.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +55 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/file-utils.d.ts +12 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +39 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +73 -0
- package/skills/clean-architecture/SKILL.md +324 -0
- package/skills/express-mvc-prisma/SKILL.md +168 -0
- package/skills/express-mvc-prisma/references/auth.md +190 -0
- package/skills/express-mvc-prisma/references/boilerplate.md +196 -0
- package/skills/express-mvc-prisma/references/error-handling.md +121 -0
- package/skills/express-mvc-prisma/references/module-scaffold.md +253 -0
- package/skills/express-mvc-prisma/references/prisma-setup.md +97 -0
- package/skills/express-mvc-prisma/references/response-helpers.md +157 -0
- package/skills/express-mvc-prisma/references/zod-validation.md +157 -0
- package/skills/fastify-rest/SKILL.md +287 -0
- package/skills/mongoose-odm/SKILL.md +281 -0
- package/skills/nextjs-fullstack/SKILL.md +328 -0
- package/skills/nextjs-fullstack/references/auth.md +270 -0
- package/skills/nextjs-fullstack/references/caching.md +157 -0
- package/skills/nextjs-fullstack/references/route-handlers.md +194 -0
- package/skills/nextjs-fullstack/references/server-actions.md +214 -0
- package/skills/nextjs-fullstack/references/server-components.md +190 -0
- package/skills/node-base/SKILL.md +139 -0
- package/skills/prisma-orm/SKILL.md +334 -0
- package/skills/react-feature-arch/SKILL.md +208 -0
- package/skills/react-feature-arch/references/api-layer.md +110 -0
- package/skills/react-feature-arch/references/components.md +192 -0
- package/skills/react-feature-arch/references/data-fetching.md +198 -0
- package/skills/react-feature-arch/references/forms.md +194 -0
- package/skills/react-feature-arch/references/routing.md +148 -0
- package/skills/react-feature-arch/references/state-management.md +107 -0
- package/skills/tailwind-css/SKILL.md +236 -0
- package/skills/tailwind-css/references/components.md +340 -0
- package/skills/tailwind-css/references/design-tokens.md +230 -0
- package/skills/tailwind-css/references/patterns.md +375 -0
- package/skills/tailwind-css/references/setup.md +165 -0
- package/skills/zod-validation/SKILL.md +267 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Patterns Reference — Responsive, Dark Mode, Animations, Typography
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Responsive Design — Mobile-First
|
|
6
|
+
|
|
7
|
+
Always style the mobile base first, then add breakpoint prefixes for larger screens:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<!-- Typography -->
|
|
11
|
+
<h1 class="text-2xl font-bold sm:text-3xl lg:text-4xl">Title</h1>
|
|
12
|
+
|
|
13
|
+
<!-- Grid layout -->
|
|
14
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
|
15
|
+
|
|
16
|
+
<!-- Stack on mobile, side-by-side on desktop -->
|
|
17
|
+
<div class="flex flex-col sm:flex-row gap-4">
|
|
18
|
+
|
|
19
|
+
<!-- Full width on mobile, auto on desktop -->
|
|
20
|
+
<button class="w-full sm:w-auto">Submit</button>
|
|
21
|
+
|
|
22
|
+
<!-- Show/hide at breakpoints -->
|
|
23
|
+
<nav class="hidden md:flex">Desktop nav</nav>
|
|
24
|
+
<button class="md:hidden">Mobile menu</button>
|
|
25
|
+
|
|
26
|
+
<!-- Padding scales -->
|
|
27
|
+
<div class="px-4 sm:px-6 lg:px-8">Page content</div>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Built-in breakpoints:**
|
|
31
|
+
| Prefix | Min-width |
|
|
32
|
+
|--------|-----------|
|
|
33
|
+
| `sm:` | 640px |
|
|
34
|
+
| `md:` | 768px |
|
|
35
|
+
| `lg:` | 1024px |
|
|
36
|
+
| `xl:` | 1280px |
|
|
37
|
+
| `2xl:` | 1536px |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Container Queries (v4 Built-In)
|
|
42
|
+
|
|
43
|
+
Container queries let components respond to their parent's size, not the viewport. Essential for reusable components in varying layouts.
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<!-- Mark the container -->
|
|
47
|
+
<div class="@container">
|
|
48
|
+
<!-- Card adapts to container width, not screen width -->
|
|
49
|
+
<div class="flex flex-col @md:flex-row gap-4">
|
|
50
|
+
<img class="w-full @md:w-48 @md:shrink-0 rounded-lg" src="..." />
|
|
51
|
+
<div class="@md:flex @md:flex-col @md:justify-center">
|
|
52
|
+
<h2 class="text-lg @md:text-xl font-semibold">Title</h2>
|
|
53
|
+
<p class="text-sm text-muted">Description</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Container breakpoints (based on container width):**
|
|
60
|
+
| Prefix | Min-width |
|
|
61
|
+
|--------|-----------|
|
|
62
|
+
| `@sm:` | 384px |
|
|
63
|
+
| `@md:` | 448px |
|
|
64
|
+
| `@lg:` | 512px |
|
|
65
|
+
| `@xl:` | 576px |
|
|
66
|
+
| `@2xl:`| 672px |
|
|
67
|
+
|
|
68
|
+
**Named containers (for nested scenarios):**
|
|
69
|
+
```html
|
|
70
|
+
<aside class="@container/sidebar">
|
|
71
|
+
<main class="@container/content">
|
|
72
|
+
<!-- Respond to different ancestor containers -->
|
|
73
|
+
<div class="@lg/sidebar:hidden @lg/content:block">
|
|
74
|
+
Only visible when content container is large
|
|
75
|
+
</div>
|
|
76
|
+
</main>
|
|
77
|
+
</aside>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Dark Mode
|
|
83
|
+
|
|
84
|
+
### Setup — Class-Based (Recommended)
|
|
85
|
+
|
|
86
|
+
```css
|
|
87
|
+
/* globals.css — define the dark variant */
|
|
88
|
+
@import "tailwindcss";
|
|
89
|
+
|
|
90
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// Zustand store or simple util
|
|
95
|
+
export function setTheme(theme: 'light' | 'dark') {
|
|
96
|
+
document.documentElement.classList.toggle('dark', theme === 'dark');
|
|
97
|
+
localStorage.setItem('theme', theme);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// On app startup — read persisted preference
|
|
101
|
+
const saved = localStorage.getItem('theme') as 'light' | 'dark' | null;
|
|
102
|
+
const system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
103
|
+
setTheme(saved ?? system);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Usage
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<!-- Colors -->
|
|
110
|
+
<div class="bg-white dark:bg-slate-900">
|
|
111
|
+
<div class="text-slate-900 dark:text-slate-100">
|
|
112
|
+
<div class="border-slate-200 dark:border-slate-700">
|
|
113
|
+
|
|
114
|
+
<!-- With CSS variable tokens (cleaner) -->
|
|
115
|
+
<div class="bg-bg text-fg border-border-col">
|
|
116
|
+
|
|
117
|
+
<!-- Images / icons -->
|
|
118
|
+
<img class="opacity-100 dark:opacity-80" />
|
|
119
|
+
<svg class="fill-slate-900 dark:fill-white" />
|
|
120
|
+
|
|
121
|
+
<!-- Shadows -->
|
|
122
|
+
<div class="shadow-md dark:shadow-slate-900/50">
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Theme Toggle Component
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
'use client'; // if Next.js
|
|
129
|
+
|
|
130
|
+
import { useState, useEffect } from 'react';
|
|
131
|
+
|
|
132
|
+
export function ThemeToggle() {
|
|
133
|
+
const [isDark, setIsDark] = useState(false);
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
setIsDark(document.documentElement.classList.contains('dark'));
|
|
137
|
+
}, []);
|
|
138
|
+
|
|
139
|
+
const toggle = () => {
|
|
140
|
+
const next = !isDark;
|
|
141
|
+
setIsDark(next);
|
|
142
|
+
document.documentElement.classList.toggle('dark', next);
|
|
143
|
+
localStorage.setItem('theme', next ? 'dark' : 'light');
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<button
|
|
148
|
+
onClick={toggle}
|
|
149
|
+
className="rounded-md p-2 hover:bg-primary/10 transition-colors"
|
|
150
|
+
aria-label="Toggle theme"
|
|
151
|
+
>
|
|
152
|
+
{isDark ? '☀️' : '🌙'}
|
|
153
|
+
</button>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Animations
|
|
161
|
+
|
|
162
|
+
### CSS Transitions (Hover, Focus)
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<!-- Color transition -->
|
|
166
|
+
<button class="bg-primary hover:bg-primary/80 transition-colors duration-200">
|
|
167
|
+
|
|
168
|
+
<!-- Scale on hover -->
|
|
169
|
+
<div class="hover:scale-105 transition-transform duration-200">
|
|
170
|
+
|
|
171
|
+
<!-- Multiple properties -->
|
|
172
|
+
<div class="hover:shadow-lg hover:-translate-y-1 transition-all duration-200">
|
|
173
|
+
|
|
174
|
+
<!-- Opacity + transform combined -->
|
|
175
|
+
<div class="hover:opacity-80 active:scale-95 transition-all duration-150">
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Enter Animations — starting: Variant (v4)
|
|
179
|
+
|
|
180
|
+
The `starting:` variant applies `@starting-style` — defines the element's initial state before it renders, so it transitions in on mount.
|
|
181
|
+
|
|
182
|
+
```html
|
|
183
|
+
<!-- Fade in on mount -->
|
|
184
|
+
<div class="opacity-100 transition-opacity duration-300 starting:opacity-0">
|
|
185
|
+
Fades in when added to DOM
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<!-- Slide up on mount -->
|
|
189
|
+
<div class="translate-y-0 opacity-100 transition-all duration-300 starting:translate-y-4 starting:opacity-0">
|
|
190
|
+
Slides up from below
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<!-- Scale in on mount -->
|
|
194
|
+
<div class="scale-100 opacity-100 transition-all duration-200 starting:scale-95 starting:opacity-0">
|
|
195
|
+
Scales in
|
|
196
|
+
</div>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Custom Animation Classes (via @theme)
|
|
200
|
+
|
|
201
|
+
After defining animations in `@theme` (see design-tokens.md):
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<div class="animate-fade-in">Fades in</div>
|
|
205
|
+
<div class="animate-slide-up">Slides up</div>
|
|
206
|
+
<div class="animate-scale-in">Scales in</div>
|
|
207
|
+
|
|
208
|
+
<!-- Built-in loading animations -->
|
|
209
|
+
<div class="animate-spin">Spinner</div>
|
|
210
|
+
<div class="animate-pulse">Skeleton</div>
|
|
211
|
+
<div class="animate-bounce">Bounce indicator</div>
|
|
212
|
+
<div class="animate-ping">Notification ping</div>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Conditional Animation (React)
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
export function Notification({ show }: { show: boolean }) {
|
|
219
|
+
return (
|
|
220
|
+
<div
|
|
221
|
+
className={cn(
|
|
222
|
+
'rounded-lg bg-success/10 px-4 py-3 text-success',
|
|
223
|
+
'transition-all duration-300',
|
|
224
|
+
show
|
|
225
|
+
? 'translate-y-0 opacity-100'
|
|
226
|
+
: '-translate-y-4 opacity-0 pointer-events-none',
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
229
|
+
Saved successfully!
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Typography — @tailwindcss/typography
|
|
238
|
+
|
|
239
|
+
Install: `npm install -D @tailwindcss/typography`
|
|
240
|
+
|
|
241
|
+
In globals.css: `@plugin "@tailwindcss/typography";`
|
|
242
|
+
|
|
243
|
+
### Usage
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<!-- Blog post, markdown output, rich text editor content -->
|
|
247
|
+
<article class="prose lg:prose-lg dark:prose-invert max-w-none">
|
|
248
|
+
<h1>Article Title</h1>
|
|
249
|
+
<p>Content is auto-styled — links, headings, lists, code, blockquotes.</p>
|
|
250
|
+
<code>code snippets</code>
|
|
251
|
+
<pre><code>code blocks</code></pre>
|
|
252
|
+
</article>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Prose Modifiers
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<!-- Size -->
|
|
259
|
+
<article class="prose prose-sm"> Small (14px) </article>
|
|
260
|
+
<article class="prose"> Base (16px) </article>
|
|
261
|
+
<article class="prose prose-lg"> Large (18px) </article>
|
|
262
|
+
<article class="prose prose-xl"> XL (20px) </article>
|
|
263
|
+
|
|
264
|
+
<!-- Dark mode -->
|
|
265
|
+
<article class="prose dark:prose-invert">
|
|
266
|
+
|
|
267
|
+
<!-- Responsive size -->
|
|
268
|
+
<article class="prose md:prose-lg lg:prose-xl">
|
|
269
|
+
|
|
270
|
+
<!-- Element overrides -->
|
|
271
|
+
<article class="prose prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-code:rounded prose-code:bg-muted/20 prose-code:px-1 prose-img:rounded-xl">
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Layout Patterns
|
|
277
|
+
|
|
278
|
+
### Page Layout
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
export function PageLayout({ children }: { children: React.ReactNode }) {
|
|
282
|
+
return (
|
|
283
|
+
<div className="min-h-screen bg-bg text-fg">
|
|
284
|
+
<header className="sticky top-0 z-40 border-b border-border-col bg-bg/80 backdrop-blur">
|
|
285
|
+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
286
|
+
<div className="flex h-16 items-center justify-between">
|
|
287
|
+
<Logo />
|
|
288
|
+
<nav className="hidden md:flex items-center gap-4">
|
|
289
|
+
<NavLinks />
|
|
290
|
+
</nav>
|
|
291
|
+
<ThemeToggle />
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
</header>
|
|
295
|
+
<main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
|
|
296
|
+
{children}
|
|
297
|
+
</main>
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Sidebar Layout
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
307
|
+
return (
|
|
308
|
+
<div className="flex min-h-screen bg-bg">
|
|
309
|
+
{/* Sidebar */}
|
|
310
|
+
<aside className="hidden w-64 shrink-0 border-r border-border-col lg:block">
|
|
311
|
+
<nav className="sticky top-0 h-screen overflow-y-auto p-4">
|
|
312
|
+
<SidebarNav />
|
|
313
|
+
</nav>
|
|
314
|
+
</aside>
|
|
315
|
+
{/* Content */}
|
|
316
|
+
<div className="flex flex-1 flex-col">
|
|
317
|
+
<header className="flex h-16 items-center border-b border-border-col px-6">
|
|
318
|
+
<h1 className="text-lg font-semibold">Dashboard</h1>
|
|
319
|
+
</header>
|
|
320
|
+
<main className="flex-1 overflow-auto p-6">{children}</main>
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Data Table
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
export function DataTable<T>({ columns, rows }: TableProps<T>) {
|
|
331
|
+
return (
|
|
332
|
+
<div className="overflow-hidden rounded-xl border border-border-col">
|
|
333
|
+
<table className="w-full text-sm">
|
|
334
|
+
<thead className="border-b border-border-col bg-card">
|
|
335
|
+
<tr>
|
|
336
|
+
{columns.map((col) => (
|
|
337
|
+
<th key={col.key} className="px-4 py-3 text-left font-medium text-muted">
|
|
338
|
+
{col.label}
|
|
339
|
+
</th>
|
|
340
|
+
))}
|
|
341
|
+
</tr>
|
|
342
|
+
</thead>
|
|
343
|
+
<tbody className="divide-y divide-border-col bg-bg">
|
|
344
|
+
{rows.map((row, i) => (
|
|
345
|
+
<tr key={i} className="hover:bg-card transition-colors">
|
|
346
|
+
{columns.map((col) => (
|
|
347
|
+
<td key={col.key} className="px-4 py-3">
|
|
348
|
+
{col.render ? col.render(row) : String((row as any)[col.key])}
|
|
349
|
+
</td>
|
|
350
|
+
))}
|
|
351
|
+
</tr>
|
|
352
|
+
))}
|
|
353
|
+
</tbody>
|
|
354
|
+
</table>
|
|
355
|
+
</div>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Empty State
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
export function EmptyState({ icon: Icon, title, description, action }: EmptyStateProps) {
|
|
364
|
+
return (
|
|
365
|
+
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border-col py-16 text-center">
|
|
366
|
+
<div className="mb-4 rounded-full bg-primary/10 p-4">
|
|
367
|
+
<Icon className="size-8 text-primary" />
|
|
368
|
+
</div>
|
|
369
|
+
<h3 className="text-lg font-semibold text-fg">{title}</h3>
|
|
370
|
+
<p className="mt-1 text-sm text-muted">{description}</p>
|
|
371
|
+
{action && <div className="mt-6">{action}</div>}
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Setup Reference — Tailwind CSS v4
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Vite + React
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create vite@latest my-app -- --template react-ts
|
|
9
|
+
cd my-app
|
|
10
|
+
npm install tailwindcss @tailwindcss/vite clsx tailwind-merge class-variance-authority
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**vite.config.ts:**
|
|
14
|
+
```ts
|
|
15
|
+
import { defineConfig } from 'vite';
|
|
16
|
+
import react from '@vitejs/plugin-react';
|
|
17
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
18
|
+
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
plugins: [react(), tailwindcss()],
|
|
21
|
+
resolve: {
|
|
22
|
+
alias: { '@': '/src' },
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**src/index.css:**
|
|
28
|
+
```css
|
|
29
|
+
@import "tailwindcss";
|
|
30
|
+
|
|
31
|
+
/* All @theme customizations go here */
|
|
32
|
+
@theme {
|
|
33
|
+
--font-sans: 'Inter', sans-serif;
|
|
34
|
+
--color-primary: oklch(0.55 0.25 262);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**main.tsx:**
|
|
39
|
+
```tsx
|
|
40
|
+
import './index.css';
|
|
41
|
+
import ReactDOM from 'react-dom/client';
|
|
42
|
+
import App from './App';
|
|
43
|
+
|
|
44
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Next.js 15
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx create-next-app@latest my-app --typescript --app --src-dir --import-alias "@/*"
|
|
53
|
+
cd my-app
|
|
54
|
+
npm install tailwindcss @tailwindcss/postcss postcss clsx tailwind-merge class-variance-authority
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**postcss.config.mjs:**
|
|
58
|
+
```js
|
|
59
|
+
export default { plugins: { '@tailwindcss/postcss': {} } };
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**src/app/globals.css:**
|
|
63
|
+
```css
|
|
64
|
+
@import "tailwindcss";
|
|
65
|
+
|
|
66
|
+
@theme {
|
|
67
|
+
--font-sans: 'Inter', sans-serif;
|
|
68
|
+
--color-primary: oklch(0.55 0.25 262);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**src/app/layout.tsx:**
|
|
73
|
+
```tsx
|
|
74
|
+
import './globals.css';
|
|
75
|
+
import type { Metadata } from 'next';
|
|
76
|
+
|
|
77
|
+
export const metadata: Metadata = { title: 'My App' };
|
|
78
|
+
|
|
79
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
80
|
+
return (
|
|
81
|
+
<html lang="en">
|
|
82
|
+
<body>{children}</body>
|
|
83
|
+
</html>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## @tailwindcss/typography Plugin
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm install -D @tailwindcss/typography
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**In globals.css — add plugin import after tailwindcss:**
|
|
97
|
+
```css
|
|
98
|
+
@import "tailwindcss";
|
|
99
|
+
@plugin "@tailwindcss/typography";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Usage:
|
|
103
|
+
```html
|
|
104
|
+
<article class="prose lg:prose-lg dark:prose-invert max-w-none">
|
|
105
|
+
<!-- Rich text content, markdown output, blog posts -->
|
|
106
|
+
</article>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## shadcn/ui Setup (Tailwind v4)
|
|
112
|
+
|
|
113
|
+
shadcn/ui components ship ready for Tailwind v4 with CSS variable–based theming.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx shadcn@latest init
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The CLI will:
|
|
120
|
+
1. Create `src/lib/utils.ts` with the `cn()` helper
|
|
121
|
+
2. Add CSS variables to `globals.css` under `:root` and `.dark`
|
|
122
|
+
3. Wire up `@theme` to expose variables as Tailwind utilities
|
|
123
|
+
|
|
124
|
+
Install components individually:
|
|
125
|
+
```bash
|
|
126
|
+
npx shadcn@latest add button
|
|
127
|
+
npx shadcn@latest add card
|
|
128
|
+
npx shadcn@latest add input
|
|
129
|
+
npx shadcn@latest add dialog
|
|
130
|
+
npx shadcn@latest add table
|
|
131
|
+
npx shadcn@latest add form # includes react-hook-form integration
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Components land in `src/components/ui/` — they are source files you own and customize.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## cn() Helper — Always Create This File
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// src/lib/utils.ts
|
|
142
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
143
|
+
import { twMerge } from 'tailwind-merge';
|
|
144
|
+
|
|
145
|
+
export function cn(...inputs: ClassValue[]) {
|
|
146
|
+
return twMerge(clsx(inputs));
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## TypeScript Path Aliases (tsconfig.json)
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"compilerOptions": {
|
|
157
|
+
"baseUrl": ".",
|
|
158
|
+
"paths": {
|
|
159
|
+
"@/*": ["./src/*"]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Vite also needs the alias in `vite.config.ts` (shown above). Next.js handles it automatically when using `--import-alias "@/*"` flag.
|