@se-studio/skills 1.0.1

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 (34) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +25 -0
  3. package/skills/contentful-cms-alt-text-audit/SKILL.md +60 -0
  4. package/skills/contentful-cms-cms-guidelines/README.md +166 -0
  5. package/skills/contentful-cms-cms-guidelines/colour-hint-prompt.md +77 -0
  6. package/skills/contentful-cms-cms-guidelines/evaluation-prompt.md +84 -0
  7. package/skills/contentful-cms-cms-guidelines/generate-component-guidelines.md +126 -0
  8. package/skills/contentful-cms-cms-guidelines/generation-prompt.md +231 -0
  9. package/skills/contentful-cms-cms-guidelines/html-component-authoring.md +401 -0
  10. package/skills/contentful-cms-cms-guidelines/validation-prompt.md +170 -0
  11. package/skills/contentful-cms-cms-guidelines/variant-loop.md +189 -0
  12. package/skills/contentful-cms-cms-guidelines/variant-proposal-prompt.md +131 -0
  13. package/skills/contentful-cms-core/SKILL.md +793 -0
  14. package/skills/contentful-cms-generate-all-guidelines/SKILL.md +313 -0
  15. package/skills/contentful-cms-generate-cms-guidelines/SKILL.md +313 -0
  16. package/skills/contentful-cms-image-guide/SKILL.md +240 -0
  17. package/skills/contentful-cms-navigation/SKILL.md +23 -0
  18. package/skills/contentful-cms-rich-text/SKILL.md +96 -0
  19. package/skills/contentful-cms-schema-org/SKILL.md +74 -0
  20. package/skills/contentful-cms-screenshots/SKILL.md +46 -0
  21. package/skills/contentful-cms-seo-descriptions/SKILL.md +54 -0
  22. package/skills/contentful-cms-templates/SKILL.md +21 -0
  23. package/skills/contentful-cms-update-cms-guidelines/SKILL.md +348 -0
  24. package/skills/performance-audit/SKILL.md +344 -0
  25. package/skills/se-marketing-sites-cms-routes-and-appshared/SKILL.md +99 -0
  26. package/skills/se-marketing-sites-create-collection/SKILL.md +295 -0
  27. package/skills/se-marketing-sites-create-component/SKILL.md +250 -0
  28. package/skills/se-marketing-sites-create-page/SKILL.md +183 -0
  29. package/skills/se-marketing-sites-curate-showcase-mocks/SKILL.md +344 -0
  30. package/skills/se-marketing-sites-handling-media/SKILL.md +195 -0
  31. package/skills/se-marketing-sites-lib-cms-structure/SKILL.md +83 -0
  32. package/skills/se-marketing-sites-register-cms-features/SKILL.md +95 -0
  33. package/skills/se-marketing-sites-styling-system/SKILL.md +122 -0
  34. package/skills/site-workflows-brand-context-builder/SKILL.md +98 -0
