@aotter/mantle 0.0.11-alpha.63 → 0.0.11-alpha.64
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/README.md +87 -12
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/generate.d.ts +2 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +181 -0
- package/dist/generate.js.map +1 -0
- package/dist/skills.d.ts +2 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +80 -0
- package/dist/skills.js.map +1 -0
- package/dist/update.d.ts +2 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +387 -0
- package/dist/update.js.map +1 -0
- package/docs/adr/0001-four-atom-manifest-model.md +6 -7
- package/docs/adr/0007-ai-as-primary-author.md +100 -138
- package/docs/adr/0008-structured-diagnostic-shape.md +79 -99
- package/docs/adr/0009-consumer-supplied-manifests.md +101 -228
- package/docs/adr/0012-views-as-public-rest.md +43 -15
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +43 -17
- package/docs/adr/0018-core-starters-repository-boundary.md +155 -0
- package/docs/adr/README.md +8 -6
- package/docs/cloudflare-low-level-composition.md +94 -0
- package/docs/design-atoms.md +59 -57
- package/docs/design-references/editorial-blog-2026-05-05.md +7 -7
- package/docs/labels.md +1 -1
- package/docs/media-uploads.md +1 -1
- package/docs/release-process.md +156 -523
- package/package.json +9 -6
- package/skills/README.md +20 -16
- package/skills/develop/SKILL.md +4 -4
- package/skills/install/SKILL.md +16 -2
- package/skills/plugin/SKILL.md +1 -1
- package/skills/provision/SKILL.md +1 -1
- package/skills/theme/SKILL.md +12 -10
- package/skills/update/SKILL.md +31 -19
- package/skills/customize-design/SKILL.md +0 -215
- package/skills/extend/SKILL.md +0 -257
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aotter/mantle",
|
|
3
|
-
"version": "0.0.11-alpha.
|
|
3
|
+
"version": "0.0.11-alpha.64",
|
|
4
4
|
"description": "Umbrella entry for @aotter/mantle. Adopters install this one package and import from subpaths: /spec, /runtime, /cloudflare, /admin-ui. Sub-packages remain individually installable on npm for tooling / alt-adapter authors. The Netlify adapter ships as a private workspace stub in v0.1 — its subpath will be added when the impl lands in v0.2.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://mantle.tools/",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"main": "./dist/spec.js",
|
|
15
15
|
"types": "./dist/spec.d.ts",
|
|
16
16
|
"bin": {
|
|
17
|
+
"mantle": "./dist/cli.js",
|
|
17
18
|
"mantle-harness": "./dist/harness-cli.js"
|
|
18
19
|
},
|
|
19
20
|
"publishConfig": {
|
|
@@ -54,10 +55,10 @@
|
|
|
54
55
|
"README.md"
|
|
55
56
|
],
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@aotter/mantle-
|
|
58
|
-
"@aotter/mantle-
|
|
59
|
-
"@aotter/mantle-
|
|
60
|
-
"@aotter/mantle-
|
|
58
|
+
"@aotter/mantle-admin-ui": "0.0.11-alpha.64",
|
|
59
|
+
"@aotter/mantle-spec": "0.0.11-alpha.64",
|
|
60
|
+
"@aotter/mantle-cloudflare": "0.0.11-alpha.64",
|
|
61
|
+
"@aotter/mantle-runtime": "0.0.11-alpha.64"
|
|
61
62
|
},
|
|
62
63
|
"peerDependencies": {
|
|
63
64
|
"@cloudflare/workers-oauth-provider": "^0.8.0",
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"better-auth": "^1.6.23",
|
|
74
75
|
"hono": "^4.12.30",
|
|
75
76
|
"typescript": "^6.0.3",
|
|
77
|
+
"vitest": "^4.1.10",
|
|
76
78
|
"zod": "^4.4.2"
|
|
77
79
|
},
|
|
78
80
|
"engines": {
|
|
@@ -81,6 +83,7 @@
|
|
|
81
83
|
"scripts": {
|
|
82
84
|
"prebuild": "rm -rf dist .tsbuildinfo",
|
|
83
85
|
"build": "tsc -b tsconfig.lib.json",
|
|
84
|
-
"typecheck": "tsc --noEmit -p tsconfig.lib.json"
|
|
86
|
+
"typecheck": "tsc --noEmit -p tsconfig.lib.json",
|
|
87
|
+
"test": "vitest run"
|
|
85
88
|
}
|
|
86
89
|
}
|
package/skills/README.md
CHANGED
|
@@ -9,26 +9,25 @@ Agent-readable skill briefs for consumers of `@aotter/mantle-*`. Discoverable by
|
|
|
9
9
|
| [`theme`](theme/SKILL.md) | `mantle:theme`: Core-owned visual workflow. Reads project context but does not depend on starter-owned skill semantics. |
|
|
10
10
|
| [`update`](update/SKILL.md) | `mantle:update`: Core-owned drift check workflow for SDK, starter snapshots, and plugin lockfiles. |
|
|
11
11
|
| [`install`](install/SKILL.md) | User wants to create a local Mantle site from a deterministic starter bundle or continue an existing local / landing-generated project. |
|
|
12
|
-
| [`customize-design`](customize-design/SKILL.md) | Legacy publication-specific design guide. Prefer `mantle:theme` for generated repos. |
|
|
13
|
-
| [`extend`](extend/SKILL.md) | Legacy atom-authoring guide. Prefer `mantle:develop` or `mantle:plugin` depending on whether the work is one-off or installable. |
|
|
14
12
|
| [`provision`](provision/SKILL.md) | User wants a local or landing-generated project shipped to Cloudflare with production auth and operator handoff. |
|
|
15
13
|
|
|
16
|
-
The skills target
|
|
17
|
-
|
|
18
|
-
or update the existing one.
|
|
14
|
+
The skills target Mantle's v0.1 grammar. The installed package version, not
|
|
15
|
+
duplicated skill prose, selects the exact runtime and embedded docs.
|
|
19
16
|
|
|
20
17
|
## Skill authority
|
|
21
18
|
|
|
22
|
-
The `mantle:*` namespace is owned by `@aotter/mantle`.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
competing
|
|
19
|
+
The `mantle:*` namespace is owned by `@aotter/mantle`. Run `mantle skills` to
|
|
20
|
+
project the installed package's `develop`, `plugin`, `theme`, and `update`
|
|
21
|
+
skills to identical `.agent` and `.claude` paths; use `mantle skills --check`
|
|
22
|
+
to fail closed on drift. The installed package and
|
|
23
|
+
`node_modules/@aotter/mantle/docs/` are the single version-matched authority.
|
|
24
|
+
Starter launch files and plugin recipes are project context, not competing
|
|
25
|
+
contracts.
|
|
28
26
|
|
|
29
|
-
##
|
|
27
|
+
## Source-repository marketplace install
|
|
30
28
|
|
|
31
|
-
The
|
|
29
|
+
The source repository is also an agent plugin bundle. These manifests are not
|
|
30
|
+
duplicated into the npm package:
|
|
32
31
|
|
|
33
32
|
- Claude Code: `.claude-plugin/plugin.json` plus `.claude-plugin/marketplace.json`.
|
|
34
33
|
- Codex: `.codex-plugin/plugin.json` plus `.agents/plugins/marketplace.json`.
|
|
@@ -37,11 +36,14 @@ The repo is also an agent plugin bundle:
|
|
|
37
36
|
|
|
38
37
|
## Audience
|
|
39
38
|
|
|
40
|
-
These are written for **AI agents acting on behalf of consumers of mantle**,
|
|
39
|
+
These are written for **AI agents acting on behalf of consumers of mantle**,
|
|
40
|
+
not for agents maintaining the Mantle SDK itself. SDK maintainers use the
|
|
41
|
+
repo-root `CLAUDE.md` from a source checkout; it is intentionally not shipped
|
|
42
|
+
inside the npm package. Two audiences, two artifacts.
|
|
41
43
|
|
|
42
44
|
## Discoverability
|
|
43
45
|
|
|
44
|
-
The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "
|
|
46
|
+
The skills target ADR-0007's "AI as primary author" thesis: agents reach these files by URL when the user invokes them by intent ("install mantle", "develop my Mantle site", "deploy"). No `/skill install` slash command is required — point the agent at a version tag or pass the version-matched markdown content directly.
|
|
45
47
|
|
|
46
48
|
## Conventions
|
|
47
49
|
|
|
@@ -56,4 +58,6 @@ Each SKILL.md ships:
|
|
|
56
58
|
- **Don't** — reviewer-style list of patterns the agent must reject (often citing ADRs).
|
|
57
59
|
- **When you're done** — what to report back to the user.
|
|
58
60
|
|
|
59
|
-
If you're writing a new SKILL, follow the same structure.
|
|
61
|
+
If you're writing a new SKILL, follow the same structure. Commands and prose
|
|
62
|
+
must match the package version that carries the skill; later prereleases may
|
|
63
|
+
revise both together.
|
package/skills/develop/SKILL.md
CHANGED
|
@@ -4,14 +4,14 @@ description: Work on any Mantle project using the Core SDK contract. Use for man
|
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: skills/develop/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Mantle Develop
|
|
11
11
|
|
|
12
|
-
This is the Core workflow skill for an existing Mantle project.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
This is the Core workflow skill for an existing Mantle project. Repo-local
|
|
13
|
+
copies are byte-for-byte projections from the installed package; its embedded
|
|
14
|
+
docs govern runtime/API behavior.
|
|
15
15
|
|
|
16
16
|
## First Read
|
|
17
17
|
|
package/skills/install/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Start a new Mantle site locally from a deterministic starter bundle
|
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: skills/install/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Mantle Install
|
|
@@ -75,6 +75,8 @@ the generated bundle JSON.
|
|
|
75
75
|
cd <target-dir>
|
|
76
76
|
git init -b main
|
|
77
77
|
pnpm install --frozen-lockfile
|
|
78
|
+
pnpm exec mantle skills
|
|
79
|
+
pnpm exec mantle skills --check
|
|
78
80
|
pnpm validate
|
|
79
81
|
pnpm typecheck
|
|
80
82
|
pnpm dev
|
|
@@ -93,6 +95,18 @@ Read these before editing:
|
|
|
93
95
|
1. `.mantle/launch-state.json`, `.mantle/features.json`, and
|
|
94
96
|
`.mantle/handoff.md`.
|
|
95
97
|
2. `package.json` for the installed `@aotter/mantle*` versions.
|
|
98
|
+
|
|
99
|
+
Install the locked dependency graph and replace any stale projected Core
|
|
100
|
+
skills before reading them:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pnpm install --frozen-lockfile
|
|
104
|
+
pnpm exec mantle skills
|
|
105
|
+
pnpm exec mantle skills --check
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then read:
|
|
109
|
+
|
|
96
110
|
3. Repo-local Mantle skills under `.agent/skills/` or `.claude/skills/`.
|
|
97
111
|
4. Matching embedded docs under `node_modules/@aotter/mantle/docs/`.
|
|
98
112
|
|
|
@@ -105,7 +119,7 @@ live URL, and auth response, then skip work that is already complete.
|
|
|
105
119
|
Then run:
|
|
106
120
|
|
|
107
121
|
```bash
|
|
108
|
-
pnpm
|
|
122
|
+
pnpm exec mantle skills --check
|
|
109
123
|
pnpm validate
|
|
110
124
|
pnpm typecheck
|
|
111
125
|
```
|
package/skills/plugin/SKILL.md
CHANGED
package/skills/theme/SKILL.md
CHANGED
|
@@ -4,31 +4,33 @@ description: Apply brand and visual direction in a generated Mantle project usin
|
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: skills/theme/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Mantle Theme
|
|
11
11
|
|
|
12
|
-
Theme work is project-owned source editing. Starters may ship
|
|
12
|
+
Theme work is project-owned source editing. Starters may ship UI files,
|
|
13
13
|
tokens, or recipes, but the skill contract is Core-owned.
|
|
14
14
|
|
|
15
15
|
## First Read
|
|
16
16
|
|
|
17
17
|
1. `.mantle/handoff.md` and `.mantle/recipes/` if present.
|
|
18
|
-
2. `styles/`, `components/`, `src/web/`, `src/theme*`, and
|
|
18
|
+
2. `styles/`, `components/`, `src/web/`, `src/theme*`, and UI-library config
|
|
19
19
|
if present.
|
|
20
|
-
3.
|
|
20
|
+
3. A vendored UI palette's manifest and license, if present.
|
|
21
21
|
4. `manifests/` to understand which content shape drives the public UI.
|
|
22
22
|
|
|
23
23
|
## Ownership
|
|
24
24
|
|
|
25
25
|
- `styles/globals.css` is the token contract. Start a whole-site reskin with
|
|
26
|
-
its `:root` and `.dark` values;
|
|
27
|
-
- `
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
its `:root` and `.dark` values; runtime components inherit those variables.
|
|
27
|
+
- `components/` is the runtime-facing component surface when present.
|
|
28
|
+
`src/web/` is project-owned composition; put new sections there.
|
|
29
|
+
- If the project includes a vendored UI reference palette, treat it as
|
|
30
|
+
offline source material and provenance, not runtime source. Copy only a
|
|
31
|
+
needed primitive or block into the project's runtime directories, or
|
|
32
|
+
fork/wrap it under `src/web/sections/`; do not import the palette from
|
|
33
|
+
Worker or runtime code.
|
|
32
34
|
|
|
33
35
|
## Work
|
|
34
36
|
|
package/skills/update/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Check a Mantle project for drift against its Core SDK, starter sour
|
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: skills/update/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
7
|
+
applies_to: mantle grammar v0.1
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Mantle Update
|
|
@@ -19,23 +19,37 @@ Use this for drift checks. Do not blindly overwrite user-owned code.
|
|
|
19
19
|
3. `.mantle/plugins.json` and `.mantle/plugins.lock.json` when plugins are
|
|
20
20
|
installed.
|
|
21
21
|
4. Existing project scripts such as `mantle:update`, `validate`, and
|
|
22
|
-
`typecheck
|
|
22
|
+
`typecheck`; the installed `mantle update` command is authoritative.
|
|
23
23
|
|
|
24
24
|
## Workflow
|
|
25
25
|
|
|
26
26
|
1. Start from a clean git worktree.
|
|
27
27
|
2. Resolve a mutable target branch to its current commit SHA, then run the
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
installed command with that immutable ref:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm exec mantle update --ref <immutable-ref>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For the one-time alpha.63 bridge, invoke the exact newer Core package and
|
|
35
|
+
provide the current bundle location because alpha.63 metadata does not
|
|
36
|
+
contain it:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm dlx @aotter/mantle@<exact-version> update \
|
|
40
|
+
--ref <immutable-ref> \
|
|
41
|
+
--bundle-base-url 'https://raw.githubusercontent.com/aotter/mantle-starters/{ref}/provision-bundles'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Do not extract or replace a repo-local updater. The Core command accepts
|
|
45
|
+
the alpha.63 no-`v` source ref, writes only the report, and records the
|
|
46
|
+
versioned bundle location for the reviewed metadata migration.
|
|
35
47
|
3. Read the generated report before editing.
|
|
36
48
|
4. Triage each path; do not treat the report as a patch or merge plan.
|
|
37
49
|
5. Port confirmed upstream changes one hunk at a time.
|
|
38
|
-
6.
|
|
50
|
+
6. Apply only the report's `.mantle/launch-state.json` and
|
|
51
|
+
`.mantle/features.json` metadata migration, preserving every other field.
|
|
52
|
+
7. Re-run:
|
|
39
53
|
|
|
40
54
|
```bash
|
|
41
55
|
pnpm validate
|
|
@@ -49,15 +63,13 @@ ref, and the local project.
|
|
|
49
63
|
|
|
50
64
|
- Review `upstream` to find starter changes worth porting. Use `local` only to
|
|
51
65
|
understand project-owned drift from the original starter.
|
|
52
|
-
- Never copy generated comparison versions of `
|
|
53
|
-
`.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
stop: the comparator is stale or incompatible. Use the target updater
|
|
60
|
-
recovery in step 2, then regenerate the report.
|
|
66
|
+
- Never copy generated comparison versions of `.mantle/launch-state.json` or
|
|
67
|
+
`.mantle/features.json`; the report omits them and gives a field-level
|
|
68
|
+
migration instead. Preserve Worker/D1 names, bindings, origins, provider
|
|
69
|
+
values, and all unlisted launch state.
|
|
70
|
+
- The updater reproduces project identity in legacy Wrangler files. If
|
|
71
|
+
`upstream` proposes `mantle-<type>` names for a real project, stop: the
|
|
72
|
+
bundle is incompatible and must not be ported.
|
|
61
73
|
- A large `local` section is normal after customization. Counts are not a
|
|
62
74
|
confidence score.
|
|
63
75
|
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: customize-design
|
|
3
|
-
description: Layer custom design over a mantle publication starter project using the L1–L4 theme stack (tokens / extraCss+icons+i18n / Header+Footer+PageShell slots / whole-template fork). Use when the user wants to rebrand, restyle, or swap UI pieces without forking the whole starter.
|
|
4
|
-
metadata:
|
|
5
|
-
source: "@aotter/mantle"
|
|
6
|
-
sourcePath: skills/customize-design/SKILL.md
|
|
7
|
-
applies_to: mantle@v0.1.0 + publication archetype
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Customize the design of a mantle publication site
|
|
11
|
-
|
|
12
|
-
You are layering a consumer theme over a project built from the `publication` starter. The baseline lives at `src/theme.default/` (read-only by convention). Consumer overrides live at `src/theme/`. Always escalate from L1 → L4 and stop at the lowest layer that solves the user's stated need.
|
|
13
|
-
|
|
14
|
-
## Layer cheatsheet
|
|
15
|
-
|
|
16
|
-
| Layer | Where | Use for |
|
|
17
|
-
|---|---|---|
|
|
18
|
-
| **L1** tokens | `src/theme/tokens.ts` | palette, font stack, type scale, measure, gutter |
|
|
19
|
-
| **L2** extraCss | `src/theme/index.ts:extraCss` | extra CSS rules (border-radius, hero size, etc.) |
|
|
20
|
-
| **L2** icons | `src/theme/icons.ts` | replace baseline icons by name; add new ones |
|
|
21
|
-
| **L2** i18n | `src/theme/i18n/<locale>.json` | retitle UI strings per locale (deep-merge) |
|
|
22
|
-
| **L3** components — chrome | `src/theme/components/{Header,Footer}.tsx` | swap navigation chrome |
|
|
23
|
-
| **L3** components — body layout | `src/theme/components/PageShell.tsx` | reshape body layout: sidebar variants, sticky CTAs, full-bleed hero, alternative Header / `<main>` / Footer arrangement |
|
|
24
|
-
| **L4** templates | `src/theme/templates/<name>.tsx` | replace a page kind end-to-end |
|
|
25
|
-
|
|
26
|
-
## Conversation pattern
|
|
27
|
-
|
|
28
|
-
1. **Map the user's intent to a layer.** "Change the colors" → L1. "Different header structure" → L3. Tell the user the layer + the escape hatch ("I'll start at L1; if it doesn't get close enough, we can escalate to a custom Header at L3").
|
|
29
|
-
2. **Fork → edit → review.** Run `pnpm theme:fork <path>`, edit the new file in `src/theme/`, ask the user to reload `pnpm dev`.
|
|
30
|
-
3. **On dissatisfaction, iterate or revert.** `pnpm theme:reset <path>` removes the override and restores the baseline. The user can roll back any single layer without affecting others.
|
|
31
|
-
|
|
32
|
-
## Layer recipes
|
|
33
|
-
|
|
34
|
-
### L1 — tokens
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pnpm theme:fork tokens.ts
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Edit `src/theme/tokens.ts`:
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
export const TOKENS_CSS = `
|
|
44
|
-
:root {
|
|
45
|
-
--paper: #fffbf3;
|
|
46
|
-
--ink: #1a1814;
|
|
47
|
-
--accent: #a3331f;
|
|
48
|
-
--font-display: "Fraunces", Georgia, serif;
|
|
49
|
-
--font-body: "Source Serif 4", Georgia, serif;
|
|
50
|
-
--measure: 36rem;
|
|
51
|
-
}
|
|
52
|
-
[data-theme="dark"] {
|
|
53
|
-
--paper: #1a1814;
|
|
54
|
-
--ink: #f1ebdf;
|
|
55
|
-
--accent: #e6594a;
|
|
56
|
-
}
|
|
57
|
-
`;
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
The override is concatenated AFTER baseline tokens, so later declarations win on standard CSS specificity. Only redeclare the vars you want to change.
|
|
61
|
-
|
|
62
|
-
**Custom web fonts**: if `--font-display` references a font not in the system stack, register it with L2 `extraCss` using `@font-face`. Don't use CSS `@import`: `extraCss` is appended after baseline rules, and browsers ignore late `@import` statements.
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
const overrides: ThemeOverride = {
|
|
66
|
-
extraCss: `
|
|
67
|
-
@font-face {
|
|
68
|
-
font-family: "FrauncesLocal";
|
|
69
|
-
src: url("/fonts/fraunces.woff2") format("woff2");
|
|
70
|
-
font-weight: 400 700;
|
|
71
|
-
font-display: swap;
|
|
72
|
-
}
|
|
73
|
-
`,
|
|
74
|
-
tokens: `:root { --font-display: "FrauncesLocal", Georgia, serif; }`,
|
|
75
|
-
};
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Revert: `pnpm theme:reset tokens.ts`.
|
|
79
|
-
|
|
80
|
-
### L2 — extraCss
|
|
81
|
-
|
|
82
|
-
Edit `src/theme/index.ts` — set the `extraCss` field directly (no fork needed, it's just a string):
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
const overrides: ThemeOverride = {
|
|
86
|
-
extraCss: `
|
|
87
|
-
.site-main { max-width: 64rem; }
|
|
88
|
-
.post-cover { border-radius: 8px; }
|
|
89
|
-
blockquote { background: var(--rule); padding: 1rem; }
|
|
90
|
-
`,
|
|
91
|
-
};
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Revert: clear the field.
|
|
95
|
-
|
|
96
|
-
### L2 — icons
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
pnpm theme:fork icons.ts
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Edit `src/theme/icons.ts`:
|
|
103
|
-
|
|
104
|
-
```ts
|
|
105
|
-
const customIcons: Record<string, string> = {
|
|
106
|
-
// override an existing baseline icon
|
|
107
|
-
globe: '<circle cx="12" cy="12" r="9"/><path d="..."/>',
|
|
108
|
-
// add a new icon
|
|
109
|
-
logo: '<path d="M12 2L2 7v10l10 5 10-5V7l-10-5z"/>',
|
|
110
|
-
};
|
|
111
|
-
export default customIcons;
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Use in any template via `icon("logo", { size: 24 })`. SVG path content only — no `<svg>` wrapper. The fork ships a stub (not a copy of the baseline), since you're extending a registry.
|
|
115
|
-
|
|
116
|
-
Revert: `pnpm theme:reset icons.ts`.
|
|
117
|
-
|
|
118
|
-
### L2 — i18n
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
pnpm theme:fork i18n/en.json
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Edit `src/theme/i18n/en.json` — partial bundle, deep-merged over baseline:
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
{
|
|
128
|
-
"header": { "posts": "Articles" },
|
|
129
|
-
"home": { "eyebrow": "the dispatch" },
|
|
130
|
-
"notFound": { "title": "Lost at sea" }
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Other keys carry forward from baseline. To support a new locale (`ja`, `de`, etc.), edit `src/i18n/<locale>.json` directly — locale set is consumer-level, not a theme slot.
|
|
135
|
-
|
|
136
|
-
Revert: `pnpm theme:reset i18n/en.json`.
|
|
137
|
-
|
|
138
|
-
### L3 — Header / Footer
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
pnpm theme:fork components/Header.tsx
|
|
142
|
-
pnpm theme:fork components/Footer.tsx
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Use this when the user wants different chrome — different brand mark, nav arrangement, language switcher, footer copy — but the page body still reads top → main → bottom. Key contracts:
|
|
146
|
-
|
|
147
|
-
- Don't change the props signature: `Header(props: HeaderProps)`.
|
|
148
|
-
- `props.site.brand`, `props.site.locales`, `props.locale`, `props.current` are available.
|
|
149
|
-
- Baseline siblings (icon registry, etc.) auto-rewritten on fork to `../../theme.default/<path>`. Keep those unless you want to drop the baseline icon set.
|
|
150
|
-
|
|
151
|
-
Same shape for `Footer`.
|
|
152
|
-
|
|
153
|
-
Revert: `pnpm theme:reset components/Header.tsx`.
|
|
154
|
-
|
|
155
|
-
### L3 — PageShell (body layout)
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
pnpm theme:fork components/PageShell.tsx
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Use this to reshape the **body layout** rather than swap chrome — for example:
|
|
162
|
-
|
|
163
|
-
- Sidebar with table-of-contents alongside `<main>` for docs-lite pages
|
|
164
|
-
- Sticky CTA bar between `<main>` and `<Footer>`
|
|
165
|
-
- Full-bleed hero section above Header on the home page
|
|
166
|
-
- Different Header / `<main>` / Footer ordering (e.g., side-rail logo)
|
|
167
|
-
- Custom `<main>` container width or padding rules per template kind
|
|
168
|
-
|
|
169
|
-
The forked PageShell takes ownership of how (or whether) to render the Header / Footer overrides. The baseline composes them top → main → bottom; a consumer-supplied PageShell can ignore or recompose them.
|
|
170
|
-
|
|
171
|
-
Revert: `pnpm theme:reset components/PageShell.tsx`.
|
|
172
|
-
|
|
173
|
-
### Layout is not forkable
|
|
174
|
-
|
|
175
|
-
Only `Header`, `Footer`, and `PageShell` are supported component slots. `theme:fork components/Layout.tsx` exits with a clear error pointing at PageShell as the body-layout escape hatch. Don't try to override Layout by hand-editing `src/theme/components/Layout.tsx` outside the fork machinery — the override surface won't register it.
|
|
176
|
-
|
|
177
|
-
`Layout` (the document envelope — `<html>` / `<head>` / `<body>` + SEO meta + theme bootstrap) is locked. If the user needs to change `<head>` content beyond what tokens / extraCss can express, that crosses the starter-family line — switch starter rather than fork every template.
|
|
178
|
-
|
|
179
|
-
### L4 — whole template (escape hatch)
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
pnpm theme:fork templates/post.tsx
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Edit `src/theme/templates/post.tsx`. The forked file imports baseline Layout via `../../theme.default/components/Layout.js`.
|
|
186
|
-
|
|
187
|
-
L4 is the last resort. If the user wants more than two L4 forks, suggest the conversation: "The shape you're describing isn't really `publication` any more — it sounds closer to `community` (member posts), `micro-shop` (catalog + orders), or `leads-inbox` (lead pipeline). Once you cross those lines, switching starter family is cheaper than forking more templates."
|
|
188
|
-
|
|
189
|
-
Revert: `pnpm theme:reset templates/post.tsx`.
|
|
190
|
-
|
|
191
|
-
## Hard rules
|
|
192
|
-
|
|
193
|
-
- Don't edit `src/theme.default/`. Use fork.
|
|
194
|
-
- Don't add new top-level keys to `ThemeOverride` — extend through the existing slots.
|
|
195
|
-
- `Layout` is locked — change envelope shape via L4 forks (every template) or pick another starter.
|
|
196
|
-
- After a fork, the file is a normal `.ts` / `.tsx` / `.json` — TS errors surface on `pnpm typecheck` as usual.
|
|
197
|
-
|
|
198
|
-
## Diagnostic recipes
|
|
199
|
-
|
|
200
|
-
| Symptom | Cause | Fix |
|
|
201
|
-
|---|---|---|
|
|
202
|
-
| `pnpm theme:fork X` exits with "Override already exists" | Already forked | `pnpm theme:reset X` first |
|
|
203
|
-
| `pnpm typecheck` fails in `src/theme/components/<Name>.tsx` after fork | A baseline import didn't auto-rewrite | Manually change `from "../<sibling>"` to `from "../../theme.default/<sibling>"` |
|
|
204
|
-
| Color change not visible after edit | Browser CSS cache | Hard reload (Cmd+Shift+R / Ctrl+F5); if it persists, restart `pnpm dev` |
|
|
205
|
-
| Forked `en.json` discards keys I didn't redeclare | Bundle isn't deep-merging | Verify `src/i18n/index.ts:deepMerge` is present. If missing, update to latest starter |
|
|
206
|
-
|
|
207
|
-
## When you're done
|
|
208
|
-
|
|
209
|
-
Tell the user three things:
|
|
210
|
-
|
|
211
|
-
1. **Layer landed at** — "I made the change at L1 (tokens) — palette only, no structural edits."
|
|
212
|
-
2. **Revert command** — "If you don't like it, `pnpm theme:reset tokens.ts` rolls it back."
|
|
213
|
-
3. **Next escalation step** — "If the colors aren't enough, the next layer would be L3 — replace the Header component."
|
|
214
|
-
|
|
215
|
-
Stop after one layer per turn unless the user explicitly says "go deeper". The point of layering is to keep each customization step reversible and inspectable.
|