create-tigra 3.0.5 → 3.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 (83) hide show
  1. package/README.md +4 -2
  2. package/bin/create-tigra.js +5 -19
  3. package/modules/email-verification/client/hooks/useVerification.ts +3 -3
  4. package/package.json +1 -1
  5. package/template/.agents/skills/security-audit/AI-AND-LLM.md +83 -0
  6. package/template/.agents/skills/security-audit/ATTACK-CLASSES.md +130 -0
  7. package/template/.agents/skills/security-audit/CLIENT-SIDE.md +83 -0
  8. package/template/.agents/skills/security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
  9. package/template/.agents/skills/security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
  10. package/template/.agents/skills/security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
  11. package/template/.agents/skills/security-audit/HUNTING.md +251 -0
  12. package/template/.agents/skills/security-audit/LICENSE +21 -0
  13. package/template/.agents/skills/security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
  14. package/template/.agents/skills/security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
  15. package/template/.agents/skills/security-audit/RECONNAISSANCE.md +156 -0
  16. package/template/.agents/skills/security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
  17. package/template/.agents/skills/security-audit/SKILL.md +192 -0
  18. package/template/.agents/skills/security-audit/SOURCE.md +5 -0
  19. package/template/.agents/skills/security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
  20. package/template/.agents/skills/security-audit/VALIDATION-AND-REPORTING.md +186 -0
  21. package/template/.agents/skills/security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
  22. package/template/.agents/skills/security-audit/report-schema.json +461 -0
  23. package/template/.agents/skills/security-audit/validate-coverage-ledger.cjs +872 -0
  24. package/template/.agents/skills/security-audit/validate-coverage-ledger.test.cjs +740 -0
  25. package/template/.agents/skills/security-audit/validate-findings.cjs +773 -0
  26. package/template/.agents/skills/security-audit/validate-findings.test.cjs +652 -0
  27. package/template/AGENTS.md +45 -0
  28. package/template/client/AGENTS.md +22 -0
  29. package/template/client/package-lock.json +410 -324
  30. package/template/client/package.json +3 -3
  31. package/template/client/src/app/(auth)/layout.tsx +9 -0
  32. package/template/client/src/app/(auth)/loading.tsx +7 -0
  33. package/template/client/src/app/(main)/layout.tsx +11 -0
  34. package/template/client/src/app/(main)/loading.tsx +7 -0
  35. package/template/client/src/app/globals.css +4 -0
  36. package/template/client/src/app/loading.tsx +2 -6
  37. package/template/client/src/app/not-found.tsx +2 -3
  38. package/template/client/src/app/providers.tsx +6 -3
  39. package/template/client/src/components/common/AppLink.tsx +84 -0
  40. package/template/client/src/components/common/EmptyState.tsx +2 -2
  41. package/template/client/src/components/common/Pagination.tsx +3 -2
  42. package/template/client/src/components/common/RouteLoadingShell.tsx +21 -0
  43. package/template/client/src/components/common/SmoothNavigationProvider.tsx +151 -0
  44. package/template/client/src/components/layout/Header.tsx +12 -12
  45. package/template/client/src/features/admin/hooks/useAdminSessions.ts +2 -2
  46. package/template/client/src/features/admin/hooks/useAdminUsers.ts +3 -3
  47. package/template/client/src/features/auth/components/AuthInitializer.tsx +3 -2
  48. package/template/client/src/features/auth/components/LoginForm.tsx +3 -3
  49. package/template/client/src/features/auth/components/RegisterForm.tsx +3 -3
  50. package/template/client/src/features/auth/hooks/useAuth.ts +2 -2
  51. package/template/client/src/features/auth/hooks/usePasswordReset.ts +2 -2
  52. package/template/client/src/hooks/useAppRouter.ts +40 -0
  53. package/template/client/src/styles/themes/default.css +1 -1
  54. package/template/gitignore +0 -6
  55. package/template/server/AGENTS.md +28 -0
  56. package/template/server/package-lock.json +671 -522
  57. package/template/server/package.json +8 -8
  58. package/template/_claude/QUICK_REFERENCE.md +0 -193
  59. package/template/_claude/README.md +0 -53
  60. package/template/_claude/commands/create-client.md +0 -878
  61. package/template/_claude/commands/create-server.md +0 -388
  62. package/template/_claude/hooks/restrict-paths.sh +0 -51
  63. package/template/_claude/rules/client/01-project-structure.md +0 -147
  64. package/template/_claude/rules/client/02-components-and-types.md +0 -146
  65. package/template/_claude/rules/client/03-data-and-state.md +0 -195
  66. package/template/_claude/rules/client/04-design-system.md +0 -408
  67. package/template/_claude/rules/client/05-security.md +0 -55
  68. package/template/_claude/rules/client/06-ux-checklist.md +0 -111
  69. package/template/_claude/rules/client/07-deployment.md +0 -99
  70. package/template/_claude/rules/client/08-lockfile-cross-platform.md +0 -79
  71. package/template/_claude/rules/client/core.md +0 -46
  72. package/template/_claude/rules/global/completion-reports.md +0 -178
  73. package/template/_claude/rules/global/core.md +0 -104
  74. package/template/_claude/rules/global/investigation-before-conclusions.md +0 -57
  75. package/template/_claude/rules/server/core.md +0 -52
  76. package/template/_claude/rules/server/database.md +0 -124
  77. package/template/_claude/rules/server/deployment.md +0 -78
  78. package/template/_claude/rules/server/project-conventions.md +0 -254
  79. package/template/_claude/rules/server/response-handling.md +0 -144
  80. package/template/_claude/settings.json +0 -15
  81. package/template/_claude/skills/clean-ui/SKILL.md +0 -63
  82. package/template/_claude/skills/role/SKILL.md +0 -39
  83. package/template/_claude/skills/theme/SKILL.md +0 -109
