@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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/bin/ai-skills.js +5 -0
  4. package/dist/cli/commands/add.d.ts +2 -0
  5. package/dist/cli/commands/add.d.ts.map +1 -0
  6. package/dist/cli/commands/add.js +66 -0
  7. package/dist/cli/commands/add.js.map +1 -0
  8. package/dist/cli/commands/doctor.d.ts +2 -0
  9. package/dist/cli/commands/doctor.d.ts.map +1 -0
  10. package/dist/cli/commands/doctor.js +33 -0
  11. package/dist/cli/commands/doctor.js.map +1 -0
  12. package/dist/cli/commands/init.d.ts +10 -0
  13. package/dist/cli/commands/init.d.ts.map +1 -0
  14. package/dist/cli/commands/init.js +145 -0
  15. package/dist/cli/commands/init.js.map +1 -0
  16. package/dist/cli/commands/list.d.ts +5 -0
  17. package/dist/cli/commands/list.d.ts.map +1 -0
  18. package/dist/cli/commands/list.js +55 -0
  19. package/dist/cli/commands/list.js.map +1 -0
  20. package/dist/cli/commands/update.d.ts +2 -0
  21. package/dist/cli/commands/update.d.ts.map +1 -0
  22. package/dist/cli/commands/update.js +49 -0
  23. package/dist/cli/commands/update.js.map +1 -0
  24. package/dist/cli/commands/validate.d.ts +2 -0
  25. package/dist/cli/commands/validate.d.ts.map +1 -0
  26. package/dist/cli/commands/validate.js +22 -0
  27. package/dist/cli/commands/validate.js.map +1 -0
  28. package/dist/cli/index.d.ts +2 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +49 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/prompts/agent-selector.d.ts +3 -0
  33. package/dist/cli/prompts/agent-selector.d.ts.map +1 -0
  34. package/dist/cli/prompts/agent-selector.js +23 -0
  35. package/dist/cli/prompts/agent-selector.js.map +1 -0
  36. package/dist/cli/prompts/stack-selector.d.ts +3 -0
  37. package/dist/cli/prompts/stack-selector.d.ts.map +1 -0
  38. package/dist/cli/prompts/stack-selector.js +60 -0
  39. package/dist/cli/prompts/stack-selector.js.map +1 -0
  40. package/dist/core/config-manager.d.ts +20 -0
  41. package/dist/core/config-manager.d.ts.map +1 -0
  42. package/dist/core/config-manager.js +107 -0
  43. package/dist/core/config-manager.js.map +1 -0
  44. package/dist/core/detector.d.ts +3 -0
  45. package/dist/core/detector.d.ts.map +1 -0
  46. package/dist/core/detector.js +50 -0
  47. package/dist/core/detector.js.map +1 -0
  48. package/dist/core/doctor.d.ts +12 -0
  49. package/dist/core/doctor.d.ts.map +1 -0
  50. package/dist/core/doctor.js +102 -0
  51. package/dist/core/doctor.js.map +1 -0
  52. package/dist/core/skill-registry.d.ts +11 -0
  53. package/dist/core/skill-registry.d.ts.map +1 -0
  54. package/dist/core/skill-registry.js +174 -0
  55. package/dist/core/skill-registry.js.map +1 -0
  56. package/dist/core/skill-resolver.d.ts +3 -0
  57. package/dist/core/skill-resolver.d.ts.map +1 -0
  58. package/dist/core/skill-resolver.js +36 -0
  59. package/dist/core/skill-resolver.js.map +1 -0
  60. package/dist/core/validator.d.ts +13 -0
  61. package/dist/core/validator.d.ts.map +1 -0
  62. package/dist/core/validator.js +99 -0
  63. package/dist/core/validator.js.map +1 -0
  64. package/dist/generators/agent-installer.d.ts +5 -0
  65. package/dist/generators/agent-installer.d.ts.map +1 -0
  66. package/dist/generators/agent-installer.js +20 -0
  67. package/dist/generators/agent-installer.js.map +1 -0
  68. package/dist/generators/agents-md.d.ts +3 -0
  69. package/dist/generators/agents-md.d.ts.map +1 -0
  70. package/dist/generators/agents-md.js +70 -0
  71. package/dist/generators/agents-md.js.map +1 -0
  72. package/dist/generators/claude-md.d.ts +3 -0
  73. package/dist/generators/claude-md.d.ts.map +1 -0
  74. package/dist/generators/claude-md.js +47 -0
  75. package/dist/generators/claude-md.js.map +1 -0
  76. package/dist/generators/skill-generator.d.ts +5 -0
  77. package/dist/generators/skill-generator.d.ts.map +1 -0
  78. package/dist/generators/skill-generator.js +34 -0
  79. package/dist/generators/skill-generator.js.map +1 -0
  80. package/dist/generators/workflows.d.ts +3 -0
  81. package/dist/generators/workflows.d.ts.map +1 -0
  82. package/dist/generators/workflows.js +57 -0
  83. package/dist/generators/workflows.js.map +1 -0
  84. package/dist/index.d.ts +13 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +13 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/types/index.d.ts +55 -0
  89. package/dist/types/index.d.ts.map +1 -0
  90. package/dist/types/index.js +2 -0
  91. package/dist/types/index.js.map +1 -0
  92. package/dist/utils/file-utils.d.ts +12 -0
  93. package/dist/utils/file-utils.d.ts.map +1 -0
  94. package/dist/utils/file-utils.js +39 -0
  95. package/dist/utils/file-utils.js.map +1 -0
  96. package/dist/utils/logger.d.ts +10 -0
  97. package/dist/utils/logger.d.ts.map +1 -0
  98. package/dist/utils/logger.js +11 -0
  99. package/dist/utils/logger.js.map +1 -0
  100. package/package.json +73 -0
  101. package/skills/clean-architecture/SKILL.md +324 -0
  102. package/skills/express-mvc-prisma/SKILL.md +168 -0
  103. package/skills/express-mvc-prisma/references/auth.md +190 -0
  104. package/skills/express-mvc-prisma/references/boilerplate.md +196 -0
  105. package/skills/express-mvc-prisma/references/error-handling.md +121 -0
  106. package/skills/express-mvc-prisma/references/module-scaffold.md +253 -0
  107. package/skills/express-mvc-prisma/references/prisma-setup.md +97 -0
  108. package/skills/express-mvc-prisma/references/response-helpers.md +157 -0
  109. package/skills/express-mvc-prisma/references/zod-validation.md +157 -0
  110. package/skills/fastify-rest/SKILL.md +287 -0
  111. package/skills/mongoose-odm/SKILL.md +281 -0
  112. package/skills/nextjs-fullstack/SKILL.md +328 -0
  113. package/skills/nextjs-fullstack/references/auth.md +270 -0
  114. package/skills/nextjs-fullstack/references/caching.md +157 -0
  115. package/skills/nextjs-fullstack/references/route-handlers.md +194 -0
  116. package/skills/nextjs-fullstack/references/server-actions.md +214 -0
  117. package/skills/nextjs-fullstack/references/server-components.md +190 -0
  118. package/skills/node-base/SKILL.md +139 -0
  119. package/skills/prisma-orm/SKILL.md +334 -0
  120. package/skills/react-feature-arch/SKILL.md +208 -0
  121. package/skills/react-feature-arch/references/api-layer.md +110 -0
  122. package/skills/react-feature-arch/references/components.md +192 -0
  123. package/skills/react-feature-arch/references/data-fetching.md +198 -0
  124. package/skills/react-feature-arch/references/forms.md +194 -0
  125. package/skills/react-feature-arch/references/routing.md +148 -0
  126. package/skills/react-feature-arch/references/state-management.md +107 -0
  127. package/skills/tailwind-css/SKILL.md +236 -0
  128. package/skills/tailwind-css/references/components.md +340 -0
  129. package/skills/tailwind-css/references/design-tokens.md +230 -0
  130. package/skills/tailwind-css/references/patterns.md +375 -0
  131. package/skills/tailwind-css/references/setup.md +165 -0
  132. package/skills/zod-validation/SKILL.md +267 -0