@@ -0,0 +1,348 @@
1
+ ---
2
+ name: update-cms-guidelines
3
+ description: Primary CMS guideline orchestration. Use `fresh` only after a full clean slate and complete pipeline (showcase → screenshots → new component prose via Phase B/C/D — never git-restore fragments). Use `sync` for incremental changes but always delete stale types. If the user is vague, ask whether they want a full clean regeneration or incremental sync before acting.
4
+ license: Private
5
+ metadata:
6
+ author: se-core-product
7
+ version: "1.1.0"
8
+ ---
9
+
10
+ # Update CMS Guidelines
11
+
12
+ This is the **primary entry point** for all CMS guideline work. It wraps the lower-level skills
13
+ into three clear modes so you always know which path to take.
14
+
15
+ ## Terms: full regeneration vs incremental
16
+
17
+ | Term | Meaning |
18
+ |---|---|
19
+ | **Full regeneration** (brand-new run) | **Clean slate** of generated artifacts, then rerun the **entire** pipeline: showcase data → mocks → field list → **all** screenshots (components **and** collections) → **new** component guideline markdown (Phase B + C + D for **every** type — no restoring old `.md` from git) → collection/external CLI step → merge → commit. Collections are CLI-regenerated; externals may need hand-filled sections after stub creation. |
20
+ | **Incremental** (`sync` + targeted work) | Discovery/registrations are the source of truth. **Remove every stale artifact** for types no longer registered (fragments, PNGs, `screenshots/index.json` rows, `accepted-variants` JSON where your app tracks them). Then add or refresh only **new** or **changed** types (screenshots + `generate-cms-guidelines` single / generate-all partial). Always merge at the end if fragments changed. |
21
+
22
+ **Not** a full regeneration: restoring `docs/cms-guidelines/components/*.md` (or externals) from `git checkout`, merge-only, or screenshots-only while leaving years-old prose in place.
23
+
24
+ ## When the user is vague
25
+
26
+ Phrases like “regenerate guidelines”, “refresh CMS docs”, “update showcase/guidelines”, or “redo screenshots” are **ambiguous**.
27
+
28
+ **Before doing substantial work, ask briefly:**
29
+
30
+ 1. Do you want a **full clean regeneration** (delete existing fragments and screenshots, rewrite every component guideline from scratch, full screenshot pass)?
31
+ 2. Or an **incremental** update (sync with registrations — **including removing** dropped types — plus only new/changed types)?
32
+ 3. Or something narrower (e.g. **merge-only**, **one component**, **collections CLI only**)?
33
+
34
+ If they insist on “everything” / “from scratch” / “totally regenerate” / “wipe and rebuild”, treat that as **full regeneration** and follow **`fresh`** including the **clean slate** steps below — do **not** shortcut with git-restored fragments.
35
+
36
+ ## When to use which mode
37
+
38
+ | Situation | Mode |
39
+ |---|---|
40
+ | New app — no guidelines exist yet | `fresh` |
41
+ | User wants **total** / **full** / **clean-slate** regeneration | `fresh` (must include **clean slate** + full **generate-all-guidelines** Phase 3 for all components) |
42
+ | Types added or removed in `registrations.ts` (incremental) | `sync` — **always** remove stale files and index entries, then add/refresh new types |
43
+ | Fragment files edited manually, combined doc needs rebuild | `merge-only` |
44
+ | Major refactor but user confirms they **do not** need new prose | Rare — confirm explicitly; otherwise use `fresh` |
45
+
46
+ ---
47
+
48
+ ## Inputs
49
+
50
+ | Input | Example |
51
+ |---|---|
52
+ | App directory (repo-relative or absolute) | `apps/example-se2026` |
53
+ | Port | `3012` |
54
+ | Discovery URL | `http://localhost:3012/api/cms/discovery/` |
55
+ | Mode | `fresh` \| `sync` \| `merge-only` |
56
+
57
+ **App ports:**
58
+
59
+ | App | Port |
60
+ |---|---|
61
+ | example-brightline | 3010 |
62
+ | example-se2026 | 3012 |
63
+ | example-om1 | 3013 |
64
+ | example-empty | 3014 |
65
+ | example-brightlifekids | 3015 |
66
+
67
+ ---
68
+
69
+ ## Mode: fresh
70
+
71
+ Full pipeline from a **clean slate** (or a brand-new app). Expect roughly **30–90+ minutes** for a typical site (component count × LLM time for Phase B/C/D).
72
+
73
+ ### Step 0 — Clean slate (mandatory for true full regeneration)
74
+
75
+ If any guideline or screenshot files already exist, **delete generated guideline outputs** before re-running the pipeline. Otherwise you risk mixed old/new content or skipping work.
76
+
77
+ **Do:**
78
+
79
+ 1. Remove per-type fragments:
80
+ `docs/cms-guidelines/components/*.md`, `docs/cms-guidelines/collections/*.md`, `docs/cms-guidelines/externals/*.md`
81
+ **Keep** editorial/meta files if present (e.g. `README.md`, `CHECKLIST.md`, `LAYOUT.md`, `GENERATE_NEXT_*.md`).
82
+ 2. Remove merged bundle: `docs/cms-guidelines/COMPONENT_GUIDELINES_FOR_LLM.md`.
83
+ 3. Remove screenshots: all `docs/cms-guidelines/screenshots/components/*.png`, `.../collections/*.png`, `.../externals/*.png` (if used), then reset `docs/cms-guidelines/screenshots/index.json` to `[]` **or** delete it so captures recreate it.
84
+ 4. Clear showcase pipeline inputs as needed: remove `src/generated/showcase-examples.json`, `src/generated/showcase-mocks-draft.json`, and the tree `src/generated/cms-discovery/accepted-variants/` (or empty `components/` / `collections/` / `externals/` subdirs). Stub `src/generated/showcase-mocks.json` per **curate-showcase-mocks** if the import must compile.
85
+ 5. Remove any **duplicate** variant path some apps still have (e.g. `generated/cms-discovery/accepted-variants/` at repo root of the app) if it is not the canonical `src/generated/...` tree.
86
+ 6. Optionally reset `generated/cms-discovery/colour-hints.json` if you want colour hints re-derived during Phase C.
87
+
88
+ **Do not** (for a claimed full regeneration):
89
+
90
+ - Restore `docs/cms-guidelines/components/*.md` or `externals/*.md` from git instead of running **generate-all-guidelines Phase 3** (Phase B + C + D per component).
91
+ - Skip collection screenshot capture when collections are registered — Phase 1 must cover **both** components and collections for a complete visual record.
92
+
93
+ After this, run Steps 1–4 in order (prerequisites → showcase mocks → field list → generate-all-guidelines).
94
+
95
+ ### Step 1 — Prerequisites
96
+
97
+ Check all of these before starting:
98
+
99
+ ```bash
100
+ # 1. Dev server is running
101
+ curl -sL http://localhost:<PORT>/api/cms/discovery/ | head -c 100
102
+ # Should return: {"components":[
103
+
104
+ # 2. agent-browser is installed
105
+ agent-browser --version
106
+ # If missing: npm install -g agent-browser && agent-browser install
107
+
108
+ # 3. Env vars present in <appDir>/.env.local
109
+ grep -E "CONTENTFUL_SPACE_ID|CONTENTFUL_ACCESS_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY" <appDir>/.env.local
110
+ ```
111
+
112
+ ### Step 2 — Curate showcase mocks
113
+
114
+ Read and follow the **curate-showcase-mocks** skill:
115
+ `.agents/skills/se-marketing-sites-curate-showcase-mocks/SKILL.md`
116
+
117
+ This produces `src/generated/showcase-mocks.json` (committed) and
118
+ `src/generated/cms-discovery/accepted-variants/{components|collections|externals}/<slug>.json` (often gitignored — **some apps commit them**; follow the app’s `.gitignore`).
119
+
120
+ When complete, confirm:
121
+ - `src/generated/showcase-mocks.json` has non-empty `components` and `collections` keys
122
+ - `src/generated/cms-discovery/accepted-variants/` has `components/` and `collections/` subdirs with JSON files
123
+
124
+ Backfill any registered types missing from the LLM draft using `showcase-examples.json` (see curate skill), and add minimal `accepted-variants` JSON for types with no variant file so screenshot capture can run.
125
+
126
+ ### Step 3 — Generate HTML Component Style Guide
127
+
128
+ Generate the design system reference used when authoring `HtmlComponent` entries:
129
+
130
+ ```bash
131
+ # From the app directory:
132
+ pnpm run cms-generate-html-style-guide
133
+ # or from repo root:
134
+ pnpm --filter <appName> exec cms-generate-html-style-guide --app-dir .
135
+ ```
136
+
137
+ Reads `tailwind.config.json` and `src/app/globals.css`; writes `docs/cms-guidelines/html-component-style-guide.md`. No dev server required. Safe to skip if the design system has not changed since the last run.
138
+
139
+ ### Step 4 — Generate field list
140
+
141
+ ```bash
142
+ # From the app directory:
143
+ pnpm run cms-discovery:field-list
144
+ # or from repo root:
145
+ pnpm --filter <appName> exec cms-generate-field-list --app-dir .
146
+ ```
147
+
148
+ Produces `generated/cms-discovery/field-list.json` (discovery API reads from this path). Some apps use `src/generated/` for showcase artifacts; see app docs.
149
+
150
+ ### Step 5 — Run generate-all-guidelines
151
+
152
+ Read and follow the **generate-all-guidelines** skill:
153
+ `.agents/skills/contentful-cms-generate-all-guidelines/SKILL.md`
154
+
155
+ Skip its prerequisite check (you already verified everything through Step 4).
156
+ Run Phases 0–5 from that skill with **full regeneration rules**:
157
+
158
+ - **Phase 0** — Discover types; **do not** skip components that already had old fragments (there should be none after clean slate).
159
+ - **Phase 1** — Bulk screenshot capture for **all components**, then **all collections** (and externals if your app captures them).
160
+ - **Phase 2** — Collections + externals (`cms-generate-collection-guidelines`)
161
+ - **Phase 3** — Component guidelines (**every** type: Phase B + C + D; **overwrite** `components/<slug>.md`)
162
+ - **Phase 4** — Merge (`cms-merge-guidelines`)
163
+ - **Phase 5** — Commit
164
+
165
+ ### After Phase 2 (externals)
166
+
167
+ If any `docs/cms-guidelines/externals/<slug>.md` stubs were created, fill in their
168
+ `## Fields & Schema` and `## Usage` sections before committing. See the
169
+ **generate-cms-guidelines** skill (`Step 2b`) for guidance on what to put in these sections.
170
+
171
+ ---
172
+
173
+ ## Mode: sync
174
+
175
+ Use when types have been added or removed in `registrations.ts` (or from the Contentful space).
176
+ This mode diffs the current registered types against existing guideline files and handles both
177
+ stale files and new types.
178
+
179
+ **Incremental is not “append only”.** If a type left registrations or discovery, you **must** remove its artifacts (markdown, PNGs, index rows, optional `accepted-variants` files). Otherwise the repo and `COMPONENT_GUIDELINES_FOR_LLM.md` will lie.
180
+
181
+ ### Phase 0 — Diff
182
+
183
+ Fetch the discovery endpoint and compute the current set of registered type slugs:
184
+
185
+ ```bash
186
+ curl -sL http://localhost:<PORT>/api/cms/discovery/ > /tmp/discovery.json
187
+ ```
188
+
189
+ Slug formula: `name.toLowerCase().replace(/\s+/g, '-').replace(/[()]/g, '')`
190
+
191
+ Examples: `"Article Browser Sticky"` → `article-browser-sticky`, `"Two Column (Rich Text)"` → `two-column-rich-text`
192
+
193
+ Build three diff lists by comparing discovery slugs against existing files:
194
+
195
+ ```bash
196
+ # List existing guideline file slugs (strip .md extension)
197
+ ls <appDir>/docs/cms-guidelines/components/ # → component file slugs
198
+ ls <appDir>/docs/cms-guidelines/collections/ # → collection file slugs
199
+ ls <appDir>/docs/cms-guidelines/externals/ # → external file slugs
200
+ ```
201
+
202
+ From the diff, identify:
203
+
204
+ | Status | Meaning | Action |
205
+ |---|---|---|
206
+ | **STALE** | File exists, type not in discovery | Remove files |
207
+ | **NEW** | Type in discovery, no file exists | Generate guidelines |
208
+ | **UNCHANGED** | Both exist | Skip |
209
+
210
+ Report the full diff to the user before taking any action.
211
+
212
+ ### Phase 1 — Remove stale files
213
+
214
+ For each **stale component** slug (`<slug>`):
215
+
216
+ ```bash
217
+ # 1. Delete the guideline fragment
218
+ rm <appDir>/docs/cms-guidelines/components/<slug>.md
219
+
220
+ # 2. Delete all screenshot variants (screenshots live under components/ or collections/ subdirs)
221
+ rm -f <appDir>/docs/cms-guidelines/screenshots/components/<slug>-*.png
222
+ rm -f <appDir>/docs/cms-guidelines/screenshots/collections/<slug>-*.png
223
+
224
+ # 3. Remove screenshot index entries for this type (index.json has typeName and file e.g. "components/<slug>-default.png")
225
+ # Read screenshots/index.json, filter out entries where typeName matches or file starts with "components/<slug>-" or "collections/<slug>-", rewrite
226
+ node -e "
227
+ const fs = require('node:fs');
228
+ const p = '<appDir>/docs/cms-guidelines/screenshots/index.json';
229
+ const idx = JSON.parse(fs.readFileSync(p, 'utf8'));
230
+ const filtered = idx.filter(e => !e.file.startsWith('components/<slug>-') && !e.file.startsWith('collections/<slug>-'));
231
+ fs.writeFileSync(p, JSON.stringify(filtered, null, 2));
232
+ console.log('Removed', idx.length - filtered.length, 'entries for <slug>');
233
+ "
234
+
235
+ # 4. Remove accepted-variants file if present (check both components/ and collections/)
236
+ rm -f <appDir>/src/generated/cms-discovery/accepted-variants/components/<slug>.json
237
+ rm -f <appDir>/src/generated/cms-discovery/accepted-variants/collections/<slug>.json
238
+ ```
239
+
240
+ For each **stale collection** slug (`<slug>`):
241
+
242
+ ```bash
243
+ # 1. Fragment
244
+ rm <appDir>/docs/cms-guidelines/collections/<slug>.md
245
+
246
+ # 2. Screenshots for that collection
247
+ rm -f <appDir>/docs/cms-guidelines/screenshots/collections/<slug>-*.png
248
+
249
+ # 3. Remove index entries (same pattern as components — filter by file prefix)
250
+ # e.g. drop rows where e.file.startsWith('collections/<slug>-')
251
+ ```
252
+
253
+ Use the same **index.json** filtering approach as for stale components (adjust the `startsWith` prefix to `collections/<slug>-`).
254
+
255
+ For each **stale external** slug:
256
+
257
+ > **Caution:** External guideline files contain hand-authored schema documentation. Confirm
258
+ > with the user before deleting. If confirmed:
259
+
260
+ ```bash
261
+ rm <appDir>/docs/cms-guidelines/externals/<slug>.md
262
+ ```
263
+
264
+ ### Phase 2 — Add new types
265
+
266
+ **New components** (one or more):
267
+
268
+ Run the [generate-all-guidelines](../generate-all-guidelines/SKILL.md) skill's Phase 1 (screenshots)
269
+ and Phase 3 (component subagents) for **only the new types**, then skip to Phase 4 (merge).
270
+
271
+ If the new component does not yet have an `accepted-variants/components/<slug>.json` file (or `collections/<slug>.json` for a collection), run:
272
+
273
+ ```bash
274
+ pnpm --filter <appName> generate-showcase-mocks -- --types "<Type Name 1>,<Type Name 2>"
275
+ ```
276
+
277
+ This regenerates the variant file for just those types. Then capture screenshots and run the
278
+ Phase 3 subagent prompt as described in generate-all-guidelines.
279
+
280
+ **New collections or externals:**
281
+
282
+ Re-running the generator is safe — it always overwrites collections and only creates externals
283
+ if the file does not already exist:
284
+
285
+ ```bash
286
+ cms-generate-collection-guidelines \
287
+ --discovery-url http://localhost:<PORT>/api/cms/discovery/ \
288
+ --app-dir <appDir>
289
+ ```
290
+
291
+ If a new external stub was created, fill in its `## Fields & Schema` and `## Usage` sections
292
+ before committing (see Mode: fresh → After Phase 2).
293
+
294
+ ### Phase 3 — Merge and commit
295
+
296
+ ```bash
297
+ # Rebuild the combined document
298
+ cms-merge-guidelines --app-dir <appDir>
299
+ # or: pnpm --filter <appName> run cms-guidelines:merge
300
+
301
+ # Commit everything
302
+ git add <appDir>/docs/cms-guidelines/ <appDir>/src/generated/cms-discovery/
303
+ git commit -m "chore(<appName>): sync CMS guidelines with registrations"
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Mode: merge-only
309
+
310
+ Rebuilds `docs/cms-guidelines/COMPONENT_GUIDELINES_FOR_LLM.md` from all existing fragment files.
311
+ Use after manual edits to fragment files, or after any of the other modes if the combined doc
312
+ looks stale.
313
+
314
+ ```bash
315
+ cms-merge-guidelines --app-dir <appDir>
316
+ # or from the app directory:
317
+ pnpm run cms-guidelines:merge
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Output files (reference)
323
+
324
+ All under `<appDir>`:
325
+
326
+ | File | Description | Committed |
327
+ |---|---|---|
328
+ | `docs/cms-guidelines/components/<slug>.md` | Per-component fragment | Yes |
329
+ | `docs/cms-guidelines/collections/<slug>.md` | Per-collection fragment | Yes |
330
+ | `docs/cms-guidelines/externals/<slug>.md` | Per-external fragment | Yes |
331
+ | `docs/cms-guidelines/screenshots/index.json` | Screenshot index | Yes |
332
+ | `docs/cms-guidelines/screenshots/{components\|collections\|externals}/*.png` | Screenshot images | Yes |
333
+ | `docs/cms-guidelines/COMPONENT_GUIDELINES_FOR_LLM.md` | Merged document (AI-readable) | Yes |
334
+ | `generated/cms-discovery/field-list.json` | Field metadata (discovery API) | Yes |
335
+ | `src/generated/showcase-mocks.json` | Curated showcase mocks | Yes |
336
+ | `src/generated/cms-discovery/accepted-variants/{components\|collections\|externals}/<slug>.json` | Variant files (per type) | Usually no (app-dependent) |
337
+ | `src/generated/showcase-examples.json` | Raw Contentful data dump | No |
338
+
339
+ ---
340
+
341
+ ## Sub-skills reference
342
+
343
+ | Purpose | Skill |
344
+ |---|---|
345
+ | Curate showcase mocks (Step 2 of `fresh`) | `.agents/skills/se-marketing-sites-curate-showcase-mocks/SKILL.md` |
346
+ | Full-site bulk generation (Step 4 of `fresh`: Phases 0–5) | `.agents/skills/contentful-cms-generate-all-guidelines/SKILL.md` |
347
+ | Single-type regeneration (after code change) | `.agents/skills/contentful-cms-generate-cms-guidelines/SKILL.md` (mode: single) |
348
+ | Per-component pipeline detail | `packages/skills/skills/contentful-cms-cms-guidelines/generate-component-guidelines.md` |
@@ -0,0 +1,344 @@
1
+ ---
2
+ name: performance-audit
3
+ description: >
4
+ Measure and improve Next.js app performance: bundle analysis, Lighthouse,
5
+ and real-user monitoring. Generic — works with any Next.js deployment on Vercel.
6
+ metadata:
7
+ author: se-core-product
8
+ version: "1.0.0"
9
+ priority: 5
10
+ promptSignals:
11
+ phrases:
12
+ - performance
13
+ - lighthouse
14
+ - bundle size
15
+ - bundle analysis
16
+ - LCP
17
+ - core web vitals
18
+ - CWV
19
+ - speed insights
20
+ - page speed
21
+ - slow pages
22
+ - optimize performance
23
+ - analyse bundle
24
+ - analyze bundle
25
+ - web vitals
26
+ retrieval:
27
+ aliases:
28
+ - perf
29
+ - web vitals
30
+ - bundle
31
+ - lighthouse audit
32
+ - performance measurement
33
+ intents:
34
+ - measure page performance
35
+ - analyze bundle sizes
36
+ - improve Lighthouse scores
37
+ - set up performance tooling
38
+ - investigate slow LCP
39
+ - add speed insights
40
+ - run bundle analyzer
41
+ entities:
42
+ - LCP
43
+ - CLS
44
+ - TBT
45
+ - TTFB
46
+ - FCP
47
+ - bundle analyzer
48
+ - Lighthouse
49
+ - Speed Insights
50
+ - Core Web Vitals
51
+ ---
52
+
53
+ # Next.js Performance Audit
54
+
55
+ ## Overview — Three-layer measurement strategy
56
+
57
+ Always use all three layers. Bundle size ≠ Lighthouse score ≠ real-user experience.
58
+
59
+ | Layer | Tool | What it measures | When to use |
60
+ |---|---|---|---|
61
+ | Bundle | `@next/bundle-analyzer` | JS/CSS weight by package, lazy vs eager chunks | Before/after any dependency or code change |
62
+ | Lab | Lighthouse | CWV scores in controlled conditions | Before deploying an optimisation |
63
+ | Field | Vercel Speed Insights | Real-user CWV on production | Ongoing monitoring post-deploy |
64
+
65
+ ---
66
+
67
+ ## Phase 1 — Bundle Analysis
68
+
69
+ ### Setup (if not already present)
70
+
71
+ ```bash
72
+ pnpm add -D @next/bundle-analyzer
73
+ ```
74
+
75
+ In `next.config.ts`, wrap the export:
76
+ ```ts
77
+ import bundleAnalyzer from '@next/bundle-analyzer';
78
+ const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true' });
79
+ export default withBundleAnalyzer(nextConfig);
80
+ ```
81
+
82
+ Add script to `package.json`:
83
+ ```json
84
+ "analyze": "ANALYZE=true next build"
85
+ ```
86
+
87
+ ### Run
88
+
89
+ ```bash
90
+ pnpm analyze
91
+ ```
92
+
93
+ Opens `client.html`, `nodejs.html`, `edge.html` in `.next/analyze/`. The treemap shows every package in every chunk with parsed and gzip sizes.
94
+
95
+ ### What to look for
96
+
97
+ **Good:**
98
+ - Heavy libraries (video players, animation libraries, charting) appear as separate lazy chunks — they're only loaded when needed.
99
+
100
+ **Investigate:**
101
+ - Any package >30 KB gzip in the first-load chunks that isn't React or Next.js internals.
102
+ - Libraries that should be server-only (e.g. CMS SDKs, data parsers) appearing in client bundles.
103
+ - The same library duplicated across multiple chunks at different versions.
104
+
105
+ **Node.js SSR bundle (`nodejs.html`):**
106
+ Large client-only packages here inflate server cold-start time. Check for missing `'use client'` boundaries.
107
+
108
+ ---
109
+
110
+ ## Phase 2 — Lighthouse
111
+
112
+ ### Prerequisites
113
+
114
+ | Node version | Lighthouse version |
115
+ |---|---|
116
+ | 20.18.x | `lighthouse@12` (`npm i -g lighthouse@12`) |
117
+ | 20.19+ or 22+ | `lighthouse@13` |
118
+
119
+ Chrome path:
120
+ - Linux / WSL: `/usr/bin/google-chrome`
121
+ - macOS: Lighthouse finds it automatically
122
+
123
+ ### Run against key page types
124
+
125
+ Substitute `<SITE_URL>` with the production (or preview) domain.
126
+
127
+ ```bash
128
+ mkdir -p .lighthouse
129
+
130
+ # Homepage
131
+ npx lighthouse@12 <SITE_URL>/ \
132
+ --chrome-path /usr/bin/google-chrome \
133
+ --chrome-flags="--headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage" \
134
+ --only-categories=performance,accessibility,best-practices,seo \
135
+ --output json --output-path .lighthouse/home.json --quiet
136
+
137
+ # A list or index page
138
+ npx lighthouse@12 <SITE_URL>/blog/ \
139
+ --chrome-path /usr/bin/google-chrome \
140
+ --chrome-flags="--headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage" \
141
+ --only-categories=performance,accessibility,best-practices,seo \
142
+ --output json --output-path .lighthouse/list.json --quiet
143
+
144
+ # A detail page (pick a URL from the sitemap)
145
+ npx lighthouse@12 <DETAIL_URL> \
146
+ --chrome-path /usr/bin/google-chrome \
147
+ --chrome-flags="--headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage" \
148
+ --only-categories=performance,accessibility,best-practices,seo \
149
+ --output json --output-path .lighthouse/detail.json --quiet
150
+ ```
151
+
152
+ To find page URLs: `curl -s <SITE_URL>/sitemap.xml | grep -oP 'https://[^<]+'`
153
+
154
+ ### Extract scores from JSON
155
+
156
+ ```bash
157
+ node -e "
158
+ const pages = ['home','list','detail'];
159
+ pages.forEach(p => {
160
+ try {
161
+ const d = JSON.parse(require('fs').readFileSync('.lighthouse/' + p + '.json', 'utf8'));
162
+ const c = d.categories;
163
+ const a = d.audits;
164
+ console.log(p.padEnd(10),
165
+ 'Perf:', Math.round(c.performance.score * 100),
166
+ '| LCP:', (a['largest-contentful-paint'].numericValue / 1000).toFixed(2) + 's',
167
+ '| CLS:', a['cumulative-layout-shift'].numericValue.toFixed(3),
168
+ '| TBT:', Math.round(a['total-blocking-time'].numericValue) + 'ms',
169
+ '| TTFB:', Math.round(a['server-response-time'].numericValue) + 'ms'
170
+ );
171
+ } catch(e) { console.log(p + ': not found'); }
172
+ });
173
+ "
174
+ ```
175
+
176
+ ### Targets
177
+
178
+ | Metric | Target | Fail threshold |
179
+ |---|---|---|
180
+ | LCP | < 2.5s | > 4s |
181
+ | CLS | < 0.1 | > 0.25 |
182
+ | TBT | < 200ms | > 600ms |
183
+ | TTFB | < 600ms | > 1800ms |
184
+ | FCP | < 1.8s | > 3s |
185
+
186
+ ### Key diagnostic signal
187
+
188
+ If a simple page (e.g. About) scores 90+ but heavier pages score < 80, the delta is almost always media loading or third-party scripts present only on those pages.
189
+
190
+ If all pages score similarly low, the issue is in the shared layout — fonts, analytics scripts, or global CSS.
191
+
192
+ ### Extracting opportunities
193
+
194
+ ```bash
195
+ node -e "
196
+ const d = JSON.parse(require('fs').readFileSync('.lighthouse/home.json', 'utf8'));
197
+ const a = d.audits;
198
+
199
+ console.log('=== LARGEST PAYLOADS ===');
200
+ (a['total-byte-weight']?.details?.items || []).slice(0, 8).forEach(i =>
201
+ console.log(' ' + ((i.totalBytes||0)/1024).toFixed(0).padStart(6) + 'KB | ' + (i.url||'').substring(0,90))
202
+ );
203
+
204
+ console.log('\n=== UNUSED JS ===');
205
+ (a['unused-javascript']?.details?.items || []).slice(0, 6).forEach(i =>
206
+ console.log(' ' + ((i.wastedBytes||0)/1024).toFixed(0).padStart(6) + 'KB | ' + (i.url||'').substring(0,90))
207
+ );
208
+
209
+ console.log('\n=== RENDER BLOCKING ===');
210
+ (a['render-blocking-resources']?.details?.items || []).forEach(i =>
211
+ console.log(' ' + Math.round(i.wastedMs||0) + 'ms | ' + (i.url||'').substring(0,90))
212
+ );
213
+ "
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Phase 3 — Real-User Monitoring (Speed Insights)
219
+
220
+ ### Setup
221
+
222
+ ```bash
223
+ pnpm add @vercel/speed-insights
224
+ ```
225
+
226
+ Add to the **root layout** (`src/app/layout.tsx`) — must be root, not a route group layout:
227
+ ```tsx
228
+ import { SpeedInsights } from '@vercel/speed-insights/next';
229
+
230
+ // Inside <body>:
231
+ <SpeedInsights />
232
+ ```
233
+
234
+ Deploy to production. View in **Vercel dashboard → Speed Insights**.
235
+
236
+ Data appears within minutes of the first production visit. Field data reflects real network conditions and will differ from Lighthouse's simulated throttling.
237
+
238
+ ---
239
+
240
+ ## Common Fixes
241
+
242
+ ### Fix 1: Heavy libraries loading on every page
243
+
244
+ **Symptom**: Lighthouse flags large unused JS. Bundle analyzer shows a library in first-load chunks but it's only used on specific pages/routes.
245
+
246
+ **Fix** — Use dynamic import with `next/dynamic` or native `import()` inside `useEffect`:
247
+ ```tsx
248
+ // next/dynamic (renders a component lazily)
249
+ import dynamic from 'next/dynamic';
250
+ const HeavyComponent = dynamic(() => import('./HeavyComponent'), { ssr: false });
251
+
252
+ // Conditional import in useEffect (for web components / side-effect-only libraries)
253
+ useEffect(() => {
254
+ if (document.querySelector('my-web-component')) {
255
+ import('my-web-component-library');
256
+ }
257
+ }, []);
258
+ ```
259
+
260
+ ---
261
+
262
+ ### Fix 2: Render-blocking third-party scripts
263
+
264
+ **Symptom**: GTM, consent scripts, or analytics tags appear in the Lighthouse render-blocking or TBT audits.
265
+
266
+ **Fix** — Use Next.js `<Script>` with appropriate strategy. Requires `'use client'`:
267
+ ```tsx
268
+ 'use client';
269
+ import Script from 'next/script';
270
+
271
+ // Load after page becomes interactive (GTM, analytics)
272
+ <Script src="https://www.googletagmanager.com/gtm.js?id=..." strategy="afterInteractive" />
273
+
274
+ // Load during browser idle time (consent banners, low-priority widgets)
275
+ <Script src="https://cdn.example.com/widget.js" strategy="lazyOnload" />
276
+ ```
277
+
278
+ ---
279
+
280
+ ### Fix 3: Legacy JS polyfills inflating bundle
281
+
282
+ **Symptom**: Lighthouse legacy-JavaScript audit flags `Array.prototype.at`, `.flat`, `.flatMap` being polyfilled.
283
+
284
+ **Fix** — Create `.browserslistrc` at the project root:
285
+ ```
286
+ last 2 Chrome versions
287
+ last 2 Firefox versions
288
+ last 2 Safari versions
289
+ last 2 Edge versions
290
+ not dead
291
+ ```
292
+
293
+ Verify audience browser share before applying — check analytics for any legacy traffic.
294
+
295
+ ---
296
+
297
+ ### Fix 4: Visuals without dimensions causing CLS
298
+
299
+ **Symptom**: CLS > 0.1. Lighthouse "Avoid large layout shifts" audit lists image or media elements.
300
+
301
+ **Fix** — Always set explicit `width` and `height` on `<img>` and `<video>` elements, or use `aspect-ratio` CSS on their container so the browser can reserve space before the asset loads:
302
+ ```tsx
303
+ <div style={{ aspectRatio: '16 / 9' }}>
304
+ <video ... />
305
+ </div>
306
+ ```
307
+
308
+ ---
309
+
310
+ ## Tracking baselines
311
+
312
+ Save Lighthouse JSON to `.lighthouse/<page>.json`. After each round of fixes, save to `.lighthouse/<page>-v2.json`, `v3.json`, etc.
313
+
314
+ Compare LCP across versions:
315
+ ```bash
316
+ node -e "
317
+ const v1 = JSON.parse(require('fs').readFileSync('.lighthouse/home.json','utf8'));
318
+ const v2 = JSON.parse(require('fs').readFileSync('.lighthouse/home-v2.json','utf8'));
319
+ const lcp = a => (a.audits['largest-contentful-paint'].numericValue/1000).toFixed(2)+'s';
320
+ const perf = a => Math.round(a.categories.performance.score * 100);
321
+ console.log('Home LCP: v1=' + lcp(v1) + ' → v2=' + lcp(v2));
322
+ console.log('Home Perf: v1=' + perf(v1) + ' → v2=' + perf(v2));
323
+ "
324
+ ```
325
+
326
+ Commit `.lighthouse/` to git for team-visible historical baselines.
327
+
328
+ ---
329
+
330
+ ## Troubleshooting
331
+
332
+ | Error | Cause | Fix |
333
+ |---|---|---|
334
+ | `Unable to connect to Chrome` | Lighthouse can't find Chrome binary | Add `--chrome-path /usr/bin/google-chrome` |
335
+ | Lighthouse fails on Node 20.18.x | Requires lighthouse@12 | Use `npm i -g lighthouse@12` |
336
+ | `ANALYZE=true next build` fails | `next.config.ts` export not wrapped | Ensure `export default withBundleAnalyzer(nextConfig)` |
337
+ | Speed Insights shows no data | Wrong layout, or not deployed to production | Must be in root `src/app/layout.tsx`; requires production traffic |
338
+ | Lighthouse score varies between runs | CPU/network simulation variability | Run 3× and average; use `--throttling-method=provided` for stable local results |
339
+
340
+ ---
341
+
342
+ ## See also
343
+
344
+ - **SE Studio stack**: if the project uses `@se-studio/core-ui`, Contentful CMS, or ImageKit, see the SE-Studio-specific performance-audit skill for additional fixes (lazy video/animation loading, variable font subsetting, lottie-player optimisation).