@se-studio/skills 1.9.10 → 1.9.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @se-studio/skills
2
2
 
3
+ ## 1.9.15
4
+
5
+ ### Patch Changes
6
+
7
+ - f03f1f1: Document the weekly production Screaming Frog fleet audit.
8
+
9
+ ## 1.9.14
10
+
11
+ ### Patch Changes
12
+
13
+ - 3d4f180: State the marketing-route and layout rules in the customer architecture block, the session skill, and the page and styling skills. A new URL is a Contentful slug. Frame width is not a max-width, and a missing text style stops the plan instead of becoming `leading-[…]`.
14
+
15
+ ## 1.9.13
16
+
17
+ ### Patch Changes
18
+
19
+ - b47c1c2: Implement se-core #252 anti-Webflow-port BLOCKERs plus Nick simplicity rules (no RTF flatten, links as-is, no hardcoded visitor copy, Tailwind over CSS islands, fail closed). Add skill `se-marketing-sites-customer-simplicity`, wire from agent-session, and soften globals.css / customCss escape hatches.
20
+
21
+ ## 1.9.12
22
+
23
+ ### Patch Changes
24
+
25
+ - fbb6952: Document Option C editor-pack publish via customer CI (reusable GitHub Actions workflow). Agents regenerate and commit packs only; they do not run `gh release create` unless Nick asks.
26
+
27
+ ## 1.9.11
28
+
29
+ ### Patch Changes
30
+
31
+ - 3ecda13: Document that different UI/behaviour needs distinct `componentType` / `collectionType`; `cmsLabel` is for authors (+ trackingLocation), never a runtime layout/renderer switch. Update create-component, create-collection, and contentful-cms-core (synced into contentful-cms bundled `core` skill).
32
+
3
33
  ## 1.9.10
4
34
 
5
35
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/skills",
3
- "version": "1.9.10",
3
+ "version": "1.9.15",
4
4
  "description": "SE Studio agent skills for marketing site development with Contentful CMS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,7 +28,7 @@ Before non-trivial code, run the placement checklist (in the skill). Summary:
28
28
 
29
29
  | Belongs in… | Examples |
30
30
  |-------------|----------|
31
- | **This repo** (`src/project/*`, `cms-edit/**`) | Brand components, styling, site routing, editor-pack |
31
+ | **This repo** (`src/project/*`, `cms-edit/**`) | Brand components, styling, editor-pack, and `src/lib/marketing-routes.json` (which article-type and person-type folders exist). A new URL is a Contentful slug, not a new `page.tsx`. |
32
32
  | **Customer shared package** (if monorepo) | Consent/analytics shared across sites in this customer |
33
33
  | **`se-core-product`** (`@se-studio/*`) | Fixes/features needed by 2+ sites, CMS infrastructure |
34
34
 
