opencastle 0.9.0 → 0.9.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencastle",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "type": "module",
5
5
  "description": "Multi-agent orchestration framework for AI coding assistants",
6
6
  "bin": {
@@ -1,25 +1,25 @@
1
1
  {
2
- "hash": "d4030ca8",
2
+ "hash": "7bf3afa3",
3
3
  "configHash": "30f8ea04",
4
- "lockfileHash": "30f69af6",
5
- "browserHash": "5c8328e9",
4
+ "lockfileHash": "4dee6fd1",
5
+ "browserHash": "388c1549",
6
6
  "optimized": {
7
7
  "astro > cssesc": {
8
8
  "src": "../../../../../node_modules/cssesc/cssesc.js",
9
9
  "file": "astro___cssesc.js",
10
- "fileHash": "5802e199",
10
+ "fileHash": "8b8909d3",
11
11
  "needsInterop": true
12
12
  },
13
13
  "astro > aria-query": {
14
14
  "src": "../../../../../node_modules/aria-query/lib/index.js",
15
15
  "file": "astro___aria-query.js",
16
- "fileHash": "3b8b6441",
16
+ "fileHash": "6711698e",
17
17
  "needsInterop": true
18
18
  },
19
19
  "astro > axobject-query": {
20
20
  "src": "../../../../../node_modules/axobject-query/lib/index.js",
21
21
  "file": "astro___axobject-query.js",
22
- "fileHash": "12a97af5",
22
+ "fileHash": "c309d205",
23
23
  "needsInterop": true
24
24
  }
25
25
  },
@@ -50,7 +50,9 @@ Agent file Skill Matrix Skill file
50
50
  | `api-layer` | | | API routes, validation, search architecture |
51
51
  | `data-pipeline` | | | ETL, scraping, data processing |
52
52
  | `testing` | | | Unit/integration tests, coverage, test planning |
53
- | `e2e-testing` | | | Browser automation, viewport testing, visual validation || `task-management` | | | Issue tracking, naming, priorities, workflow states |
53
+ | `e2e-testing` | | | Browser automation, viewport testing, visual validation |
54
+ | `task-management` | | | Issue tracking, naming, priorities, workflow states |
55
+
54
56
  ### Disciplines
55
57
 
56
58
  | Slot | Approach | Skill | Description |
@@ -19,6 +19,119 @@ All deployment configuration is project-specific. See [deployment-config.md](../
19
19
  - Use short cache durations for frequently changing assets (e.g., favicon: `max-age=86400`)
20
20
  - Load the **security-hardening** skill for full header inventory and CSP configuration
21
21
 
22
+ ## Environment Variable Management
23
+
24
+ ### Layering & Precedence
25
+
26
+ Environment variables follow a layered override model (lowest to highest priority):
27
+
28
+ 1. `.env` — shared defaults, committed to repo (no secrets)
29
+ 2. `.env.local` — developer-specific overrides, git-ignored
30
+ 3. `.env.production` / `.env.preview` — environment-specific values
31
+ 4. Platform-injected variables — set in hosting dashboard, highest priority
32
+
33
+ ### Validation at Startup
34
+
35
+ Validate required variables at application startup. Fail fast with clear messages:
36
+
37
+ ```typescript
38
+ // src/lib/env.ts
39
+ import { z } from 'zod';
40
+
41
+ const envSchema = z.object({
42
+ DATABASE_URL: z.string().url(),
43
+ API_SECRET: z.string().min(32),
44
+ PUBLIC_SITE_URL: z.string().url(),
45
+ CRON_SECRET: z.string().min(16),
46
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
47
+ });
48
+
49
+ export const env = envSchema.parse(process.env);
50
+ ```
51
+
52
+ ### Naming & .gitignore
53
+
54
+ - `PUBLIC_*` or `NEXT_PUBLIC_*` — safe to expose to the browser
55
+ - `SECRET_*` or `*_SECRET` — server-only, never bundled into client code
56
+ - `CRON_SECRET` — authenticates scheduled job endpoints
57
+ - Use `SCREAMING_SNAKE_CASE` for all variable names
58
+ - Always gitignore `.env.local`, `.env.*.local`, and `.env.production`
59
+
60
+ ## CI/CD Pipeline Patterns
61
+
62
+ ### Branch-Based Deployment
63
+
64
+ ```
65
+ main branch → Production deployment (auto)
66
+ feature/* → Preview deployment (auto)
67
+ fix/* → Preview deployment (auto)
68
+ ```
69
+
70
+ ### Generic Pipeline Stages
71
+
72
+ Every pipeline should include these stages in order:
73
+
74
+ 1. **Install** — restore dependencies from lockfile (`--frozen-lockfile`)
75
+ 2. **Lint** — static analysis and formatting checks
76
+ 3. **Test** — unit and integration tests with coverage
77
+ 4. **Build** — production build (catches type errors and build-time issues)
78
+ 5. **Deploy** — push artifacts to hosting platform
79
+
80
+ ### Cron Job Authentication
81
+
82
+ Protect scheduled endpoints with a shared secret:
83
+
84
+ ```typescript
85
+ // app/api/cron/route.ts
86
+ export async function GET(request: Request) {
87
+ const authHeader = request.headers.get('authorization');
88
+ if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
89
+ return new Response('Unauthorized', { status: 401 });
90
+ }
91
+ // ... run scheduled task
92
+ return Response.json({ ok: true });
93
+ }
94
+ ```
95
+
96
+ ## Caching Strategy
97
+
98
+ ### Cache Duration Reference
99
+
100
+ | Asset Type | `Cache-Control` Header | Rationale |
101
+ |---|---|---|
102
+ | Hashed static assets (JS, CSS) | `public, max-age=31536000, immutable` | Content-addressed filenames; safe to cache forever |
103
+ | Images / fonts | `public, max-age=31536000, immutable` | Typically fingerprinted; long-lived |
104
+ | Favicon / manifest | `public, max-age=86400` | Changes rarely but should refresh within a day |
105
+ | HTML pages (SSG) | `public, max-age=0, must-revalidate` | Serve stale while revalidating |
106
+ | API responses | `private, no-cache` | User-specific or frequently changing |
107
+ | Prerendered pages (ISR) | `public, s-maxage=3600, stale-while-revalidate=86400` | CDN caches for 1 hour, serves stale for up to 1 day |
108
+
109
+ Apply cache headers via framework config (e.g., `headers()` in `next.config.js`) or CDN rules. Match each route pattern to the appropriate duration from the table above.
110
+
111
+ ## Security Headers
112
+
113
+ Apply these headers globally via framework config or middleware. See the **security-hardening** skill for full CSP configuration.
114
+
115
+ ```javascript
116
+ // Recommended security headers
117
+ const securityHeaders = [
118
+ { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
119
+ { key: 'X-Content-Type-Options', value: 'nosniff' },
120
+ { key: 'X-Frame-Options', value: 'DENY' },
121
+ { key: 'X-XSS-Protection', value: '1; mode=block' },
122
+ { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
123
+ { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
124
+ { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline';" },
125
+ ];
126
+ ```
127
+
128
+ **Key rules:**
129
+
130
+ - HSTS `max-age` must be at least 1 year (31536000 seconds) for preload eligibility
131
+ - `X-Frame-Options: DENY` prevents clickjacking — use `SAMEORIGIN` only if you embed your own pages
132
+ - CSP should be as restrictive as possible; expand only when needed, document each exception
133
+ - Disable unused browser features via `Permissions-Policy`
134
+
22
135
  ## Release Process
23
136
 
24
137
  ### 1. Pre-Release Audit
@@ -31,7 +144,7 @@ All deployment configuration is project-specific. See [deployment-config.md](../
31
144
  - Identify features adjacent to changes and spot-check them
32
145
  - Run full test suites for all affected projects (not just changed files)
33
146
  - Check deployment preview builds for visual regressions
34
- - Verify critical user flows still work (homepage, search, venue detail)
147
+ - Verify critical user flows still work (e.g., primary navigation, form submissions, authenticated pages)
35
148
 
36
149
  ### 3. Changelog & Release Notes
37
150
  - Generate changelog from commit messages and PR titles since last release
@@ -48,4 +161,32 @@ All deployment configuration is project-specific. See [deployment-config.md](../
48
161
  - Confirm deployment succeeded on production
49
162
  - Smoke-test production URLs for critical pages
50
163
  - Monitor error rates and performance metrics post-release
51
- - Document rollback steps if issues arise
164
+ - Have rollback steps documented and ready (see § Rollback Procedures)
165
+
166
+ ## Rollback Procedures
167
+
168
+ **Two rollback strategies** (prefer platform-level when available):
169
+
170
+ 1. **Platform rollback** — promote the last known-good deployment from the hosting dashboard
171
+ 2. **Git revert** — `git revert -m 1 HEAD && git push origin main` (triggers a clean redeploy)
172
+
173
+ ### Rollback Checklist
174
+
175
+ - [ ] Confirm the issue is deployment-related (not a data or third-party issue)
176
+ - [ ] Roll back via platform or git revert — never force-push to `main`
177
+ - [ ] Verify the rollback deployment is healthy (smoke tests)
178
+ - [ ] Notify the team with a summary of what was rolled back and why
179
+ - [ ] Create a post-mortem ticket to investigate root cause
180
+
181
+ ## Anti-Patterns
182
+
183
+ | Anti-Pattern | Why It's Wrong | Correct Approach |
184
+ |---|---|---|
185
+ | Hardcoding secrets in source code | Secrets leak via git history, logs, and client bundles | Use environment variables; validate with Zod at startup |
186
+ | Skipping preview deployments | Bugs reach production without visual review | Deploy every branch to a preview environment |
187
+ | Using `Cache-Control: no-store` everywhere | Destroys performance; every request hits origin | Use appropriate cache durations per asset type (see table above) |
188
+ | Force-pushing to `main` to "fix" a bad deploy | Destroys git history; breaks other developers' branches | Use `git revert` to cleanly undo changes |
189
+ | Disabling security headers "temporarily" | Temporary becomes permanent; opens attack surface | Keep headers strict; expand only with documented exceptions |
190
+ | Running builds without `--frozen-lockfile` | Non-deterministic installs; works locally, fails in CI | Always use `--frozen-lockfile` (or equivalent) in CI |
191
+ | Storing `.env.local` in the repository | Developer secrets and tokens leak to all contributors | Add `.env.local` to `.gitignore`; share via secure vault |
192
+ | No startup validation of env vars | App starts but crashes later with cryptic errors | Validate all required variables at boot (fail fast) |
@@ -38,11 +38,9 @@ Generic documentation templates and writing standards. For project-specific dire
38
38
  ## Roadmap Update Template
39
39
 
40
40
  When a feature is completed:
41
- 1. Change status to `COMPLETE`
42
- 2. Add completion date
43
- 3. List modified files
44
- 4. Update the summary table at the top
45
- 5. Move to completed section if applicable
41
+ 1. Change status to `COMPLETE` and add completion date
42
+ 2. List modified files and update the summary table
43
+ 3. Move to completed section if applicable
46
44
 
47
45
  ## Architecture Decision Record Template
48
46
 
@@ -57,16 +55,107 @@ When a feature is completed:
57
55
  **Alternatives Considered:** [What else was evaluated]
58
56
  ```
59
57
 
58
+ ## README Template
59
+
60
+ Use this structure for library or feature-level READMEs:
61
+
62
+ ```markdown
63
+ # Feature / Library Name
64
+
65
+ One-sentence summary of what this does and why it exists.
66
+
67
+ ## Quick Start
68
+
69
+ Brief usage example or setup steps.
70
+
71
+ ## Architecture
72
+
73
+ High-level overview. Include a Mermaid diagram for non-trivial systems.
74
+
75
+ ## Key Files
76
+
77
+ | File | Purpose |
78
+ |------|---------|
79
+ | `src/handler.ts` | Request handling logic |
80
+ | `src/schema.ts` | Validation schemas |
81
+ ```
82
+
83
+ ## Mermaid Diagram Patterns
84
+
85
+ Keep diagrams focused — one concern per diagram.
86
+ ### Flowchart (decision logic, pipelines)
87
+
88
+ ```mermaid
89
+ flowchart TD
90
+ A[Receive Request] --> B{Authenticated?}
91
+ B -- Yes --> C[Process Request]
92
+ B -- No --> D[Return 401]
93
+ C --> E{Valid Input?}
94
+ E -- Yes --> F[Execute Action]
95
+ E -- No --> G[Return 400]
96
+ ```
97
+
98
+ ### Sequence Diagram (API flows, multi-service interactions)
99
+
100
+ ```mermaid
101
+ sequenceDiagram
102
+ participant Client
103
+ participant API
104
+ participant DB
105
+ Client->>API: POST /resource
106
+ API->>DB: INSERT record
107
+ DB-->>API: OK
108
+ API-->>Client: 201 Created
109
+ ```
110
+
111
+ ### ER Diagram (data models, relationships)
112
+
113
+ ```mermaid
114
+ erDiagram
115
+ USER ||--o{ ORDER : places
116
+ ORDER ||--|{ LINE_ITEM : contains
117
+ USER { string id PK; string email }
118
+ ORDER { string id PK; date created_at }
119
+ ```
120
+
121
+ ### Diagram Guidelines
122
+
123
+ - Add a title comment (`%% Title: ...`) at the top of complex diagrams
124
+ - Use verb labels on relationship arrows
125
+ - Limit nodes to 10–12 per diagram; split larger systems into sub-diagrams
126
+ - Prefer `flowchart TD` (top-down) for pipelines and `flowchart LR` (left-right) for request flows
127
+
128
+ ## Changelog Entry Template
129
+
130
+ Follow Conventional Commits format. Group entries by type under a version heading:
131
+
132
+ ```markdown
133
+ ## [1.2.0] — YYYY-MM-DD
134
+
135
+ ### Added
136
+ - feat: Add retry logic to API client (#123)
137
+
138
+ ### Fixed
139
+ - fix: Resolve race condition in queue processor (#127)
140
+
141
+ ### Changed
142
+ - refactor: Extract validation into shared module (#125)
143
+ ```
144
+
145
+ ### Changelog Rules
146
+
147
+ - One line per change; reference the PR or issue number
148
+ - Use imperative mood: "Add", "Fix", "Remove" — not "Added", "Fixed"
149
+ - Group under `Added`, `Fixed`, `Changed`, `Removed`, `Deprecated`, `Security`
150
+ - Most recent version at the top
151
+
60
152
  ## Writing Guidelines
61
153
 
62
154
  - Write clear, concise prose — avoid jargon unless necessary
63
- - Include diagrams (Mermaid or ASCII) for architecture
64
- - Link to related files and docs using relative paths
65
- - Keep line length under 400 characters; break at 80 for readability
66
- - Use tables for structured data
67
- - Include "Last Updated" dates on all documents
68
- - Archive outdated docs rather than deleting
69
- - Cross-reference between documents when relevant
155
+ - Include Mermaid diagrams for architecture
156
+ - Link to related files using relative paths
157
+ - Use tables for structured data; include "Last Updated" dates on all documents
158
+ - Archive outdated docs rather than deleting; cross-reference between documents
70
159
 
71
160
  ### Formatting Rules
72
161
 
@@ -85,3 +174,27 @@ Include YAML front matter at the beginning of instruction/skill files:
85
174
  - `title` / `name`: The title of the document
86
175
  - `description`: A brief description of the document content
87
176
  - `applyTo`: (for instruction files) Glob pattern for which files the instructions apply to
177
+
178
+ ## Documentation Review Checklist
179
+
180
+ Before merging docs, verify:
181
+
182
+ - [ ] **Accuracy** — all code snippets, file paths, and commands are correct and tested
183
+ - [ ] **Completeness** — no TODO placeholders or empty sections remain
184
+ - [ ] **Links** — all internal and external links resolve (no 404s)
185
+ - [ ] **Front matter** — YAML front matter is present and valid
186
+ - [ ] **Formatting** — consistent heading levels, list style, whitespace, and Mermaid renders
187
+ - [ ] **Cross-references** — related docs link to each other; "Last Updated" is current
188
+
189
+ ## Anti-Patterns
190
+
191
+ | Anti-Pattern | Why It's Bad | Do This Instead |
192
+ |-------------|-------------|-----------------|
193
+ | Wall of text with no headings | Unnavigable; readers skip it | Break into sections with H2/H3 |
194
+ | Duplicating content across files | Copies drift; causes confusion | Link to a single source of truth |
195
+ | Screenshots without alt text | Inaccessible; breaks when UI changes | Use Mermaid diagrams or describe the UI |
196
+ | Documenting implementation details | Becomes stale as code changes | Document intent and contracts |
197
+ | Using absolute file paths | Breaks on other machines | Use relative paths from doc location |
198
+ | Huge monolithic README | Low signal-to-noise | Split into focused docs, link from README |
199
+ | Undated documents | No way to judge currency | Always include "Last Updated" date |
200
+ | Using H1 inside document body | Conflicts with auto-generated title | Start body headings at H2 |
@@ -41,4 +41,155 @@ Interpret creatively and make unexpected choices that feel genuinely designed fo
41
41
 
42
42
  **IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
43
43
 
44
- Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
44
+ Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
45
+
46
+ ## Design System Foundations
47
+
48
+ Every design starts with a token layer. Define CSS custom properties that encode the aesthetic direction — never scatter raw values through stylesheets. A well-structured variable system makes the entire interface feel cohesive even as complexity grows.
49
+
50
+ ```css
51
+ /* --- Palette: warm editorial with a punch of citron --- */
52
+ :root {
53
+ --color-ink: #1a1614;
54
+ --color-paper: #f5f0e8;
55
+ --color-accent: #c8e630; /* citron — the memorable detail */
56
+ --color-muted: #9b9083;
57
+ --color-surface: #eae3d8;
58
+ --color-border: rgba(26, 22, 20, 0.08);
59
+
60
+ /* Typography scale — modular ratio 1.25 (Major Third) */
61
+ --text-sm: clamp(0.875rem, 0.83rem + 0.22vw, 1rem);
62
+ --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
63
+ --text-xl: clamp(1.563rem, 1.35rem + 1.06vw, 2rem);
64
+ --text-2xl: clamp(1.953rem, 1.6rem + 1.77vw, 2.75rem);
65
+ --text-hero: clamp(2.441rem, 1.8rem + 3.2vw, 4.5rem);
66
+
67
+ /* Spacing — 4px base, geometric progression */
68
+ --space-2: 0.5rem; --space-4: 1rem;
69
+ --space-6: 1.5rem; --space-8: 2rem;
70
+ --space-16: 4rem; --space-32: 8rem;
71
+
72
+ /* Motion — intentional easing curves, not defaults */
73
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
74
+ --ease-in-out-back: cubic-bezier(0.68, -0.6, 0.32, 1.6);
75
+ --duration-fast: 150ms;
76
+ --duration-normal: 300ms;
77
+ --duration-slow: 600ms;
78
+
79
+ /* Elevation */
80
+ --shadow-md: 0 4px 16px rgba(26, 22, 20, 0.08);
81
+ --shadow-lg: 0 12px 48px rgba(26, 22, 20, 0.12);
82
+ }
83
+ ```
84
+
85
+ **Anti-pattern:** Never scatter raw hex/px values through stylesheets. Every value should trace back to a token. Change the palette once and the entire interface follows.
86
+
87
+ ## Component Patterns
88
+
89
+ ### Distinctive Card
90
+
91
+ A card should never look like a Bootstrap default. Give it tension — an unexpected border treatment, an oversized label, or a hover that reveals hidden depth.
92
+
93
+ ```css
94
+ .card {
95
+ position: relative;
96
+ background: var(--color-paper);
97
+ border: 1px solid var(--color-border);
98
+ border-left: 4px solid var(--color-accent);
99
+ padding: var(--space-8) var(--space-6);
100
+ transition: transform var(--duration-normal) var(--ease-out-expo),
101
+ box-shadow var(--duration-normal) var(--ease-out-expo);
102
+ }
103
+
104
+ .card:hover {
105
+ transform: translateY(-3px);
106
+ box-shadow: var(--shadow-lg);
107
+ }
108
+
109
+ .card__label {
110
+ position: absolute;
111
+ top: calc(-1 * var(--space-3));
112
+ left: var(--space-4);
113
+ background: var(--color-accent);
114
+ color: var(--color-ink);
115
+ font-size: var(--text-xs);
116
+ font-weight: 700;
117
+ letter-spacing: 0.08em;
118
+ text-transform: uppercase;
119
+ padding: var(--space-1) var(--space-3);
120
+ }
121
+ ```
122
+
123
+ ### Hero Section with Staggered Reveal
124
+
125
+ Orchestrate entrance animations with `animation-delay` for a cinematic first impression. One coordinated sequence beats a dozen scattered `fadeIn`s.
126
+
127
+ ```css
128
+ @keyframes rise {
129
+ from { opacity: 0; transform: translateY(24px); }
130
+ to { opacity: 1; transform: translateY(0); }
131
+ }
132
+
133
+ .hero { overflow: hidden; padding: var(--space-32) var(--space-8); }
134
+ .hero__eyebrow { animation: rise var(--duration-slow) var(--ease-out-expo) both; animation-delay: 100ms; }
135
+ .hero__headline { animation: rise var(--duration-slow) var(--ease-out-expo) both; animation-delay: 250ms; }
136
+ .hero__body { animation: rise var(--duration-slow) var(--ease-out-expo) both; animation-delay: 400ms; }
137
+ .hero__cta { animation: rise var(--duration-slow) var(--ease-out-expo) both; animation-delay: 550ms; }
138
+ ```
139
+
140
+ **Anti-pattern:** Don't animate everything. If the nav bounces, the sidebar slides, and the footer pulses — it's visual noise, not design. Motion has a narrative: one orchestrated entrance, then stillness.
141
+
142
+ ## Typography Pairing Examples
143
+
144
+ Never reach for the same font twice. Each project deserves a pairing chosen for its specific character. These are starting points — not a rotation list.
145
+
146
+ | Aesthetic | Display | Body | Mood |
147
+ |-----------|---------|------|------|
148
+ | Editorial luxury | Playfair Display | Source Serif 4 | Authoritative, rich serif contrast |
149
+ | Swiss precision | Darker Grotesque | IBM Plex Sans | Sharp, high-contrast grotesque |
150
+ | Warm humanist | Fraunces | Nunito Sans | Friendly optical sizes, approachable |
151
+ | Brutalist edge | Monument Extended | JetBrains Mono | Wide + monospace = raw technical power |
152
+ | Art nouveau organic | Cormorant Garamond | Lora | Flowing, calligraphic sensibility |
153
+ | Retro-futuristic | Syne | Outfit | Geometric boldness meets clean body |
154
+
155
+ Always include a fallback chain that preserves metrics: `'Fraunces', 'Georgia', serif` not just `'Fraunces', serif`. And never default to the same "safe" choices (Inter, Roboto, system-ui) — if every project looks the same, the typography isn't doing its job.
156
+
157
+ ## Design Quality Checklist
158
+
159
+ Run this checklist before delivering any frontend work. Every item is a gate — if something fails, the design isn't finished.
160
+
161
+ ### Identity & Cohesion
162
+ - [ ] Can you name the aesthetic direction in 2-3 words? (e.g., "warm editorial," "cold brutalist")
163
+ - [ ] Are color, typography, spacing, and motion all telling the same visual story?
164
+ - [ ] Is there at least one memorable detail — something unexpected that delights?
165
+
166
+ ### Typography
167
+ - [ ] Display and body fonts are distinct and intentionally paired
168
+ - [ ] Type scale uses `clamp()` for fluid responsive sizing — no fixed `px` breakpoints
169
+ - [ ] Line heights are tuned: ~1.1–1.2 for headings, ~1.5–1.7 for body
170
+ - [ ] Letter-spacing is adjusted for uppercase text and small sizes
171
+
172
+ ### Color & Contrast
173
+ - [ ] Palette is defined as CSS custom properties — no raw hex in component styles
174
+ - [ ] There is a clear dominant/accent hierarchy — not five competing colors
175
+ - [ ] Text passes WCAG AA contrast minimums (4.5:1 body, 3:1 large text)
176
+ - [ ] Dark/light theme (if applicable) is not just color inversion — both feel intentional
177
+
178
+ ### Layout & Spacing
179
+ - [ ] Spacing flows from a consistent scale — no random `margin: 37px`
180
+ - [ ] At least one layout choice breaks the expected grid — overlap, bleed, asymmetry
181
+ - [ ] Component padding and gaps use spacing tokens, not ad-hoc values
182
+ - [ ] The design holds at mobile, tablet, and desktop without layout collapse
183
+
184
+ ### Motion & Interaction
185
+ - [ ] Page entrance has a coordinated animation sequence (staggered reveals)
186
+ - [ ] Hover/focus states exist for all interactive elements
187
+ - [ ] Animations use custom easing curves — never `linear` or bare `ease`
188
+ - [ ] Motion serves narrative purpose — no decoration-only animation
189
+ - [ ] `prefers-reduced-motion` is respected with a `@media` query fallback
190
+
191
+ ### Production Readiness
192
+ - [ ] No hardcoded widths that break at unexpected viewports
193
+ - [ ] Images and decorative elements have proper `alt` text or `aria-hidden`
194
+ - [ ] Focus indicators are visible and styled to match the aesthetic
195
+ - [ ] Performance: no layout thrashing from scroll-triggered animations without `will-change`
@@ -9,11 +9,9 @@ description: "Next.js App Router best practices for server/client components, ro
9
9
 
10
10
  ## Project Structure
11
11
 
12
- - **Use `app/` directory** (App Router) for all routes.
12
+ - **Use `app/` directory** (App Router) for all routes; colocate files near where they're used.
13
13
  - Top-level: `app/`, `public/`, `lib/`, `components/`, `contexts/`, `styles/`, `hooks/`, `types/`.
14
- - Colocate files near where they're used.
15
- - **Route Groups**: parentheses `(admin)` — group without affecting URL.
16
- - **Private Folders**: underscore `_internal` — opt out of routing.
14
+ - **Route Groups** `(admin)` group without affecting URL. **Private Folders** `_internal` — opt out of routing.
17
15
  - Feature folders for large apps: `app/dashboard/`, `app/auth/`.
18
16
 
19
17
  ## Server and Client Components
@@ -22,39 +20,116 @@ description: "Next.js App Router best practices for server/client components, ro
22
20
 
23
21
  **Client Components** — add `'use client'` at top. Use for interactivity, state, browser APIs.
24
22
 
23
+ ### Decision Table
24
+
25
+ | Need | Component Type | Why |
26
+ |------|---------------|-----|
27
+ | Fetch data at request time | Server | Direct DB/API access, no client waterfall |
28
+ | Read cookies/headers | Server | Available only on the server |
29
+ | Interactive UI (clicks, inputs) | Client | Requires event handlers |
30
+ | Use `useState` / `useEffect` | Client | React hooks need client runtime |
31
+ | Access browser APIs (localStorage, geolocation) | Client | Not available on server |
32
+ | Render static/non-interactive content | Server | Smaller bundle, faster paint |
33
+ | Show loading spinners for async children | Server (with `<Suspense>`) | Streams HTML progressively |
34
+
25
35
  ### Critical Rule
26
36
 
27
37
  **Never use `next/dynamic` with `{ ssr: false }` inside a Server Component.** This causes build/runtime errors.
28
38
 
29
- **Correct approach:**
30
- 1. Move all client-only logic into a dedicated Client Component (`'use client'`).
31
- 2. Import and use that Client Component directly in the Server Component.
39
+ **Correct approach:** Move client-only logic into a dedicated `'use client'` component, then import it normally.
32
40
 
33
41
  ```tsx
34
- // Server Component
42
+ // Server Component — imports a Client Component directly
35
43
  import DashboardNavbar from '@/components/DashboardNavbar';
36
-
37
44
  export default async function DashboardPage() {
38
- return (
39
- <>
40
- <DashboardNavbar /> {/* Client Component */}
41
- </>
42
- );
45
+ return <><DashboardNavbar /></>;
43
46
  }
44
47
  ```
45
48
 
46
- ## Component Practices
49
+ ## Data Fetching Patterns
50
+
51
+ ### Server-Side Fetching
52
+
53
+ Fetch directly in `async` Server Components. Next.js deduplicates identical `fetch` calls.
54
+
55
+ ```tsx
56
+ export default async function ProjectsPage() {
57
+ const projects = await fetch('https://api.example.com/projects', {
58
+ next: { revalidate: 60 }, // ISR: revalidate every 60s
59
+ }).then((res) => res.json());
60
+ return <ul>{projects.map((p: { id: string; name: string }) => <li key={p.id}>{p.name}</li>)}</ul>;
61
+ }
62
+ ```
63
+
64
+ ### Server Actions (mutations)
65
+
66
+ Define with `'use server'`. Call from Client Components via `action` or `startTransition`.
67
+
68
+ ```tsx
69
+ // lib/actions.ts
70
+ 'use server';
71
+ import { revalidatePath } from 'next/cache';
72
+ export async function createItem(formData: FormData) {
73
+ const name = formData.get('name') as string;
74
+ await db.items.create({ data: { name } });
75
+ revalidatePath('/items');
76
+ }
77
+ ```
78
+
79
+ ```tsx
80
+ // components/CreateItemForm.tsx — calls the Server Action
81
+ 'use client';
82
+ import { createItem } from '@/lib/actions';
83
+ export default function CreateItemForm() {
84
+ return <form action={createItem}><input name="name" required /><button type="submit">Add</button></form>;
85
+ }
86
+ ```
87
+
88
+ ## Error Handling
89
+
90
+ Each route segment can export `error.tsx` (must be a Client Component) and `not-found.tsx`.
91
+
92
+ ```tsx
93
+ // app/dashboard/error.tsx — must be a Client Component
94
+ 'use client';
95
+ export default function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
96
+ return <div role="alert"><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;
97
+ }
98
+ ```
99
+
100
+ ```tsx
101
+ // app/projects/[id]/page.tsx — use notFound() to trigger not-found.tsx
102
+ import { notFound } from 'next/navigation';
103
+ export default async function ProjectPage({ params }: { params: { id: string } }) {
104
+ const project = await getProject(params.id);
105
+ if (!project) notFound();
106
+ return <h1>{project.name}</h1>;
107
+ }
108
+ ```
47
109
 
48
- - PascalCase for files/exports. camelCase for hooks.
110
+ ## Middleware
111
+
112
+ Place `middleware.ts` at the project root (next to `app/`). Runs before every matched request.
113
+
114
+ ```ts
115
+ import { NextResponse, type NextRequest } from 'next/server';
116
+ export function middleware(request: NextRequest) {
117
+ const token = request.cookies.get('session')?.value;
118
+ if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
119
+ return NextResponse.redirect(new URL('/login', request.url));
120
+ }
121
+ return NextResponse.next();
122
+ }
123
+ export const config = { matcher: ['/dashboard/:path*', '/settings/:path*'] };
124
+ ```
125
+
126
+ ## Component Practices & Naming
127
+
128
+ - PascalCase for component files/exports. camelCase for hooks.
49
129
  - Shared components in `components/`. Route-specific in route folder.
50
130
  - TypeScript interfaces for props. Explicit types and defaults.
51
131
  - Co-locate tests with components.
52
-
53
- ## Naming Conventions
54
-
55
- - Folders: `kebab-case`.
56
- - Components: `PascalCase`. Hooks: `camelCase`. Assets: `kebab-case`.
57
- - Types/Interfaces: `PascalCase`. Constants: `UPPER_SNAKE_CASE`.
132
+ - Folders: `kebab-case`. Types/Interfaces: `PascalCase`. Constants: `UPPER_SNAKE_CASE`.
58
133
 
59
134
  ## API Routes (Route Handlers)
60
135
 
@@ -65,13 +140,61 @@ export default async function DashboardPage() {
65
140
  - Validate with Zod/Yup. Return appropriate status codes.
66
141
  - Protect sensitive routes with middleware or server-side session checks.
67
142
 
143
+ ## Performance Patterns
144
+
145
+ ### Dynamic Imports (Client Components only)
146
+
147
+ Lazy-load heavy Client Components to reduce initial bundle size.
148
+
149
+ ```tsx
150
+ 'use client';
151
+ import dynamic from 'next/dynamic';
152
+ const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
153
+ loading: () => <p>Loading chart…</p>,
154
+ });
155
+ ```
156
+
157
+ ### Parallel Data Fetching
158
+
159
+ Initiate independent fetches simultaneously — don't `await` sequentially.
160
+
161
+ ```tsx
162
+ export default async function DashboardPage() {
163
+ const [metrics, activity] = await Promise.all([getMetrics(), getRecentActivity()]);
164
+ return <><MetricsPanel data={metrics} /><ActivityFeed items={activity} /></>;
165
+ }
166
+ ```
167
+
168
+ ### Streaming with Suspense
169
+
170
+ Wrap slow data sections in `<Suspense>` so the shell renders immediately.
171
+
172
+ ```tsx
173
+ import { Suspense } from 'react';
174
+ export default function Layout({ children }: { children: React.ReactNode }) {
175
+ return <main><Suspense fallback={<p>Loading…</p>}>{children}</Suspense></main>;
176
+ }
177
+ ```
178
+
68
179
  ## General Best Practices
69
180
 
70
- - TypeScript with `strict` mode.
71
- - ESLint with official Next.js config.
181
+ - TypeScript with `strict` mode. ESLint with official Next.js config.
72
182
  - Secrets in `.env.local` — never committed.
73
183
  - Built-in Image and Font optimization.
74
184
  - Suspense and loading states for async data.
75
185
  - Avoid large client bundles — keep logic in Server Components.
76
186
  - Semantic HTML and ARIA attributes.
77
187
  - Do NOT create example/demo files unless explicitly requested.
188
+
189
+ ## Anti-Patterns
190
+
191
+ | Anti-Pattern | Why It's Wrong | Do This Instead |
192
+ |-------------|---------------|-----------------|
193
+ | `'use client'` on every component | Bloats JS bundle, defeats RSC benefits | Default to Server Components; add `'use client'` only when needed |
194
+ | Sequential `await` for independent data | Creates a waterfall, slows page load | Use `Promise.all()` for parallel fetches |
195
+ | `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to a Client Component, import normally |
196
+ | Fetching in `useEffect` when server fetch works | Extra client roundtrip, loading flash | Fetch in the Server Component or use Server Actions |
197
+ | Giant `layout.tsx` with all providers | Hard to test, couples unrelated concerns | Split providers into a `Providers` Client Component |
198
+ | Catching errors without `error.tsx` | Unhandled errors crash the page | Add `error.tsx` per route segment |
199
+ | Hardcoding secrets in source files | Security risk, leaks in version control | Use `.env.local` and `process.env` |
200
+ | Skipping `loading.tsx` / `<Suspense>` | Blank screen while data loads | Add `loading.tsx` or wrap in `<Suspense>` |
@@ -7,36 +7,194 @@ description: "Technical SEO patterns for meta tags, structured data, sitemaps, U
7
7
 
8
8
  # SEO Patterns
9
9
 
10
+ ## Core Principles
11
+
12
+ - Every public page MUST have a unique `<title>` and `<meta name="description">`.
13
+ - Structured data MUST validate against Google's Rich Results Test before shipping.
14
+ - Server-render all content critical for indexing — never rely on client-side JS for primary content.
15
+ - Canonical URLs are mandatory on every page to prevent duplicate content issues.
16
+
10
17
  ## Meta Tags & Open Graph
11
- - `<title>`, `<meta name="description">` for every page template
12
- - Open Graph tags (`og:title`, `og:description`, `og:image`, `og:type`)
13
- - Twitter Card tags (`twitter:card`, `twitter:title`, `twitter:image`)
14
- - Canonical URLs (`<link rel="canonical">`)
15
- - Robots directives (`noindex`, `nofollow` where appropriate)
18
+
19
+ Every page template must include the full set of meta tags:
20
+
21
+ ```tsx
22
+ // Next.js App Router layout or page metadata
23
+ export const metadata: Metadata = {
24
+ title: 'Product Name — Short Descriptor',
25
+ description: 'Concise 150-160 char description with primary keyword.',
26
+ alternates: { canonical: 'https://example.com/page-slug' },
27
+ openGraph: {
28
+ title: 'Product Name — Short Descriptor',
29
+ description: 'Concise description for social sharing.',
30
+ url: 'https://example.com/page-slug',
31
+ type: 'website',
32
+ images: [{ url: 'https://example.com/og-image.jpg', width: 1200, height: 630 }],
33
+ },
34
+ twitter: {
35
+ card: 'summary_large_image',
36
+ title: 'Product Name — Short Descriptor',
37
+ images: ['https://example.com/og-image.jpg'],
38
+ },
39
+ robots: { index: true, follow: true },
40
+ };
41
+ ```
42
+
43
+ ### Meta Tag Checklist
44
+
45
+ - [ ] `<title>` is unique, 50-60 chars, includes primary keyword
46
+ - [ ] `<meta name="description">` is unique, 150-160 chars, includes CTA
47
+ - [ ] `<link rel="canonical">` points to the single authoritative URL
48
+ - [ ] `og:title`, `og:description`, `og:image` (1200×630 px min), `og:type` are set
49
+ - [ ] `twitter:card`, `twitter:title`, `twitter:image` are set
50
+ - [ ] `robots` directives are correct (`noindex` on admin/draft pages only)
16
51
 
17
52
  ## Structured Data (JSON-LD)
18
- - **LocalBusiness** / **Restaurant** / **CafeOrCoffeeShop** for venue pages
19
- - **BreadcrumbList** for navigation hierarchy
20
- - **WebSite** with **SearchAction** for sitelinks search box
21
- - **ItemList** for venue listing pages
22
- - **Organization** for the site entity
23
- - Validate against schema.org and Google's requirements
53
+
54
+ Use JSON-LD `<script>` blocks never microdata or RDFa. Choose schema types based on page purpose:
55
+
56
+ | Page Type | Schema Type(s) | Required Properties |
57
+ |-----------|----------------|---------------------|
58
+ | Homepage | `WebSite`, `Organization` | `name`, `url`, `searchAction`, `logo` |
59
+ | Detail page | `Product`, `Article`, or domain-specific type | `name`, `description`, `image` |
60
+ | Listing / category page | `ItemList` + `ListItem` | `itemListElement`, `position`, `url` |
61
+ | Breadcrumb navigation | `BreadcrumbList` | `itemListElement`, `position`, `name` |
62
+ | Blog post | `Article` or `BlogPosting` | `headline`, `datePublished`, `author` |
63
+ | FAQ page | `FAQPage` | `mainEntity` with `Question` + `Answer` |
64
+
65
+ ### Example: Breadcrumb + Article
66
+
67
+ ```tsx
68
+ function StructuredData({ breadcrumbs, article }: Props) {
69
+ const breadcrumbLd = {
70
+ '@context': 'https://schema.org',
71
+ '@type': 'BreadcrumbList',
72
+ itemListElement: breadcrumbs.map((crumb, i) => ({
73
+ '@type': 'ListItem',
74
+ position: i + 1,
75
+ name: crumb.label,
76
+ item: crumb.url,
77
+ })),
78
+ };
79
+
80
+ const articleLd = {
81
+ '@context': 'https://schema.org',
82
+ '@type': 'Article',
83
+ headline: article.title,
84
+ description: article.summary,
85
+ image: article.imageUrl,
86
+ datePublished: article.publishedAt,
87
+ dateModified: article.updatedAt,
88
+ author: { '@type': 'Person', name: article.author },
89
+ };
90
+
91
+ return (
92
+ <>
93
+ <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbLd) }} />
94
+ <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(articleLd) }} />
95
+ </>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ### Validation
101
+
102
+ - Run every JSON-LD block through [Google's Rich Results Test](https://search.google.com/test/rich-results) before merging.
103
+ - Validate against [schema.org](https://schema.org) definitions for required/recommended properties.
104
+ - Check the Search Console **Enhancements** report after deployment.
24
105
 
25
106
  ## Sitemap & Crawlability
26
- - Dynamic XML sitemap generation for all venue pages
27
- - Sitemap index for large venue counts (705+ and growing)
28
- - `robots.txt` configuration
29
- - Internal linking structure
30
- - Page speed impact on crawl budget
107
+
108
+ - Generate an XML sitemap dynamically from your data source (CMS, database, filesystem).
109
+ - Use a **sitemap index** when page count exceeds 50,000 URLs or file size exceeds 50 MB.
110
+ - Include `<lastmod>` timestamps — omit if you can't guarantee accuracy.
111
+ - Submit sitemaps via Google Search Console and reference them in `robots.txt`.
112
+
113
+ ### Example: robots.txt
114
+
115
+ ```txt
116
+ User-agent: *
117
+ Allow: /
118
+ Disallow: /admin/
119
+ Disallow: /api/
120
+ Disallow: /preview/
121
+
122
+ Sitemap: https://example.com/sitemap.xml
123
+ ```
124
+
125
+ ### Crawlability Checklist
126
+
127
+ - [ ] `robots.txt` allows crawling of all public pages
128
+ - [ ] `robots.txt` blocks admin, API, and preview routes
129
+ - [ ] XML sitemap is auto-generated and up to date
130
+ - [ ] Sitemap is referenced in `robots.txt`
131
+ - [ ] Internal links connect all public pages (no orphan pages)
132
+ - [ ] Page load time < 3s (crawl budget efficiency)
31
133
 
32
134
  ## URL Strategy
33
- - Clean, keyword-relevant slugs for venue pages
34
- - Consistent URL patterns across venue categories
35
- - Redirect handling for renamed/moved venues (301 redirects)
36
- - Trailing slash consistency
135
+
136
+ - Use lowercase, hyphen-separated slugs: `/blog/my-post-title`
137
+ - Keep URLs short, keyword-relevant, and human-readable.
138
+ - Enforce trailing-slash consistency (pick one, redirect the other).
139
+ - Implement 301 redirects for any renamed or moved pages.
140
+
141
+ | Pattern | Good | Bad |
142
+ |---------|------|-----|
143
+ | Slug format | `/products/blue-widget` | `/products/Blue_Widget` |
144
+ | Hierarchy | `/blog/2026/seo-tips` | `/blog?id=42` |
145
+ | Consistency | Always `/path/` or `/path` | Mixed trailing slashes |
146
+ | Parameters | `/products?sort=price` | `/products/sort/price/asc` |
147
+
148
+ ### Redirect Example (Next.js)
149
+
150
+ ```ts
151
+ // next.config.ts
152
+ const nextConfig = {
153
+ async redirects() {
154
+ return [
155
+ { source: '/old-page', destination: '/new-page', permanent: true },
156
+ { source: '/blog/:slug/amp', destination: '/blog/:slug', permanent: true },
157
+ ];
158
+ },
159
+ };
160
+ ```
37
161
 
38
162
  ## Rendering & Indexability
39
- - Ensure critical content is server-rendered (not client-only)
40
- - Verify pages are indexable with `fetch as Googlebot`
41
- - Check hydration doesn't break structured data
42
- - Image optimization for search (alt text, file names, lazy loading below fold)
163
+
164
+ - **Server-render** all content that must be indexed — titles, descriptions, body text, structured data.
165
+ - **Client-hydrated** interactive elements (filters, modals) are fine, but content behind JS-only rendering will not be indexed reliably.
166
+ - Use semantic HTML (`<h1>`–`<h6>`, `<article>`, `<nav>`, `<main>`) for crawlers to understand page structure.
167
+
168
+ ### Image SEO
169
+
170
+ | Attribute | Purpose | Example |
171
+ |-----------|---------|---------|
172
+ | `alt` | Describes image for screen readers + crawlers | `alt="Blue widget on white background"` |
173
+ | `loading` | Lazy-load below-fold images | `loading="lazy"` |
174
+ | `width` / `height` | Prevents layout shift (CLS) | `width={800} height={600}` |
175
+ | File name | Keyword signal | `blue-widget-front.webp` |
176
+ | Format | Performance + quality | Use WebP/AVIF with JPEG fallback |
177
+
178
+ ### Indexability Checklist
179
+
180
+ - [ ] Primary content renders in initial HTML (view source, not inspect)
181
+ - [ ] `<h1>` is unique per page and contains the primary keyword
182
+ - [ ] Structured data is present in the server-rendered HTML
183
+ - [ ] Images have descriptive `alt` text
184
+ - [ ] No `noindex` on pages that should be indexed
185
+ - [ ] Hydration does not remove or rewrite structured data scripts
186
+
187
+ ## Anti-Patterns
188
+
189
+ | Anti-Pattern | Why It's Bad | Correct Approach |
190
+ |---|---|---|
191
+ | Duplicate `<title>` across pages | Dilutes ranking signals; confuses crawlers | Unique, keyword-specific title per page |
192
+ | Missing canonical URL | Causes duplicate content penalties | Add `<link rel="canonical">` to every page |
193
+ | Client-only rendered content | Googlebot may not execute JS reliably | Server-render all indexable content |
194
+ | Hardcoded sitemap file | Goes stale as pages are added/removed | Generate sitemap dynamically from data source |
195
+ | Using `noindex` as a "temporary" fix | Often forgotten; pages stay de-indexed | Fix the underlying issue instead |
196
+ | Stuffing keywords in meta tags | Penalized by search engines | Write natural, user-focused descriptions |
197
+ | Missing `alt` text on images | Lost image search traffic + accessibility failure | Descriptive alt text on every meaningful image |
198
+ | Structured data without validation | Silent errors cause rich result loss | Validate with Google Rich Results Test before merge |
199
+ | Blocking CSS/JS in `robots.txt` | Prevents Googlebot from rendering the page | Only block admin/API routes |
200
+ | Mixed trailing slash URLs | Splits link equity between two URLs | Pick one convention, 301-redirect the other |