@@ -1,408 +0,0 @@
1
- > **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
2
-
3
- # Design System
4
-
5
- ## Philosophy: "Mobile-First Neuro-Minimalism"
6
-
7
- **Mobile is the default. Desktop is the enhancement.** 80%+ of traffic is mobile — design for thumbs first, cursors second.
8
-
9
- Clean, airy, "expensive" look inspired by Linear, Vercel, Stripe, Arc. Every visual decision reduces cognitive load. Every screen must feel like a native app on mobile.
10
-
11
- ---
12
-
13
- ## CSS Architecture (Source of Truth)
14
-
15
- This project uses **Tailwind CSS v4** with **HEX colors** and the `@theme inline` directive (not the legacy `tailwind.config.ts`).
16
-
17
- **Key differences from Tailwind v3:**
18
- - No `tailwind.config.ts` — all config is CSS-based via `@theme inline`
19
- - Colors use **HEX** values (e.g., `#c15f3c`), with `rgba()` for alpha values
20
- - `@custom-variant dark` replaces `darkMode: 'class'`
21
- - No `@layer base { :root { } }` — variables defined on `:root` via theme file
22
-
23
- ---
24
-
25
- ## Theme System (Color Management)
26
-
27
- **ALL color variables live in `src/styles/themes/default.css`, NOT in `globals.css` or components.** This is the single source of truth for the entire app's color palette.
28
-
29
- ### How It Works
30
-
31
- ```
32
- src/
33
- ├── app/globals.css ← imports the theme + defines smooth transitions
34
- └── styles/themes/
35
- └── default.css ← Claude-inspired warm palette (HEX)
36
- ```
37
-
38
- `globals.css` imports the theme:
39
-
40
- ```css
41
- @import "../styles/themes/default.css";
42
- ```
43
-
44
- ### Theme File Structure
45
-
46
- `default.css` defines ALL semantic color variables for both `:root` (light) and `.dark` (dark mode) using HEX:
47
-
48
- ```css
49
- :root {
50
- --radius: 0.625rem;
51
- --background: #f4f3ee;
52
- --foreground: #1a170f;
53
- --primary: #c15f3c;
54
- --primary-foreground: #ffffff;
55
- /* ... all ~35 semantic tokens */
56
- }
57
-
58
- .dark {
59
- --background: #15130d;
60
- --foreground: #e9e8e3;
61
- --primary: #d6724f;
62
- /* ... dark mode overrides for all tokens */
63
- }
64
- ```
65
-
66
- ### Customizing Colors
67
-
68
- To change the brand palette, edit the HEX values in `default.css`. That's it — every color in the app updates instantly for both light and dark modes.
69
-
70
- ### Smooth Theme Transitions
71
-
72
- `globals.css` includes a global transition rule in `@layer base` that smoothly animates color changes when toggling light/dark mode:
73
-
74
- ```css
75
- *, *::before, *::after {
76
- transition-property: background-color, color, border-color, box-shadow;
77
- transition-duration: 200ms;
78
- transition-timing-function: ease-out;
79
- }
80
- ```
81
-
82
- ### Light/Dark Mode Toggle
83
-
84
- - Managed by `next-themes` with `attribute="class"` and `defaultTheme="light"`
85
- - The `ThemeToggle` component (`components/common/ThemeToggle.tsx`) provides the UI
86
- - The Header component also includes a sun/moon toggle button
87
-
88
- ### CRITICAL RULES — Color Management
89
-
90
- 1. **NEVER add or modify color variables in `globals.css`.** All `:root` and `.dark` color variables belong in `default.css` only.
91
- 2. **NEVER hardcode hex/rgb values in components.** Always use semantic tokens (`bg-primary`, `text-foreground`).
92
- 3. **NEVER use OKLCH color values.** All colors must be HEX (e.g., `#c15f3c`). Use `rgba()` only when alpha transparency is needed.
93
- 4. **NEVER rename CSS variables.** The variable names (`--primary`, `--background`, `--muted`, etc.) are locked for consistency. Only edit their HEX values.
94
- 5. **NEVER modify the smooth transition rules in `globals.css`.** The `transition-property`, `transition-duration`, and `transition-timing-function` on `*` are part of the theme system and must not be changed or removed.
95
- 6. **NEVER modify the `@theme inline` block in `globals.css`.** It maps CSS vars to Tailwind — it does NOT define colors. Colors come from `default.css`.
96
- 7. **To change the brand palette**: edit the HEX values in `default.css`. Never scatter color values across multiple files.
97
- 8. **New semantic tokens**: If you need a new token (rare), add it to both `:root` and `.dark` in `default.css`.
98
-
99
- ---
100
-
101
- ## Font Preset System (Font Management)
102
-
103
- **Font families are defined in font preset files, NOT hardcoded in components.** This mirrors the color theme preset system — switch the entire font pairing by changing one import.
104
-
105
- ### How It Works
106
-
107
- ```
108
- src/
109
- ├── app/
110
- │ ├── layout.tsx ← loads fonts via next/font/google
111
- │ └── globals.css ← imports ONE font preset (switch here)
112
- └── styles/fonts/
113
- └── inter-jetbrains.css ← Default (Inter + JetBrains Mono)
114
- ```
115
-
116
- ### Three Semantic Font Roles
117
-
118
- | Role | CSS Variable | Tailwind Class | Default Font |
119
- |------|-------------|----------------|--------------|
120
- | Body text | `--font-sans-value` | `font-sans` | Inter |
121
- | Headings | `--font-heading-value` | `font-heading` | Inter |
122
- | Code/mono | `--font-mono-value` | `font-mono` | JetBrains Mono |
123
-
124
- ### How to Switch Fonts
125
-
126
- Switching fonts requires two changes:
127
-
128
- **Step 1 — Update font imports in `layout.tsx`:**
129
-
130
- ```tsx
131
- // Change these imports to your desired fonts
132
- import { Roboto, Fira_Code } from 'next/font/google';
133
-
134
- const roboto = Roboto({
135
- variable: '--font-roboto',
136
- subsets: ['latin'],
137
- weight: ['400', '500', '600', '700'],
138
- });
139
-
140
- const firaCode = Fira_Code({
141
- variable: '--font-fira-code',
142
- subsets: ['latin'],
143
- });
144
-
145
- // Update the className to use new variables
146
- <body className={`${roboto.variable} ${firaCode.variable} font-sans antialiased`}>
147
- ```
148
-
149
- **Step 2 — Update the font preset file (or create a new one):**
150
-
151
- ```css
152
- /* styles/fonts/roboto-fira.css */
153
- :root {
154
- --font-sans-value: var(--font-roboto);
155
- --font-heading-value: var(--font-roboto);
156
- --font-mono-value: var(--font-fira-code);
157
- }
158
- ```
159
-
160
- Then update the import in `globals.css`:
161
- ```css
162
- @import "../styles/fonts/roboto-fira.css";
163
- ```
164
-
165
- ### Font Preset Structure
166
-
167
- Each preset maps raw font variables (set by `next/font/google` in `layout.tsx`) to semantic roles:
168
-
169
- ```css
170
- :root {
171
- --font-sans-value: var(--font-inter); /* body text */
172
- --font-heading-value: var(--font-inter); /* headings */
173
- --font-mono-value: var(--font-jetbrains-mono); /* code */
174
- }
175
- ```
176
-
177
- ### Creating a Custom Font Preset
178
-
179
- 1. Choose your fonts from [Google Fonts](https://fonts.google.com)
180
- 2. Update `layout.tsx` — import fonts via `next/font/google`, set CSS variable names
181
- 3. Create a new preset file in `src/styles/fonts/` (or edit the existing one)
182
- 4. Map your font variables to the three semantic roles
183
- 5. Update the import in `globals.css` to point to your preset
184
-
185
- ### CRITICAL RULES — Font Management
186
-
187
- 1. **NEVER hardcode font-family values in components.** Always use Tailwind classes (`font-sans`, `font-heading`, `font-mono`).
188
- 2. **ALL font-family mappings live in the font preset file**, not in `globals.css` or components.
189
- 3. **To change fonts**: update `layout.tsx` imports + update the font preset file. Never scatter font-family values across the codebase.
190
- 4. **The `@theme inline` block in `globals.css` maps preset variables to Tailwind** — it does NOT define fonts. Fonts come from the preset.
191
- 5. **Fonts are loaded via `next/font/google`** — this self-hosts fonts automatically at build time. No external requests at runtime, no manual file downloads needed.
192
-
193
- ---
194
-
195
- ## Mobile-First Responsive Strategy
196
-
197
- **All Tailwind utilities are written for mobile first.** `md:` and `lg:` are progressive enhancements, not the other way around.
198
-
199
- ### Rules
200
-
201
- 1. **Write mobile styles as the base.** Add `md:` / `lg:` to override for larger screens. Never use `max-*:` breakpoints.
202
- 2. **Breakpoints**: `sm:` (640px) → `md:` (768px) → `lg:` (1024px) → `xl:` (1280px). Scale UP, never down.
203
- 3. **Test mobile first** during development. Open DevTools at 375px before checking desktop.
204
- 4. **Every screen must be fully usable at 375px width.** No horizontal scroll, no truncated actions, no hidden critical UI.
205
-
206
- ### Viewport & Safe Areas
207
-
208
- - **Use `dvh` instead of `vh`** for full-height layouts — accounts for mobile browser chrome (URL bar, bottom bar).
209
- - **Viewport meta**: `<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">`.
210
- - **Respect safe areas** on notched/dynamic island devices:
211
- - Bottom-fixed elements: add `pb-[env(safe-area-inset-bottom)]`
212
- - Top-fixed elements: add `pt-[env(safe-area-inset-top)]`
213
- - **No `100vh` anywhere.** Always `100dvh` or `min-h-dvh`.
214
-
215
- ### Anti-patterns
216
-
217
- - Do NOT write desktop-first classes like `w-1/3 max-md:w-full`. Write `w-full md:w-1/3`.
218
- - Do NOT hide mobile-critical content behind `hidden md:block`. Content strategy must work on mobile first.
219
- - Do NOT use fixed pixel widths. Use `w-full`, percentage-based, or `max-w-*` utilities.
220
-
221
- ## Color Usage
222
-
223
- | Token | Purpose | Example use |
224
- |---|---|---|
225
- | `primary` | Main CTAs, links, active states | "Get started" button |
226
- | `secondary` | Secondary actions | Cancel, back |
227
- | `destructive` | Delete, errors | Delete button, error alert |
228
- | `success` | Success states | "Action completed" |
229
- | `warning` | Warnings | "Pending approval" |
230
- | `info` | Information | "New feature" badge |
231
- | `muted` | Disabled, placeholders | Disabled input |
232
- | `accent` | Highlights | "Featured" badge |
233
- | `card` | Card backgrounds | Content card |
234
- | `border` | Borders, dividers | Card border |
235
-
236
- ### Color Rules
237
-
238
- 1. **Never hardcode**: No `bg-blue-500`, no `bg-[#3b82f6]`, no `style={{ color }}`. Always semantic tokens.
239
- 2. **Semantic names by purpose**: `bg-destructive` not `bg-red`.
240
- 3. **Always pair bg + foreground**: `bg-primary text-primary-foreground` for contrast.
241
- 4. **Single source of truth**: Change colors ONLY in the active theme preset file (`src/styles/themes/*.css`). Never in `globals.css`, never in components.
242
- 5. **90% monochrome**: 90% of UI uses `background`, `foreground`, `muted`, `border`. Color is the exception.
243
- 6. **Opacity for hierarchy**: Use `bg-primary/10`, `bg-primary/5` for tinted backgrounds.
244
-
245
- ---
246
-
247
- ## Surfaces & Depth
248
-
249
- - **Border radius**: `rounded-xl` (12px) or `rounded-2xl` (16px) for cards, modals, containers.
250
- - **Shadows** (layered by elevation):
251
- - Resting cards: `shadow-sm`
252
- - Hovered/elevated: `shadow-md` to `shadow-lg`
253
- - Modals/popovers: `shadow-xl`
254
- - **Glassmorphism**: Only on sticky headers, floating toolbars, modal backdrops. Never on content cards.
255
- Use `backdrop-blur-md` + `bg-background/80` in Tailwind.
256
- - **No pure black/white**: Use `--background` and `--foreground` tokens (already off-pure).
257
-
258
- ---
259
-
260
- ## Typography
261
-
262
- - **Font**: Defined by the active font preset (default: Inter for sans/heading, JetBrains Mono for mono). See "Font Preset System" above for how to switch.
263
- - **Headings**: Use `font-heading`. `text-wrap: balance`, `leading-tight`. Mobile-first responsive sizes:
264
- - H1: `text-2xl md:text-3xl lg:text-4xl`
265
- - H2: `text-xl md:text-2xl`
266
- - H3: `text-lg md:text-xl`
267
- - **Body**: `text-base`, `leading-relaxed`. Max reading width: `max-w-prose` (~65ch).
268
- - **Data/numbers**: Always `tabular-nums` for alignment.
269
- - **Captions/meta**: `text-sm text-muted-foreground`.
270
- - **Mobile readability**: Minimum `text-sm` (14px) for any readable text. Never go below 12px.
271
-
272
- ---
273
-
274
- ## Spacing
275
-
276
- - **Whitespace IS the divider.** Prefer spacing over visible borders/lines.
277
- - **Section gap = 2x internal gap**: Mobile: `space-y-10` between sections, `space-y-4` within. Desktop: `space-y-16` between sections, `space-y-6` within.
278
- - **Stick to scale**: `4, 6, 8, 12, 16, 20, 24` from Tailwind. Avoid arbitrary values.
279
- - **Container**: `container mx-auto px-4 sm:px-6 lg:px-8` (mobile gets comfortable 16px padding).
280
-
281
- ---
282
-
283
- ## Motion & Interactions
284
-
285
- Every interactive element MUST have visible `:active` and `:focus-visible` states. `:hover` is a desktop enhancement — never the only feedback.
286
-
287
- ### Standard Patterns (Mobile-First)
288
- ```
289
- Button: transition-all duration-200 ease-out active:scale-[0.97] md:hover:brightness-110
290
- Card: transition-all duration-300 ease-out active:scale-[0.98] md:hover:shadow-lg md:hover:-translate-y-0.5
291
- Link: transition-colors duration-150 active:opacity-70 md:hover:text-primary
292
- ```
293
-
294
- ### Rules
295
- - **`active:` is the primary feedback** on mobile. Tap must feel instant and responsive.
296
- - **`hover:` is desktop-only** — always prefix with `md:hover:` to avoid sticky hover on touch devices.
297
- - **No hover-gated functionality**: Anything revealed on hover (tooltips, menus) MUST have a tap/click alternative.
298
- - **Transform + opacity only** — never animate layout properties (`width`, `height`, `top`).
299
- - **Respect `prefers-reduced-motion`**: Use `motion-safe:` / `motion-reduce:` variants.
300
- - **Motion budget**: Max 2-3 animated elements in viewport at once.
301
- - **Zero CLS**: Animations must never cause layout shift.
302
-
303
- ---
304
-
305
- ## Touch & Interaction Design
306
-
307
- ### Touch Targets
308
-
309
- - **Minimum size**: 44x44px (`min-h-11 min-w-11`). Recommended: 48x48px (`min-h-12 min-w-12`).
310
- - **Spacing between targets**: Minimum 8px gap to prevent mis-taps.
311
- - **Icon-only buttons**: Use `p-2.5` or `p-3` to ensure the tap area is large enough even if the icon is small.
312
- - **Inline links in text**: Add `py-1` for vertical tap padding without affecting line height visually.
313
-
314
- ### Thumb Zone Design
315
-
316
- - **Primary actions in the bottom third** of the screen — thumbs naturally rest there.
317
- - **Avoid top corners** for critical interactive elements (hardest to reach one-handed).
318
- - **Sticky bottom CTAs**: Primary action buttons stick to bottom of viewport on mobile: `sticky bottom-0 pb-[env(safe-area-inset-bottom)]`.
319
- - **FABs (Floating Action Buttons)**: Position `bottom-6 right-4` for primary creation actions.
320
-
321
- ### Gesture Support
322
-
323
- - **Swipe-to-dismiss** on bottom sheets and drawers (use Vaul / shadcn Drawer).
324
- - **Pull-to-refresh** where contextually appropriate (feed pages, lists).
325
- - **Swipe actions on list items** for quick actions (archive, delete) — use sparingly, always with undo.
326
- - **Pinch-to-zoom** on images and maps — never disable native zoom.
327
-
328
- ### Mobile Navigation Patterns
329
-
330
- | Nav items | Mobile pattern | Desktop pattern |
331
- |---|---|---|
332
- | 2-5 core routes | **Bottom tab bar** (sticky, always visible) | Top horizontal nav |
333
- | 6+ routes | Bottom tab bar (4 items + "More") | Sidebar or top nav with dropdowns |
334
- | Contextual actions | **Bottom sheet** (Drawer component) | Dropdown menu or popover |
335
- | Filters/settings | Full-screen sheet or slide-over panel | Side panel or modal |
336
-
337
- - **Bottom nav is the default** on mobile. Top nav on `md:` and above.
338
- - **Bottom sheets over modals** for contextual actions on mobile — they're within thumb reach and feel native.
339
- - **Sticky action bars**: Form submit buttons, checkout CTAs — `sticky bottom-0` on mobile.
340
- - **No hamburger menus** for ≤5 items. Use bottom tab bar instead.
341
-
342
- ### BottomNav Overlap — Fixed Bottom Elements
343
-
344
- When a page has its own `fixed bottom-0` element (e.g., sticky order bar, floating CTA), it **will be hidden behind BottomNav** on mobile. The BottomNav is `fixed bottom-0 z-50` with a height of `4rem` (64px).
345
-
346
- **Define the CSS variable** in the BottomNav component:
347
- ```css
348
- :root {
349
- --bottom-nav-height: 4rem;
350
- }
351
- ```
352
-
353
- **Every fixed bottom element on a page** must offset itself above BottomNav on mobile:
354
- ```
355
- bottom-[var(--bottom-nav-height)] md:bottom-0
356
- ```
357
- Or the shorthand equivalent:
358
- ```
359
- bottom-16 md:bottom-0
360
- ```
361
-
362
- This is not optional — without it, BottomNav covers the element and users cannot tap it on mobile.
363
-
364
- ---
365
-
366
- ## Images
367
-
368
- - **Format priority**: AVIF > WebP > JPEG (Next.js `<Image>` handles this).
369
- - **LCP image**: Always add `priority` prop.
370
- - **Blur placeholder**: Use `placeholder="blur"` with `blurDataURL`.
371
- - **Always set** explicit `width`/`height` or use `aspect-ratio` to prevent CLS.
372
-
373
- ---
374
-
375
- ## Modern CSS Features (Use Where Appropriate)
376
-
377
- | Feature | Use for |
378
- |---|---|
379
- | `@container` | Component-level responsive behavior |
380
- | CSS Subgrid | Child alignment with parent grid |
381
- | `dvh` | Full-height layouts (avoids mobile browser bar) |
382
- | `<dialog>` | Modals (with glassmorphism backdrop) |
383
- | Popover API | Dropdowns, tooltips |
384
- | `:has()` | Parent-based styling without JS |
385
- | `content-visibility: auto` | Long lists/pages performance |
386
- | `@starting-style` | Entry animations |
387
-
388
- ---
389
-
390
- ## Component Visual Patterns
391
-
392
- - **Cards**: `rounded-xl border border-border/50 bg-card shadow-sm transition-all duration-300 active:scale-[0.98] md:hover:shadow-md md:hover:-translate-y-0.5`
393
- - **Empty states**: Centered, muted icon, 2-line text max, one clear CTA.
394
- - **Loading**: Skeleton loaders matching content shape. Show immediately, no delay.
395
- - **Modals (desktop)**: Max `max-w-lg`. Dismissible with Escape + backdrop click. Glassmorphism backdrop.
396
- - **Bottom sheets (mobile)**: Prefer over centered modals on mobile. Use shadcn Drawer (Vaul). Swipe-down to dismiss. Max 70% viewport height for partial sheets. Respect `pb-[env(safe-area-inset-bottom)]`.
397
- - **Lists**: Full-width on mobile (no horizontal padding on list items — let them bleed to edges for native feel). Add dividers with `border-b border-border/50`.
398
-
399
- ---
400
-
401
- ## Dark Mode
402
-
403
- - Use `next-themes` with `attribute="class"`, `defaultTheme="light"`.
404
- - Smooth transitions handled by the global CSS transition rules in `globals.css` — do NOT add `disableTransitionOnChange` to `ThemeProvider`.
405
- - Reduce shadow visibility in dark mode (use subtle light borders instead).
406
- - Consider `brightness-90` on images in dark mode.
407
- - Add `suppressHydrationWarning` to `<html>` tag.
408
- - **Hydration guard for conditional theme renders**: `useTheme()` returns `undefined` on the server. Any component that renders *different JSX* based on `theme` (e.g. showing a Sun icon OR a Moon icon — not both) will throw a hydration mismatch. Either render both icons and style the active one (see `components/common/ThemeToggle.tsx`), or add a `mounted` guard: `const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []);` and return `null`/a placeholder until `mounted` is true.
@@ -1,55 +0,0 @@
1
- > **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
2
-
3
- # Security
4
-
5
- ## Token Storage
6
-
7
- - **httpOnly cookies** (`access_token` + `refresh_token`) set by the server via `Set-Cookie` headers.
8
- - Tokens are **never accessible from JavaScript** — no localStorage, no Redux token state.
9
- - Axios sends cookies automatically via `withCredentials: true`.
10
- - `AuthInitializer` hydrates user state on page load by calling `getMe()` (cookie sent automatically).
11
- - On logout: call logout API endpoint (server clears cookies), dispatch `logout()` action, redirect to `/login`.
12
-
13
- ## Environment Variables
14
-
15
- - **Never prefix secrets with `NEXT_PUBLIC_`** — only public values (API URL, app name) get the prefix.
16
- - Server-only secrets (DB URL, JWT secret) have no prefix and are only accessible in Server Components / Server Actions.
17
- - Validate env with Zod at startup.
18
-
19
- ## File Upload Validation
20
-
21
- - Allowed types: `image/jpeg`, `image/png`, `image/webp`
22
- - Max size: 5MB
23
- - Validate both MIME type AND file extension.
24
-
25
- ## Security Headers (`next.config.ts`)
26
-
27
- ```
28
- X-Frame-Options: DENY
29
- X-Content-Type-Options: nosniff
30
- Referrer-Policy: origin-when-cross-origin
31
- X-XSS-Protection: 1; mode=block
32
- ```
33
-
34
- ## Content Security Policy
35
-
36
- ```
37
- default-src 'self';
38
- script-src 'self' 'unsafe-eval' 'unsafe-inline';
39
- style-src 'self' 'unsafe-inline';
40
- img-src 'self' blob: data: https: ${apiOrigin};
41
- font-src 'self';
42
- object-src 'none';
43
- base-uri 'self';
44
- form-action 'self';
45
- frame-ancestors 'none';
46
- connect-src 'self' ${NEXT_PUBLIC_API_BASE_URL};
47
- ```
48
-
49
- ## Rules
50
-
51
- 1. **Never inject raw HTML** without sanitizing it first (use DOMPurify with allowed tags/attrs).
52
- 2. **Never log sensitive data** (tokens, passwords, full user objects). Dev-only: log IDs at most.
53
- 3. **Validate all inputs** with Zod — client-side (UX) AND server-side (security).
54
- 4. **External links**: Always `target="_blank" rel="noopener noreferrer"`.
55
- 5. **Sanitize URLs**: Only allow `http:`, `https:`, `mailto:` protocols before rendering as links.
@@ -1,111 +0,0 @@
1
- > **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
2
-
3
- # Neuro-UX Checklist
4
-
5
- ## Mobile-First UX
6
-
7
- **80%+ of traffic is mobile. Design for thumbs, not cursors. Every screen must feel like a native app.**
8
-
9
- - Every screen must be fully usable on a 375px viewport. No exceptions.
10
- - **One-handed reachability**: critical actions in the thumb zone (bottom 60% of screen).
11
- - **App-like feel**: smooth transitions between views, no full-page flash reloads, instant feedback on every tap.
12
- - **Content above the fold**: the most important action or information visible without scrolling on mobile.
13
- - **Reduce input friction**: use native input types (`type="tel"`, `type="email"`, `inputMode="numeric"`), autofill, and smart defaults.
14
- - **Scroll is natural**: long pages are fine on mobile — vertical scroll is free. Horizontal scroll is forbidden.
15
-
16
- ---
17
-
18
- ## Cognitive Load — Miller's Law
19
-
20
- - **Max 5-7 interactive elements** per viewport section. Use progressive disclosure (tabs, accordions, "Show more") for more.
21
-
22
- ## Gestalt Principles
23
-
24
- - **Proximity**: Related items close together. Gap between groups = 2x gap within groups.
25
- - **Grid alignment**: All elements on a strict grid. No floating/misaligned elements.
26
- - **Similarity**: Same action = same visual style across all pages.
27
- - **Common region**: Group related controls in a visual container (`bg-muted/50` or subtle border).
28
-
29
- ## Instant Feedback
30
-
31
- | User action | Required feedback | Timing |
32
- |---|---|---|
33
- | Tap interactive | Press state (`active:scale-[0.97]`) | Instant |
34
- | Tap button | Press state + ripple or scale | Instant |
35
- | Submit form | Loading state on button + disable | Instant |
36
- | Successful action | Toast notification | <500ms |
37
- | Failed action | Inline error OR toast | <500ms |
38
- | Navigate | Skeleton or smooth page transition | Instant |
39
- | Swipe drawer/sheet | Follow finger + snap or dismiss | Instant |
40
- | Pull to refresh | Pull indicator + content reload | Instant |
41
- | Hover interactive (desktop only) | Color/shadow/scale change | <100ms |
42
-
43
- - **Optimistic UI**: Update UI immediately before server confirms. Revert on error.
44
- - **Skeletons over spinners. Always.** Spinners only inside buttons during submission.
45
- - **Touch feedback is non-negotiable**: Every tappable element must visually respond to touch immediately.
46
-
47
- ## Nielsen's 10 Heuristics (Mobile-Adapted)
48
-
49
- 1. **System status**: Show loading, toast on completion, inline validation as user types. On mobile: progress indicators for multi-step flows.
50
- 2. **Real-world match**: Human language ("Sign in" not "Authenticate"). Locale-formatted dates/currencies. Native input types (`type="email"`, `inputMode="numeric"`).
51
- 3. **User control**: Swipe gestures for dismiss/back. Bottom sheets dismissible by swipe-down. Every modal dismissible with Escape (desktop) + backdrop + swipe (mobile). Undo for destructive actions. Back always works — preserve scroll position on back navigation.
52
- 4. **Consistency**: Same action = same button style, position, label everywhere. Bottom nav consistent across all pages on mobile.
53
- 5. **Error prevention**: Real-time validation. Disable submit until valid. Type-appropriate inputs with correct keyboard (`inputMode`). Confirm destructive actions with bottom sheet confirmation on mobile.
54
- 6. **Recognition > recall**: Visible labels (not placeholder-only). Show recent searches. Bottom nav on mobile, top nav on desktop.
55
- 7. **Flexibility**: Keyboard shortcuts (Cmd+K) on desktop. Gesture shortcuts (swipe actions on list items) on mobile. Preserve filters in URL. Bulk actions where appropriate.
56
- 8. **Minimalist design**: Every element earns its place. Prefer whitespace over separators. On mobile: even more aggressive — reduce to one primary action per screen.
57
- 9. **Error recovery**: Say what went wrong + how to fix it. Highlight the field. Never clear form on error. On mobile: scroll to the first error field automatically.
58
- 10. **Help**: Contextual tooltips (tap-to-show on mobile, hover on desktop). Dismissible onboarding hints.
59
-
60
- ## Performance Targets
61
-
62
- | Metric | Mobile Target | Desktop Target |
63
- |---|---|---|
64
- | Lighthouse Performance | 90+ | 98+ |
65
- | Lighthouse Accessibility | 98+ | 98+ |
66
- | LCP | <2.5s | <2.0s |
67
- | CLS | 0 | 0 |
68
- | INP | <150ms | <200ms |
69
-
70
- - **Test on real mobile devices** or throttled Chrome DevTools (4x CPU slowdown, Fast 3G).
71
- - **Bundle size matters more on mobile**: lazy-load routes, code-split aggressively, defer non-critical JS.
72
-
73
- ## Accessibility
74
-
75
- - All interactive elements reachable via Tab in logical order.
76
- - Focus rings: `:focus-visible` only (not `:focus`). Style: `ring-2 ring-primary/50`.
77
- - Icon-only buttons: must have `aria-label`.
78
- - One `h1` per page. No skipped heading levels.
79
- - Dynamic content updates: `aria-live="polite"`.
80
- - All animations in `motion-safe:` variant.
81
- - WCAG AA contrast: 4.5:1 for text, 3:1 for large text/UI.
82
- - **Touch targets**: min 44x44px, recommended 48x48px. No exceptions.
83
- - **Spacing between touch targets**: min 8px gap to prevent mis-taps.
84
- - **No hover-only functionality**: everything accessible via tap on mobile.
85
-
86
- ## Microcopy
87
-
88
- - **Buttons**: Action verbs — "Save changes", "Create item", "Delete account". Never "Submit" or "OK".
89
- - **Toasts**: Under 10 words. Success: confirm. Error: what happened + what to do.
90
- - **Empty states**: Explain what this area is for + CTA to fill it. ("No items yet. Create your first one.")
91
- - **Form errors**: Specific to the field. Below the field. Red text + red border.
92
-
93
- ## Page Audit Checklist
94
-
95
- 1. Interactive elements per section ≤ 7?
96
- 2. All elements on grid?
97
- 3. Every button/link has active + focus-visible? (hover is `md:` only)
98
- 4. Skeletons for all async content?
99
- 5. Inline field-level errors on forms?
100
- 6. Every modal/sheet dismissible with Escape (desktop) + swipe (mobile)?
101
- 7. Tab through all elements in logical order?
102
- 8. Text passes WCAG AA contrast?
103
- 9. All animation in `motion-safe:`?
104
- 10. LCP image has `priority`?
105
- 11. All touch targets ≥ 44x44px?
106
- 12. Primary actions in thumb zone (bottom of screen)?
107
- 13. Bottom sheet used instead of centered modal on mobile?
108
- 14. Tested on 375px viewport width?
109
- 15. No hover-only functionality?
110
- 16. Safe areas respected on fixed/sticky elements?
111
- 17. Native input types used (`type="email"`, `inputMode="numeric"`, etc.)?
@@ -1,99 +0,0 @@
1
- > **SCOPE**: These rules apply specifically to the **client** directory (Next.js App Router).
2
-
3
- # Deployment & Docker
4
-
5
- This project is deployed via **Docker** on **Coolify** (or any Docker-based platform). The `Dockerfile` is the production deployment contract. Every code change must remain compatible with it.
6
-
7
- ---
8
-
9
- ## Dockerfile Architecture
10
-
11
- The client uses a **3-stage multi-stage build** with Next.js `standalone` output:
12
-
13
- ```
14
- Stage 1 (dependencies) → Installs all node_modules (cached layer)
15
- Stage 2 (builder) → Copies deps, builds Next.js with standalone output
16
- Stage 3 (production) → Alpine + dumb-init, non-root user, copies standalone + static + public
17
- ```
18
-
19
- **Entry point**: `node server.js` (generated by Next.js standalone output in `.next/standalone/`)
20
- **Health check**: `GET /` — returns 200 if the Next.js server is running.
21
-
22
- ### Critical Config Requirement
23
-
24
- `next.config.ts` **must** have `output: "standalone"`. Without it, the Dockerfile will fail because `.next/standalone/` won't be generated. Never remove this setting.
25
-
26
- ---
27
-
28
- ## Build Arguments (NEXT_PUBLIC_* Variables)
29
-
30
- Next.js **inlines** all `NEXT_PUBLIC_*` values into the JavaScript bundle at build time. They are NOT read from the environment at runtime. This means:
31
-
32
- - Every `NEXT_PUBLIC_*` variable must be declared as `ARG` + `ENV` in the **builder stage** of the Dockerfile.
33
- - They must be passed via `--build-arg` during `docker build` (or via Coolify's Build Arguments UI).
34
- - **If you add a new `NEXT_PUBLIC_*` env var, you MUST add it to the Dockerfile builder stage.**
35
-
36
- ```dockerfile
37
- # In the builder stage:
38
- ARG NEXT_PUBLIC_NEW_VAR
39
- ENV NEXT_PUBLIC_NEW_VAR=$NEXT_PUBLIC_NEW_VAR
40
- ```
41
-
42
- Current build arguments:
43
- - `NEXT_PUBLIC_API_BASE_URL` — Backend API URL (e.g., `https://api.yourdomain.com/api/v1`)
44
- - `NEXT_PUBLIC_APP_NAME` — App display name
45
-
46
- ---
47
-
48
- ## When to Update the Dockerfile
49
-
50
- | You did this... | Update Dockerfile? | What to change |
51
- |---|---|---|
52
- | Added a new npm dependency | No | Automatic — installed during build |
53
- | Added a native/system dependency (e.g., `sharp` for image optimization) | **Yes** | Add `apk add` in the production stage |
54
- | Added a new `NEXT_PUBLIC_*` env var | **Yes** | Add `ARG` + `ENV` in the builder stage |
55
- | Changed the public directory structure | No | Automatic — `COPY /app/public ./public` |
56
- | Added server-only env vars (no `NEXT_PUBLIC_` prefix) | No | Injected at runtime via Coolify |
57
- | Changed the default port | **Yes** | Update `ENV PORT`, `EXPOSE`, and `HEALTHCHECK` |
58
- | Added custom `next.config.ts` rewrites/redirects | No | Baked into the build automatically |
59
- | Added API routes (`app/api/`) | No | Included in standalone output |
60
- | Removed `output: "standalone"` from next.config.ts | **BREAKING** | Entire Dockerfile depends on standalone output |
61
-
62
- ---
63
-
64
- ## Critical Rules
65
-
66
- 1. **Never remove `output: "standalone"`** from `next.config.ts`. The entire Docker build depends on it. Without it, the standalone server won't be generated and the Dockerfile will fail.
67
-
68
- 2. **Every `NEXT_PUBLIC_*` var needs a Dockerfile `ARG`.** Next.js inlines these at build time. If you add one to `.env.example` but forget the Dockerfile, the value will be `undefined` in production.
69
-
70
- 3. **Static assets and public files are separate.** The standalone output does NOT include `.next/static/` or `public/`. The Dockerfile copies them explicitly. If you add a new top-level directory that must be available at runtime (unlikely), add a `COPY` line.
71
-
72
- 4. **Non-root user.** The app runs as `nextjs:nodejs` (UID 1001). Next.js standalone doesn't write to disk at runtime, so this is straightforward.
73
-
74
- 5. **No secrets in the image.** Server-only env vars (without `NEXT_PUBLIC_` prefix) are injected at runtime. Never hardcode secrets or use `ENV` for sensitive values in the Dockerfile.
75
-
76
- 6. **Keep `.dockerignore` in sync.** When adding directories that should NOT be in the build context (test fixtures, docs, Storybook), add them to `.dockerignore`. When adding files needed at build time, ensure they're not ignored.
77
-
78
- 7. **`HOSTNAME="0.0.0.0"`** is required. Without it, Next.js standalone only listens on `127.0.0.1` inside the container, making it unreachable from outside.
79
-
80
- ---
81
-
82
- ## Coolify-Specific Notes
83
-
84
- - **Build Arguments**: Set `NEXT_PUBLIC_API_BASE_URL` and `NEXT_PUBLIC_APP_NAME` in Coolify's "Build Arguments" section (not environment variables — those are runtime only).
85
- - **Environment Variables**: Server-only vars (database URLs, API keys used in Server Components/Route Handlers) go in Coolify's "Environment Variables" section — available at runtime.
86
- - **Port**: Default `3000`. Override via `PORT` environment variable in Coolify.
87
- - **No volume mounts needed**: Next.js client apps are stateless — no uploads, no local file writes.
88
-
89
- ---
90
-
91
- ## Files That Matter for Deployment
92
-
93
- | File | Purpose | Must exist? |
94
- |---|---|---|
95
- | `Dockerfile` | Production build instructions | Yes |
96
- | `.dockerignore` | Excludes files from Docker build context | Yes |
97
- | `next.config.ts` | Must have `output: "standalone"` | Yes |
98
- | `package.json` | Dependencies and build script | Yes |
99
- | `public/` | Static assets (favicon, images) | Yes |