@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,340 @@
|
|
|
1
|
+
# Component Patterns Reference
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## cn() — Class Merging Helper
|
|
6
|
+
|
|
7
|
+
**Always create this file. Every component depends on it.**
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// src/lib/utils.ts
|
|
11
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
12
|
+
import { twMerge } from 'tailwind-merge';
|
|
13
|
+
|
|
14
|
+
export function cn(...inputs: ClassValue[]) {
|
|
15
|
+
return twMerge(clsx(inputs));
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`clsx` handles conditional class logic. `twMerge` resolves Tailwind conflicts so caller-provided classes always win:
|
|
20
|
+
```tsx
|
|
21
|
+
// Without cn: "rounded-md rounded-full" — broken, both apply
|
|
22
|
+
// With cn: "rounded-full" — tailwind-merge picks the last/winner
|
|
23
|
+
<Button className="rounded-full" />
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Button — CVA Pattern
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// src/components/ui/Button.tsx
|
|
32
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
33
|
+
import { cn } from '@/lib/utils';
|
|
34
|
+
|
|
35
|
+
const buttonVariants = cva(
|
|
36
|
+
// Base styles — always applied
|
|
37
|
+
[
|
|
38
|
+
'inline-flex items-center justify-center gap-2 rounded-md font-medium',
|
|
39
|
+
'transition-colors select-none',
|
|
40
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
|
|
41
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
42
|
+
],
|
|
43
|
+
{
|
|
44
|
+
variants: {
|
|
45
|
+
variant: {
|
|
46
|
+
default: 'bg-primary text-white hover:bg-primary/90 focus-visible:ring-primary',
|
|
47
|
+
outline: 'border border-current bg-transparent hover:bg-primary/10 text-primary',
|
|
48
|
+
ghost: 'hover:bg-primary/10 hover:text-primary',
|
|
49
|
+
destructive: 'bg-danger text-white hover:bg-danger/90 focus-visible:ring-danger',
|
|
50
|
+
link: 'text-primary underline-offset-4 hover:underline p-0 h-auto',
|
|
51
|
+
},
|
|
52
|
+
size: {
|
|
53
|
+
sm: 'h-8 px-3 text-sm',
|
|
54
|
+
md: 'h-10 px-4',
|
|
55
|
+
lg: 'h-12 px-6 text-base',
|
|
56
|
+
icon: 'size-10 shrink-0',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
defaultVariants: {
|
|
60
|
+
variant: 'default',
|
|
61
|
+
size: 'md',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export interface ButtonProps
|
|
67
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
68
|
+
VariantProps<typeof buttonVariants> {
|
|
69
|
+
isLoading?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function Button({
|
|
73
|
+
className,
|
|
74
|
+
variant,
|
|
75
|
+
size,
|
|
76
|
+
isLoading,
|
|
77
|
+
children,
|
|
78
|
+
disabled,
|
|
79
|
+
...props
|
|
80
|
+
}: ButtonProps) {
|
|
81
|
+
return (
|
|
82
|
+
<button
|
|
83
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
84
|
+
disabled={disabled || isLoading}
|
|
85
|
+
{...props}
|
|
86
|
+
>
|
|
87
|
+
{isLoading && (
|
|
88
|
+
<svg className="size-4 animate-spin" viewBox="0 0 24 24" fill="none">
|
|
89
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
90
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z" />
|
|
91
|
+
</svg>
|
|
92
|
+
)}
|
|
93
|
+
{children}
|
|
94
|
+
</button>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Usage:
|
|
100
|
+
```tsx
|
|
101
|
+
<Button>Default</Button>
|
|
102
|
+
<Button variant="outline">Outline</Button>
|
|
103
|
+
<Button variant="destructive" size="sm">Delete</Button>
|
|
104
|
+
<Button variant="ghost" size="icon"><TrashIcon /></Button>
|
|
105
|
+
<Button isLoading>Saving...</Button>
|
|
106
|
+
<Button className="w-full">Full Width</Button> {/* className always wins */}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Card
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
// src/components/ui/Card.tsx
|
|
115
|
+
import { cn } from '@/lib/utils';
|
|
116
|
+
|
|
117
|
+
type DivProps = React.HTMLAttributes<HTMLDivElement>;
|
|
118
|
+
|
|
119
|
+
export function Card({ className, ...props }: DivProps) {
|
|
120
|
+
return (
|
|
121
|
+
<div
|
|
122
|
+
className={cn('rounded-xl border border-border-col bg-card shadow-card', className)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function CardHeader({ className, ...props }: DivProps) {
|
|
129
|
+
return (
|
|
130
|
+
<div
|
|
131
|
+
className={cn('flex flex-col gap-1.5 border-b border-border-col px-6 py-4', className)}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
|
138
|
+
return (
|
|
139
|
+
<h3
|
|
140
|
+
className={cn('text-lg font-semibold leading-none tracking-tight text-fg', className)}
|
|
141
|
+
{...props}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
147
|
+
return (
|
|
148
|
+
<p className={cn('text-sm text-muted', className)} {...props} />
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function CardContent({ className, ...props }: DivProps) {
|
|
153
|
+
return <div className={cn('px-6 py-4', className)} {...props} />;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function CardFooter({ className, ...props }: DivProps) {
|
|
157
|
+
return (
|
|
158
|
+
<div
|
|
159
|
+
className={cn('flex items-center border-t border-border-col px-6 py-4', className)}
|
|
160
|
+
{...props}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Usage:
|
|
167
|
+
```tsx
|
|
168
|
+
<Card>
|
|
169
|
+
<CardHeader>
|
|
170
|
+
<CardTitle>Users</CardTitle>
|
|
171
|
+
<CardDescription>Manage your team members</CardDescription>
|
|
172
|
+
</CardHeader>
|
|
173
|
+
<CardContent>
|
|
174
|
+
<UserList users={users} />
|
|
175
|
+
</CardContent>
|
|
176
|
+
<CardFooter className="justify-end gap-2">
|
|
177
|
+
<Button variant="outline">Cancel</Button>
|
|
178
|
+
<Button>Save</Button>
|
|
179
|
+
</CardFooter>
|
|
180
|
+
</Card>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Input
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// src/components/ui/Input.tsx
|
|
189
|
+
import { cn } from '@/lib/utils';
|
|
190
|
+
|
|
191
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
192
|
+
error?: string;
|
|
193
|
+
label?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function Input({ className, error, label, id, ...props }: InputProps) {
|
|
197
|
+
return (
|
|
198
|
+
<div className="flex flex-col gap-1.5">
|
|
199
|
+
{label && (
|
|
200
|
+
<label htmlFor={id} className="text-sm font-medium text-fg">
|
|
201
|
+
{label}
|
|
202
|
+
</label>
|
|
203
|
+
)}
|
|
204
|
+
<input
|
|
205
|
+
id={id}
|
|
206
|
+
className={cn(
|
|
207
|
+
'flex h-10 w-full rounded-md border bg-bg px-3 py-2 text-sm',
|
|
208
|
+
'placeholder:text-muted',
|
|
209
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary',
|
|
210
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
211
|
+
error
|
|
212
|
+
? 'border-danger focus-visible:ring-danger'
|
|
213
|
+
: 'border-border-col',
|
|
214
|
+
className,
|
|
215
|
+
)}
|
|
216
|
+
{...props}
|
|
217
|
+
/>
|
|
218
|
+
{error && <p className="text-xs text-danger">{error}</p>}
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Badge
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
// src/components/ui/Badge.tsx
|
|
230
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
231
|
+
import { cn } from '@/lib/utils';
|
|
232
|
+
|
|
233
|
+
const badgeVariants = cva(
|
|
234
|
+
'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors',
|
|
235
|
+
{
|
|
236
|
+
variants: {
|
|
237
|
+
variant: {
|
|
238
|
+
default: 'bg-primary text-white',
|
|
239
|
+
secondary: 'bg-primary/10 text-primary',
|
|
240
|
+
outline: 'border border-current text-fg',
|
|
241
|
+
success: 'bg-success/10 text-success',
|
|
242
|
+
warning: 'bg-warning/10 text-warning',
|
|
243
|
+
destructive: 'bg-danger/10 text-danger',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
defaultVariants: { variant: 'default' },
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
interface BadgeProps
|
|
251
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
252
|
+
VariantProps<typeof badgeVariants> {}
|
|
253
|
+
|
|
254
|
+
export function Badge({ className, variant, ...props }: BadgeProps) {
|
|
255
|
+
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Skeleton (Loading Placeholder)
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
// src/components/ui/Skeleton.tsx
|
|
265
|
+
import { cn } from '@/lib/utils';
|
|
266
|
+
|
|
267
|
+
export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
268
|
+
return (
|
|
269
|
+
<div
|
|
270
|
+
className={cn('animate-pulse rounded-md bg-border-col', className)}
|
|
271
|
+
{...props}
|
|
272
|
+
/>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Usage patterns
|
|
277
|
+
export function UserCardSkeleton() {
|
|
278
|
+
return (
|
|
279
|
+
<div className="flex items-center gap-3 p-4">
|
|
280
|
+
<Skeleton className="size-10 rounded-full" />
|
|
281
|
+
<div className="flex flex-col gap-2">
|
|
282
|
+
<Skeleton className="h-4 w-32" />
|
|
283
|
+
<Skeleton className="h-3 w-24" />
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Modal / Dialog
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
// src/components/ui/Modal.tsx
|
|
296
|
+
'use client'; // if Next.js
|
|
297
|
+
|
|
298
|
+
import { useEffect } from 'react';
|
|
299
|
+
import { cn } from '@/lib/utils';
|
|
300
|
+
|
|
301
|
+
interface ModalProps {
|
|
302
|
+
isOpen: boolean;
|
|
303
|
+
onClose: () => void;
|
|
304
|
+
children: React.ReactNode;
|
|
305
|
+
className?: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function Modal({ isOpen, onClose, children, className }: ModalProps) {
|
|
309
|
+
// Close on Escape key
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
const handler = (e: KeyboardEvent) => {
|
|
312
|
+
if (e.key === 'Escape') onClose();
|
|
313
|
+
};
|
|
314
|
+
document.addEventListener('keydown', handler);
|
|
315
|
+
return () => document.removeEventListener('keydown', handler);
|
|
316
|
+
}, [onClose]);
|
|
317
|
+
|
|
318
|
+
if (!isOpen) return null;
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
322
|
+
{/* Backdrop */}
|
|
323
|
+
<div
|
|
324
|
+
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
|
|
325
|
+
onClick={onClose}
|
|
326
|
+
/>
|
|
327
|
+
{/* Content */}
|
|
328
|
+
<div
|
|
329
|
+
className={cn(
|
|
330
|
+
'relative z-10 w-full max-w-md rounded-xl bg-bg p-6 shadow-lg',
|
|
331
|
+
'animate-scale-in',
|
|
332
|
+
className,
|
|
333
|
+
)}
|
|
334
|
+
>
|
|
335
|
+
{children}
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Design Tokens Reference — @theme
|
|
2
|
+
|
|
3
|
+
All custom tokens go in `@theme` in your global CSS file. Tailwind generates utility classes automatically from the variable names.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Colors — OKLCH (Preferred in v4)
|
|
8
|
+
|
|
9
|
+
OKLCH provides perceptually uniform lightness, making it easy to create consistent color scales.
|
|
10
|
+
Format: `oklch(lightness saturation hue)` — lightness 0–1, saturation 0–0.4, hue 0–360.
|
|
11
|
+
|
|
12
|
+
```css
|
|
13
|
+
@import "tailwindcss";
|
|
14
|
+
|
|
15
|
+
@theme {
|
|
16
|
+
/* Primary brand color + scale */
|
|
17
|
+
--color-primary: oklch(0.55 0.25 262);
|
|
18
|
+
--color-primary-50: oklch(0.97 0.02 262);
|
|
19
|
+
--color-primary-100: oklch(0.93 0.05 262);
|
|
20
|
+
--color-primary-500: oklch(0.55 0.25 262);
|
|
21
|
+
--color-primary-600: oklch(0.48 0.25 262);
|
|
22
|
+
--color-primary-900: oklch(0.25 0.15 262);
|
|
23
|
+
|
|
24
|
+
/* Semantic colors */
|
|
25
|
+
--color-success: oklch(0.65 0.15 142);
|
|
26
|
+
--color-warning: oklch(0.75 0.17 80);
|
|
27
|
+
--color-danger: oklch(0.55 0.25 27);
|
|
28
|
+
--color-info: oklch(0.60 0.20 230);
|
|
29
|
+
|
|
30
|
+
/* Neutral surface colors — works well with dark mode */
|
|
31
|
+
--color-surface: white;
|
|
32
|
+
--color-surface-raised: oklch(0.98 0 0);
|
|
33
|
+
--color-border: oklch(0.90 0 0);
|
|
34
|
+
--color-muted: oklch(0.55 0 0);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Generated utilities: `bg-primary`, `bg-primary-500`, `text-danger`, `border-success`, `text-muted`, etc.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Dark Mode Color Tokens
|
|
43
|
+
|
|
44
|
+
Define light/dark values with CSS custom properties, then reference them in `@theme`:
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
@import "tailwindcss";
|
|
48
|
+
|
|
49
|
+
/* Light mode defaults */
|
|
50
|
+
:root {
|
|
51
|
+
--bg: white;
|
|
52
|
+
--fg: oklch(0.15 0 0);
|
|
53
|
+
--border-col: oklch(0.88 0 0);
|
|
54
|
+
--muted-col: oklch(0.55 0 0);
|
|
55
|
+
--card-col: oklch(0.97 0 0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Dark mode overrides */
|
|
59
|
+
.dark {
|
|
60
|
+
--bg: oklch(0.12 0 0);
|
|
61
|
+
--fg: oklch(0.95 0 0);
|
|
62
|
+
--border-col: oklch(0.25 0 0);
|
|
63
|
+
--muted-col: oklch(0.55 0 0);
|
|
64
|
+
--card-col: oklch(0.16 0 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Expose to Tailwind */
|
|
68
|
+
@theme {
|
|
69
|
+
--color-bg: var(--bg);
|
|
70
|
+
--color-fg: var(--fg);
|
|
71
|
+
--color-border-col: var(--border-col);
|
|
72
|
+
--color-muted: var(--muted-col);
|
|
73
|
+
--color-card: var(--card-col);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
```html
|
|
79
|
+
<div class="bg-bg text-fg border border-border-col">
|
|
80
|
+
<div class="bg-card rounded-lg p-4">Card content</div>
|
|
81
|
+
</div>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Typography Tokens
|
|
87
|
+
|
|
88
|
+
```css
|
|
89
|
+
@theme {
|
|
90
|
+
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
|
91
|
+
--font-serif: 'Playfair Display', ui-serif, Georgia, serif;
|
|
92
|
+
--font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
|
|
93
|
+
|
|
94
|
+
/* Font sizes with paired line-heights */
|
|
95
|
+
--text-xs: 0.75rem / 1rem;
|
|
96
|
+
--text-sm: 0.875rem / 1.25rem;
|
|
97
|
+
--text-base: 1rem / 1.5rem;
|
|
98
|
+
--text-lg: 1.125rem / 1.75rem;
|
|
99
|
+
--text-xl: 1.25rem / 1.75rem;
|
|
100
|
+
--text-2xl: 1.5rem / 2rem;
|
|
101
|
+
--text-3xl: 1.875rem / 2.25rem;
|
|
102
|
+
--text-4xl: 2.25rem / 2.5rem;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Generated utilities: `font-sans`, `font-serif`, `font-mono`, `text-xs` through `text-4xl`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Spacing Tokens
|
|
111
|
+
|
|
112
|
+
Spacing tokens affect: `p-*`, `m-*`, `gap-*`, `w-*`, `h-*`, `top-*`, `left-*`, etc.
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
@theme {
|
|
116
|
+
/* Semantic spacing (for consistent page layout) */
|
|
117
|
+
--spacing-page: 2rem; /* px-page */
|
|
118
|
+
--spacing-section: 4rem; /* py-section */
|
|
119
|
+
--spacing-card: 1.5rem; /* p-card */
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Border Radius Tokens
|
|
126
|
+
|
|
127
|
+
```css
|
|
128
|
+
@theme {
|
|
129
|
+
--radius-sm: 0.25rem; /* rounded-sm */
|
|
130
|
+
--radius-md: 0.5rem; /* rounded-md */
|
|
131
|
+
--radius-lg: 0.75rem; /* rounded-lg */
|
|
132
|
+
--radius-xl: 1rem; /* rounded-xl */
|
|
133
|
+
--radius-card: 0.75rem; /* rounded-card */
|
|
134
|
+
--radius-pill: 9999px; /* rounded-pill */
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Shadow Tokens
|
|
141
|
+
|
|
142
|
+
```css
|
|
143
|
+
@theme {
|
|
144
|
+
--shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.05);
|
|
145
|
+
--shadow-md: 0 4px 6px -1px oklch(0 0 0 / 0.1), 0 2px 4px -2px oklch(0 0 0 / 0.1);
|
|
146
|
+
--shadow-lg: 0 10px 15px -3px oklch(0 0 0 / 0.1), 0 4px 6px -4px oklch(0 0 0 / 0.1);
|
|
147
|
+
--shadow-card: 0 2px 8px oklch(0 0 0 / 0.08);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Animation Tokens
|
|
154
|
+
|
|
155
|
+
```css
|
|
156
|
+
@theme {
|
|
157
|
+
--animate-fade-in: fade-in 0.2s ease-out;
|
|
158
|
+
--animate-slide-up: slide-up 0.3s ease-out;
|
|
159
|
+
--animate-slide-down: slide-down 0.3s ease-out;
|
|
160
|
+
--animate-scale-in: scale-in 0.2s ease-out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@keyframes fade-in {
|
|
164
|
+
from { opacity: 0; }
|
|
165
|
+
to { opacity: 1; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@keyframes slide-up {
|
|
169
|
+
from { transform: translateY(0.5rem); opacity: 0; }
|
|
170
|
+
to { transform: translateY(0); opacity: 1; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@keyframes slide-down {
|
|
174
|
+
from { transform: translateY(-0.5rem); opacity: 0; }
|
|
175
|
+
to { transform: translateY(0); opacity: 1; }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@keyframes scale-in {
|
|
179
|
+
from { transform: scale(0.95); opacity: 0; }
|
|
180
|
+
to { transform: scale(1); opacity: 1; }
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Generated utilities: `animate-fade-in`, `animate-slide-up`, `animate-slide-down`, `animate-scale-in`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Custom Utilities — @utility
|
|
189
|
+
|
|
190
|
+
```css
|
|
191
|
+
/* Static utility */
|
|
192
|
+
@utility center-flex {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
justify-content: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Truncate with line clamp */
|
|
199
|
+
@utility truncate-2 {
|
|
200
|
+
display: -webkit-box;
|
|
201
|
+
-webkit-line-clamp: 2;
|
|
202
|
+
-webkit-box-orient: vertical;
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Usage: `<div class="center-flex truncate-2">`.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Custom Variants — @custom-variant
|
|
212
|
+
|
|
213
|
+
```css
|
|
214
|
+
/* Hover on mobile (touch devices long-press) */
|
|
215
|
+
@custom-variant touch-hover {
|
|
216
|
+
@media (hover: none) {
|
|
217
|
+
&:active;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Group peer variants are built-in; this shows the pattern */
|
|
222
|
+
@custom-variant data-active {
|
|
223
|
+
&[data-active="true"];
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Usage:
|
|
228
|
+
```html
|
|
229
|
+
<button class="data-active:bg-primary" data-active="true">Active</button>
|
|
230
|
+
```
|