@@ -24,8 +24,18 @@ Source of truth: `se-core-product` `docs/PHILOSOPHY.md`. Code review: `CODE_REVI
24
24
 
25
25
  - Keep T0/T1 skeleton files thin; document deviations in the site README.
26
26
  - Use core route handlers (`generatePage`, robots, sitemaps, etc.). Do not reimplement.
27
+ - A new marketing URL is a Contentful page slug (use `indexed: false` for an experiment). Do not add `src/app/**/page.tsx` for it. App Router pages are the CMS catch-all, a folder named in `src/lib/marketing-routes.json` for an article type or a person type, and the fixed misc files (page-test, preview, sitemap, robots, markdown, download, og, api, cms-dev).
28
+ - Every marketing block is `<Section>` with `content-cols-grid`. Width is `col-span-*`, `max-w-cols-N`, or `max-w-cols-gap-N`. Type is a `fontTable` class (`h1`, `p2`, `sizingInformation.*`). Vertical space is `section-spacing` on `Section`.
29
+ - Figma stays the source for layout, copy, colour, and type when a text style is set. Implement with the grid and the font table. Do not paste MCP arbitrary classes.
30
+ - Plan-time stops: the frame width (1440, 1920, whatever the artboard is) is not `max-w-[…]`. Type with no text style (`leading-[1.2]`, raw `text-[32px]`) means the designer did not specify the font — name the node and wait. Do not pick a class and continue.
27
31
  - Mobile-first Tailwind. Preserve heading hierarchy. Size visuals with core helpers. Avoid duplicate mobile/desktop markup for the same content.
28
32
  - UI changes: early Vercel preview (mobile + laptop) → Nick visual → full breakpoints including oversize (~1920+) before deep polish/code review.
33
+ - **Remakes are not Webflow reconstruction** (PHILOSOPHY §5a / `se-marketing-sites-customer-simplicity`):
34
+ - CMS entry → registered component/collection only; **fail closed** when a field is empty (omit UI / fix CMS — never invent from the live site).
35
+ - Body/`additionalCopy` via `RtfOrString` — no RTF→plain flatten for layout.
36
+ - CMS `links` as-is with standard link helpers — no invented roles unless Nick asks.
37
+ - No hardcoded visitor copy, live URLs, or brand media fallbacks in TS.
38
+ - Tailwind + design tokens over CSS islands; do not grow Webflow-ported page CSS. `globals.css` / HtmlComponent `customCss` for true one-offs or shared primitives only.
29
39
 
30
40
  ## Caching / performance / images
31
41
 
@@ -52,14 +52,28 @@ Run all five lenses. Skip a lens only when the diff clearly cannot touch it (say
52
52
  - Consent granted by default where a CMP decides, or bypassing site consent gating.
53
53
  - Forms: preview posting to production CRM/forms destinations or GTM, or bypassing server-side submit.
54
54
  - Hard-coded heading skips, `Visual` without size helpers, or needless duplicate mobile/desktop content.
55
+ - A new `src/app/**/page.tsx` for a marketing URL. That URL is a Contentful slug (often `indexed: false` for an experiment). Allowed app pages: the CMS catch-all, an article type or person type named in `src/lib/marketing-routes.json`, and the fixed misc set (page-test, preview, sitemap, robots, markdown, download, og, api, cms-dev).
56
+ - Arbitrary layout standing in for the grid or an unspecified text style: `max-w-[1440px]` or `max-w-[1920px]` (that number is the Figma frame, not a max-width), `leading-[…]` / `text-[32px]` with no `fontTable` class, section padding via `py-[…]` instead of `<Section>`, `content-cols-grid`, and `max-w-cols-N`. Name the Figma node in the plan and wait. Do not guess a style and do not paste MCP classes.
55
57
  - Failing typecheck, lint, or tests; new behaviour with no test where the site already tests that layer.
56
58
  - Smoke/discovery/`server-config` hacks to unblock deploy smoke (belongs in `@se-studio/site-check`).
59
+ - **Remake / port simplicity** (PHILOSOPHY §5a / skill `se-marketing-sites-customer-simplicity`):
60
+ - Stub or heading-match fallback as the live renderer; unfinished types must stay unregistered or preview-only.
61
+ - Catalogue fetch (`getAllArticleLinks` / `getAllPersonLinks` / walking another page) inside a component or layout.
62
+ - Layout/behaviour multiplex on `cmsLabel` / heading / `anchor` (type ≠ label).
63
+ - Flattening body/`additionalCopy` RTF → plain strings or paragraph heuristics to drive layout (use `RtfOrString`).
64
+ - Inventing or filtering CMS `links` into roles / special chrome unless Nick explicitly asked.
65
+ - Hardcoded visitor copy, live URLs, CDN paths, or brand media fallbacks in TS — fix CMS instead.
66
+ - New Webflow/FlowKit CSS dumps or growing page CSS islands (`etf.css`-style, `fk-*`, minified sheets) for layout; prefer Tailwind + tokens. `globals.css` / `customCss` only for true one-offs or shared primitives.
67
+ - Filling missing CMS by inventing from the live site (must **fail closed**: omit UI or fix CMS).
68
+ - Second public URL system or custom App Router page for a type `generatePage` already covers.
69
+ - Mixed `@se-studio/*` majors on one site without an aligned bump plan.
57
70
 
58
71
  **NIT**
59
72
 
60
73
  - Prefer existing core components over one-offs.
61
74
  - Don’t rename existing tracking events only to match other sites.
62
75
  - Clearer names, smaller functions, docs polish.
76
+ - Pixel-perfect vs Figma before the early visual gate (not a merge blocker early). Webflow remakes follow CMS→component + tokens, not pixel CSS dumps.
63
77
 
64
78
  ---
65
79
 
@@ -70,7 +84,7 @@ If the PR changes UI, confirm the session followed agent-session visual phases:
70
84
  1. **Early:** Vercel preview, mobile (~390) + laptop (~1280), stopped for Nick’s visual review.
71
85
  2. **Full (after Nick is happy):** tablet (~768), laptop (~1280), **oversize (~1920+)**, plus mobile — flag overflow, stretched heroes, broken grids.
72
86
 
73
- Do not treat “pixel-perfect vs Figma” as a merge blocker before the early visual gate completes.
87
+ Do not treat “pixel-perfect vs Figma” as a merge blocker before the early visual gate completes. Remakes from live Webflow/HTML are **not** a fidelity chase against the old site — implement CMS fields with tokens; flag invented structure as **BLOCKER** above.
74
88
 
75
89
  ---
76
90
 
@@ -11,6 +11,8 @@ metadata:
11
11
 
12
12
  `HtmlComponent` is a CMS content type that renders developer-authored HTML directly into the page. It bypasses the standard component registration system and is intended for one-off or highly custom layouts that the structured component library cannot produce.
13
13
 
14
+ **Last resort, not the default remake path.** Prefer a registered `componentType` / `collectionType` + Tailwind tokens. Do not use HtmlComponent + `customCss` to reconstruct a Webflow page when a registered component would work. See `se-marketing-sites-customer-simplicity` and PHILOSOPHY §5a.
15
+
14
16
  ---
15
17
 
16
18
  ## Before You Start
@@ -299,7 +299,7 @@ export const HeroRenderer: ComponentRenderer<ProjectConfig> = ({ information })
299
299
 
300
300
  ## Code Style Requirements
301
301
 
302
- - **Styling**: Tailwind only. Do not use CSS modules (`*.module.css`). Use Tailwind classes or `src/app/globals.css` for shared styles.
302
+ - **Styling**: Tailwind utilities + design tokens first. Do not use CSS modules (`*.module.css`). Prefer classes in TSX over new CSS. `src/app/globals.css` is for **shared primitives only** (keyframes, RTF element maps, true design-system utilities) — not per-page Webflow/layout reconstruction. Growing page CSS islands is review debt (see `se-marketing-sites-customer-simplicity`).
303
303
  - **Biome** for linting/formatting (not ESLint/Prettier)
304
304
  - Single quotes, semicolons, trailing commas
305
305
  - `import type` for type-only imports
@@ -218,8 +218,8 @@ Based on the SE Studio content model:
218
218
  ### Component fields
219
219
  | Field | Type | Notes |
220
220
  |-------|------|-------|
221
- | `cmsLabel` | string | Internal name (required) |
222
- | `componentType` | string | Type discriminator (e.g. `HeroSimple`) |
221
+ | `cmsLabel` | string | Internal name for authors in Contentful (required). Not a runtime layout/renderer switch — never branch code on it. |
222
+ | `componentType` | string | Type discriminator for renderer lookup (e.g. `HeroSimple`). Different UI/behaviour needs a distinct value. |
223
223
  | `heading` | string | Main heading (only shown when `showHeading` is true) |
224
224
  | `showHeading` | boolean | Whether to display the heading |
225
225
  | `preHeading` | string | Text above the heading |
@@ -234,7 +234,7 @@ Based on the SE Studio content model:
234
234
  Same as Component plus:
235
235
  | Field | Type | Notes |
236
236
  |-------|------|-------|
237
- | `collectionType` | string | Type discriminator (e.g. `CardGrid`) |
237
+ | `collectionType` | string | Type discriminator for renderer lookup (e.g. `CardGrid`). Different UI/behaviour needs a distinct value; do not switch on `cmsLabel`. |
238
238
 
239
239
  ### Page fields
240
240
  | Field | Type | Notes |
@@ -91,10 +91,14 @@ git add cms-edit/editor-pack # or cms-edit/<site>/editor-pack
91
91
  git commit -m "chore(cms-edit): regenerate editor pack"
92
92
  ```
93
93
 
94
- ## Step 6 — Publish (commit + push only)
94
+ ## Step 6 — Publish (commit + push; CI owns GitHub Releases)
95
95
 
96
96
  **Absolute rule for agents:** hosted MCP is **never** published via `vercel deploy`, `pnpm cms-edit:deploy*`, or `deploy-cms-edit-host.sh`. Commit and push is the only deploy path for customer repos with `cms-edit/host/`.
97
97
 
98
+ **Option C (shared multi-tenant host):** Customer CI publishes the committed pack as a GitHub Release (`editor-pack.tar.gz`) via the reusable workflow `Something-Else-Studio/se-core-product/.github/workflows/cms-edit-pack-publish.yml`. Agents do **not** run `gh release create` (or treat the printed command from `cms-edit host pack-publish` as something to execute) unless Nick explicitly asks. Do **not** regenerate the pack in CI — publish the committed `editor-pack/` only. Tags are immutable (`cms-edit-pack-<projectKey>-v<version>`); bump version for a new Release, then update the host registry `packTag` when intentional.
99
+
100
+ See `packages/contentful-cms/HOSTED.md` (Option C section) for the caller workflow sketch.
101
+
98
102
  ### Customer repo with `cms-edit/host/` (SE Studio, Pedestal, Headwater, Brightline, …)
99
103
 
100
104
  Commit `editor-pack/` (and any other `cms-edit/**` changes) and push **`develop`**. Hosted MCP Production is the marketing production branch (`main`, HSD `production`). Merge there only after ask + confirm (`production-merge.md`). `ignoreCommand` skips `develop` and feature refs.
@@ -108,7 +112,7 @@ Do **not** run `pnpm cms-edit:deploy` or `vercel deploy` after this step.
108
112
 
109
113
  ### Customers without `cms-edit/host/` in their repo
110
114
 
111
- Rare layout — coordinate with the repo owner. Agents still must not run deploy scripts unless explicitly asked.
115
+ Rare layout — coordinate with the repo owner. Agents still must not run deploy scripts unless explicitly asked. For Option C tenants, pack publish still goes through customer CI → GitHub Release (same reusable workflow), not a local `gh release create`.
112
116
 
113
117
  ## Homepage slug reminder
114
118
 
@@ -59,6 +59,20 @@ Prioritized P0–P3 report from a single crawl export.
59
59
 
60
60
  ---
61
61
 
62
+ ## Fleet production audit
63
+
64
+ From se-core-product, crawl every marketing production site and write P0–P3 reports. One site at a time. Quit the Screaming Frog GUI first.
65
+
66
+ ```bash
67
+ pnpm seo:screaming-frog:fleet # crawl + audit
68
+ pnpm seo:screaming-frog:fleet -- --list
69
+ node scripts/screaming-frog-fleet.mjs --install-agent
70
+ ```
71
+
72
+ Reports land in `~/spider/fleet-audits/<YYYY-MM-DD>/`. The command does not pass `--tag`, so it does not replace a deployment `content` baseline. `--install-agent` loads a user LaunchAgent (`studio.se.screaming-frog-fleet`) for Sunday 22:00 local time.
73
+
74
+ Start URL: the manifest `production` profile. When that profile is a `*.vercel.app` alias and cms-edit `productionSiteUrl` is a public host, the public host is crawled. Eagle stays on its configured Vercel production URL. Preview-only issue downgrades (noindex, preview robots) are not applied.
75
+
62
76
  ## Other commands
63
77
 
64
78
  ```bash
@@ -7,6 +7,8 @@ description: "Guide for the (cms-routes) route group and appShared pattern. Use
7
7
 
8
8
  Reference app: **example-empty** (this monorepo). Customer sites with fuller route sets: see `docs/RELATED_PROJECTS.md` (e.g. OM1 uses `resources/`, `team/`, `topics/` instead of `articles/`, `people/`, `tags/`).
9
9
 
10
+ Do not add a `page.tsx` for a marketing URL. That URL is a Contentful slug on the catch-all. Extra folders are only article types and person types listed in `src/lib/marketing-routes.json`.
11
+
10
12
  ## Route Group: (cms-routes)
11
13
 
12
14
  All CMS-driven pages live under `src/app/(cms-routes)/`. The layout enables dynamic params:
@@ -29,9 +31,9 @@ export default function CmsRoutesLayout({ children }: { children: React.ReactNod
29
31
  | `/{slug1}/{slug2}/...` | `[level1]/[...slugs]/page.ts` | Multi-level pages |
30
32
  | `/articles/` | `articles/page.ts` | Articles index (uses ARTICLES_BASE) |
31
33
  | `/articles/{type}/` | `articles/[articleType]/page.ts` | Article type index |
32
- | `/articles/{type}/{tag}/` | `articles/[articleType]/[tag]/page.ts` | Article type + tag index |
33
- | `/articles/{type}/{tag}/{article}/` | `articles/[articleType]/[tag]/[...slugs]/page.ts` | Individual article |
34
- | `/tags/`, `/tags/{tag}/` | `tags/` | Tag index (if enabled) |
34
+ | `/articles/{type}/{tag}/` | `articles/[articleType]/[tag]/page.ts` | Article type + tag index. Only when that base sets `tagInPath: true`. Hand-written `generateArticleTypeTag*` page. |
35
+ | `/articles/{type}/{tag}/{article}/` | `articles/[articleType]/[tag]/[...slugs]/page.ts` | Individual article with a tag segment. Same `tagInPath` rule. |
36
+ | `/tags/`, `/tags/{tag}/` | `tags/` | Tag index when `tags: true` |
35
37
  | `/people/`, `/people/{person}/` | `people/` | People (if enabled) |
36
38
 
37
39
  Base paths come from `@/lib/constants` (e.g. ARTICLES_BASE = `/learning-hub` or `/articles`).
@@ -97,7 +99,7 @@ export default async function (props: PageProps<'/[level1]'>) {
97
99
  }
98
100
  ```
99
101
 
100
- For article routes, use `converterContext.urlCalculators.article`, `articleType`, `articleTypeTag` to compute the path. Import `converterContext` from `@/lib/converter-context` when that module exists (so route files don't pull in cms-server); otherwise from `@/lib/cms-server`.
102
+ A dynamic `articles` route uses `converterContext.urlCalculators` (`article`, `articleType`, `articleTypeTag`). Import `converterContext` from `@/lib/converter-context` when that module exists (so route files don't pull in cms-server); otherwise from `@/lib/cms-server`. A fixed article folder, and a dynamic base whose path is not `articles`, passes the path of that file (`/work/`, `/work/${articleSlug}/`). `article()` prefixes `ARTICLES_BASE` and, when primary tags are on, inserts `other`. That is a different URL from the file that was written. The generator does this. Hand-written `[tag]` pages still use `articleTypeTag`.
101
103
 
102
104
  ## See Also
103
105
 
@@ -289,3 +289,12 @@ export const MyCollectionRegistration = defineCollection({
289
289
  * Pass server-rendered cards as `children` to the client wrapper.
290
290
  7. **Register**:
291
291
  * Add to `collectionRegistrationsList` in `src/lib/registrations.ts`. The `name` is defined only in your `defineCollection(...)` call.
292
+ 8. **Type vs label (hard rule)**:
293
+ * New UI or behaviour → new Contentful `collectionType` enum value + registration. Never `if (cmsLabel)` / parse labels for layout or renderer choice.
294
+ * `cmsLabel` is for authors (and may pass to `trackingLocation`); it is not a runtime discriminator.
295
+ * Position/variant-only differences may share one Core under multiple `defineCollection` registrations; drive layout from `collectionType`.
296
+ 9. **Simplicity (remakes / ports)**:
297
+ * Do **not** flatten item body fields for layout; use `<RtfOrString>`.
298
+ * Do **not** invent link roles from CMS `links` unless Nick asks.
299
+ * Prefer Tailwind tokens over CSS islands; empty fields → omit (**fail closed**).
300
+ * Load `se-marketing-sites-customer-simplicity` on remake tasks.
@@ -231,9 +231,10 @@ export const MyNewRegistration = defineComponent({
231
231
  * Add `'use client'` to that file only.
232
232
  * Import and use in Core component.
233
233
  5. **Build Wrapper Component**:
234
- * Wrap Core with `<Section>`.
234
+ * Wrap Core with `<Section>` (`content-cols-grid`, `section-spacing`). Do not replace Section with `max-w-[1440px]` or `py-16`.
235
235
  * Pass `componentName={componentType}` and `information`.
236
- * Set layout (e.g., `positionClassName="col-span-full"`).
236
+ * Set layout with `col-span-*`, `max-w-cols-N`, or `max-w-cols-gap-N`.
237
+ * Type is a `fontTable` class or `sizingInformation.*`. A Figma frame width is not a max-width. A node with no text style (`leading-[1.2]`, raw `text-[32px]`) stops the plan — do not guess a class.
237
238
  6. **Build Renderer**:
238
239
  * Default export (short name).
239
240
  * Wrap with `<UnusedChecker>`.
@@ -244,3 +245,13 @@ export const MyNewRegistration = defineComponent({
244
245
  * Provide mock data for showcase.
245
246
  8. **Register Component**:
246
247
  * Add to `componentRegistrationsList` in `src/lib/registrations.ts`. The `name` is defined only in your `defineComponent(...)` call; do not duplicate it as a Record key.
248
+ 9. **Type vs label (hard rule)**:
249
+ * New UI or behaviour → new Contentful `componentType` enum value + registration. Never `if (cmsLabel)` / parse labels for layout or renderer choice.
250
+ * `cmsLabel` is for authors (and may pass to `trackingLocation`); it is not a runtime discriminator.
251
+ * Position/variant-only differences may share one Core under multiple `defineComponent` registrations; drive layout from `componentType`.
252
+ 10. **Simplicity (remakes / ports)**:
253
+ * Layers are the registration pattern — not an invitation to bespoke parsers.
254
+ * Do **not** flatten body/`additionalCopy` for layout; use `<RtfOrString>`.
255
+ * Do **not** invent or filter CMS `links` into roles unless Nick asks.
256
+ * Prefer Tailwind tokens over new `globals.css` layout rules; CSS islands are debt.
257
+ * Empty field → omit UI (**fail closed**). Load `se-marketing-sites-customer-simplicity` on remake tasks.
@@ -9,6 +9,18 @@ This guide explains how to create new pages and handle routing in the SE Core Pr
9
9
 
10
10
  Reference app: **example-empty** (this monorepo). Customer implementations: see `docs/RELATED_PROJECTS.md`.
11
11
 
12
+ ## Do not add a route for a marketing URL
13
+
14
+ A new URL is a Contentful page slug, served by the catch-all (`[level1]` and `[...slugs]`). An experiment is a CMS page with `indexed: false`, not a new folder under `src/app`.
15
+
16
+ App Router `page` files are only:
17
+
18
+ - the CMS catch-all
19
+ - a folder named in `src/lib/marketing-routes.json` for an article type or a person type (`node scripts/generate-marketing-routes.mjs <app> --write` writes the thin article index and detail wrappers; `[tag]` segments are hand-written when that base sets `tagInPath: true`)
20
+ - the fixed misc files (page-test, preview, sitemap, robots, markdown, download, og, api, cms-dev)
21
+
22
+ `pnpm skeleton:check` fails an extra `page.ts` once `marketing-routes.json` is present. Search and other non-page tools stay misc routes. They are not a second copy of a CMS page.
23
+
12
24
  ## Page Architecture
13
25
 
14
26
  ### 1. (cms-routes) Route Group
@@ -33,9 +45,9 @@ export default function CmsRoutesLayout({ children }: { children: React.ReactNod
33
45
  | `/{slug1}/{slug2}/...` | `[level1]/[...slugs]/page.ts` | Multi-level pages |
34
46
  | `/articles/` | `articles/page.ts` | Articles index (uses ARTICLES_BASE from constants) |
35
47
  | `/articles/{type}/` | `articles/[articleType]/page.ts` | Article type index |
36
- | `/articles/{type}/{tag}/` | `articles/[articleType]/[tag]/page.ts` | Article type + tag index |
37
- | `/articles/{type}/{tag}/{article}/` | `articles/[articleType]/[tag]/[...slugs]/page.ts` | Individual article |
38
- | `/tags/`, `/tags/{tag}/` | `tags/` | Tag index (if enabled) |
48
+ | `/articles/{type}/{tag}/` | `articles/[articleType]/[tag]/page.ts` | Article type + tag index. Only when that base sets `tagInPath: true`. Hand-written `generateArticleTypeTag*` page. `--write` does not emit it. |
49
+ | `/articles/{type}/{tag}/{article}/` | `articles/[articleType]/[tag]/[...slugs]/page.ts` | Individual article with a tag segment. Same `tagInPath` rule. |
50
+ | `/tags/`, `/tags/{tag}/` | `tags/` | Tag index when `tags: true` |
39
51
  | `/people/`, `/people/{person}/` | `people/` | People (if enabled) |
40
52
 
41
53
  Base paths (e.g. `/articles` vs `/learning-hub`) come from `@/lib/constants`.
@@ -104,19 +116,9 @@ The appShared modules render content with:
104
116
 
105
117
  For route files that only need `converterContext` or `buildInformation` (e.g. `generateStaticParams`, sitemap, path computation), import from `@/lib/converter-context` when that module exists, so you don't pull in cms-server.
106
118
 
107
- ## Creating a Specialized Route
108
-
109
- For a route that behaves differently from standard CMS pages (e.g. search, dashboard):
110
-
111
- 1. Create the route under `(cms-routes)` or a separate route group.
112
- 2. Either create a new appShared module (if the pattern is reusable) or implement inline.
113
- 3. Use `getPageWithErrors`, `getArticleWithErrors`, etc. from `@/lib/cms-server`.
114
- 4. Use `projectRendererConfig` from `@/lib/cms-server`.
115
- 5. Render with `CmsContent` using `contents`, `rendererConfig`, and `pageContext`.
116
-
117
119
  ## Custom Layouts
118
120
 
119
- If a route needs a different layout (e.g. no header/footer), create `layout.tsx` in that route directory.
121
+ A different chrome (for example no header) is a `layout.tsx` on a route that already exists. It is not a new URL. A new marketing URL is still a Contentful slug.
120
122
 
121
123
  ## Troubleshooting
122
124
 
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: se-marketing-sites-customer-simplicity
3
+ description: "Guardrail for customer marketing remakes and ports. Load on Webflow/HTML remake, match-live-site, or parity tasks: CMS to component only, fail closed, no RTF flatten, link inventing, CSS islands, or hardcoded copy."
4
+ ---
5
+
6
+ # Customer site simplicity (remakes / ports)
7
+
8
+ CMS field in → registered UI out. Do not reconstruct the live site.
9
+
10
+ Canonical: `docs/PHILOSOPHY.md` §5a. Review: `customer-code-review.md`. Session: `site-workflows-agent-session` (**Do not port Webflow / HTML pages**). Subtract first: `principle-subtract-before-you-add`. How to build a type: `se-marketing-sites-create-component` / `create-collection`.
11
+
12
+ ## Do
13
+
14
+ - CMS entry → `componentType` / `collectionType` → Registration → Core on core route handlers
15
+ - Empty field → **omit** that UI (or fix CMS). Never invent from the live site
16
+ - Body / `additionalCopy` via `RtfOrString`
17
+ - CMS `links` as-is with `SectionLinks` / `TrackedLink` (or site equivalents)
18
+ - Tailwind + design tokens; `Visual` + `calculateVisualSizes`; `getSizingInformation`
19
+
20
+ ## Do not (BLOCKER)
21
+
22
+ | Antipattern | Instead |
23
+ |-------------|---------|
24
+ | Stub / heading-match as live renderer | Full layers, or stay unregistered |
25
+ | Catalogue fetch inside a component | Collection `contents` / related fields |
26
+ | Multiplex on `cmsLabel` / heading / `anchor` | One type = one layout (R4.2a) |
27
+ | RTF → plain / paragraph heuristics for layout | `RtfOrString` only |
28
+ | Invent / filter `links` into roles | Render links as given (unless Nick asks) |
29
+ | Hardcoded visitor copy, live URLs, CDN/media fallbacks | Fix CMS / import |
30
+ | Webflow/FlowKit CSS dumps / page CSS islands | Tokens + Tailwind utilities |
31
+ | Second public URL system / custom page for `generatePage` types | One IA; type owns the prefix |
32
+ | Mixed `@se-studio/*` majors | Aligned bump |
33
+
34
+ ## Escape hatches (narrow)
35
+
36
+ - `globals.css`: shared primitives only (keyframes, RTF element maps, true design-system utilities) — **not** per-page layout ports
37
+ - HtmlComponent + `customCss`: last resort when a registered type cannot express the interaction — **not** the default Webflow remake path
38
+
39
+ ## Related
40
+
41
+ - `site-workflows-agent-session`, `se-marketing-sites-styling-system`, `coding-discipline`
42
+ - Do not load Cursor Webflow DevLink / FlowKit skills on `@se-studio` customer sites
@@ -93,6 +93,16 @@ While configurable, most projects use:
93
93
  * `laptop`: ~1024px+
94
94
  * `desktop`: ~1440px+
95
95
 
96
+ Desktop at 1440 is the artboard the grid is built from. It is not a `max-w-[1440px]`. The grid (`container-cols-grid` outside, `content-cols-grid` inside) is what changes at other widths.
97
+
98
+ ### Sections
99
+
100
+ A marketing block is `<Section>` from `@/framework/Section`. That sets `container-cols-grid` and `section-spacing`. Do not replace it with `mx-auto max-w-[…] px-… py-16`.
101
+
102
+ Width inside the section is a column span, `max-w-cols-N`, or `max-w-cols-gap-N` (spacing tokens from `--spacing-cols-*`). Type is a `fontTable` class (`h1`, `p1`) or `sizingInformation.h1` from `getSizingInformation`. Do not write `text-[32px]`, `leading-[1.2]`, or `tracking-[…]`.
103
+
104
+ If Figma has no text style on a node, stop and name that node. Do not guess a class. If the only width in the file is the frame, use the grid.
105
+
96
106
  ### Layout Helpers
97
107
 
98
108
  Common layout patterns using Tailwind grid utilities:
@@ -124,9 +134,13 @@ Wrap the `RtfOrString` component with the appropriate class:
124
134
 
125
135
  ## Global Styles
126
136
 
127
- Project-specific global styles, animations, and custom utility classes are located in `src/app/globals.css`. This file imports the generated Tailwind CSS and defines:
137
+ Project-specific global styles live in `src/app/globals.css`. Prefer **Tailwind utilities and design tokens in TSX** for layout. Treat new layout selectors in `globals.css` or extra `*.css` files as **review debt** unless they are shared design-system primitives.
138
+
139
+ `globals.css` is appropriate for:
128
140
 
129
141
  * Root variables (if any)
130
- * Animation keyframes
131
- * Custom component classes (e.g., `.btn-primary`)
132
- * Rich Text Formatting classes
142
+ * Animation keyframes / `--animate-*` tokens
143
+ * Rich Text Formatting element maps (`.rtf-*`)
144
+ * True shared design-system utilities (rare; prefer tokens)
145
+
146
+ Do **not** grow per-page Webflow ports or CSS islands for layout (see `se-marketing-sites-customer-simplicity`). HtmlComponent `customCss` is a last-resort escape hatch for one-offs, not the default remake path.
@@ -231,9 +231,26 @@ When editing Contentful in parallel on the same space, use distinct cms-edit ses
231
231
  - **Do not** edit `se-core-product` unless placement is `core` and user approves mode switch
232
232
  - Consume `@se-studio/*` from npm — not monorepo workspace links
233
233
 
234
+ ### Do not port Webflow / HTML pages
235
+
236
+ Rebuild remakes as **CMS components and collections** on core handlers (`generatePage`, registrations, `RtfOrString`, Tailwind tokens). Never “match this Webflow section in one file,” dump FlowKit/`fk-*` CSS, multiplex on `cmsLabel`/heading, flatten RTF for layout, invent link roles, or hardcode live copy/URLs. **Fail closed** when CMS is empty — omit UI or fix CMS; do not invent from the live site.
237
+
238
+ Load skill **`se-marketing-sites-customer-simplicity`** on any Webflow/HTML remake, “match the live site,” or parity task. Also: `principle-subtract-before-you-add`. Canonical rules: `docs/PHILOSOPHY.md` §5a; review BLOCKERs in [`customer-code-review.md`](../../references/agent-session/customer-code-review.md).
239
+
240
+ Do **not** use Cursor Webflow DevLink / FlowKit skills on `@se-studio` customer sites (wrong stack: CSS modules / `_webflow-style.css`).
241
+
234
242
  ### Figma direction (new designs) + phased visual gates
235
243
 
236
- **Until the user explicitly says otherwise**, when implementing UI from a Figma file or node, treat Figma as the **direction / source of truth** for structure, spacing, type, colour, radii, alignment, and control patterns. Do not “improve” or normalize away from the comps to match older site patterns. Prefer Figma MCP design context over CMS drafts or live-site memory.
244
+ **Until the user explicitly says otherwise**, when implementing UI from a **new** Figma file or node, treat Figma as the **direction / source of truth** for structure, spacing, type, colour, radii, alignment, and control patterns. Do not “improve” or normalize away from the comps to match older site patterns. Prefer Figma MCP design context over CMS drafts or live-site memory.
245
+
246
+ Two things in that file are not decisions. Raise them in the plan and do not write the code until there is a concrete answer:
247
+
248
+ - The frame width (1440, 1920, whatever the artboard is) is one canvas size. The grid is how that frame adapts at other widths. It is not `max-w-[1440px]`. If the comp really seems to need a hard cap at the frame width, say so and wait.
249
+ - Type with no text style (`leading-[1.2]`, raw `text-[32px]`) means the designer did not specify the font. Name the node. Do not copy the leading and do not guess a `fontTable` class (`h1`, `p1`, `sizingInformation.*`).
250
+
251
+ Implement what Figma did specify with `<Section>`, `content-cols-grid`, column spans or `max-w-cols-N` / `max-w-cols-gap-N`, and a font-table class. Do not paste arbitrary classes from Figma MCP.
252
+
253
+ **Remakes from live Webflow/HTML are different:** Figma SoT does **not** apply. Use CMS structure + design tokens; do not chase pixel CSS dumps from the old site. See **Do not port Webflow / HTML pages** above.
237
254
 
238
255
  **Do not** chase pixel-perfect polish before the **early visual gate**. Iterate with Nick on a Vercel preview first.
239
256
 
@@ -259,7 +276,7 @@ Flag layout blowouts, overflow, sparse/stretched heroes, broken grids, and type
259
276
 
260
277
  Non-UI changes skip visual phases and go to code review.
261
278
 
262
- **Related skills:** `site-workflows-figma-design-snapshot` / `site-workflows-apply-design-snapshot`; Figma MCP `get_design_context`.
279
+ **Related skills:** `se-marketing-sites-customer-simplicity` (remakes / ports); `principle-subtract-before-you-add`; `site-workflows-figma-design-snapshot` / `site-workflows-apply-design-snapshot`; Figma MCP `get_design_context`.
263
280
 
264
281
  ### Cross-project handoffs
265
282
 
@@ -247,13 +247,15 @@ Use `getSizingInformation(index)` from `@/lib/SizingInformation` for dynamic sem
247
247
  | laptop | 1024px | 12 |
248
248
  | desktop | 1440px | 12 |
249
249
 
250
- Gap: 16px at all breakpoints. Margin: 16px at all breakpoints.
250
+ 1440 is the desktop artboard. It is not `max-w-[1440px]`. The grid is what adapts at other widths.
251
251
 
252
- Use `content-cols-grid` for standard content layout.
252
+ Gap and margin come from `tailwind.config.json`.
253
+
254
+ A marketing block is `<Section>` (`container-cols-grid` + `section-spacing`) with an inner `content-cols-grid`. Width is `col-span-*`, `max-w-cols-N`, or `max-w-cols-gap-N`. Type is a `fontTable` class. Do not use `py-16` as section padding, or `leading-[…]` / `text-[32px]` when the file has no text style — stop and ask which style.
253
255
 
254
256
  ## Section Spacing
255
257
 
256
- Standard vertical padding: `py-16 laptop:py-24`
258
+ Vertical rhythm is `section-spacing` on `<Section>`, not a hand-written `py-*`.
257
259
 
258
260
  ## RTF Classes
259
261