omgkit 2.1.1 → 2.2.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/package.json +1 -1
- package/plugin/skills/SKILL_STANDARDS.md +743 -0
- package/plugin/skills/databases/mongodb/SKILL.md +797 -28
- package/plugin/skills/databases/prisma/SKILL.md +776 -30
- package/plugin/skills/databases/redis/SKILL.md +885 -25
- package/plugin/skills/devops/aws/SKILL.md +686 -28
- package/plugin/skills/devops/github-actions/SKILL.md +684 -29
- package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
- package/plugin/skills/frameworks/django/SKILL.md +920 -20
- package/plugin/skills/frameworks/express/SKILL.md +1361 -35
- package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
- package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
- package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
- package/plugin/skills/frameworks/rails/SKILL.md +594 -28
- package/plugin/skills/frameworks/spring/SKILL.md +528 -35
- package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
- package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
- package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
- package/plugin/skills/frontend/responsive/SKILL.md +847 -21
- package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
- package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
- package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
- package/plugin/skills/languages/javascript/SKILL.md +935 -31
- package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
- package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
- package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
- package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
- package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
- package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
- package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
- package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
- package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
- package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
- package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
- package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
- package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
- package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
- package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
- package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
- package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
- package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
- package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
- package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
- package/plugin/skills/security/better-auth/SKILL.md +1065 -28
- package/plugin/skills/security/oauth/SKILL.md +968 -31
- package/plugin/skills/security/owasp/SKILL.md +894 -33
- package/plugin/skills/testing/playwright/SKILL.md +764 -38
- package/plugin/skills/testing/pytest/SKILL.md +873 -36
- package/plugin/skills/testing/vitest/SKILL.md +980 -35
|
@@ -1,52 +1,848 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tailwindcss
|
|
3
|
-
description: Tailwind CSS styling
|
|
3
|
+
description: Tailwind CSS utility-first styling with responsive design, dark mode, animations, and component patterns
|
|
4
|
+
category: frontend
|
|
5
|
+
triggers:
|
|
6
|
+
- tailwind
|
|
7
|
+
- tailwindcss
|
|
8
|
+
- utility classes
|
|
9
|
+
- css framework
|
|
10
|
+
- styling
|
|
4
11
|
---
|
|
5
12
|
|
|
6
|
-
# Tailwind CSS
|
|
13
|
+
# Tailwind CSS
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<!-- Spacing -->
|
|
11
|
-
<div class="p-4 m-2 space-y-4">
|
|
15
|
+
Enterprise-grade **utility-first CSS framework** following industry best practices. This skill covers responsive design, dark mode, custom configurations, animations, component patterns, and optimization strategies used by top engineering teams.
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
<div class="flex items-center justify-between gap-4">
|
|
17
|
+
## Purpose
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
<div class="grid grid-cols-3 gap-4">
|
|
19
|
+
Build beautiful, responsive UIs efficiently:
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
- Write utility-first CSS
|
|
22
|
+
- Implement responsive designs
|
|
23
|
+
- Configure dark mode theming
|
|
24
|
+
- Create reusable component patterns
|
|
25
|
+
- Customize design tokens
|
|
26
|
+
- Optimize for production
|
|
27
|
+
- Build accessible interfaces
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
### 1. Configuration Setup
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
// tailwind.config.js
|
|
35
|
+
/** @type {import('tailwindcss').Config} */
|
|
36
|
+
module.exports = {
|
|
37
|
+
content: [
|
|
38
|
+
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
|
39
|
+
"./components/**/*.{js,ts,jsx,tsx}",
|
|
40
|
+
"./app/**/*.{js,ts,jsx,tsx}",
|
|
41
|
+
],
|
|
42
|
+
darkMode: "class", // or 'media'
|
|
43
|
+
theme: {
|
|
44
|
+
extend: {
|
|
45
|
+
// Custom colors
|
|
46
|
+
colors: {
|
|
47
|
+
primary: {
|
|
48
|
+
50: "#eff6ff",
|
|
49
|
+
100: "#dbeafe",
|
|
50
|
+
200: "#bfdbfe",
|
|
51
|
+
300: "#93c5fd",
|
|
52
|
+
400: "#60a5fa",
|
|
53
|
+
500: "#3b82f6",
|
|
54
|
+
600: "#2563eb",
|
|
55
|
+
700: "#1d4ed8",
|
|
56
|
+
800: "#1e40af",
|
|
57
|
+
900: "#1e3a8a",
|
|
58
|
+
950: "#172554",
|
|
59
|
+
},
|
|
60
|
+
secondary: {
|
|
61
|
+
50: "#f8fafc",
|
|
62
|
+
// ... other shades
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// Custom spacing
|
|
66
|
+
spacing: {
|
|
67
|
+
"18": "4.5rem",
|
|
68
|
+
"88": "22rem",
|
|
69
|
+
"128": "32rem",
|
|
70
|
+
},
|
|
71
|
+
// Custom fonts
|
|
72
|
+
fontFamily: {
|
|
73
|
+
sans: ["Inter", "system-ui", "sans-serif"],
|
|
74
|
+
mono: ["JetBrains Mono", "monospace"],
|
|
75
|
+
},
|
|
76
|
+
// Custom font sizes
|
|
77
|
+
fontSize: {
|
|
78
|
+
"2xs": ["0.625rem", { lineHeight: "0.75rem" }],
|
|
79
|
+
},
|
|
80
|
+
// Custom border radius
|
|
81
|
+
borderRadius: {
|
|
82
|
+
"4xl": "2rem",
|
|
83
|
+
},
|
|
84
|
+
// Custom animations
|
|
85
|
+
animation: {
|
|
86
|
+
"fade-in": "fadeIn 0.3s ease-in-out",
|
|
87
|
+
"slide-up": "slideUp 0.3s ease-out",
|
|
88
|
+
"spin-slow": "spin 3s linear infinite",
|
|
89
|
+
"pulse-slow": "pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
|
90
|
+
},
|
|
91
|
+
keyframes: {
|
|
92
|
+
fadeIn: {
|
|
93
|
+
"0%": { opacity: "0" },
|
|
94
|
+
"100%": { opacity: "1" },
|
|
95
|
+
},
|
|
96
|
+
slideUp: {
|
|
97
|
+
"0%": { transform: "translateY(10px)", opacity: "0" },
|
|
98
|
+
"100%": { transform: "translateY(0)", opacity: "1" },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
// Custom box shadows
|
|
102
|
+
boxShadow: {
|
|
103
|
+
"soft": "0 2px 15px -3px rgba(0, 0, 0, 0.07), 0 10px 20px -2px rgba(0, 0, 0, 0.04)",
|
|
104
|
+
"inner-soft": "inset 0 2px 4px 0 rgba(0, 0, 0, 0.05)",
|
|
105
|
+
},
|
|
106
|
+
// Custom breakpoints
|
|
107
|
+
screens: {
|
|
108
|
+
"xs": "475px",
|
|
109
|
+
"3xl": "1920px",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
plugins: [
|
|
114
|
+
require("@tailwindcss/forms"),
|
|
115
|
+
require("@tailwindcss/typography"),
|
|
116
|
+
require("@tailwindcss/aspect-ratio"),
|
|
117
|
+
require("@tailwindcss/container-queries"),
|
|
118
|
+
],
|
|
119
|
+
};
|
|
21
120
|
```
|
|
22
121
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
122
|
+
```css
|
|
123
|
+
/* globals.css */
|
|
124
|
+
@tailwind base;
|
|
125
|
+
@tailwind components;
|
|
126
|
+
@tailwind utilities;
|
|
127
|
+
|
|
128
|
+
@layer base {
|
|
129
|
+
:root {
|
|
130
|
+
--background: 0 0% 100%;
|
|
131
|
+
--foreground: 222.2 84% 4.9%;
|
|
132
|
+
--primary: 221.2 83.2% 53.3%;
|
|
133
|
+
--primary-foreground: 210 40% 98%;
|
|
134
|
+
--secondary: 210 40% 96.1%;
|
|
135
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
136
|
+
--muted: 210 40% 96.1%;
|
|
137
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
138
|
+
--accent: 210 40% 96.1%;
|
|
139
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
140
|
+
--destructive: 0 84.2% 60.2%;
|
|
141
|
+
--destructive-foreground: 210 40% 98%;
|
|
142
|
+
--border: 214.3 31.8% 91.4%;
|
|
143
|
+
--input: 214.3 31.8% 91.4%;
|
|
144
|
+
--ring: 221.2 83.2% 53.3%;
|
|
145
|
+
--radius: 0.5rem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.dark {
|
|
149
|
+
--background: 222.2 84% 4.9%;
|
|
150
|
+
--foreground: 210 40% 98%;
|
|
151
|
+
--primary: 217.2 91.2% 59.8%;
|
|
152
|
+
--primary-foreground: 222.2 47.4% 11.2%;
|
|
153
|
+
--secondary: 217.2 32.6% 17.5%;
|
|
154
|
+
--secondary-foreground: 210 40% 98%;
|
|
155
|
+
--muted: 217.2 32.6% 17.5%;
|
|
156
|
+
--muted-foreground: 215 20.2% 65.1%;
|
|
157
|
+
--accent: 217.2 32.6% 17.5%;
|
|
158
|
+
--accent-foreground: 210 40% 98%;
|
|
159
|
+
--destructive: 0 62.8% 30.6%;
|
|
160
|
+
--destructive-foreground: 210 40% 98%;
|
|
161
|
+
--border: 217.2 32.6% 17.5%;
|
|
162
|
+
--input: 217.2 32.6% 17.5%;
|
|
163
|
+
--ring: 224.3 76.3% 48%;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@layer base {
|
|
168
|
+
* {
|
|
169
|
+
@apply border-border;
|
|
170
|
+
}
|
|
171
|
+
body {
|
|
172
|
+
@apply bg-background text-foreground;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
27
175
|
```
|
|
28
176
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
177
|
+
### 2. Component Patterns
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
// Button component with variants
|
|
181
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
182
|
+
variant?: "primary" | "secondary" | "outline" | "ghost" | "destructive";
|
|
183
|
+
size?: "sm" | "md" | "lg";
|
|
184
|
+
loading?: boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function Button({
|
|
188
|
+
variant = "primary",
|
|
189
|
+
size = "md",
|
|
190
|
+
loading,
|
|
191
|
+
className,
|
|
192
|
+
children,
|
|
193
|
+
disabled,
|
|
194
|
+
...props
|
|
195
|
+
}: ButtonProps) {
|
|
196
|
+
const baseStyles = `
|
|
197
|
+
inline-flex items-center justify-center
|
|
198
|
+
font-medium rounded-lg
|
|
199
|
+
transition-colors duration-200
|
|
200
|
+
focus:outline-none focus:ring-2 focus:ring-offset-2
|
|
201
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
const variants = {
|
|
205
|
+
primary: `
|
|
206
|
+
bg-primary-600 text-white
|
|
207
|
+
hover:bg-primary-700
|
|
208
|
+
focus:ring-primary-500
|
|
209
|
+
`,
|
|
210
|
+
secondary: `
|
|
211
|
+
bg-secondary-100 text-secondary-900
|
|
212
|
+
hover:bg-secondary-200
|
|
213
|
+
focus:ring-secondary-500
|
|
214
|
+
dark:bg-secondary-800 dark:text-secondary-100
|
|
215
|
+
`,
|
|
216
|
+
outline: `
|
|
217
|
+
border-2 border-primary-600 text-primary-600
|
|
218
|
+
hover:bg-primary-50
|
|
219
|
+
focus:ring-primary-500
|
|
220
|
+
dark:border-primary-400 dark:text-primary-400
|
|
221
|
+
`,
|
|
222
|
+
ghost: `
|
|
223
|
+
text-gray-700 hover:bg-gray-100
|
|
224
|
+
focus:ring-gray-500
|
|
225
|
+
dark:text-gray-300 dark:hover:bg-gray-800
|
|
226
|
+
`,
|
|
227
|
+
destructive: `
|
|
228
|
+
bg-red-600 text-white
|
|
229
|
+
hover:bg-red-700
|
|
230
|
+
focus:ring-red-500
|
|
231
|
+
`,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const sizes = {
|
|
235
|
+
sm: "px-3 py-1.5 text-sm gap-1.5",
|
|
236
|
+
md: "px-4 py-2 text-base gap-2",
|
|
237
|
+
lg: "px-6 py-3 text-lg gap-2.5",
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<button
|
|
242
|
+
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
|
|
243
|
+
disabled={disabled || loading}
|
|
244
|
+
{...props}
|
|
245
|
+
>
|
|
246
|
+
{loading && (
|
|
247
|
+
<svg
|
|
248
|
+
className="animate-spin h-4 w-4"
|
|
249
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
250
|
+
fill="none"
|
|
251
|
+
viewBox="0 0 24 24"
|
|
252
|
+
>
|
|
253
|
+
<circle
|
|
254
|
+
className="opacity-25"
|
|
255
|
+
cx="12"
|
|
256
|
+
cy="12"
|
|
257
|
+
r="10"
|
|
258
|
+
stroke="currentColor"
|
|
259
|
+
strokeWidth="4"
|
|
260
|
+
/>
|
|
261
|
+
<path
|
|
262
|
+
className="opacity-75"
|
|
263
|
+
fill="currentColor"
|
|
264
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
265
|
+
/>
|
|
266
|
+
</svg>
|
|
267
|
+
)}
|
|
268
|
+
{children}
|
|
269
|
+
</button>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Card component
|
|
274
|
+
interface CardProps {
|
|
275
|
+
children: React.ReactNode;
|
|
276
|
+
className?: string;
|
|
277
|
+
hoverable?: boolean;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function Card({ children, className, hoverable }: CardProps) {
|
|
281
|
+
return (
|
|
282
|
+
<div
|
|
283
|
+
className={`
|
|
284
|
+
bg-white dark:bg-gray-800
|
|
285
|
+
rounded-xl shadow-soft
|
|
286
|
+
border border-gray-200 dark:border-gray-700
|
|
287
|
+
${hoverable ? "hover:shadow-lg hover:-translate-y-1 transition-all duration-200" : ""}
|
|
288
|
+
${className}
|
|
289
|
+
`}
|
|
290
|
+
>
|
|
291
|
+
{children}
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function CardHeader({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
297
|
+
return (
|
|
298
|
+
<div className={`px-6 py-4 border-b border-gray-200 dark:border-gray-700 ${className}`}>
|
|
299
|
+
{children}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function CardContent({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
305
|
+
return <div className={`px-6 py-4 ${className}`}>{children}</div>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function CardFooter({ children, className }: { children: React.ReactNode; className?: string }) {
|
|
309
|
+
return (
|
|
310
|
+
<div className={`px-6 py-4 border-t border-gray-200 dark:border-gray-700 ${className}`}>
|
|
311
|
+
{children}
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Input component
|
|
317
|
+
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
318
|
+
label?: string;
|
|
319
|
+
error?: string;
|
|
320
|
+
hint?: string;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function Input({ label, error, hint, className, id, ...props }: InputProps) {
|
|
324
|
+
const inputId = id || label?.toLowerCase().replace(/\s/g, "-");
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<div className="space-y-1">
|
|
328
|
+
{label && (
|
|
329
|
+
<label
|
|
330
|
+
htmlFor={inputId}
|
|
331
|
+
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
332
|
+
>
|
|
333
|
+
{label}
|
|
334
|
+
{props.required && <span className="text-red-500 ml-1">*</span>}
|
|
335
|
+
</label>
|
|
336
|
+
)}
|
|
337
|
+
<input
|
|
338
|
+
id={inputId}
|
|
339
|
+
className={`
|
|
340
|
+
w-full px-4 py-2
|
|
341
|
+
border rounded-lg
|
|
342
|
+
bg-white dark:bg-gray-800
|
|
343
|
+
text-gray-900 dark:text-gray-100
|
|
344
|
+
placeholder-gray-400 dark:placeholder-gray-500
|
|
345
|
+
transition-colors duration-200
|
|
346
|
+
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
|
|
347
|
+
disabled:bg-gray-100 disabled:cursor-not-allowed
|
|
348
|
+
${error
|
|
349
|
+
? "border-red-500 focus:ring-red-500"
|
|
350
|
+
: "border-gray-300 dark:border-gray-600"
|
|
351
|
+
}
|
|
352
|
+
${className}
|
|
353
|
+
`}
|
|
354
|
+
{...props}
|
|
355
|
+
/>
|
|
356
|
+
{hint && !error && (
|
|
357
|
+
<p className="text-sm text-gray-500 dark:text-gray-400">{hint}</p>
|
|
358
|
+
)}
|
|
359
|
+
{error && (
|
|
360
|
+
<p className="text-sm text-red-500 dark:text-red-400">{error}</p>
|
|
361
|
+
)}
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
41
365
|
```
|
|
42
366
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
367
|
+
### 3. Responsive Design
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
// Responsive layout patterns
|
|
371
|
+
export function ResponsiveGrid({ children }: { children: React.ReactNode }) {
|
|
372
|
+
return (
|
|
373
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
|
374
|
+
{children}
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Responsive navigation
|
|
380
|
+
export function Navigation() {
|
|
381
|
+
return (
|
|
382
|
+
<nav className="bg-white dark:bg-gray-900 shadow-sm">
|
|
383
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
384
|
+
<div className="flex justify-between h-16">
|
|
385
|
+
{/* Logo */}
|
|
386
|
+
<div className="flex-shrink-0 flex items-center">
|
|
387
|
+
<Logo className="h-8 w-auto" />
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
{/* Desktop navigation */}
|
|
391
|
+
<div className="hidden md:flex md:items-center md:space-x-8">
|
|
392
|
+
<NavLink href="/">Home</NavLink>
|
|
393
|
+
<NavLink href="/products">Products</NavLink>
|
|
394
|
+
<NavLink href="/about">About</NavLink>
|
|
395
|
+
<NavLink href="/contact">Contact</NavLink>
|
|
396
|
+
</div>
|
|
397
|
+
|
|
398
|
+
{/* Mobile menu button */}
|
|
399
|
+
<div className="flex items-center md:hidden">
|
|
400
|
+
<button
|
|
401
|
+
className="
|
|
402
|
+
inline-flex items-center justify-center
|
|
403
|
+
p-2 rounded-md
|
|
404
|
+
text-gray-400 hover:text-gray-500 hover:bg-gray-100
|
|
405
|
+
focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500
|
|
406
|
+
"
|
|
407
|
+
>
|
|
408
|
+
<span className="sr-only">Open main menu</span>
|
|
409
|
+
<MenuIcon className="h-6 w-6" />
|
|
410
|
+
</button>
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
{/* Mobile menu */}
|
|
416
|
+
<div className="md:hidden">
|
|
417
|
+
<div className="px-2 pt-2 pb-3 space-y-1">
|
|
418
|
+
<MobileNavLink href="/">Home</MobileNavLink>
|
|
419
|
+
<MobileNavLink href="/products">Products</MobileNavLink>
|
|
420
|
+
<MobileNavLink href="/about">About</MobileNavLink>
|
|
421
|
+
<MobileNavLink href="/contact">Contact</MobileNavLink>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
</nav>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Responsive hero section
|
|
429
|
+
export function Hero() {
|
|
430
|
+
return (
|
|
431
|
+
<section className="relative bg-gray-900 overflow-hidden">
|
|
432
|
+
{/* Background pattern */}
|
|
433
|
+
<div className="absolute inset-0 bg-gradient-to-br from-primary-600 to-primary-900 opacity-90" />
|
|
434
|
+
|
|
435
|
+
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 sm:py-32 lg:py-40">
|
|
436
|
+
<div className="text-center lg:text-left lg:max-w-2xl">
|
|
437
|
+
{/* Responsive typography */}
|
|
438
|
+
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-white tracking-tight">
|
|
439
|
+
Build amazing products
|
|
440
|
+
<span className="block text-primary-300 mt-2">with confidence</span>
|
|
441
|
+
</h1>
|
|
442
|
+
|
|
443
|
+
<p className="mt-6 text-lg sm:text-xl text-gray-300 max-w-xl mx-auto lg:mx-0">
|
|
444
|
+
Start building your next project with our comprehensive component
|
|
445
|
+
library and design system.
|
|
446
|
+
</p>
|
|
447
|
+
|
|
448
|
+
{/* Responsive button layout */}
|
|
449
|
+
<div className="mt-10 flex flex-col sm:flex-row gap-4 justify-center lg:justify-start">
|
|
450
|
+
<Button size="lg" className="w-full sm:w-auto">
|
|
451
|
+
Get Started
|
|
452
|
+
</Button>
|
|
453
|
+
<Button variant="outline" size="lg" className="w-full sm:w-auto">
|
|
454
|
+
Learn More
|
|
455
|
+
</Button>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
</section>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### 4. Dark Mode
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
// Dark mode toggle
|
|
468
|
+
import { useEffect, useState } from "react";
|
|
469
|
+
|
|
470
|
+
export function DarkModeToggle() {
|
|
471
|
+
const [isDark, setIsDark] = useState(false);
|
|
472
|
+
|
|
473
|
+
useEffect(() => {
|
|
474
|
+
// Check initial preference
|
|
475
|
+
const isDarkMode =
|
|
476
|
+
localStorage.getItem("theme") === "dark" ||
|
|
477
|
+
(!localStorage.getItem("theme") &&
|
|
478
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
479
|
+
setIsDark(isDarkMode);
|
|
480
|
+
document.documentElement.classList.toggle("dark", isDarkMode);
|
|
481
|
+
}, []);
|
|
482
|
+
|
|
483
|
+
const toggleDarkMode = () => {
|
|
484
|
+
const newIsDark = !isDark;
|
|
485
|
+
setIsDark(newIsDark);
|
|
486
|
+
document.documentElement.classList.toggle("dark", newIsDark);
|
|
487
|
+
localStorage.setItem("theme", newIsDark ? "dark" : "light");
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<button
|
|
492
|
+
onClick={toggleDarkMode}
|
|
493
|
+
className="
|
|
494
|
+
p-2 rounded-lg
|
|
495
|
+
text-gray-500 hover:text-gray-700
|
|
496
|
+
dark:text-gray-400 dark:hover:text-gray-200
|
|
497
|
+
hover:bg-gray-100 dark:hover:bg-gray-800
|
|
498
|
+
transition-colors duration-200
|
|
499
|
+
"
|
|
500
|
+
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
|
|
501
|
+
>
|
|
502
|
+
{isDark ? (
|
|
503
|
+
<SunIcon className="h-5 w-5" />
|
|
504
|
+
) : (
|
|
505
|
+
<MoonIcon className="h-5 w-5" />
|
|
506
|
+
)}
|
|
507
|
+
</button>
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Dark mode aware components
|
|
512
|
+
export function DarkModeCard({ title, description }: { title: string; description: string }) {
|
|
513
|
+
return (
|
|
514
|
+
<div
|
|
515
|
+
className="
|
|
516
|
+
p-6 rounded-xl
|
|
517
|
+
bg-white dark:bg-gray-800
|
|
518
|
+
border border-gray-200 dark:border-gray-700
|
|
519
|
+
shadow-sm hover:shadow-md
|
|
520
|
+
transition-shadow duration-200
|
|
521
|
+
"
|
|
522
|
+
>
|
|
523
|
+
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
|
524
|
+
{title}
|
|
525
|
+
</h3>
|
|
526
|
+
<p className="mt-2 text-gray-600 dark:text-gray-300">
|
|
527
|
+
{description}
|
|
528
|
+
</p>
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### 5. Animation Patterns
|
|
535
|
+
|
|
536
|
+
```tsx
|
|
537
|
+
// Animated components
|
|
538
|
+
export function AnimatedCard({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
|
|
539
|
+
return (
|
|
540
|
+
<div
|
|
541
|
+
className="animate-slide-up opacity-0"
|
|
542
|
+
style={{
|
|
543
|
+
animationDelay: `${delay}ms`,
|
|
544
|
+
animationFillMode: "forwards",
|
|
545
|
+
}}
|
|
546
|
+
>
|
|
547
|
+
{children}
|
|
548
|
+
</div>
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Hover animations
|
|
553
|
+
export function HoverCard({ children }: { children: React.ReactNode }) {
|
|
554
|
+
return (
|
|
555
|
+
<div
|
|
556
|
+
className="
|
|
557
|
+
group
|
|
558
|
+
relative
|
|
559
|
+
bg-white dark:bg-gray-800
|
|
560
|
+
rounded-xl shadow-sm
|
|
561
|
+
transition-all duration-300
|
|
562
|
+
hover:shadow-xl hover:-translate-y-2
|
|
563
|
+
cursor-pointer
|
|
564
|
+
"
|
|
565
|
+
>
|
|
566
|
+
{/* Gradient overlay on hover */}
|
|
567
|
+
<div
|
|
568
|
+
className="
|
|
569
|
+
absolute inset-0 rounded-xl
|
|
570
|
+
bg-gradient-to-br from-primary-500/0 to-primary-500/0
|
|
571
|
+
group-hover:from-primary-500/5 group-hover:to-primary-500/10
|
|
572
|
+
transition-all duration-300
|
|
573
|
+
"
|
|
574
|
+
/>
|
|
575
|
+
<div className="relative z-10">{children}</div>
|
|
576
|
+
</div>
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Loading skeleton
|
|
581
|
+
export function Skeleton({ className }: { className?: string }) {
|
|
582
|
+
return (
|
|
583
|
+
<div
|
|
584
|
+
className={`
|
|
585
|
+
animate-pulse
|
|
586
|
+
bg-gray-200 dark:bg-gray-700
|
|
587
|
+
rounded
|
|
588
|
+
${className}
|
|
589
|
+
`}
|
|
590
|
+
/>
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export function CardSkeleton() {
|
|
595
|
+
return (
|
|
596
|
+
<div className="p-6 bg-white dark:bg-gray-800 rounded-xl">
|
|
597
|
+
<Skeleton className="h-6 w-3/4 mb-4" />
|
|
598
|
+
<Skeleton className="h-4 w-full mb-2" />
|
|
599
|
+
<Skeleton className="h-4 w-5/6 mb-2" />
|
|
600
|
+
<Skeleton className="h-4 w-4/6" />
|
|
601
|
+
<div className="mt-6 flex gap-2">
|
|
602
|
+
<Skeleton className="h-10 w-24" />
|
|
603
|
+
<Skeleton className="h-10 w-24" />
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Transition group
|
|
610
|
+
export function FadeTransition({ show, children }: { show: boolean; children: React.ReactNode }) {
|
|
611
|
+
return (
|
|
612
|
+
<div
|
|
613
|
+
className={`
|
|
614
|
+
transition-all duration-300 ease-in-out
|
|
615
|
+
${show ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-4 pointer-events-none"}
|
|
616
|
+
`}
|
|
617
|
+
>
|
|
618
|
+
{children}
|
|
619
|
+
</div>
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### 6. Utility Class Patterns
|
|
625
|
+
|
|
626
|
+
```tsx
|
|
627
|
+
// Using clsx/cn for conditional classes
|
|
628
|
+
import { clsx, type ClassValue } from "clsx";
|
|
629
|
+
import { twMerge } from "tailwind-merge";
|
|
630
|
+
|
|
631
|
+
export function cn(...inputs: ClassValue[]) {
|
|
632
|
+
return twMerge(clsx(inputs));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Usage
|
|
636
|
+
interface BadgeProps {
|
|
637
|
+
variant?: "default" | "success" | "warning" | "error";
|
|
638
|
+
children: React.ReactNode;
|
|
639
|
+
className?: string;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export function Badge({ variant = "default", children, className }: BadgeProps) {
|
|
643
|
+
return (
|
|
644
|
+
<span
|
|
645
|
+
className={cn(
|
|
646
|
+
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
|
|
647
|
+
{
|
|
648
|
+
"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200":
|
|
649
|
+
variant === "default",
|
|
650
|
+
"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200":
|
|
651
|
+
variant === "success",
|
|
652
|
+
"bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200":
|
|
653
|
+
variant === "warning",
|
|
654
|
+
"bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200":
|
|
655
|
+
variant === "error",
|
|
656
|
+
},
|
|
657
|
+
className
|
|
658
|
+
)}
|
|
659
|
+
>
|
|
660
|
+
{children}
|
|
661
|
+
</span>
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Responsive spacing utility
|
|
666
|
+
export function Stack({
|
|
667
|
+
children,
|
|
668
|
+
spacing = "md",
|
|
669
|
+
direction = "vertical",
|
|
670
|
+
}: {
|
|
671
|
+
children: React.ReactNode;
|
|
672
|
+
spacing?: "sm" | "md" | "lg";
|
|
673
|
+
direction?: "vertical" | "horizontal";
|
|
674
|
+
}) {
|
|
675
|
+
const spacingClasses = {
|
|
676
|
+
sm: direction === "vertical" ? "space-y-2" : "space-x-2",
|
|
677
|
+
md: direction === "vertical" ? "space-y-4" : "space-x-4",
|
|
678
|
+
lg: direction === "vertical" ? "space-y-6" : "space-x-6",
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
return (
|
|
682
|
+
<div
|
|
683
|
+
className={cn(
|
|
684
|
+
direction === "horizontal" && "flex items-center",
|
|
685
|
+
spacingClasses[spacing]
|
|
686
|
+
)}
|
|
687
|
+
>
|
|
688
|
+
{children}
|
|
689
|
+
</div>
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### 7. Form Patterns
|
|
695
|
+
|
|
696
|
+
```tsx
|
|
697
|
+
// Complete form example
|
|
698
|
+
export function ContactForm() {
|
|
699
|
+
return (
|
|
700
|
+
<form className="space-y-6">
|
|
701
|
+
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
|
702
|
+
<Input
|
|
703
|
+
label="First Name"
|
|
704
|
+
name="firstName"
|
|
705
|
+
placeholder="John"
|
|
706
|
+
required
|
|
707
|
+
/>
|
|
708
|
+
<Input
|
|
709
|
+
label="Last Name"
|
|
710
|
+
name="lastName"
|
|
711
|
+
placeholder="Doe"
|
|
712
|
+
required
|
|
713
|
+
/>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
716
|
+
<Input
|
|
717
|
+
label="Email"
|
|
718
|
+
name="email"
|
|
719
|
+
type="email"
|
|
720
|
+
placeholder="john@example.com"
|
|
721
|
+
required
|
|
722
|
+
/>
|
|
723
|
+
|
|
724
|
+
<div className="space-y-1">
|
|
725
|
+
<label
|
|
726
|
+
htmlFor="message"
|
|
727
|
+
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
|
728
|
+
>
|
|
729
|
+
Message
|
|
730
|
+
</label>
|
|
731
|
+
<textarea
|
|
732
|
+
id="message"
|
|
733
|
+
name="message"
|
|
734
|
+
rows={4}
|
|
735
|
+
className="
|
|
736
|
+
w-full px-4 py-2
|
|
737
|
+
border border-gray-300 dark:border-gray-600
|
|
738
|
+
rounded-lg
|
|
739
|
+
bg-white dark:bg-gray-800
|
|
740
|
+
text-gray-900 dark:text-gray-100
|
|
741
|
+
placeholder-gray-400 dark:placeholder-gray-500
|
|
742
|
+
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
|
|
743
|
+
resize-none
|
|
744
|
+
"
|
|
745
|
+
placeholder="Your message..."
|
|
746
|
+
/>
|
|
747
|
+
</div>
|
|
748
|
+
|
|
749
|
+
<div className="flex items-center">
|
|
750
|
+
<input
|
|
751
|
+
id="terms"
|
|
752
|
+
name="terms"
|
|
753
|
+
type="checkbox"
|
|
754
|
+
className="
|
|
755
|
+
h-4 w-4
|
|
756
|
+
rounded
|
|
757
|
+
border-gray-300
|
|
758
|
+
text-primary-600
|
|
759
|
+
focus:ring-primary-500
|
|
760
|
+
"
|
|
761
|
+
/>
|
|
762
|
+
<label
|
|
763
|
+
htmlFor="terms"
|
|
764
|
+
className="ml-2 text-sm text-gray-600 dark:text-gray-400"
|
|
765
|
+
>
|
|
766
|
+
I agree to the{" "}
|
|
767
|
+
<a href="/terms" className="text-primary-600 hover:underline">
|
|
768
|
+
terms and conditions
|
|
769
|
+
</a>
|
|
770
|
+
</label>
|
|
771
|
+
</div>
|
|
772
|
+
|
|
773
|
+
<Button type="submit" className="w-full sm:w-auto">
|
|
774
|
+
Send Message
|
|
775
|
+
</Button>
|
|
776
|
+
</form>
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
## Use Cases
|
|
782
|
+
|
|
783
|
+
### Responsive Dashboard Layout
|
|
784
|
+
|
|
785
|
+
```tsx
|
|
786
|
+
export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
787
|
+
return (
|
|
788
|
+
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
|
789
|
+
{/* Sidebar */}
|
|
790
|
+
<aside
|
|
791
|
+
className="
|
|
792
|
+
fixed inset-y-0 left-0
|
|
793
|
+
z-50 w-64
|
|
794
|
+
bg-white dark:bg-gray-800
|
|
795
|
+
border-r border-gray-200 dark:border-gray-700
|
|
796
|
+
transform -translate-x-full lg:translate-x-0
|
|
797
|
+
transition-transform duration-300
|
|
798
|
+
"
|
|
799
|
+
>
|
|
800
|
+
{/* Sidebar content */}
|
|
801
|
+
</aside>
|
|
802
|
+
|
|
803
|
+
{/* Main content */}
|
|
804
|
+
<main className="lg:pl-64">
|
|
805
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
806
|
+
{children}
|
|
807
|
+
</div>
|
|
808
|
+
</main>
|
|
809
|
+
</div>
|
|
810
|
+
);
|
|
811
|
+
}
|
|
46
812
|
```
|
|
47
813
|
|
|
48
814
|
## Best Practices
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- Use
|
|
815
|
+
|
|
816
|
+
### Do's
|
|
817
|
+
|
|
818
|
+
- Use mobile-first responsive design
|
|
819
|
+
- Leverage utility composition
|
|
820
|
+
- Create reusable component patterns
|
|
821
|
+
- Use CSS variables for theming
|
|
822
|
+
- Implement dark mode properly
|
|
823
|
+
- Use the cn() utility for conditional classes
|
|
824
|
+
- Configure purge/content correctly
|
|
825
|
+
- Use semantic class grouping
|
|
826
|
+
- Optimize for production build
|
|
827
|
+
- Follow consistent spacing scales
|
|
828
|
+
|
|
829
|
+
### Don'ts
|
|
830
|
+
|
|
831
|
+
- Don't overuse @apply
|
|
832
|
+
- Don't create overly specific utilities
|
|
833
|
+
- Don't ignore accessibility
|
|
834
|
+
- Don't repeat complex class combinations
|
|
835
|
+
- Don't hardcode colors outside theme
|
|
836
|
+
- Don't skip responsive testing
|
|
837
|
+
- Don't forget dark mode variants
|
|
838
|
+
- Don't use arbitrary values excessively
|
|
839
|
+
- Don't ignore the configuration file
|
|
840
|
+
- Don't mix styling paradigms
|
|
841
|
+
|
|
842
|
+
## References
|
|
843
|
+
|
|
844
|
+
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
845
|
+
- [Tailwind UI](https://tailwindui.com/)
|
|
846
|
+
- [Headless UI](https://headlessui.com/)
|
|
847
|
+
- [Tailwind CSS Best Practices](https://tailwindcss.com/docs/reusing-styles)
|
|
848
|
+
- [Tailwind CSS Plugins](https://tailwindcss.com/docs/plugins)
|