@@ -0,0 +1,194 @@
1
+ # Forms Reference — React Hook Form v7 + Zod v4
2
+
3
+ ---
4
+
5
+ ## Schema File — {feature}.schema.ts
6
+
7
+ Define Zod schemas once. Derive TypeScript types with `z.infer<>`. Share types between forms, API calls, and hooks.
8
+
9
+ ```ts
10
+ // features/users/users.schema.ts
11
+ import { z } from 'zod';
12
+
13
+ export const createUserSchema = z.object({
14
+ name: z.string().min(2, 'Name must be at least 2 characters'),
15
+ email: z.string().email('Invalid email address'),
16
+ role: z.enum(['USER', 'ADMIN', 'MANAGER']).default('USER'),
17
+ });
18
+
19
+ export const updateUserSchema = createUserSchema.partial();
20
+
21
+ // Types derived from schema — single source of truth
22
+ export type CreateUserDto = z.infer<typeof createUserSchema>;
23
+ export type UpdateUserDto = z.infer<typeof updateUserSchema>;
24
+ export type User = { id: string; createdAt: string } & CreateUserDto;
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Create Form
30
+
31
+ ```tsx
32
+ // features/users/components/CreateUserForm.tsx
33
+ import { useForm } from 'react-hook-form';
34
+ import { zodResolver } from '@hookform/resolvers/zod';
35
+ import { createUserSchema, type CreateUserDto } from '../users.schema';
36
+ import { useCreateUser } from '../users.hooks';
37
+
38
+ interface Props {
39
+ onSuccess?: () => void;
40
+ }
41
+
42
+ export function CreateUserForm({ onSuccess }: Props) {
43
+ const createUser = useCreateUser();
44
+
45
+ const {
46
+ register,
47
+ handleSubmit,
48
+ reset,
49
+ formState: { errors, isSubmitting },
50
+ } = useForm<CreateUserDto>({
51
+ resolver: zodResolver(createUserSchema),
52
+ defaultValues: { role: 'USER' },
53
+ });
54
+
55
+ const onSubmit = async (data: CreateUserDto) => {
56
+ await createUser.mutateAsync(data);
57
+ reset();
58
+ onSuccess?.();
59
+ };
60
+
61
+ return (
62
+ <form onSubmit={handleSubmit(onSubmit)}>
63
+ <div>
64
+ <label>Name</label>
65
+ <input {...register('name')} placeholder="Full name" />
66
+ {errors.name && <span className="error">{errors.name.message}</span>}
67
+ </div>
68
+
69
+ <div>
70
+ <label>Email</label>
71
+ <input {...register('email')} type="email" placeholder="email@example.com" />
72
+ {errors.email && <span className="error">{errors.email.message}</span>}
73
+ </div>
74
+
75
+ <div>
76
+ <label>Role</label>
77
+ <select {...register('role')}>
78
+ <option value="USER">User</option>
79
+ <option value="ADMIN">Admin</option>
80
+ <option value="MANAGER">Manager</option>
81
+ </select>
82
+ </div>
83
+
84
+ {createUser.error && (
85
+ <p className="error">{(createUser.error as any).message}</p>
86
+ )}
87
+
88
+ <button type="submit" disabled={isSubmitting || createUser.isPending}>
89
+ {createUser.isPending ? 'Creating...' : 'Create User'}
90
+ </button>
91
+ </form>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Edit Form (pre-populated)
99
+
100
+ ```tsx
101
+ // features/users/components/EditUserForm.tsx
102
+ import { useForm } from 'react-hook-form';
103
+ import { zodResolver } from '@hookform/resolvers/zod';
104
+ import { updateUserSchema, type UpdateUserDto, type User } from '../users.schema';
105
+ import { useUpdateUser } from '../users.hooks';
106
+
107
+ export function EditUserForm({ user, onSuccess }: { user: User; onSuccess?: () => void }) {
108
+ const updateUser = useUpdateUser();
109
+
110
+ const {
111
+ register,
112
+ handleSubmit,
113
+ formState: { errors, isDirty, isSubmitting },
114
+ } = useForm<UpdateUserDto>({
115
+ resolver: zodResolver(updateUserSchema),
116
+ defaultValues: { name: user.name, email: user.email, role: user.role },
117
+ });
118
+
119
+ const onSubmit = async (data: UpdateUserDto) => {
120
+ await updateUser.mutateAsync({ id: user.id, data });
121
+ onSuccess?.();
122
+ };
123
+
124
+ return (
125
+ <form onSubmit={handleSubmit(onSubmit)}>
126
+ <input {...register('name')} />
127
+ {errors.name && <span>{errors.name.message}</span>}
128
+
129
+ <input {...register('email')} type="email" />
130
+ {errors.email && <span>{errors.email.message}</span>}
131
+
132
+ <button type="submit" disabled={!isDirty || isSubmitting || updateUser.isPending}>
133
+ {updateUser.isPending ? 'Saving...' : 'Save Changes'}
134
+ </button>
135
+ </form>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Cross-Field Validation
143
+
144
+ ```ts
145
+ const passwordSchema = z.object({
146
+ password: z.string().min(8, 'Password must be at least 8 characters'),
147
+ confirmPassword: z.string(),
148
+ }).refine((d) => d.password === d.confirmPassword, {
149
+ message: 'Passwords do not match',
150
+ path: ['confirmPassword'],
151
+ });
152
+ ```
153
+
154
+ ---
155
+
156
+ ## React 19 useActionState Form (no RHF — simple cases only)
157
+
158
+ Use RHF for complex forms. Use `useActionState` only for simple forms where progressive enhancement is needed.
159
+
160
+ ```tsx
161
+ import { useActionState } from 'react';
162
+ import { createUserSchema } from '../users.schema';
163
+ import { usersApi } from '../users.api';
164
+
165
+ async function submitAction(_prev: any, formData: FormData) {
166
+ const result = createUserSchema.safeParse({
167
+ name: formData.get('name'),
168
+ email: formData.get('email'),
169
+ });
170
+ if (!result.success) return { error: result.error.issues[0].message };
171
+ try {
172
+ await usersApi.create(result.data);
173
+ return { success: true };
174
+ } catch (err: any) {
175
+ return { error: err.message ?? 'Failed to create user' };
176
+ }
177
+ }
178
+
179
+ export function SimpleCreateForm() {
180
+ const [state, formAction, isPending] = useActionState(submitAction, null);
181
+
182
+ return (
183
+ <form action={formAction}>
184
+ <input name="name" required />
185
+ <input name="email" type="email" required />
186
+ {state?.error && <p className="error">{state.error}</p>}
187
+ {state?.success && <p className="success">User created!</p>}
188
+ <button type="submit" disabled={isPending}>
189
+ {isPending ? 'Saving...' : 'Create'}
190
+ </button>
191
+ </form>
192
+ );
193
+ }
194
+ ```
@@ -0,0 +1,148 @@
1
+ # Routing Reference — React Router v7
2
+
3
+ ---
4
+
5
+ ## src/app/router.tsx
6
+
7
+ ```tsx
8
+ import { createBrowserRouter, Navigate } from 'react-router';
9
+ import { lazy, Suspense } from 'react';
10
+ import { RootLayout } from '@/components/layout/RootLayout';
11
+ import { AuthLayout } from '@/components/layout/AuthLayout';
12
+ import { DashLayout } from '@/components/layout/DashLayout';
13
+ import { ProtectedRoute } from '@/components/layout/ProtectedRoute';
14
+ import { PageSpinner } from '@/components/ui/PageSpinner';
15
+
16
+ // Lazy-loaded route components — code-split per route
17
+ const LoginPage = lazy(() => import('@/features/auth/auth.page').then(m => ({ default: m.LoginPage })));
18
+ const RegisterPage = lazy(() => import('@/features/auth/register.page').then(m => ({ default: m.RegisterPage })));
19
+ const DashboardPage = lazy(() => import('@/features/dashboard/dashboard.page').then(m => ({ default: m.DashboardPage })));
20
+ const UsersPage = lazy(() => import('@/features/users/users.page').then(m => ({ default: m.UsersPage })));
21
+ const UserDetailPage = lazy(() => import('@/features/users/user-detail.page').then(m => ({ default: m.UserDetailPage })));
22
+
23
+ const withSuspense = (Component: React.ComponentType) => (
24
+ <Suspense fallback={<PageSpinner />}>
25
+ <Component />
26
+ </Suspense>
27
+ );
28
+
29
+ export const router = createBrowserRouter([
30
+ {
31
+ path: '/',
32
+ element: <RootLayout />,
33
+ children: [
34
+ { index: true, element: <Navigate to="/dashboard" replace /> },
35
+
36
+ // Public routes
37
+ {
38
+ path: 'auth',
39
+ element: <AuthLayout />,
40
+ children: [
41
+ { path: 'login', element: withSuspense(LoginPage) },
42
+ { path: 'register', element: withSuspense(RegisterPage) },
43
+ ],
44
+ },
45
+
46
+ // Protected routes
47
+ {
48
+ element: <ProtectedRoute />,
49
+ children: [
50
+ {
51
+ element: <DashLayout />,
52
+ children: [
53
+ { path: 'dashboard', element: withSuspense(DashboardPage) },
54
+ { path: 'users', element: withSuspense(UsersPage) },
55
+ { path: 'users/:id', element: withSuspense(UserDetailPage) },
56
+ ],
57
+ },
58
+ ],
59
+ },
60
+
61
+ // Role-restricted routes
62
+ {
63
+ element: <ProtectedRoute allowedRoles={['ADMIN']} />,
64
+ children: [
65
+ { path: 'admin', element: withSuspense(AdminPage) },
66
+ ],
67
+ },
68
+ ],
69
+ },
70
+ ]);
71
+ ```
72
+
73
+ ---
74
+
75
+ ## src/app/App.tsx
76
+
77
+ ```tsx
78
+ import { RouterProvider } from 'react-router';
79
+ import { router } from './router';
80
+ import { Providers } from './providers';
81
+
82
+ export default function App() {
83
+ return (
84
+ <Providers>
85
+ <RouterProvider router={router} />
86
+ </Providers>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## ProtectedRoute Component
94
+
95
+ ```tsx
96
+ // components/layout/ProtectedRoute.tsx
97
+ import { Navigate, Outlet } from 'react-router';
98
+ import { useShallow } from 'zustand/react/shallow';
99
+ import { useAuthStore } from '@/store/auth.store';
100
+
101
+ interface Props {
102
+ allowedRoles?: string[];
103
+ }
104
+
105
+ export function ProtectedRoute({ allowedRoles }: Props) {
106
+ const { isAuthenticated, user } = useAuthStore(
107
+ useShallow((s) => ({ isAuthenticated: s.isAuthenticated, user: s.user })),
108
+ );
109
+
110
+ if (!isAuthenticated) return <Navigate to="/auth/login" replace />;
111
+
112
+ if (allowedRoles && user && !allowedRoles.includes(user.role)) {
113
+ return <Navigate to="/unauthorized" replace />;
114
+ }
115
+
116
+ return <Outlet />;
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Navigation Hooks
123
+
124
+ ```tsx
125
+ import { useNavigate, useParams, useSearchParams, Link, NavLink } from 'react-router';
126
+
127
+ // Navigate programmatically
128
+ const navigate = useNavigate();
129
+ navigate('/users');
130
+ navigate(`/users/${id}`);
131
+ navigate(-1); // go back
132
+
133
+ // Route params
134
+ const { id } = useParams<{ id: string }>();
135
+
136
+ // Search params — for filters, pagination, tabs
137
+ const [searchParams, setSearchParams] = useSearchParams();
138
+ const page = Number(searchParams.get('page') ?? 1);
139
+ const tab = searchParams.get('tab') ?? 'all';
140
+
141
+ setSearchParams({ page: String(page + 1), tab });
142
+
143
+ // Link vs NavLink
144
+ <Link to={`/users/${user.id}`}>{user.name}</Link>
145
+ <NavLink to="/dashboard" className={({ isActive }) => isActive ? 'active' : ''}>
146
+ Dashboard
147
+ </NavLink>
148
+ ```
@@ -0,0 +1,107 @@
1
+ # State Management Reference — Zustand v5
2
+
3
+ Zustand manages **global UI state only** (auth session, theme, sidebar).
4
+ Server data lives in TanStack Query — never duplicate API responses in Zustand.
5
+
6
+ ---
7
+
8
+ ## Auth Store
9
+
10
+ ```ts
11
+ // src/store/auth.store.ts
12
+ import { create } from 'zustand';
13
+ import { persist } from 'zustand/middleware';
14
+
15
+ interface User {
16
+ id: string;
17
+ name: string;
18
+ email: string;
19
+ role: string;
20
+ }
21
+
22
+ interface AuthState {
23
+ user: User | null;
24
+ token: string | null;
25
+ isAuthenticated: boolean;
26
+ login: (user: User, token: string) => void;
27
+ logout: () => void;
28
+ }
29
+
30
+ export const useAuthStore = create<AuthState>()(
31
+ persist(
32
+ (set) => ({
33
+ user: null,
34
+ token: null,
35
+ isAuthenticated: false,
36
+ login: (user, token) => set({ user, token, isAuthenticated: true }),
37
+ logout: () => set({ user: null, token: null, isAuthenticated: false }),
38
+ }),
39
+ { name: 'auth-storage' }, // persisted to localStorage
40
+ ),
41
+ );
42
+ ```
43
+
44
+ ---
45
+
46
+ ## UI Store
47
+
48
+ ```ts
49
+ // src/store/ui.store.ts
50
+ import { create } from 'zustand';
51
+
52
+ interface UIState {
53
+ sidebarOpen: boolean;
54
+ theme: 'light' | 'dark';
55
+ toggleSidebar: () => void;
56
+ setTheme: (theme: 'light' | 'dark') => void;
57
+ }
58
+
59
+ export const useUIStore = create<UIState>()((set) => ({
60
+ sidebarOpen: true,
61
+ theme: 'light',
62
+ toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
63
+ setTheme: (theme) => set({ theme }),
64
+ }));
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Access State Outside React (e.g., axios interceptor)
70
+
71
+ ```ts
72
+ // Zustand v5 — getState() for accessing store outside a React component
73
+ import { useAuthStore } from '@/store/auth.store';
74
+
75
+ const token = useAuthStore.getState().token;
76
+ const logout = useAuthStore.getState().logout;
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Shallow Comparison (prevent extra re-renders)
82
+
83
+ ```tsx
84
+ import { useShallow } from 'zustand/react/shallow';
85
+
86
+ // Re-renders only when user OR isAuthenticated changes
87
+ const { user, isAuthenticated } = useAuthStore(
88
+ useShallow((s) => ({ user: s.user, isAuthenticated: s.isAuthenticated })),
89
+ );
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Usage in Components
95
+
96
+ ```tsx
97
+ // Selecting a single value — no shallow needed
98
+ const token = useAuthStore((s) => s.token);
99
+
100
+ // Selecting an action — always stable, no re-render risk
101
+ const logout = useAuthStore((s) => s.logout);
102
+
103
+ // Selecting multiple values — use useShallow
104
+ const { user, isAuthenticated } = useAuthStore(
105
+ useShallow((s) => ({ user: s.user, isAuthenticated: s.isAuthenticated })),
106
+ );
107
+ ```
@@ -0,0 +1,236 @@
1
+ ---
2
+ name: tailwind-css
3
+ version: 1.0.0
4
+ description: >
5
+ Use this skill for any Tailwind CSS task: project setup, design tokens, component styling, responsive design, dark mode, animations, or utility patterns. Triggers: "add Tailwind", "style with Tailwind", "dark mode", "responsive layout", "component variants", "design system", "tailwind v4", "cn helper", "CVA", "container queries", "shadcn".
6
+ stack: [tailwind, css]
7
+ depends: []
8
+ ---
9
+
10
+ # Tailwind CSS v4 — Production Skill
11
+
12
+ **Version target:** Tailwind CSS v4 · @tailwindcss/vite or @tailwindcss/postcss · TypeScript 5
13
+
14
+ ---
15
+
16
+ ## Tech Stack
17
+
18
+ | Tool | Package |
19
+ |---|---|
20
+ | Core | tailwindcss v4 |
21
+ | Vite integration | @tailwindcss/vite |
22
+ | Next.js integration | @tailwindcss/postcss |
23
+ | Class merging | clsx + tailwind-merge |
24
+ | Component variants | class-variance-authority (CVA) |
25
+ | Component library | shadcn/ui (optional) |
26
+ | Typography | @tailwindcss/typography |
27
+
28
+ ---
29
+
30
+ ## v4 vs v3 — Critical Differences
31
+
32
+ | Feature | v3 | v4 |
33
+ |---|---|---|
34
+ | Import | `@tailwind base/components/utilities` | `@import "tailwindcss"` (single line) |
35
+ | Config file | `tailwind.config.js` | CSS `@theme` directive (no JS config needed) |
36
+ | Custom tokens | JS `theme.extend` | `@theme` block in CSS |
37
+ | Custom utilities | Plugin system | `@utility` directive |
38
+ | Custom variants | Plugin `addVariant` | `@custom-variant` directive |
39
+ | Dark mode config | `darkMode: 'class'` in JS | `@custom-variant dark` in CSS |
40
+ | Container queries | External plugin | Built-in (`@container`, `@sm:`, `@md:`) |
41
+ | Gradients | `bg-gradient-to-r` | `bg-linear-to-r` |
42
+ | Content detection | `content: [...]` in config | Automatic — no config needed |
43
+ | Colors | HEX / HSL | HEX / HSL / **OKLCH** (preferred) |
44
+
45
+ ---
46
+
47
+ ## Setup
48
+
49
+ ### Vite + React
50
+
51
+ ```bash
52
+ npm install tailwindcss @tailwindcss/vite
53
+ ```
54
+
55
+ **vite.config.ts:**
56
+ ```ts
57
+ import { defineConfig } from 'vite';
58
+ import react from '@vitejs/plugin-react';
59
+ import tailwindcss from '@tailwindcss/vite';
60
+
61
+ export default defineConfig({
62
+ plugins: [react(), tailwindcss()],
63
+ });
64
+ ```
65
+
66
+ **src/index.css:**
67
+ ```css
68
+ @import "tailwindcss";
69
+ ```
70
+
71
+ ### Next.js 15
72
+
73
+ ```bash
74
+ npm install tailwindcss @tailwindcss/postcss postcss
75
+ ```
76
+
77
+ **postcss.config.mjs:**
78
+ ```js
79
+ export default { plugins: { '@tailwindcss/postcss': {} } };
80
+ ```
81
+
82
+ **src/app/globals.css:**
83
+ ```css
84
+ @import "tailwindcss";
85
+ ```
86
+
87
+ See `references/setup.md` for full setup including path aliases, typography plugin, and shadcn/ui init.
88
+
89
+ ---
90
+
91
+ ## Design Tokens — @theme
92
+
93
+ All design tokens live in `@theme` in your global CSS file. Tailwind auto-generates utility classes from them.
94
+
95
+ ```css
96
+ @import "tailwindcss";
97
+
98
+ @theme {
99
+ /* Colors — OKLCH preferred for perceptual uniformity */
100
+ --color-primary: oklch(0.55 0.25 262);
101
+ --color-secondary: oklch(0.84 0.16 84);
102
+ --color-danger: oklch(0.55 0.25 27);
103
+
104
+ /* Fonts */
105
+ --font-sans: 'Inter', sans-serif;
106
+ --font-mono: 'JetBrains Mono', monospace;
107
+
108
+ /* Border radius */
109
+ --radius-card: 0.75rem;
110
+ --radius-pill: 9999px;
111
+
112
+ /* Custom spacing */
113
+ --spacing-page: 2rem;
114
+ }
115
+ ```
116
+
117
+ Generated utilities: `bg-primary`, `text-secondary`, `border-danger`, `font-sans`, `rounded-card`, `p-page`, etc.
118
+
119
+ See `references/design-tokens.md` for the full token reference.
120
+
121
+ ---
122
+
123
+ ## Component Patterns
124
+
125
+ ### cn() Helper — Required for all components
126
+
127
+ ```ts
128
+ // src/lib/utils.ts
129
+ import { clsx, type ClassValue } from 'clsx';
130
+ import { twMerge } from 'tailwind-merge';
131
+
132
+ export function cn(...inputs: ClassValue[]) {
133
+ return twMerge(clsx(inputs));
134
+ }
135
+ ```
136
+
137
+ Allows callers to override classes without CSS conflicts:
138
+ ```tsx
139
+ <Button className="w-full" /> // overrides default width cleanly
140
+ ```
141
+
142
+ ### CVA — Component Variants
143
+
144
+ ```ts
145
+ import { cva, type VariantProps } from 'class-variance-authority';
146
+
147
+ const button = cva(
148
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',
149
+ {
150
+ variants: {
151
+ variant: {
152
+ default: 'bg-primary text-white hover:bg-primary/90',
153
+ outline: 'border border-current bg-transparent hover:bg-primary/10',
154
+ ghost: 'hover:bg-primary/10 hover:text-primary',
155
+ destructive: 'bg-danger text-white hover:bg-danger/90',
156
+ },
157
+ size: {
158
+ sm: 'h-8 px-3 text-sm',
159
+ md: 'h-10 px-4',
160
+ lg: 'h-12 px-6 text-lg',
161
+ icon: 'h-10 w-10',
162
+ },
163
+ },
164
+ defaultVariants: { variant: 'default', size: 'md' },
165
+ },
166
+ );
167
+
168
+ interface ButtonProps
169
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
170
+ VariantProps<typeof button> {}
171
+
172
+ export function Button({ className, variant, size, ...props }: ButtonProps) {
173
+ return (
174
+ <button className={cn(button({ variant, size }), className)} {...props} />
175
+ );
176
+ }
177
+ ```
178
+
179
+ See `references/components.md` for Card, Input, Badge, and Modal patterns.
180
+
181
+ ---
182
+
183
+ ## Responsive Design
184
+
185
+ **Mobile-first — always start with base (mobile), add larger breakpoints:**
186
+
187
+ ```html
188
+ <!-- Mobile: 1 col → md: 2 col → lg: 3 col -->
189
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
190
+
191
+ <!-- Mobile: full width → sm: auto -->
192
+ <button class="w-full sm:w-auto">Submit</button>
193
+ ```
194
+
195
+ **Container queries (v4 built-in) — adapt to parent size, not viewport:**
196
+
197
+ ```html
198
+ <div class="@container">
199
+ <div class="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3">
200
+ <!-- Layout based on container width, not screen width -->
201
+ </div>
202
+ </div>
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Dark Mode
208
+
209
+ ```css
210
+ /* Class-based dark mode in globals.css */
211
+ @custom-variant dark (&:where(.dark, .dark *));
212
+ ```
213
+
214
+ ```tsx
215
+ // Toggle on <html> element
216
+ document.documentElement.classList.toggle('dark');
217
+ ```
218
+
219
+ ```html
220
+ <div class="bg-white dark:bg-slate-900 text-slate-900 dark:text-white">
221
+ Content
222
+ </div>
223
+ ```
224
+
225
+ See `references/patterns.md` for full dark mode strategy, animations, and layout patterns.
226
+
227
+ ---
228
+
229
+ ## Core References
230
+
231
+ | Topic | File |
232
+ |---|---|
233
+ | Setup — Vite, Next.js, shadcn/ui | `references/setup.md` |
234
+ | Design tokens — @theme, colors, fonts, spacing | `references/design-tokens.md` |
235
+ | Components — cn(), CVA, Button, Card, Input | `references/components.md` |
236
+ | Patterns — responsive, dark mode, animations, typography | `references/patterns.md` |