caracms 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +57 -2
- package/bin/caracms.mjs +174 -0
- package/bin/gate.mjs +275 -0
- package/bin/init.mjs +712 -0
- package/bin/media.mjs +662 -0
- package/bin/site-runner.mjs +61 -0
- package/bin/templates/Page.astro.txt +31 -0
- package/bin/templates/TextBlock.astro.txt +13 -0
- package/bin/templates/caracms.config.ts.txt +22 -0
- package/bin/templates/catch-all.astro.txt +25 -0
- package/bin/templates/health.ts.txt +6 -0
- package/bin/templates/railway.ts.txt +147 -0
- package/package.json +66 -4
- package/skills/caracms-adopt/SKILL.md +508 -0
- package/skills/caracms-adopt/look-same.mjs +957 -0
- package/skills/caracms-blocks/SKILL.md +376 -0
- package/skills/caracms-content/SKILL.md +201 -0
- package/skills/caracms-tina/SKILL.md +427 -0
- package/skills/caracms-tina/field-kinds.md +88 -0
- package/skills/caracms-tina/tina-to-caracms.mjs +1011 -0
- package/src/block-runs.ts +84 -0
- package/src/cards.ts +158 -0
- package/src/cli/schemas.ts +122 -0
- package/src/cli/site.ts +66 -0
- package/src/cli/validate.ts +471 -0
- package/src/code-routes-disk.ts +67 -0
- package/src/code-routes.ts +134 -0
- package/src/columns.ts +39 -0
- package/src/components/Block.astro +46 -0
- package/src/components/BlockRunTree.astro +45 -0
- package/src/components/Blocks.astro +139 -0
- package/src/components/Buttons.astro +116 -0
- package/src/components/Fonts.astro +14 -0
- package/src/components/Form.astro +221 -0
- package/src/components/Head.astro +62 -0
- package/src/components/Link.astro +68 -0
- package/src/components/Lockup.astro +116 -0
- package/src/components/Media.astro +124 -0
- package/src/components/MediaPlayer.astro +102 -0
- package/src/components/RenderDocument.astro +64 -0
- package/src/components/RichText.astro +59 -0
- package/src/components/Text.astro +81 -0
- package/src/components/attributes.ts +33 -0
- package/src/components/descriptor.ts +45 -0
- package/src/components/form-client.ts +182 -0
- package/src/components/media-player.ts +46 -0
- package/src/components/media-props.ts +35 -0
- package/src/components/sitemap.xml.ts +36 -0
- package/src/config.ts +825 -0
- package/src/content-schema.ts +166 -0
- package/src/content.ts +422 -0
- package/src/document.ts +579 -0
- package/src/editor/README.md +405 -0
- package/src/editor/addresses.ts +61 -0
- package/src/editor/brand.ts +118 -0
- package/src/editor/client/block-list/BlockList.tsx +342 -0
- package/src/editor/client/block-list/Picker.tsx +105 -0
- package/src/editor/client/block-list/UnknownBlock.tsx +119 -0
- package/src/editor/client/block-list/insert-points.ts +56 -0
- package/src/editor/client/block-list/link.ts +71 -0
- package/src/editor/client/block-list/reorder.ts +105 -0
- package/src/editor/client/block-list/summary.ts +118 -0
- package/src/editor/client/collections/CollectionGrid.tsx +143 -0
- package/src/editor/client/collections/CollectionTable.tsx +406 -0
- package/src/editor/client/collections/table.ts +270 -0
- package/src/editor/client/context.tsx +27 -0
- package/src/editor/client/documents/DocMenu.tsx +154 -0
- package/src/editor/client/documents/actions.ts +192 -0
- package/src/editor/client/documents/dialogs.tsx +687 -0
- package/src/editor/client/editor.ts +995 -0
- package/src/editor/client/fields/ButtonsField.tsx +87 -0
- package/src/editor/client/fields/DateField.tsx +200 -0
- package/src/editor/client/fields/FieldControl.tsx +61 -0
- package/src/editor/client/fields/FieldFrame.tsx +109 -0
- package/src/editor/client/fields/GroupField.tsx +113 -0
- package/src/editor/client/fields/HiddenToggle.tsx +51 -0
- package/src/editor/client/fields/LinkField.tsx +307 -0
- package/src/editor/client/fields/ListField.tsx +642 -0
- package/src/editor/client/fields/LockupField.tsx +365 -0
- package/src/editor/client/fields/MediaField.tsx +823 -0
- package/src/editor/client/fields/PageFields.tsx +82 -0
- package/src/editor/client/fields/ReferenceField.tsx +209 -0
- package/src/editor/client/fields/RichTextField.tsx +306 -0
- package/src/editor/client/fields/SelectField.tsx +63 -0
- package/src/editor/client/fields/SeoGroup.tsx +282 -0
- package/src/editor/client/fields/SimpleFields.tsx +89 -0
- package/src/editor/client/fields/TextField.tsx +51 -0
- package/src/editor/client/fields/date.ts +73 -0
- package/src/editor/client/fields/host.tsx +172 -0
- package/src/editor/client/fields/list-summary.ts +85 -0
- package/src/editor/client/fields/lockup-rules.ts +181 -0
- package/src/editor/client/fields/parts.tsx +458 -0
- package/src/editor/client/fields/rich-text-commands.ts +183 -0
- package/src/editor/client/fields/rich-text-doc.ts +156 -0
- package/src/editor/client/fields/rich-text-schema.ts +224 -0
- package/src/editor/client/fields/stale.ts +58 -0
- package/src/editor/client/inbox/InboxStage.tsx +272 -0
- package/src/editor/client/inbox/store.ts +311 -0
- package/src/editor/client/live/format.ts +77 -0
- package/src/editor/client/live/marks.tsx +90 -0
- package/src/editor/client/live/notices.tsx +213 -0
- package/src/editor/client/live/publish.tsx +617 -0
- package/src/editor/client/live/session.tsx +162 -0
- package/src/editor/client/main.tsx +12 -0
- package/src/editor/client/media/LibraryStage.tsx +75 -0
- package/src/editor/client/media/ReplaceDialog.tsx +355 -0
- package/src/editor/client/media/store.ts +618 -0
- package/src/editor/client/nav/DocRow.tsx +122 -0
- package/src/editor/client/nav/search.ts +71 -0
- package/src/editor/client/nav/tree.ts +93 -0
- package/src/editor/client/overlays/OverlayMount.tsx +26 -0
- package/src/editor/client/overlays/editor-marks.ts +159 -0
- package/src/editor/client/overlays/index.ts +50 -0
- package/src/editor/client/overlays/layer.ts +437 -0
- package/src/editor/client/overlays/overlay-css.ts +177 -0
- package/src/editor/client/overlays/rich-text-bar.ts +717 -0
- package/src/editor/client/pane/Pane.tsx +91 -0
- package/src/editor/client/pane/levels/BlockLevel.tsx +123 -0
- package/src/editor/client/pane/levels/CollectionLevel.tsx +115 -0
- package/src/editor/client/pane/levels/DocumentLevel.tsx +93 -0
- package/src/editor/client/pane/levels/DocumentsLevel.tsx +390 -0
- package/src/editor/client/pane/levels/FileLevel.tsx +132 -0
- package/src/editor/client/pane/levels/FormLevel.tsx +550 -0
- package/src/editor/client/pane/levels/MediaLevel.tsx +657 -0
- package/src/editor/client/pane/levels/SettingsM6.tsx +299 -0
- package/src/editor/client/pane/levels/SubmissionsLevel.tsx +185 -0
- package/src/editor/client/pane/parts.tsx +179 -0
- package/src/editor/client/pane/switcher.tsx +165 -0
- package/src/editor/client/preview/driver.ts +640 -0
- package/src/editor/client/preview/field-text.ts +71 -0
- package/src/editor/client/preview/follow-links.ts +234 -0
- package/src/editor/client/preview/layout.ts +37 -0
- package/src/editor/client/preview/link-target.ts +50 -0
- package/src/editor/client/preview/markers.ts +86 -0
- package/src/editor/client/preview/page-edit.ts +632 -0
- package/src/editor/client/preview/page-editor-registry.ts +63 -0
- package/src/editor/client/preview/page-editor.ts +235 -0
- package/src/editor/client/preview/render-sequencer.ts +113 -0
- package/src/editor/client/preview/selection.ts +116 -0
- package/src/editor/client/preview/warnings.ts +61 -0
- package/src/editor/client/shell/App.tsx +101 -0
- package/src/editor/client/shell/Bar.tsx +197 -0
- package/src/editor/client/shell/Brand.tsx +33 -0
- package/src/editor/client/shell/Icons.tsx +87 -0
- package/src/editor/client/shell/NarrowNote.tsx +47 -0
- package/src/editor/client/shell/Popovers.tsx +47 -0
- package/src/editor/client/shell/Stage.tsx +132 -0
- package/src/editor/client/shell/Toast.tsx +61 -0
- package/src/editor/client/state/document-store.ts +465 -0
- package/src/editor/client/state/keeper.ts +713 -0
- package/src/editor/client/state/live.ts +257 -0
- package/src/editor/client/state/schema.ts +42 -0
- package/src/editor/client/state/store.ts +39 -0
- package/src/editor/fonts/OFL-GeistMono.txt +93 -0
- package/src/editor/fonts/OFL-InstrumentSans.txt +93 -0
- package/src/editor/fonts/geist-mono-latin-ext-wght-normal.woff2 +0 -0
- package/src/editor/fonts/geist-mono-latin-wght-normal.woff2 +0 -0
- package/src/editor/fonts/instrument-sans-latin-ext-wght-normal.woff2 +0 -0
- package/src/editor/fonts/instrument-sans-latin-wght-normal.woff2 +0 -0
- package/src/editor/middleware.ts +62 -0
- package/src/editor/routes/_live.ts +26 -0
- package/src/editor/routes/admin.astro +80 -0
- package/src/editor/routes/create.ts +19 -0
- package/src/editor/routes/delete.ts +19 -0
- package/src/editor/routes/document.ts +39 -0
- package/src/editor/routes/documents.ts +30 -0
- package/src/editor/routes/github.ts +23 -0
- package/src/editor/routes/history.ts +9 -0
- package/src/editor/routes/lock.ts +9 -0
- package/src/editor/routes/login/assets.d.ts +10 -0
- package/src/editor/routes/login/login.ts +43 -0
- package/src/editor/routes/media.ts +16 -0
- package/src/editor/routes/move.ts +17 -0
- package/src/editor/routes/notice.ts +9 -0
- package/src/editor/routes/preview.astro +77 -0
- package/src/editor/routes/publish.ts +9 -0
- package/src/editor/routes/status.ts +9 -0
- package/src/editor/routes/submissions.ts +16 -0
- package/src/editor/routes/try-again.ts +9 -0
- package/src/editor/routes/write.ts +18 -0
- package/src/editor/server/admin-props.ts +151 -0
- package/src/editor/server/cards.ts +61 -0
- package/src/editor/server/create.ts +414 -0
- package/src/editor/server/drafts-store.ts +127 -0
- package/src/editor/server/media/alt.ts +107 -0
- package/src/editor/server/media/bucket.ts +295 -0
- package/src/editor/server/media/config.ts +78 -0
- package/src/editor/server/media/handler.ts +221 -0
- package/src/editor/server/media/library.ts +254 -0
- package/src/editor/server/media/memory.ts +184 -0
- package/src/editor/server/media/mux.ts +241 -0
- package/src/editor/server/media/sniff.ts +599 -0
- package/src/editor/server/media/upload.ts +175 -0
- package/src/editor/server/mode.ts +63 -0
- package/src/editor/server/move.ts +295 -0
- package/src/editor/server/render.tsx +9 -0
- package/src/editor/server/replace.ts +72 -0
- package/src/editor/server/settings.ts +35 -0
- package/src/editor/server/write.ts +366 -0
- package/src/editor/styles/editor.css +502 -0
- package/src/editor/styles/fonts.css +40 -0
- package/src/editor/styles/levels/README.md +4 -0
- package/src/editor/styles/levels/collections.css +99 -0
- package/src/editor/styles/levels/document-level.css +10 -0
- package/src/editor/styles/levels/documents.css +9 -0
- package/src/editor/styles/levels/fields.css +171 -0
- package/src/editor/styles/levels/files.css +95 -0
- package/src/editor/styles/levels/live.css +10 -0
- package/src/editor/styles/levels/m5a-documents.css +51 -0
- package/src/editor/styles/levels/m6.css +82 -0
- package/src/editor/styles/levels/media.css +175 -0
- package/src/editor/styles/levels/switcher.css +3 -0
- package/src/editor/styles/tokens.css +114 -0
- package/src/editor/tsconfig.json +16 -0
- package/src/editor/types.ts +881 -0
- package/src/editor/ui-state.ts +113 -0
- package/src/email.ts +67 -0
- package/src/fields.ts +1129 -0
- package/src/files-route.ts +201 -0
- package/src/files.ts +116 -0
- package/src/forms/email.ts +149 -0
- package/src/forms/endpoint.ts +347 -0
- package/src/forms/inbox.ts +273 -0
- package/src/forms/rate-limit.ts +130 -0
- package/src/forms/route.ts +24 -0
- package/src/forms/rules.ts +278 -0
- package/src/forms/store.ts +486 -0
- package/src/head.ts +125 -0
- package/src/index.ts +434 -0
- package/src/json-source.ts +116 -0
- package/src/links.ts +121 -0
- package/src/live/README.md +43 -0
- package/src/live/auth/allow-rules.ts +70 -0
- package/src/live/auth/allow.ts +128 -0
- package/src/live/auth/auth.ts +155 -0
- package/src/live/auth/email.ts +84 -0
- package/src/live/auth/guard.ts +111 -0
- package/src/live/auth/index.ts +68 -0
- package/src/live/auth/links.ts +101 -0
- package/src/live/auth/login.ts +245 -0
- package/src/live/auth/middleware.ts +53 -0
- package/src/live/auth/request.ts +56 -0
- package/src/live/auth/routes.ts +9 -0
- package/src/live/auth/session.ts +181 -0
- package/src/live/auth/views.ts +355 -0
- package/src/live/autosaves.ts +306 -0
- package/src/live/backend.ts +652 -0
- package/src/live/branches.ts +19 -0
- package/src/live/drafts-store.ts +591 -0
- package/src/live/editor.ts +465 -0
- package/src/live/git.ts +1021 -0
- package/src/live/github.ts +271 -0
- package/src/live/locals.ts +24 -0
- package/src/live/locks.ts +273 -0
- package/src/live/merge.ts +197 -0
- package/src/live/messages.ts +151 -0
- package/src/live/mode.ts +210 -0
- package/src/live/redis.ts +533 -0
- package/src/live/runtime.ts +164 -0
- package/src/live/status.ts +109 -0
- package/src/live/sweep.ts +228 -0
- package/src/live/timings.ts +47 -0
- package/src/live/webhook.ts +39 -0
- package/src/lockup-css.ts +58 -0
- package/src/measure.ts +145 -0
- package/src/media-value.ts +239 -0
- package/src/media.ts +255 -0
- package/src/read.ts +256 -0
- package/src/redirect-middleware.ts +49 -0
- package/src/redirects.ts +76 -0
- package/src/relink.ts +293 -0
- package/src/reserved.ts +24 -0
- package/src/rich-text.ts +690 -0
- package/src/schema-key.ts +63 -0
- package/src/seo.ts +124 -0
- package/src/serialize.ts +697 -0
- package/src/skipped.ts +35 -0
- package/src/slug.ts +121 -0
- package/src/text-styles.ts +62 -0
- package/src/virtual.d.ts +28 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: caracms-adopt
|
|
3
|
+
description: Bring an existing Astro site onto caracms — run `caracms init`, then convert the site's pages to caracms documents one at a time (its layout into the page component, its sections into blocks, its content collections into caracms collections, its settings into Site Settings), move its images with `caracms media import`, and prove visitors' pages look the same with the look-same script. Use when adding caracms to a site, or converting a page, collection or the header and footer of one.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bringing an existing Astro site onto caracms
|
|
7
|
+
|
|
8
|
+
**The bar: visitors' pages look the same.** Moving to caracms changes what editors can do,
|
|
9
|
+
not what visitors see. Every converted page is checked against the site as it was, with
|
|
10
|
+
the look-same script below.
|
|
11
|
+
|
|
12
|
+
Read `caracms-blocks` for how blocks, collections and the page component work, and
|
|
13
|
+
`caracms-content` for the content files. This skill is the order to do things in.
|
|
14
|
+
|
|
15
|
+
Commands run from the site's root: `npx caracms …`, or `pnpm caracms …` in a pnpm site.
|
|
16
|
+
|
|
17
|
+
## 1. Add the plumbing: `caracms init`
|
|
18
|
+
|
|
19
|
+
Start from a clean git checkout (untracked files are fine). The order is:
|
|
20
|
+
|
|
21
|
+
1. **Install caracms**, pinned, if `package.json` has none yet: `npm install caracms --save-exact`
|
|
22
|
+
(or `pnpm add caracms --save-exact`).
|
|
23
|
+
2. **Commit the install** (`package.json` and the lockfile). `init --write` refuses while
|
|
24
|
+
tracked files have uncommitted changes, so that its own change is one diff to review.
|
|
25
|
+
3. **`npx caracms init`**, the dry run (below), and read it.
|
|
26
|
+
4. **`npx caracms init --write`**.
|
|
27
|
+
5. **Install again** (`npm install` / `pnpm install`): `init` adds dependencies to
|
|
28
|
+
`package.json`.
|
|
29
|
+
|
|
30
|
+
**`npx caracms init`** is a dry run: it writes nothing and prints
|
|
31
|
+
|
|
32
|
+
- one line per file: `add` (a new file), `edit` (an existing one), `link` / `unlink` (a
|
|
33
|
+
skill link in `.claude/skills/`);
|
|
34
|
+
- a diff of each;
|
|
35
|
+
- `Already done: …` on a site partly set up;
|
|
36
|
+
- `Notes:`, things it can't do itself, with the lines to add by hand (an `astro.config` it
|
|
37
|
+
can't parse, a `tsconfig.json` it can't follow, a `.railway/railway.ts` the site already
|
|
38
|
+
has, an existing `/health` route);
|
|
39
|
+
- what to run next.
|
|
40
|
+
|
|
41
|
+
If something is in the way it prints `caracms init: N things are in the way, so nothing
|
|
42
|
+
was planned:` with each problem and what to do, and exits 1. Fix them all and run it
|
|
43
|
+
again. The blockers: a `content/` folder holding files that aren't caracms's; a route or
|
|
44
|
+
`public/` folder at `/admin`, `/_caracms` or `/files`; the site's own `/sitemap.xml` or
|
|
45
|
+
`/robots.txt` route (caracms serves both); a route that already catches every top-level
|
|
46
|
+
address (`src/pages/[...slug].astro`); an adapter other than `@astrojs/node`, or the Node
|
|
47
|
+
adapter in middleware mode; an Astro outside caracms's range, or none; a Tina site
|
|
48
|
+
(`tinacms` in its dependencies, or tracked files under `tina/`: those come in through a
|
|
49
|
+
port, not `init`; an untracked leftover `tina/__generated__/` is only a note). `init --write` also refuses with uncommitted changes
|
|
50
|
+
to tracked files: usually the install of step 1, not yet committed.
|
|
51
|
+
|
|
52
|
+
Read the diff. Then **`npx caracms init --write`**, install again (`npm install` /
|
|
53
|
+
`pnpm install`), and commit that as one change.
|
|
54
|
+
|
|
55
|
+
pnpm 12.11 and later may print `Agent skills awaiting approval: caracms` after the install.
|
|
56
|
+
That's pnpm's own gate: it links the skills a dependency ships into `.claude/skills/` only
|
|
57
|
+
once you approve them with `pnpm approve` (`pnpm permissions` lists what's waiting).
|
|
58
|
+
`init` has already linked caracms's skills itself, so there's nothing to do; leave it
|
|
59
|
+
unapproved, and don't approve it in passing (`pnpm approve` also reviews build scripts).
|
|
60
|
+
|
|
61
|
+
`init` added:
|
|
62
|
+
|
|
63
|
+
- `caracms.config.ts`: a `pages` collection at `/` (nested), one `text` block, no text
|
|
64
|
+
styles;
|
|
65
|
+
- `src/components/caracms/Page.astro` (the page component) and `TextBlock.astro`;
|
|
66
|
+
- `src/pages/[...path].astro`, which serves the documents in `content/pages/` and nothing
|
|
67
|
+
else, and `src/pages/health.ts`;
|
|
68
|
+
- `content/site-settings.json` (its `brand.name` is the package name title-cased: correct
|
|
69
|
+
it), and `schemas/`;
|
|
70
|
+
- `astro.config` edits: the Node adapter (standalone), `integrations: [caracms(site)]`,
|
|
71
|
+
`devToolbar: { enabled: false }`;
|
|
72
|
+
- `package.json`: `@astrojs/node`, `railway` (dev), `"start": "caracms start"`;
|
|
73
|
+
`.gitignore`: `.caracms/`; the tsconfig's `allowImportingTsExtensions`;
|
|
74
|
+
- `.railway/railway.ts` (when the site had none), the skill links and a block in
|
|
75
|
+
`CLAUDE.md`.
|
|
76
|
+
|
|
77
|
+
Then `astro build`: every existing page must still build. The pages stay prerendered, but
|
|
78
|
+
the output moves to `dist/client/` and `dist/server/`, and `npm start` is now
|
|
79
|
+
`caracms start` (`PORT`, default 8080). Anything in the site that reads `dist/` directly
|
|
80
|
+
(its own built-site checks, a script) needs the new paths: the pages are in
|
|
81
|
+
`dist/client/`.
|
|
82
|
+
|
|
83
|
+
Re-run `init` after updating caracms: it re-links the skills and refreshes the CLAUDE.md
|
|
84
|
+
block and the schemas. On a set-up site with nothing to do it says
|
|
85
|
+
`caracms init: nothing to do. caracms is set up here.`
|
|
86
|
+
|
|
87
|
+
## 2. Set up the comparison before changing a page
|
|
88
|
+
|
|
89
|
+
Keep the site as it was running, to compare against: a git worktree at the commit before
|
|
90
|
+
`init`, built and served the way the site served it.
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
git worktree add ../<site>-baseline <commit before init>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Install and build there and serve it on its own port. The new site is `astro dev`
|
|
97
|
+
(give it a port of its own: `astro dev --port <port>`), or a build served with
|
|
98
|
+
`PORT=<port> npm start`. For look-same's HTML diff both must be builds (section 9).
|
|
99
|
+
|
|
100
|
+
**Run the site's own tests now**, before any page moves, and note what passes. Tests that
|
|
101
|
+
read the old routes (`src/pages/about.astro`), the old collection files (YAML, Markdown)
|
|
102
|
+
or `dist/` will break as pages move. When they do, change where they read (the documents
|
|
103
|
+
are `content/pages/<path>.json` and `content/<collection>/<slug>.json`, the built pages
|
|
104
|
+
`dist/client/`), not what they check. A check that a page's own CSS isn't on another page
|
|
105
|
+
is the exception (section 4).
|
|
106
|
+
|
|
107
|
+
**Images need the media variables** in the site's `.env` (never committed):
|
|
108
|
+
`CARACMS_MEDIA_URL` (imgproxy's origin), `IMGPROXY_KEY`, `IMGPROXY_SALT` and `S3_BUCKET`
|
|
109
|
+
to show images, plus `S3_ENDPOINT`, `S3_REGION`, `S3_ACCESS_KEY_ID` and
|
|
110
|
+
`S3_SECRET_ACCESS_KEY` to import and upload them. Without them `astro dev` draws a
|
|
111
|
+
caracms image as nothing, and `astro build` stops with a sentence naming what's missing.
|
|
112
|
+
If they aren't there, say so; don't invent values.
|
|
113
|
+
|
|
114
|
+
## 3. The page component
|
|
115
|
+
|
|
116
|
+
**Reuse the site's own layout.** `src/components/caracms/Page.astro` renders the layout
|
|
117
|
+
the old routes used (`src/layouts/…`), with `<Head>` in its head and `<Blocks>` where the
|
|
118
|
+
page's content went (see `caracms-blocks` → The page component). Give the layout what it
|
|
119
|
+
needs to stay usable by the code routes too: its own `<title>` and description only when
|
|
120
|
+
no `<Head>` is passed (a slot or an optional prop), so a document page has one of each.
|
|
121
|
+
Its markup, classes, CSS imports and scripts stay as they are. Set `<html data-preview>`
|
|
122
|
+
from `preview`.
|
|
123
|
+
|
|
124
|
+
**Keep the old title format.** `<Head>` titles a page `<document title> · <brand name>`.
|
|
125
|
+
If the old titles used another separator (`About | Fernhill`), set the config's
|
|
126
|
+
`titleSeparator: ' | '` (spaces included; see `caracms-blocks` → The page component), not a
|
|
127
|
+
Site Settings value: the format is design. Code routes that build their own `<title>` use
|
|
128
|
+
`settings.seo.titleSeparator` and `settings.brand.name` from `siteSettings` (section 7), so
|
|
129
|
+
every title matches. Check each page's `<title>` line in look-same's HTML diff (section 9).
|
|
130
|
+
|
|
131
|
+
How `<Head>` chooses a title:
|
|
132
|
+
|
|
133
|
+
1. the page's own `seo.title`, exactly as written;
|
|
134
|
+
2. otherwise, on the home page only, Site Settings' `seo.title`, then the brand's name,
|
|
135
|
+
with no separator (the document's `title` only when both are empty);
|
|
136
|
+
3. otherwise `<document title><titleSeparator><brand name>`.
|
|
137
|
+
|
|
138
|
+
So a page whose old title was `<page> | <brand>` drops its `seo.title` and lets step 3
|
|
139
|
+
build it (give the document the old `<page>` part as its `title`). A home title in another
|
|
140
|
+
form, such as `<brand> | <tagline>`, goes in the home page's own `seo.title`
|
|
141
|
+
(`content/pages/index.json`), not Site Settings'. That way the home page's title sits with
|
|
142
|
+
the home page, where an editor changing it will look, and it wins whatever Site Settings
|
|
143
|
+
holds.
|
|
144
|
+
|
|
145
|
+
If the old site showed one site-wide title on every page without its own (a Tina template
|
|
146
|
+
does), set the config's `titleFallback: 'site'` beside `titleSeparator`: step 2 then holds
|
|
147
|
+
on every page, not only the home page (the paged-listing ` — page N` still follows). The
|
|
148
|
+
default, `'page-and-brand'`, is the list above.
|
|
149
|
+
|
|
150
|
+
**Per-page layout props.** The old routes often passed the layout something by hand: the
|
|
151
|
+
header's colour or variant, `<main>`'s classes, a script in `<head>` for one page, the
|
|
152
|
+
current page for the nav's highlight. A document has no route to do that, so the page
|
|
153
|
+
component works them out:
|
|
154
|
+
|
|
155
|
+
- from the page's address, when it's about where the page is (the nav's current item, a
|
|
156
|
+
section of the site): `const { path } = Astro.props`, the document's address (`/about`),
|
|
157
|
+
the same on the public page and in the preview, and null when the document has no page.
|
|
158
|
+
Never read `Astro.url` or `?path=` for it: in the preview they're the editor's own
|
|
159
|
+
address. Pass it on to `<Head path={path}>` as `init`'s page component does;
|
|
160
|
+
|
|
161
|
+
- from a page field, when an editor might choose it: give the `pages` collection
|
|
162
|
+
`fields` (`fields: { footer: select({ options: […] }) }`) and read it from
|
|
163
|
+
`Astro.props.data`;
|
|
164
|
+
- from the page's first block, when it follows from how the page opens:
|
|
165
|
+
|
|
166
|
+
```astro
|
|
167
|
+
---
|
|
168
|
+
const LOOKS: Record<string, { header: string; mainClass?: string; intro?: boolean }> = {
|
|
169
|
+
homeHero: { header: 'blue', mainClass: 'home-page', intro: true },
|
|
170
|
+
aboutHero: { header: 'ink', mainClass: 'bg-ink text-white' },
|
|
171
|
+
};
|
|
172
|
+
const first = 'blocks' in document ? document.blocks[0]?.type : undefined;
|
|
173
|
+
const look = (first && LOOKS[first]) || { header: 'ink' };
|
|
174
|
+
---
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
A head script that plays an intro or hides content goes in only when `!preview`: the
|
|
178
|
+
editor wants the settled page.
|
|
179
|
+
|
|
180
|
+
## 4. Convert page by page
|
|
181
|
+
|
|
182
|
+
For each page, in one commit:
|
|
183
|
+
|
|
184
|
+
1. **Its sections become blocks.** Each section component becomes a block: a config entry
|
|
185
|
+
(`caracms.config.ts` → `blocks`) with a field for each thing an editor would change,
|
|
186
|
+
and a component that renders **the same elements and classes** as before, with the
|
|
187
|
+
text drawn through `Text` / `RichText` / `Lockup`, links through `Link` / `Buttons` and
|
|
188
|
+
images through `Media` (use `as` and `class` to keep the old elements; `Text` always
|
|
189
|
+
renders one, so pick the element the old markup had, `as="span"` inside a sentence).
|
|
190
|
+
Attributes pass through as written (`<Block data-hero>` renders bare). In a component
|
|
191
|
+
slot write `{label}{" "}<span>`, not `{label} <span>`: Astro's compiler drops that
|
|
192
|
+
space (see `caracms-blocks` → A block component). Reuse a block
|
|
193
|
+
across pages when the sections are the same; don't make one block per page. Things
|
|
194
|
+
that aren't content (an animation's settings, a decorative SVG) stay in the component.
|
|
195
|
+
2. **Its content becomes a document**: `content/pages/<path>.json` (`index.json` for
|
|
196
|
+
Home), with the page's copy word for word, its `$schema`, a `title`, and its blocks in
|
|
197
|
+
order. Images by key (step 5).
|
|
198
|
+
3. **Its `.astro` route is deleted** (`src/pages/about.astro` for `/about`,
|
|
199
|
+
`src/pages/index.astro` for Home), in the same change. A page is code or a document,
|
|
200
|
+
never both: while the route exists, its address belongs to it.
|
|
201
|
+
4. **Lists of pages in the site's code become content.** Search the code for the page's path (`'/about'`).
|
|
202
|
+
A site often lists its pages by hand: a table of each page's colour or header variant,
|
|
203
|
+
a sitemap or page list its tests check, a nav it doesn't read from settings. An editor
|
|
204
|
+
who adds a page in `/admin` can't add it to those, so the new page would fall through to
|
|
205
|
+
a default no one chose. Turn each list into something the page brings with it:
|
|
206
|
+
- a page field, when it's a choice per page (`fields: { ground: select({ options: […] }) }`
|
|
207
|
+
on the `pages` collection, read from `Astro.props.data` in the page component, which
|
|
208
|
+
can write it onto the page for a script to read);
|
|
209
|
+
- the page's first block, when it follows from how the page opens (the `LOOKS` map in
|
|
210
|
+
section 3);
|
|
211
|
+
- the content itself, when it's a list of pages: read the documents
|
|
212
|
+
(`contentReader(site)` from `caracms/content` in a code route, or the files in
|
|
213
|
+
`content/pages/` in a test) rather than a list kept by hand.
|
|
214
|
+
|
|
215
|
+
Give each a default that looks right on a plain new page.
|
|
216
|
+
5. `npx caracms validate`, `astro build`, the site's own tests (section 2), and the
|
|
217
|
+
look-same check for that page. After a script writes the content files (a conversion
|
|
218
|
+
from the old CMS's), run `npx caracms validate --fix`: it lays each file out as the
|
|
219
|
+
editor saves it (two-space JSON, whitespace only), so an editor's first save doesn't
|
|
220
|
+
reformat the whole file.
|
|
221
|
+
|
|
222
|
+
**Ship a general block.** With the first converted page, add a block any new page can be
|
|
223
|
+
made from, a heading and rich text, even if the site's pages don't need one yet: `init`'s
|
|
224
|
+
`text` block is one plain field, so without it a plain new page (a Careers page, a
|
|
225
|
+
policy) means writing code. Style it with the site's own CSS for headings and paragraphs:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// src/components/caracms/prose.ts
|
|
229
|
+
import { richText, text, type BlockConfig, type BlockData } from 'caracms/config';
|
|
230
|
+
|
|
231
|
+
export const prose = {
|
|
232
|
+
label: 'Heading and text',
|
|
233
|
+
component: 'src/components/caracms/ProseBlock.astro',
|
|
234
|
+
fields: {
|
|
235
|
+
heading: text({ heading: true }),
|
|
236
|
+
body: richText({ headings: ['h2', 'h3'] }),
|
|
237
|
+
},
|
|
238
|
+
} satisfies BlockConfig;
|
|
239
|
+
|
|
240
|
+
export type ProseData = BlockData<typeof prose.fields>;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
with `prose` in `caracms.config.ts` → `blocks`, and a component that draws `heading`
|
|
244
|
+
through `Text` and `body` through `RichText` (see `caracms-blocks` → A block component).
|
|
245
|
+
Give it a `LOOKS` entry (or whatever the page component reads) so a page that opens with
|
|
246
|
+
it gets a sensible header.
|
|
247
|
+
|
|
248
|
+
**Every block's CSS loads on every document page.** `[...path].astro` serves every
|
|
249
|
+
document, so it imports every block component, and Astro bundles all their CSS for it.
|
|
250
|
+
Astro scopes a component's `<style>` to its own elements (`data-astro-cid-*`), so About's
|
|
251
|
+
styles can't touch Home's blocks; that part is bytes, not looks. caracms's `<Media>` brings
|
|
252
|
+
its `.cms-player` styles the same way. What isn't fine is a block with `<style is:global>`
|
|
253
|
+
or a global stylesheet import: that now applies on every document page, so move it to the
|
|
254
|
+
layout or scope it. A site check that one page's CSS is absent from another can't hold any
|
|
255
|
+
more; change it to check the looks.
|
|
256
|
+
|
|
257
|
+
**The order changes too, and order can decide who wins.** The page's CSS now comes in
|
|
258
|
+
other chunks and another order (a chunk per shared component, every block's styles on
|
|
259
|
+
every page). The cascade settles a tie in specificity by order, so check where a global
|
|
260
|
+
rule (the layout's stylesheet, a reset, Tailwind) and a block's scoped rule set the same
|
|
261
|
+
property on the same element:
|
|
262
|
+
|
|
263
|
+
- **How Astro scopes**: the site's `astro.config` `scopedStyleStrategy`. The default since
|
|
264
|
+
Astro 3 is `'attribute'`: each compound in a scoped selector gets `[data-astro-cid-…]`,
|
|
265
|
+
so `.title` is written out as `.title[data-astro-cid-x]`, one class's worth of
|
|
266
|
+
specificity more per compound (`.a .b` gains two). `'class'` adds a class the same way.
|
|
267
|
+
`'where'` adds `:where(.astro-x)`, which adds no specificity.
|
|
268
|
+
- **With `'attribute'` or `'class'`**, a scoped rule beats a global rule with the same
|
|
269
|
+
selector whatever the order. Order decides only when they tie after the extra: a global
|
|
270
|
+
`.card .title` (two classes) against a block's `.title` (a class plus the attribute),
|
|
271
|
+
`.prose h2` against a block's `h2`, and any selector inside `:global(…)`, which isn't scoped.
|
|
272
|
+
- **With `'where'`**, every scoped selector counts as written, so a global rule with the
|
|
273
|
+
same selector ties, and the one loaded last wins: that's what can change.
|
|
274
|
+
- **Global against global** is all order: a block's `is:global` styles or global import
|
|
275
|
+
against the layout's stylesheet. `!important` and `@layer` (an unlayered rule beats a
|
|
276
|
+
layered one) win regardless of order.
|
|
277
|
+
|
|
278
|
+
**List each page's stylesheet order, old and new.** look-same's HTML diff sorts `<head>`
|
|
279
|
+
(section 9), so it shows which stylesheets changed but not their order. With both sites
|
|
280
|
+
served as builds (section 2; a dev server inlines its CSS), print each page's `<link rel="stylesheet">` and `<style>` tags in the
|
|
281
|
+
order the page loads them:
|
|
282
|
+
|
|
283
|
+
```sh
|
|
284
|
+
for page in / /about /team; do
|
|
285
|
+
for site in http://localhost:<baseline port> http://localhost:<new port>; do
|
|
286
|
+
echo "== $site$page"
|
|
287
|
+
curl -s "$site$page" | grep -oE '<link[^>]*stylesheet[^>]*>|<style[^>]*>'
|
|
288
|
+
done
|
|
289
|
+
done
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Find the global stylesheet in each list (the layout's import, Tailwind). A stylesheet that
|
|
293
|
+
was after it and is now before it, or the other way round, is where a tie can change
|
|
294
|
+
sides: open that chunk (`dist/client/_astro/<name>.css`) and look for the ties above, and
|
|
295
|
+
grep the moved components for `:global(`. Then look at the elements they style in the
|
|
296
|
+
screenshots at both widths. Nothing else tests the order: a tie the screenshots don't show (a hover, a
|
|
297
|
+
state behind a toggle) needs a look by hand, and a note in the pull request.
|
|
298
|
+
|
|
299
|
+
**`draft: true` and similar per-entry markers** (copy that's a placeholder, say) have no
|
|
300
|
+
built-in equivalent: caracms's drafts are unpublished edits, not a flag on a page. If
|
|
301
|
+
something in the site reads the marker, keep it as a `toggle()` field on the collection;
|
|
302
|
+
otherwise drop it. Either way, say so in the pull request.
|
|
303
|
+
|
|
304
|
+
**Code routes.** Every route in `src/pages/` other than caracms's `[...path].astro` (and a
|
|
305
|
+
collection's own route, below) is the site's code, and its address is reserved: a static
|
|
306
|
+
route (`contact.astro`) reserves exactly `/contact`; a route with parameters
|
|
307
|
+
(`work/[slug].astro`) reserves everything under `/work/`. The editor refuses a page there
|
|
308
|
+
("… is served by the site's own code (…), so a page can't go there."), and `validate`
|
|
309
|
+
reports a document on one as an error, telling you to delete the route if the document
|
|
310
|
+
replaces it.
|
|
311
|
+
|
|
312
|
+
**What stays code:** pages that aren't content: a design-system or style-guide page,
|
|
313
|
+
developer pages, anything driven by logic rather than copy. Leave them as routes. Pages
|
|
314
|
+
can still link to them, and code can keep using the site's Astro content collections
|
|
315
|
+
(`getCollection`) for anything not yet moved.
|
|
316
|
+
|
|
317
|
+
## 5. Images: `caracms media import`
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
npx caracms media import src/assets/team/ana.jpg src/assets/hero.webp
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
It puts each file in the media bucket (under `uploads/<hash>/<name>.<ext>`, as the
|
|
324
|
+
editor's uploads are) and prints one JSON array on stdout:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
[{ "file": "src/assets/team/ana.jpg", "key": "uploads/3f9c0a1b2c3d/ana.jpg", "w": 1200, "h": 1500, "existed": false }]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
- It reads `S3_ENDPOINT`, `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY_ID` and
|
|
331
|
+
`S3_SECRET_ACCESS_KEY` from the environment, else the site's `.env`.
|
|
332
|
+
- The same image again writes nothing and prints its key with `"existed": true`. It never
|
|
333
|
+
deletes anything.
|
|
334
|
+
- JPEG, PNG, WebP, AVIF, HEIC/HEIF, GIF and SVG, up to 40 MB and 50 megapixels. If any file
|
|
335
|
+
is refused (its type or size), none are written. Exit 0 fine, 1 failed, 2 bad usage.
|
|
336
|
+
|
|
337
|
+
In the content an image is `{ "key", "alt", "w", "h" }` from that output, with the old
|
|
338
|
+
page's alt text. Keep `w` and `h`: without them the image renders, but its box isn't kept
|
|
339
|
+
while it loads, and `validate` warns on every image (not a file such as a PDF) that lacks
|
|
340
|
+
them. For images already in the bucket, run `media import` on their files again: it
|
|
341
|
+
uploads nothing and prints `w` and `h`. A crop keeps the centre unless the value has a `focal` point: if the old
|
|
342
|
+
page cropped elsewhere (an `object-position`), set `focal` to match. The images come
|
|
343
|
+
through imgproxy now, so their bytes and sometimes their sub-pixel sizes differ from
|
|
344
|
+
Astro's; their crops and boxes must not.
|
|
345
|
+
|
|
346
|
+
Once a page's images are in the bucket and its content points at them, the old files can
|
|
347
|
+
go only when nothing in the code still uses them.
|
|
348
|
+
|
|
349
|
+
## 6. Content collections
|
|
350
|
+
|
|
351
|
+
The site's Astro content collections (`src/content.config.ts`) become caracms collections
|
|
352
|
+
when editors should change them:
|
|
353
|
+
|
|
354
|
+
- A collection with pages of its own: `kind: 'fields'` with a `urlBase`, and its route.
|
|
355
|
+
- A collection only other pages show (a team, an FAQ, logos): `kind: 'fields'` with **no
|
|
356
|
+
`urlBase`** and a `component` (the editor's preview of one document). A block shows them
|
|
357
|
+
with `cms.read.documents('<name>')`, which orders them **by title**: keep the old order
|
|
358
|
+
with a `number()` field sorted in the component, or a `list(reference({ to }))` on the
|
|
359
|
+
page. **Take the old order from the old page's HTML, not the data file.** It's often
|
|
360
|
+
implicit: `getCollection` can return a file collection's entries in id (slug) order,
|
|
361
|
+
not the order the YAML lists them. Number the documents in the order the old page
|
|
362
|
+
shows, and check it in look-same's HTML diff.
|
|
363
|
+
- Each entry becomes `content/<name>/<slug>.json`: `$schema`, `title`, then the fields.
|
|
364
|
+
Map each zod field to a caracms field kind; images through `media import`.
|
|
365
|
+
- **A zod rule that refuses a value** (`.refine()`, `.regex()`, a `.min()` on a string)
|
|
366
|
+
has no caracms equivalent: a caracms field can't refuse what an editor types. Write the
|
|
367
|
+
rule into the field's `help` (`text({ help: 'End with a full stop.' })`), and use what
|
|
368
|
+
the field kinds do offer: `select` options instead of a free string, `text({ max })`
|
|
369
|
+
(a soft limit the editor counts, never a refusal).
|
|
370
|
+
- Code that still reads the old collection with `getCollection` keeps working until you
|
|
371
|
+
move it. A code route can read caracms's documents with `contentReader(site)` from
|
|
372
|
+
`caracms/content` (`site` from `virtual:caracms/config`); the page component gets the
|
|
373
|
+
same reader as `Astro.props.read`.
|
|
374
|
+
- Delete the old collection's files and its entry in `content.config.ts` only when nothing
|
|
375
|
+
reads it any more.
|
|
376
|
+
|
|
377
|
+
## 7. Site Settings
|
|
378
|
+
|
|
379
|
+
Data the whole site uses (the menu, footer links, contact details, social links) becomes
|
|
380
|
+
Site Settings fields: `caracms.config.ts` → `settings: { fields: { … } }`, with the values
|
|
381
|
+
in `content/site-settings.json`. The page component's header and footer read them from
|
|
382
|
+
`Astro.props.settings`. Set `brand.name` to the site's real name. The logo stays the
|
|
383
|
+
site's own file.
|
|
384
|
+
|
|
385
|
+
**A header and footer the code routes share** read the same object there:
|
|
386
|
+
`await siteSettings(site, Astro.locals)`, with `siteSettings` from `caracms/content` and
|
|
387
|
+
`site` from `virtual:caracms/config`. Pass it into the shared component from both sides.
|
|
388
|
+
|
|
389
|
+
**Placeholder links** (`#`) are valid link values; keep them as links. An address-only
|
|
390
|
+
link that isn't a valid address renders as `null`: draw no link, never `href=""`.
|
|
391
|
+
|
|
392
|
+
**A value in both places** (the site's name in `brand.name` and in the old settings file
|
|
393
|
+
that code routes still read for their titles): an editor changing one would leave the
|
|
394
|
+
other behind. Point the code routes at Site Settings with `siteSettings`; where something
|
|
395
|
+
can't move yet, list each duplicate in the pull request.
|
|
396
|
+
|
|
397
|
+
## 8. Motion under the preview
|
|
398
|
+
|
|
399
|
+
The editor's preview renders the page in iframes. Opening a document loads them fresh;
|
|
400
|
+
each edit morphs the new render into the frame's `<body>` without reloading. A site with GSAP, ScrollTrigger or Lenis has
|
|
401
|
+
to cope (details in `caracms-blocks` → Scripts and the live preview):
|
|
402
|
+
|
|
403
|
+
- Set `<html data-preview>` in the page component, and in the preview leave the scroll
|
|
404
|
+
native (Lenis with `smoothWheel: false`).
|
|
405
|
+
- In the preview, every animation shows its finished state from the first load: take the
|
|
406
|
+
site's settled path (what it does on a back/forward visit or under reduced motion), and
|
|
407
|
+
leave an intro's head script out.
|
|
408
|
+
- Listen for `caracms:rendered` on `document` (fired after each edit's morph, never on a
|
|
409
|
+
load), set up again on the settled path, and `ScrollTrigger.refresh()`.
|
|
410
|
+
|
|
411
|
+
Check it in the editor: `astro dev`, open `/admin` (with no GitHub App configured it runs
|
|
412
|
+
in local mode: no sign-in, and edits save to the files on disk), open a page, change a
|
|
413
|
+
heading, and watch that the preview updates without jumping.
|
|
414
|
+
|
|
415
|
+
## 9. The look-same check
|
|
416
|
+
|
|
417
|
+
`look-same.mjs` is beside this skill (`.claude/skills/caracms-adopt/look-same.mjs`). It
|
|
418
|
+
screenshots each page of the old site and the new, full page, at 1440 and 390 wide, with
|
|
419
|
+
reduced motion and lazy content scrolled in, and writes a side-by-side report. It also
|
|
420
|
+
diffs each page's HTML. It needs
|
|
421
|
+
`playwright-core` and Google Chrome on the machine. Install the tool in a temporary folder
|
|
422
|
+
**outside the site**, so the site's `package.json` and lockfile don't change:
|
|
423
|
+
|
|
424
|
+
```sh
|
|
425
|
+
npm i --prefix /tmp/look-same playwright-core
|
|
426
|
+
node .claude/skills/caracms-adopt/look-same.mjs --playwright /tmp/look-same \
|
|
427
|
+
--old http://localhost:<baseline port> --new http://localhost:<new port> \
|
|
428
|
+
--pages /,/about,/team --out /tmp/look-same/report
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
- `--pages` is a comma list or a file with one path per line. `--widths` defaults to
|
|
432
|
+
`1440,390`. `--out` gets `index.html`, `scores.json`, `shots/` and `html/`. An option
|
|
433
|
+
it doesn't know is refused (exit 1), so a misspelt flag doesn't quietly do nothing.
|
|
434
|
+
- **Any status is compared.** A page that answers 404 (or anything) on both sides is
|
|
435
|
+
compared like any other, so put a path that doesn't exist in the list to check the
|
|
436
|
+
site's 404 page. Two different statuses are a difference (`statusDiffers: true`), not an
|
|
437
|
+
error: both pages are still shot and diffed. Each result has `status: { old, new }`,
|
|
438
|
+
and the report shows it beside the page.
|
|
439
|
+
- **Images**: before each shot it waits for the images that have started loading, up to
|
|
440
|
+
`--image-wait` seconds (default 5). A lazy image that never starts (in a hidden tab
|
|
441
|
+
panel, a closed `<details>`) isn't waited for. A shot that hit the cap is still taken
|
|
442
|
+
and says so: `imageCap` in `scores.json` (per side, how many images were still loading
|
|
443
|
+
and the first few addresses), a line in the report and on the console. Look at that
|
|
444
|
+
shot before trusting its score.
|
|
445
|
+
- Each pair gets `differingShare` (the share of pixels that differ) and `heightDelta`
|
|
446
|
+
(new height minus old, in CSS pixels). It prints one line per page and width.
|
|
447
|
+
- **The HTML diff**, per page: both pages' HTML as the server sends it, one tag or text
|
|
448
|
+
per line, with asset hashes, `data-astro-cid-*`, attribute order and what renders the
|
|
449
|
+
same normalised away (the script's header lists exactly what). `scores.json` → `html`
|
|
450
|
+
has `differingLines` per page; the diff is in `html/<page>.diff` and in `index.html`.
|
|
451
|
+
Point both `--old` and `--new` at **builds**: a dev server's injected scripts and inlined
|
|
452
|
+
CSS would fill the diff.
|
|
453
|
+
- **A port from another CMS** leaves its own attributes and per-render ids, which fill the
|
|
454
|
+
diff with lines that aren't differences. `--ignore-attr <list>` drops attributes by name,
|
|
455
|
+
a comma list where a trailing `*` matches by prefix. `--mask-ids <regex|auto>` writes ids
|
|
456
|
+
drawn at random per render as `[id1]`, `[id2]`, …, and the same in what points at them
|
|
457
|
+
(`for`, `aria-controls`, `aria-labelledby` and the other id lists, `href="#…"`,
|
|
458
|
+
`url(#…)`). The regex is tested against the id's value, unanchored (`'^(st|sp)-'`);
|
|
459
|
+
`auto` masks React's `:r1:` and ids ending in a run of 5 or more letters and digits with
|
|
460
|
+
both in it (`st-a8f3k2`, not `section-12345`). A reference that no longer points at its
|
|
461
|
+
element still shows (`[id2]` against `[missing1]`). For a Tina site:
|
|
462
|
+
|
|
463
|
+
```sh
|
|
464
|
+
node .claude/skills/caracms-adopt/look-same.mjs --playwright /tmp/look-same \
|
|
465
|
+
--old http://localhost:<baseline port> --new http://localhost:<new port> \
|
|
466
|
+
--pages pages.txt --out /tmp/look-same/report \
|
|
467
|
+
--ignore-attr 'data-tina-*' --mask-ids auto
|
|
468
|
+
```
|
|
469
|
+
- **An element that varies run to run on the old site itself** (a wordmark fitted to its box
|
|
470
|
+
while the display font loads, so two shots of the same build differ) can't be fixed by
|
|
471
|
+
either site. `--css <rules>` adds the same CSS to both pages just before each shot: hide
|
|
472
|
+
the element or pin its size, e.g.
|
|
473
|
+
`--css '[data-wordmark-wrap]{visibility:hidden!important}'`. It changes the shots only,
|
|
474
|
+
not the HTML diff, and the report names the rules; keep them to the one element, since
|
|
475
|
+
whatever they cover is no longer compared.
|
|
476
|
+
- **Spaces**: a space between inline content shows as `␣` at the start of the next text,
|
|
477
|
+
wherever it sat (`<span>sub </span><em>` and `<span>sub</span> <em>` are the same), and a
|
|
478
|
+
space beside a block tag is dropped, since neither shows. So a `␣` line in the diff is a
|
|
479
|
+
space that was really added or lost.
|
|
480
|
+
- **`<head>`'s children are compared in any order** (caracms writes `<title>` and the
|
|
481
|
+
description in its own place), so a `<head>` line in the diff is a tag that changed or
|
|
482
|
+
is new. Check the `<title>` line on every page: a different separator or brand name
|
|
483
|
+
shows there (section 3). Order in `<body>` is compared.
|
|
484
|
+
- Exit 0 when the report is written (whatever the scores, statuses or image caps), 1 for a
|
|
485
|
+
usage error, 2 when a page couldn't be fetched at all.
|
|
486
|
+
- **Read both.** Screenshots miss what doesn't show: an attribute, a list in the wrong
|
|
487
|
+
order under an image or a cover, a space a font hides (`␣→` in old, `→` in new, after `VIEW ALL`:
|
|
488
|
+
a slot's `{label} <span>` that needs `{label}{" "}<span>`), a link's `href`. The HTML diff misses what CSS does. Expect caracms's own
|
|
489
|
+
`og:` and `twitter:` meta tags in the diff; everything else in it is a difference to fix
|
|
490
|
+
or to explain in the pull request.
|
|
491
|
+
- **The bar:** text, layout and spacing the same: `heightDelta` 0 and a `differingShare`
|
|
492
|
+
near 0. Image encoding and sub-pixel scaling may differ a little; a person judges those
|
|
493
|
+
from `index.html`. Anything else is a difference to fix, not to explain away.
|
|
494
|
+
|
|
495
|
+
## 10. Before the pull request
|
|
496
|
+
|
|
497
|
+
1. `npx caracms validate`: no errors (see `caracms-content` → Getting it merged).
|
|
498
|
+
2. `astro build`: every page builds.
|
|
499
|
+
3. The site's own tests, as they passed before `init` (section 2).
|
|
500
|
+
4. The look-same check of every page you converted: screenshots and HTML diff.
|
|
501
|
+
5. **A new page needs no code.** In `/admin`, add a page made of the general block, then
|
|
502
|
+
look at it in the preview and, after `astro build`, on the built site: its header,
|
|
503
|
+
colours and anything else the site set per page come out right, and the site's own
|
|
504
|
+
tests still pass with it there. Anything that's wrong points at a list still kept in
|
|
505
|
+
code (section 4, step 4: lists of pages). Delete the test page before the pull request.
|
|
506
|
+
|
|
507
|
+
Work on a branch and open a pull request; never commit to `main` directly, and never
|
|
508
|
+
touch the `drafts` branch.
|