@se-studio/project-build 1.0.130 → 1.0.132
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 +12 -0
- package/README.md +13 -14
- package/package.json +2 -4
- package/dist/management/sync-skills.d.ts +0 -4
- package/dist/management/sync-skills.d.ts.map +0 -1
- package/dist/management/sync-skills.js +0 -97
- package/dist/management/sync-skills.js.map +0 -1
- package/dist/seskills.d.ts +0 -9
- package/dist/seskills.d.ts.map +0 -1
- package/dist/seskills.js +0 -32
- package/dist/seskills.js.map +0 -1
- package/skills/contentful-cms/alt-text-audit/SKILL.md +0 -60
- package/skills/contentful-cms/cms-guidelines/README.md +0 -166
- package/skills/contentful-cms/cms-guidelines/colour-hint-prompt.md +0 -77
- package/skills/contentful-cms/cms-guidelines/evaluation-prompt.md +0 -84
- package/skills/contentful-cms/cms-guidelines/generate-component-guidelines.md +0 -126
- package/skills/contentful-cms/cms-guidelines/generation-prompt.md +0 -231
- package/skills/contentful-cms/cms-guidelines/html-component-authoring.md +0 -401
- package/skills/contentful-cms/cms-guidelines/validation-prompt.md +0 -170
- package/skills/contentful-cms/cms-guidelines/variant-loop.md +0 -189
- package/skills/contentful-cms/cms-guidelines/variant-proposal-prompt.md +0 -131
- package/skills/contentful-cms/core/SKILL.md +0 -793
- package/skills/contentful-cms/generate-all-guidelines/SKILL.md +0 -313
- package/skills/contentful-cms/generate-cms-guidelines/SKILL.md +0 -313
- package/skills/contentful-cms/image-guide/SKILL.md +0 -240
- package/skills/contentful-cms/manifest.json +0 -11
- package/skills/contentful-cms/navigation/SKILL.md +0 -23
- package/skills/contentful-cms/rich-text/SKILL.md +0 -96
- package/skills/contentful-cms/schema-org/SKILL.md +0 -74
- package/skills/contentful-cms/screenshots/SKILL.md +0 -46
- package/skills/contentful-cms/seo-descriptions/SKILL.md +0 -54
- package/skills/contentful-cms/templates/SKILL.md +0 -21
- package/skills/contentful-cms/update-cms-guidelines/SKILL.md +0 -348
- package/skills/se-marketing-sites/cms-routes-and-appshared/SKILL.md +0 -99
- package/skills/se-marketing-sites/create-collection/SKILL.md +0 -295
- package/skills/se-marketing-sites/create-component/SKILL.md +0 -250
- package/skills/se-marketing-sites/create-page/SKILL.md +0 -129
- package/skills/se-marketing-sites/curate-showcase-mocks/SKILL.md +0 -343
- package/skills/se-marketing-sites/handling-media/SKILL.md +0 -195
- package/skills/se-marketing-sites/lib-cms-structure/SKILL.md +0 -83
- package/skills/se-marketing-sites/register-cms-features/SKILL.md +0 -95
- package/skills/se-marketing-sites/styling-system/SKILL.md +0 -122
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: curate-showcase-mocks
|
|
3
|
-
description: Extracts real component/collection data from Contentful and curates the best examples into showcase-mocks.json, making the CMS showcase display realistic content instead of generic placeholder text.
|
|
4
|
-
license: Private
|
|
5
|
-
metadata:
|
|
6
|
-
author: se-core-product
|
|
7
|
-
version: "2.1.0"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Curate Showcase Mocks
|
|
11
|
-
|
|
12
|
-
This skill refreshes the CMS showcase with realistic content from the live Contentful space.
|
|
13
|
-
It uses a **two-phase CLI pipeline** — extraction followed by LLM-driven curation — to produce
|
|
14
|
-
`src/generated/showcase-mocks.json`, which the showcase loads at runtime.
|
|
15
|
-
|
|
16
|
-
It is **Step 2** of **`update-cms-guidelines`** mode **`fresh`** (after the clean slate). Realistic mocks and `accepted-variants` files are **required** before bulk screenshot capture in **generate-all-guidelines**.
|
|
17
|
-
|
|
18
|
-
## When to use
|
|
19
|
-
|
|
20
|
-
Run this skill when:
|
|
21
|
-
- A new app has been populated in Contentful and the showcase still shows placeholder text
|
|
22
|
-
- The CMS content has been significantly updated and the showcase looks stale
|
|
23
|
-
- New component or collection types have been added and need good mock data
|
|
24
|
-
- You are doing a **full CMS guidelines regeneration** and need fresh showcase data before Phase 1 / 1b screenshots
|
|
25
|
-
|
|
26
|
-
## Prerequisites
|
|
27
|
-
|
|
28
|
-
- The app's `.env.local` must contain `CONTENTFUL_SPACE_ID`, `CONTENTFUL_ACCESS_TOKEN`, and `CONTENTFUL_ENVIRONMENT_NAME`
|
|
29
|
-
- An LLM API key in `.env.local`: `OPENAI_API_KEY` (preferred) or `ANTHROPIC_API_KEY`
|
|
30
|
-
- Optional: `CONTENTFUL_PREVIEW_ACCESS_TOKEN` to also include draft/unpublished entries
|
|
31
|
-
- For monorepo apps: all packages must be built (`pnpm build` from repo root)
|
|
32
|
-
- The app must be fully set up (see **First-time setup** below if not yet done)
|
|
33
|
-
|
|
34
|
-
## First-time setup (new apps)
|
|
35
|
-
|
|
36
|
-
Before running this skill on a new app, four one-time changes are required.
|
|
37
|
-
|
|
38
|
-
### 1 — Add scripts to `package.json`
|
|
39
|
-
|
|
40
|
-
**Monorepo app** (uses local packages via relative paths):
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
"generate-showcase": "node ../../packages/project-build/dist/generate-showcase-data.js",
|
|
44
|
-
"generate-showcase-mocks": "node ../../packages/project-build/dist/generate-showcase-mocks.js"
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**External/standalone project** (uses npm-installed packages):
|
|
48
|
-
|
|
49
|
-
```json
|
|
50
|
-
"generate-showcase": "node node_modules/@se-studio/project-build/dist/generate-showcase-data.js",
|
|
51
|
-
"generate-showcase-mocks": "node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js"
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Alternatively for external projects, use the installed binaries directly (available on PATH after install):
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
generate-showcase-data # runs in-place from the project root
|
|
58
|
-
generate-showcase-mocks
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 2 — Gitignore the generated files
|
|
62
|
-
|
|
63
|
-
In the app's `.gitignore`, add:
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
# Generated showcase files (large, regenerable — curated mocks committed separately)
|
|
67
|
-
src/generated/showcase-examples.json
|
|
68
|
-
src/generated/showcase-mocks-draft.json
|
|
69
|
-
src/generated/cms-discovery/accepted-variants/
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 3 — Wire the render pages to use curated mocks
|
|
73
|
-
|
|
74
|
-
Both `src/app/(cms-dev)/cms/showcase/render/page.tsx` and `render-all/page.tsx` need to import
|
|
75
|
-
`mergeShowcaseMocks` and the curated mocks file.
|
|
76
|
-
|
|
77
|
-
**`render/page.tsx`** — change from:
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage } from '@se-studio/core-ui';
|
|
81
|
-
// ... other imports ...
|
|
82
|
-
|
|
83
|
-
export default async function Page(...) {
|
|
84
|
-
return (
|
|
85
|
-
<ShowcaseRenderPage
|
|
86
|
-
componentMockMap={componentMockMap}
|
|
87
|
-
collectionMockMap={collectionMockMap}
|
|
88
|
-
collectionCardMockMap={collectionCardMockMap}
|
|
89
|
-
...
|
|
90
|
-
/>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
to:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
import { DEFAULT_SHOWCASE_CONTROL_STATE, ShowcaseRenderPage, mergeShowcaseMocks } from '@se-studio/core-ui';
|
|
99
|
-
// ... other imports ...
|
|
100
|
-
import curatedMocks from '@/generated/showcase-mocks.json';
|
|
101
|
-
|
|
102
|
-
const { componentMockMap: mergedComponentMocks, collectionMockMap: mergedCollectionMocks, collectionCardMockMap: mergedCardMocks } =
|
|
103
|
-
mergeShowcaseMocks(curatedMocks, { componentMockMap, collectionMockMap, collectionCardMockMap });
|
|
104
|
-
|
|
105
|
-
export default async function Page(...) {
|
|
106
|
-
return (
|
|
107
|
-
<ShowcaseRenderPage
|
|
108
|
-
componentMockMap={mergedComponentMocks}
|
|
109
|
-
collectionMockMap={mergedCollectionMocks}
|
|
110
|
-
collectionCardMockMap={mergedCardMocks}
|
|
111
|
-
...
|
|
112
|
-
/>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Apply the same pattern to **`render-all/page.tsx`**.
|
|
118
|
-
|
|
119
|
-
### 4 — Create an empty `showcase-mocks.json` stub
|
|
120
|
-
|
|
121
|
-
Create `src/generated/showcase-mocks.json` so the import compiles before curated data exists:
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"curatedAt": "2026-01-01T00:00:00.000Z",
|
|
126
|
-
"components": {},
|
|
127
|
-
"collections": {}
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Once these four steps are done, proceed with the steps below.
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## Pipeline Overview
|
|
136
|
-
|
|
137
|
-
The workflow is now a **three-step pipeline**:
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
Step 1: generate-showcase-data → showcase-examples.json (all CMS data, sorted by completeness)
|
|
141
|
-
Step 2: generate-showcase-mocks → showcase-mocks-draft.json (LLM selects best + proposes variants)
|
|
142
|
-
Step 3: AI review (this skill) → showcase-mocks.json (final curated mocks, committed)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Steps 1 and 2 are automated CLI commands. Step 3 is where you (the AI) review the draft,
|
|
146
|
-
adjust anything the LLM got wrong, handle layout variants, and write the final committed file.
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## Steps
|
|
151
|
-
|
|
152
|
-
### Step 1 — Run the extraction script
|
|
153
|
-
|
|
154
|
-
Run the CLI to fetch all component/collection entries from Contentful:
|
|
155
|
-
|
|
156
|
-
**Monorepo:**
|
|
157
|
-
```bash
|
|
158
|
-
pnpm --filter {appName} generate-showcase
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**External project (from project root):**
|
|
162
|
-
```bash
|
|
163
|
-
node node_modules/@se-studio/project-build/dist/generate-showcase-data.js
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Optional flags:**
|
|
167
|
-
- `--include-drafts` — also fetches draft/unpublished entries via the Preview API (requires `CONTENTFUL_PREVIEW_ACCESS_TOKEN`)
|
|
168
|
-
- `--all-types` — fetches all entry content types without a filter (instead of fetching component and collection separately)
|
|
169
|
-
|
|
170
|
-
This writes `src/generated/showcase-examples.json` — every example grouped by type, sorted by
|
|
171
|
-
`fieldCompleteness` descending. This file is gitignored.
|
|
172
|
-
|
|
173
|
-
### Step 2 — Run the LLM curation script
|
|
174
|
-
|
|
175
|
-
**Monorepo:**
|
|
176
|
-
```bash
|
|
177
|
-
pnpm --filter {appName} generate-showcase-mocks
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**External project:**
|
|
181
|
-
```bash
|
|
182
|
-
node node_modules/@se-studio/project-build/dist/generate-showcase-mocks.js
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
This reads `showcase-examples.json`, calls the LLM (OpenAI or Anthropic — auto-detected from
|
|
186
|
-
`.env.local`), and produces:
|
|
187
|
-
- `src/generated/showcase-mocks-draft.json` — best mock per type + proposed variant param sets
|
|
188
|
-
- `src/generated/cms-discovery/accepted-variants/{components|collections|externals}/<slug>.json` — per-type variant files (in mode subdir)
|
|
189
|
-
|
|
190
|
-
**Optional flags:**
|
|
191
|
-
- `--model <model>` — override the LLM model (e.g. `--model gpt-4o`)
|
|
192
|
-
- `--types <Type1,Type2>` — limit to specific type names (comma-separated, useful for re-running single types)
|
|
193
|
-
|
|
194
|
-
### Step 3 — Read the draft and registrations
|
|
195
|
-
|
|
196
|
-
Read these files:
|
|
197
|
-
|
|
198
|
-
1. `src/generated/showcase-mocks-draft.json` — the LLM's selected mocks + variant proposals
|
|
199
|
-
2. `src/lib/registrations.ts` — the app's component/collection registrations
|
|
200
|
-
|
|
201
|
-
From the registrations file, collect:
|
|
202
|
-
- The full list of registered component type names
|
|
203
|
-
- The full list of registered collection type names
|
|
204
|
-
- Any registrations with `showcaseExclude: true` — these types must be skipped entirely
|
|
205
|
-
|
|
206
|
-
Check the draft against the registrations: are there registered types missing from the draft?
|
|
207
|
-
If so, check `showcase-examples.json` directly to find examples for those types.
|
|
208
|
-
|
|
209
|
-
### Step 4 — Review and refine mocks
|
|
210
|
-
|
|
211
|
-
For each type in the draft:
|
|
212
|
-
|
|
213
|
-
**Validate the LLM selection:**
|
|
214
|
-
- Confirm the selected mock has a real heading (not a test entry)
|
|
215
|
-
- Confirm it has a working visual URL (if the field exists)
|
|
216
|
-
- Confirm the mock fields are complete and reflect real brand content
|
|
217
|
-
|
|
218
|
-
**Add layout variants not in the CMS:**
|
|
219
|
-
Some types have meaningful variants that may not exist as separate CMS entries — a flipped
|
|
220
|
-
layout, a narrow/wide version, or a no-visual state. These can be represented by adjusting
|
|
221
|
-
the base mock with URL param overrides. Review each type's variant proposals from the LLM
|
|
222
|
-
(in `showcase-mocks-draft.json`) and add any important ones that are missing:
|
|
223
|
-
|
|
224
|
-
- For a Hero with a left-layout variant: add a variant with `{ "showcaseParams": { "flip": "true" } }` or similar
|
|
225
|
-
- For a content block with a narrow option: `{ "showcaseParams": { "width": "narrow" } }`
|
|
226
|
-
- Adjust the LLM's proposed params to match the actual param names used in the component's showcase controls
|
|
227
|
-
|
|
228
|
-
**For collections — curating cards:**
|
|
229
|
-
- Select ALL cards from the best example (do not cap — use the real count)
|
|
230
|
-
- If fewer than 4 cards, check the next-best example for additional cards
|
|
231
|
-
- Cards with no heading and no body are useless — skip them
|
|
232
|
-
|
|
233
|
-
### Step 5 — Write showcase-mocks.json
|
|
234
|
-
|
|
235
|
-
Write the curated data to `src/generated/showcase-mocks.json`. This file IS committed to git.
|
|
236
|
-
|
|
237
|
-
Use the LLM draft as the starting point and apply your corrections from Step 4.
|
|
238
|
-
|
|
239
|
-
**Format:**
|
|
240
|
-
|
|
241
|
-
```json
|
|
242
|
-
{
|
|
243
|
-
"curatedAt": "2026-02-28T14:00:00.000Z",
|
|
244
|
-
"components": {
|
|
245
|
-
"Hero": {
|
|
246
|
-
"heading": "Mental health care for kids and teens. Parenting support for you.",
|
|
247
|
-
"preHeading": "Brightline",
|
|
248
|
-
"body": { "json": { "nodeType": "document", "content": [...] } },
|
|
249
|
-
"visual": { "width": 1200, "height": 800, "url": "https://images.ctfassets.net/..." },
|
|
250
|
-
"backgroundColour": "Off White"
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
"collections": {
|
|
254
|
-
"FAQ": {
|
|
255
|
-
"mock": { "heading": "Top FAQs" },
|
|
256
|
-
"cards": [
|
|
257
|
-
{ "heading": "Who is Brightline?", "body": { "json": { ... } } }
|
|
258
|
-
]
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Rules for the output:**
|
|
265
|
-
- Set `curatedAt` to the current ISO timestamp
|
|
266
|
-
- Only include types that have real CMS data
|
|
267
|
-
- For component mocks: include only fields present in the source data
|
|
268
|
-
- For collection mocks: always include `mock` and `cards`
|
|
269
|
-
- Rich Text body fields must be `{ "json": <Document> }` — copy as-is
|
|
270
|
-
- Visual fields must include `width`, `height`, AND `url` — never strip `url`
|
|
271
|
-
- Include `widthPercent` when present on media/externalVideo entries
|
|
272
|
-
- Include `otherMedia` when present — copy the full array including `url` and `widthPercent`
|
|
273
|
-
- Do NOT invent or modify content — use the real data from the examples file
|
|
274
|
-
- Include `backgroundColour` and `textColour` where the source has them
|
|
275
|
-
- For cards: include ALL fields present in source, including `backgroundColour`, `textColour`, `links`
|
|
276
|
-
|
|
277
|
-
### Step 6 — Verify
|
|
278
|
-
|
|
279
|
-
After writing, briefly confirm:
|
|
280
|
-
- The file is valid JSON
|
|
281
|
-
- Component and collection counts look reasonable (non-zero)
|
|
282
|
-
- Key types (Hero, main collections) have entries
|
|
283
|
-
- Visual fields include `url` properties
|
|
284
|
-
|
|
285
|
-
---
|
|
286
|
-
|
|
287
|
-
## Output files
|
|
288
|
-
|
|
289
|
-
| File | Location | Committed |
|
|
290
|
-
|------|----------|-----------|
|
|
291
|
-
| `showcase-examples.json` | `src/generated/showcase-examples.json` | No (gitignored) |
|
|
292
|
-
| `showcase-mocks-draft.json` | `src/generated/showcase-mocks-draft.json` | No (gitignored) |
|
|
293
|
-
| `accepted-variants/` | `src/generated/cms-discovery/accepted-variants/{components\|collections\|externals}/` | No (gitignored) |
|
|
294
|
-
| `showcase-mocks.json` | `src/generated/showcase-mocks.json` | Yes |
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## How the showcase uses this
|
|
299
|
-
|
|
300
|
-
The showcase uses a **two-layer model**:
|
|
301
|
-
|
|
302
|
-
1. **Base: mock data** — `showcase-mocks.json` is merged over inline registration mocks via
|
|
303
|
-
`mergeShowcaseMocks()` from `@se-studio/core-ui`. Curated data takes precedence; types without
|
|
304
|
-
curated data fall back to their inline `mock:` in the registration.
|
|
305
|
-
|
|
306
|
-
2. **Patch: URL params** — Any control field in the URL (e.g. `?backgroundColour=Navy`) overrides
|
|
307
|
-
the mock. The control bar encodes only values that differ from the mock baseline.
|
|
308
|
-
|
|
309
|
-
`?clean=true` skips mock data entirely and uses the default showcase control state only.
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## External project setup (seskills)
|
|
314
|
-
|
|
315
|
-
### Installing skills
|
|
316
|
-
|
|
317
|
-
For standalone projects not in the monorepo, install or update skills via:
|
|
318
|
-
|
|
319
|
-
```bash
|
|
320
|
-
seskills sync
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
This copies all SE Studio cursor skills (including this one) into the project's `.agents/skills/`
|
|
324
|
-
directory. The `seskills` binary is provided by `@se-studio/project-build`.
|
|
325
|
-
|
|
326
|
-
### Auto-sync on package update
|
|
327
|
-
|
|
328
|
-
Add a `postinstall` script to the project's `package.json` so skills automatically sync
|
|
329
|
-
whenever `pnpm install` is run (including after bumping `@se-studio/project-build`):
|
|
330
|
-
|
|
331
|
-
```json
|
|
332
|
-
"postinstall": "seskills sync --if-installed"
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
The `--if-installed` flag makes the sync a no-op if run inside the monorepo itself
|
|
336
|
-
(where `project-build` is a workspace package, not an installed npm package), so it
|
|
337
|
-
is safe to add to any project.
|
|
338
|
-
|
|
339
|
-
### Script paths
|
|
340
|
-
|
|
341
|
-
The CLI scripts are available as installed binaries after `npm install @se-studio/project-build`.
|
|
342
|
-
Both `generate-showcase-data` and `generate-showcase-mocks` read `.env.local` from `process.cwd()`,
|
|
343
|
-
so run them from the project root.
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: handling-media
|
|
3
|
-
description: Guide for using images, videos, and animations in marketing sites using the ResponsiveVisual and VisualComponent systems.
|
|
4
|
-
license: Private
|
|
5
|
-
metadata:
|
|
6
|
-
author: se-core-product
|
|
7
|
-
version: "1.0.0"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Handling Media
|
|
11
|
-
|
|
12
|
-
This project uses a unified media system to handle images, videos, and animations efficiently.
|
|
13
|
-
|
|
14
|
-
## Core Components
|
|
15
|
-
|
|
16
|
-
### 1. `ResponsiveVisual` (or `VisualComponent`)
|
|
17
|
-
|
|
18
|
-
This is the main component for rendering media from Contentful. It handles:
|
|
19
|
-
* Responsive image sources (desktop/mobile).
|
|
20
|
-
* Format optimization (WebP/AVIF).
|
|
21
|
-
* Video auto-play/looping.
|
|
22
|
-
* Lottie animations.
|
|
23
|
-
* Lazy loading & Priority hints.
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import VisualComponent from '@/framework/VisualComponent';
|
|
27
|
-
// or
|
|
28
|
-
import { Visual } from '@se-studio/core-ui';
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Usage Pattern
|
|
32
|
-
|
|
33
|
-
### Basic Usage
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
<VisualComponent
|
|
37
|
-
visual={field.visual}
|
|
38
|
-
className="w-full h-auto"
|
|
39
|
-
/>
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Responsive Sizing (`visualSizes`)
|
|
43
|
-
|
|
44
|
-
You **MUST** provide `visualSizes` to ensure the browser loads the correct image size. This uses the `sizes` attribute.
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { calculateVisualSizes } from '@se-studio/core-ui';
|
|
48
|
-
|
|
49
|
-
// Example: Full width on mobile, 50% width on laptop
|
|
50
|
-
const sizes = calculateVisualSizes(1, { laptop: 0.5 });
|
|
51
|
-
|
|
52
|
-
<VisualComponent
|
|
53
|
-
visual={visual}
|
|
54
|
-
visualSizes={sizes}
|
|
55
|
-
/>
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
* `1`: 100vw (Mobile default)
|
|
59
|
-
* `laptop: 0.5`: 50vw (Laptop breakpoint)
|
|
60
|
-
|
|
61
|
-
### visualSizes Must Mirror Col-Span
|
|
62
|
-
|
|
63
|
-
`visualSizes` **must** match the visual's layout (col-span, container width). The browser uses these values to pick the right image size.
|
|
64
|
-
|
|
65
|
-
* **Rule**: `col-span-N` in a 12-col grid → use `N/12` (e.g. 6 cols → 0.5, 5 cols → 5/12).
|
|
66
|
-
* **Full width** (`col-span-full`) → use `1`.
|
|
67
|
-
* **First arg** = mobile/default; breakpoint keys (`laptop`, `tablet`, `desktop`) = that breakpoint.
|
|
68
|
-
* **Common mistake**: Reversing mobile vs laptop. If mobile is full width and laptop is smaller, use `(1, { laptop: 0.5 })`, **not** `(0.5, { laptop: 1 })`.
|
|
69
|
-
* **Fixed-size icons** (~96–128px): use small ratios like `0.2`–`0.25`; **never** use values > 1 (e.g. `(100)` is invalid and causes massive over-fetch).
|
|
70
|
-
* **Example table**:
|
|
71
|
-
|
|
72
|
-
| Layout | visualSizes |
|
|
73
|
-
|--------|-------------|
|
|
74
|
-
| Full width | `(1)` |
|
|
75
|
-
| 6 cols on laptop, full on mobile | `(1, { laptop: 0.5 })` |
|
|
76
|
-
| 5 cols on laptop, full on mobile | `(1, { laptop: 5/12 })` |
|
|
77
|
-
| 2 cols in 12 on laptop, half on mobile | `(0.5, { laptop: 2/12 })` |
|
|
78
|
-
| Small icon (~96px) | `(0.25)` |
|
|
79
|
-
|
|
80
|
-
### CMS Width & Position (`widthPercent`, `horizontalPosition`)
|
|
81
|
-
|
|
82
|
-
Visuals in Contentful can have a `widthPercent` (e.g. 50%) and `horizontalPosition` (Left / Middle / Right). These control how wide the visual renders on laptop+ and where it aligns within its container. **How these fields are applied depends on the component rendering the visual.**
|
|
83
|
-
|
|
84
|
-
**Key principle**: The `Visual` component is a "fill my container" renderer — it does **not** apply width constraints itself. Width and position are always the responsibility of the wrapping component.
|
|
85
|
-
|
|
86
|
-
#### Which components handle it automatically
|
|
87
|
-
|
|
88
|
-
* **`VisualComponent`** (framework, `@/framework/VisualComponent`): Handles `widthPercent` and `horizontalPosition` in both embedded and non-embedded modes. Also adjusts `visualSizes` so the browser fetches an appropriately sized image.
|
|
89
|
-
* **`ResponsiveVisual`** (core-ui): Wraps the `Visual` in a div that applies `--image-width` and horizontal positioning. Components using `ResponsiveVisual` get this for free.
|
|
90
|
-
|
|
91
|
-
#### Custom components using `Visual` directly
|
|
92
|
-
|
|
93
|
-
If your component renders `Visual` directly and the visual may have a `widthPercent`, you must handle it yourself:
|
|
94
|
-
|
|
95
|
-
1. **Wrap in a positioning div** using helpers from `core-ui`:
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
import { getVisualWidthPercent } from '@se-studio/core-data-types';
|
|
99
|
-
import {
|
|
100
|
-
calculateHorizontalPositionClassName,
|
|
101
|
-
calculateImageWidthStyleVariable,
|
|
102
|
-
calculateVisualSizes,
|
|
103
|
-
Visual,
|
|
104
|
-
cn,
|
|
105
|
-
} from '@se-studio/core-ui';
|
|
106
|
-
|
|
107
|
-
const imageWidthStyle = calculateImageWidthStyleVariable(visual);
|
|
108
|
-
const imageHorizontalPosition = imageWidthStyle
|
|
109
|
-
? calculateHorizontalPositionClassName(visual)
|
|
110
|
-
: undefined;
|
|
111
|
-
|
|
112
|
-
<div
|
|
113
|
-
className={cn(
|
|
114
|
-
'w-full',
|
|
115
|
-
imageHorizontalPosition,
|
|
116
|
-
imageWidthStyle && 'laptop:w-(--image-width)',
|
|
117
|
-
)}
|
|
118
|
-
style={imageWidthStyle}
|
|
119
|
-
>
|
|
120
|
-
<Visual visual={visual} visualSizes={visualSizes} />
|
|
121
|
-
</div>
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
2. **Factor width into `visualSizes`** so the browser doesn't over-fetch:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
const widthPercent = getVisualWidthPercent(visual);
|
|
128
|
-
const laptopRatio = widthPercent ? widthPercent / 100 : undefined;
|
|
129
|
-
const visualSizes = laptopRatio
|
|
130
|
-
? calculateVisualSizes(1, { laptop: laptopRatio })
|
|
131
|
-
: calculateVisualSizes(1);
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
#### Collections with multiple visuals
|
|
135
|
-
|
|
136
|
-
Collections like `SeparatedVisualsCollection` may interpret `widthPercent` differently — as relative proportions between visuals rather than absolute percentages. The collection controls the layout and passes the computed slot width to `visualSizes`. Don't apply the standard wrapper pattern blindly in collections; match `visualSizes` to the actual rendered slot width.
|
|
137
|
-
|
|
138
|
-
### LCP Optimization (`calculateImagePriority`)
|
|
139
|
-
|
|
140
|
-
For the Hero component (or whatever is at the top of the page), you must prioritize the image load to improve LCP (Largest Contentful Paint).
|
|
141
|
-
|
|
142
|
-
Use `calculateImagePriority(index)` where `index` is the component's position on the page.
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { calculateImagePriority } from '@se-studio/core-ui';
|
|
146
|
-
|
|
147
|
-
<VisualComponent
|
|
148
|
-
visual={visual}
|
|
149
|
-
{...calculateImagePriority(index)}
|
|
150
|
-
/>
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
If `index` is `0`, this sets `priority={true}` (or `fetchPriority="high"`). If `index > 0`, it defaults to lazy loading.
|
|
154
|
-
|
|
155
|
-
### Analytics Tracking
|
|
156
|
-
|
|
157
|
-
Pass `componentLabel` (usually `cmsLabel`) and `analyticsContext` to enable click tracking on media elements (if they are linked).
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
<VisualComponent
|
|
161
|
-
visual={visual}
|
|
162
|
-
componentLabel={cmsLabel}
|
|
163
|
-
analyticsContext={contentContext.analyticsContext}
|
|
164
|
-
/>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Media Types
|
|
168
|
-
|
|
169
|
-
The `IResponsiveVisual` type (from `@se-studio/core-data-types`) supports:
|
|
170
|
-
|
|
171
|
-
1. **Image**: Standard Contentful image asset.
|
|
172
|
-
2. **Video**: Hosted video file (mp4/webm). The component handles `<video>` tag rendering with autoplay/loop/muted attributes suitable for background videos.
|
|
173
|
-
3. **Animation**: Lottie JSON file. Renders using a Lottie player.
|
|
174
|
-
|
|
175
|
-
The component automatically detects the type and renders the appropriate element.
|
|
176
|
-
|
|
177
|
-
## Mobile Specific Visuals
|
|
178
|
-
|
|
179
|
-
Contentful models often support a separate `mobileVisual` field. The `VisualComponent` handles switching between them using `<picture>` tags or CSS media queries internally.
|
|
180
|
-
|
|
181
|
-
You typically pass the combined object or handle it via props if the component exposes both:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
// If your component merges them into one responsive object before rendering:
|
|
185
|
-
<VisualComponent visual={combinedVisual} />
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Or if using raw fields:
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
<VisualComponent
|
|
192
|
-
visual={visual}
|
|
193
|
-
mobileVisual={mobileVisual}
|
|
194
|
-
/>
|
|
195
|
-
```
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: lib-cms-structure
|
|
3
|
-
description: Guide for the lib directory structure in SE Core Product CMS apps. Use when setting up lib/, migrating from contentful-config, or adding new CMS configuration.
|
|
4
|
-
license: Private
|
|
5
|
-
metadata:
|
|
6
|
-
author: se-core-product
|
|
7
|
-
version: "1.0.0"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Lib Directory Structure
|
|
11
|
-
|
|
12
|
-
Reference apps: **example-se2026**, **example-brightline**, **example-om1**.
|
|
13
|
-
|
|
14
|
-
## File Responsibilities
|
|
15
|
-
|
|
16
|
-
| File | Client-safe? | Purpose |
|
|
17
|
-
|------|--------------|---------|
|
|
18
|
-
| `cms.ts` | Yes | Types, buildComponentRecord / buildCollectionRecord / buildExternalRecord (array → Record), then build*Maps from those Records, createConverterContext |
|
|
19
|
-
| `cms-server.ts` | No (server-only) | createAppHelpers, buildOptions, getContentfulConfig, projectRendererConfig |
|
|
20
|
-
| `config.ts` | Yes | isProduction, isDevelopment |
|
|
21
|
-
| `server-config.ts` | No | draftOnly, videoPrefix, baseUrl, revalidationSecret |
|
|
22
|
-
| `constants.ts` | Yes | ARTICLES_BASE, TAGS_BASE, PEOPLE_BASE, enable flags, customer name |
|
|
23
|
-
| `registrations.ts` | Yes | componentRegistrationsList, collectionRegistrationsList, externalComponentRegistrationsList (arrays) |
|
|
24
|
-
| `SizingInformation.ts` | Yes | getSizingInformation for dynamic heading sizes |
|
|
25
|
-
|
|
26
|
-
## Server vs Client Boundary
|
|
27
|
-
|
|
28
|
-
- **cms-server.ts** has `import 'server-only'`. Never import it in `'use client'` components.
|
|
29
|
-
- **cms.ts** is client-safe: types, maps, converter context factory (no env access).
|
|
30
|
-
- **registrations.ts** imports from cms.ts and project components; keep it client-safe.
|
|
31
|
-
|
|
32
|
-
## constants.ts Shape
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
export const ARTICLES_SLUG = 'articles'; // or 'learning-hub'
|
|
36
|
-
export const TAGS_SLUG = 'tags';
|
|
37
|
-
export const PEOPLE_SLUG = 'people';
|
|
38
|
-
export const DEFAULT_TOPIC = 'other';
|
|
39
|
-
|
|
40
|
-
export const ARTICLES_BASE = `/${ARTICLES_SLUG}`;
|
|
41
|
-
export const TAGS_BASE = `/${TAGS_SLUG}`;
|
|
42
|
-
export const PEOPLE_BASE = `/${PEOPLE_SLUG}`;
|
|
43
|
-
|
|
44
|
-
export const customerName = '...';
|
|
45
|
-
export const applicationName = '...';
|
|
46
|
-
export const siteTitle = '...';
|
|
47
|
-
export const siteDescription = '...';
|
|
48
|
-
export const enablePerson = false;
|
|
49
|
-
export const enablePeopleIndex = false;
|
|
50
|
-
export const enableTag = false;
|
|
51
|
-
export const enableTagsIndex = false;
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Data Flow
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
registrations.ts (*RegistrationsList arrays)
|
|
58
|
-
→ cms.ts (build*Record from arrays, then build*Maps)
|
|
59
|
-
→ builds maps → cms-server.ts
|
|
60
|
-
↓
|
|
61
|
-
projectRendererConfig
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Add new components/collections to the appropriate *RegistrationsList array in `registrations.ts`. The `cms.ts` imports those arrays, builds Records via `buildComponentRecord` / `buildCollectionRecord` / `buildExternalRecord` (which enforce that every CMS type has a registration), then builds the maps used by `cms-server.ts`.
|
|
65
|
-
|
|
66
|
-
## Circular Dependency: Do Not Import cms-server in Components/Collections
|
|
67
|
-
|
|
68
|
-
Components and collections in the registration chain (imported by `registrations.ts`) must **not** import from `cms-server` directly. Doing so creates a circular dependency (cms-server → cms → registrations → components → cms-server) that causes TDZ errors during RSC serialization.
|
|
69
|
-
|
|
70
|
-
**Instead**, use the helpers from `@se-studio/core-ui`:
|
|
71
|
-
|
|
72
|
-
- `getPreviewFieldProps(rendererConfig, id, fieldId)` – for field preview props
|
|
73
|
-
- `getPreviewResponsiveVisualFieldProps(rendererConfig, id, visualFieldId, mobileVisualFieldId)` – for responsive visuals
|
|
74
|
-
- `rendererConfig.previewHelpers?.getPreviewParentProps?.(information)`
|
|
75
|
-
- `rendererConfig.fetchHelpers?.getAllArticleLinks?.()`
|
|
76
|
-
- `rendererConfig.fetchHelpers?.getRelatedArticles?.(information, contentContext, count)`
|
|
77
|
-
|
|
78
|
-
These are passed via `projectRendererConfig` in `cms-server.ts`. `Section` and `SectionLinks` accept `previewHelpers` / `rendererConfig` props. See `docs/SERVER_CLIENT_BOUNDARIES.md`.
|
|
79
|
-
|
|
80
|
-
## See Also
|
|
81
|
-
|
|
82
|
-
- **register-cms-features** – Adding components and collections
|
|
83
|
-
- **cms-routes-and-appshared** – Route structure and appShared
|