Skip to content

Commit 72ddf81

Browse files
docs(puzzle-page): document optional PuzzleConfig.image field
Updates docs/puzzle-page.md to match the previous two commits (optional image field on PuzzleConfig, wired into og:image/twitter:image). - "How to configure/add a new puzzle" step 1: mentions the new optional `image` field, that `amuseLabsPuzzle()` doesn't take it (set it afterwards on the returned object if needed), and that validatePuzzleConfigs rejects a present-but-empty image. - "SEO: meta description, Open Graph, Twitter card": documents buildPuzzlePageMetaData (the new pure function render.puzzlePage.web.tsx uses) including image, and explicitly states - as a deliberate, confirmed decision rather than an oversight - that DCR has no site-wide default/fallback share image anywhere (confirmed against frontend's MetaData.opengraphProperties/SimplePage, which likewise only add og:image via explicit per-page overrides, never a default), so an unset `image` simply omits og:image/twitter:image entirely, matching existing sitewide behaviour. Also notes none of the 6 current V0 puzzles have a real image configured yet - a placeholder capability for whenever real, licensed images are provided, not filled in as part of this change. No functional/code changes in this commit - documentation only. tsc --noEmit clean, full-repo eslint clean, full test suite passing (176 suites / 1280 tests). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 4797dd7 commit 72ddf81

1 file changed

Lines changed: 32 additions & 11 deletions

File tree

dotcom-rendering/docs/puzzle-page.md

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,15 @@ iframe-based slug:
118118

119119
1. Add a new key to `src/model/puzzles/puzzleConfigs.ts`'s `puzzleConfigs`
120120
record (`slug`, `puzzleGroup`, `iframe: { provider, urlTemplate }`,
121-
`shareEnabled`, `printEnabled`, `hasArchive`, `description`). If it's
122-
another AmuseLabs-hosted puzzle, reuse the
123-
`amuseLabsPuzzle(slug, puzzleGroup, description)` helper.
121+
`shareEnabled`, `printEnabled`, `hasArchive`, `description`, optional
122+
`image`). If it's another AmuseLabs-hosted puzzle, reuse the
123+
`amuseLabsPuzzle(slug, puzzleGroup, description)` helper (note: this
124+
helper doesn't take `image` — set it afterwards on the returned object
125+
if/when a real image is available for that puzzle).
124126
`validatePuzzleConfigs` runs once at module load and throws immediately
125127
if the entry is malformed (mismatched `slug`, unknown `puzzleGroup`,
126-
empty `iframe.provider`/`iframe.urlTemplate`, or empty `description`).
128+
empty `iframe.provider`/`iframe.urlTemplate`, empty `description`, or a
129+
present-but-empty `image`).
127130
**Write real, distinct, human-quality copy for `description`** — it
128131
becomes the page's `<meta name="description">` and its derived Open
129132
Graph/Twitter description (see "SEO" below); don't copy-paste one
@@ -138,22 +141,40 @@ iframe-based slug:
138141

139142
Each `PuzzleConfig` entry carries a curated `description` (a short,
140143
genuinely-written meta description, distinct per puzzle — see step 1
141-
above). `render.puzzlePage.web.tsx` uses
142-
`puzzlePage.puzzleConfig.description` (the config already resolved by
143-
`handler.puzzlePage.web.ts`, not a fresh lookup) for three things:
144+
above) and an optional `image` (a full preview/share image URL).
145+
`render.puzzlePage.web.tsx` derives the page's SEO metadata from these via
146+
a small, pure, directly-unit-tested function,
147+
`buildPuzzlePageMetaData(webTitle, puzzleConfig)`
148+
(`src/server/render.puzzlePage.web.test.ts`):
144149

145150
- The page's `<meta name="description">` (previously hardcoded to `''`,
146151
which silently fell back to DCR's generic, site-wide description — a
147152
real SEO gap, since a generic/absent description risks Google or social
148153
previews auto-generating a snippet from page content instead of showing
149154
clean, curated copy).
150-
- `openGraphData: { 'og:title': webTitle, 'og:description': description }`.
151-
- `twitterData: { 'twitter:title': webTitle, 'twitter:description': description }`.
155+
- `openGraphData: { 'og:title': webTitle, 'og:description': description }`,
156+
plus `'og:image': image` **only when `puzzleConfig.image` is set**.
157+
- `twitterData: { 'twitter:title': webTitle, 'twitter:description': description }`,
158+
plus `'twitter:image': image` **only when `puzzleConfig.image` is set**.
159+
160+
**When `image` is unset, `og:image`/`twitter:image` are omitted entirely**
161+
(not sent empty, not defaulted to a placeholder) — `htmlPageTemplate`'s
162+
`generateMetaTags()` only emits a `<meta>` tag for keys actually present in
163+
the object it's given, so an absent key simply produces no tag. This is a
164+
deliberate, confirmed decision, not an oversight: **DCR has no site-wide
165+
default/fallback share image anywhere** for pages without one (checked
166+
`frontend`'s `MetaData.opengraphProperties`/`SimplePage` — no image is set
167+
by default there either, only via explicit per-page overrides), so an
168+
unset `image` here matches existing sitewide behaviour rather than needing
169+
a new default asset. **None of the 6 current V0 puzzles have a real image
170+
configured** — this is a placeholder capability for whenever real,
171+
licensed preview images are provided by the team, not filled in as part of
172+
adding the field.
152173

153174
Puzzle Page has no separate source of Open Graph/Twitter copy (unlike
154175
Article, where `frontend` sends its own `openGraphData`/`twitterData`), so
155-
these are derived directly from `webTitle`/`description` rather than
156-
requiring bespoke copy per field.
176+
these are derived directly from `webTitle`/`description`/`image` rather
177+
than requiring bespoke copy per field.
157178

158179
### The `FEPuzzlePageType` request contract
159180

0 commit comments

Comments
 (0)