@the-smithy/create-hearth-site 0.24.1 → 0.26.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.
Files changed (60) hide show
  1. package/README.md +2 -1
  2. package/bin/create-hearth-site.js +1 -1
  3. package/package.json +4 -3
  4. package/template/AGENTS.md +191 -9
  5. package/template/CLAUDE.md +2 -124
  6. package/template/GEMINI.md +3 -0
  7. package/template/HOSTING.md +88 -0
  8. package/template/README.md +30 -29
  9. package/template/SITE.md +1 -1
  10. package/template/_gitignore +2 -1
  11. package/template/package.json +3 -2
  12. package/template/scripts/hearth.mjs +82 -31
  13. package/template/src/app/donate/thank-you/page.tsx +1 -1
  14. package/template/src/app/globals.css +1 -0
  15. package/template/src/app/layout.tsx +1 -1
  16. package/template/src/app/not-found.tsx +1 -1
  17. package/template/src/app/registered/page.tsx +1 -1
  18. package/template/src/hearth/base.css +66 -0
  19. package/template/src/hearth/blocks/collections.tsx +6 -5
  20. package/template/src/hearth/blocks/content.tsx +64 -28
  21. package/template/src/hearth/blocks/custom.tsx +9 -1
  22. package/template/src/hearth/blocks/events.tsx +25 -17
  23. package/template/src/hearth/blocks/giving.tsx +24 -20
  24. package/template/src/hearth/blocks/index.tsx +29 -4
  25. package/template/src/hearth/blocks/info.tsx +15 -13
  26. package/template/src/hearth/blocks/media.tsx +10 -9
  27. package/template/src/hearth/components/EventView.tsx +61 -29
  28. package/template/src/hearth/components/FundraiserView.tsx +77 -0
  29. package/template/src/hearth/components/NotHere.tsx +9 -0
  30. package/template/src/hearth/components/PageContent.tsx +23 -0
  31. package/template/src/hearth/components/Registration.tsx +3 -2
  32. package/template/src/hearth/components/Share.tsx +11 -11
  33. package/template/src/hearth/format.ts +5 -3
  34. package/template/src/hearth/forms/ContactForm.tsx +5 -3
  35. package/template/src/hearth/forms/DonateForm.tsx +49 -32
  36. package/template/src/hearth/forms/DonationForm.tsx +13 -0
  37. package/template/src/hearth/forms/RegistrationForm.tsx +16 -14
  38. package/template/src/hearth/forms/VolunteerForm.tsx +10 -8
  39. package/template/src/hearth/forms/fields.tsx +17 -5
  40. package/template/src/hearth/hearth.css +3 -47
  41. package/template/src/hearth/layout/MobileNav.tsx +1 -1
  42. package/template/src/hearth/layout/NavLink.tsx +6 -2
  43. package/template/src/hearth/layout/RootLayout.tsx +8 -32
  44. package/template/src/hearth/layout/SiteFooter.tsx +13 -6
  45. package/template/src/hearth/layout/SiteFrame.tsx +48 -0
  46. package/template/src/hearth/layout/SiteHeader.tsx +9 -4
  47. package/template/src/hearth/manifest.json +3 -1
  48. package/template/src/hearth/overrides.ts +48 -0
  49. package/template/src/hearth/pages/donate.tsx +24 -15
  50. package/template/src/hearth/pages/event.tsx +3 -2
  51. package/template/src/hearth/pages/events.tsx +22 -10
  52. package/template/src/hearth/pages/fundraiser.tsx +10 -63
  53. package/template/src/hearth/pages/fundraisers.tsx +21 -11
  54. package/template/src/hearth/pages/not-found.tsx +28 -8
  55. package/template/src/hearth/pages/page.tsx +9 -15
  56. package/template/src/hearth/pages/registered.tsx +23 -10
  57. package/template/src/hearth/pages/thank-you.tsx +23 -10
  58. package/template/src/hearth/theme/defaults.ts +125 -2
  59. package/template/src/hearth/ui.tsx +20 -5
  60. package/template/src/theme/overrides.tsx +19 -5
package/README.md CHANGED
@@ -39,7 +39,8 @@ cd acme-website
39
39
  ```
40
40
 
41
41
  Add an API key to `.env.local` (Hearth → the site → API & webhooks → API keys), and run `pnpm dev`. The
42
- new site's README walks through the first run, previews, deploying and troubleshooting.
42
+ new site's README walks through the first run, previews and troubleshooting. Its `HOSTING.md` covers deploying to
43
+ Netlify or Vercel, and its `AGENTS.md` holds the instructions for coding agents.
43
44
 
44
45
  ## Documentation
45
46
 
@@ -19,7 +19,7 @@ const self = JSON.parse(await readFile(path.join(here, "..", "package.json"), "u
19
19
 
20
20
  /** Files that exist in the template folder during development but never belong in a new site. */
21
21
  // The template's lockfile is the Hearth workspace's (the SDK as a workspace link); a new site makes its own.
22
- const SKIP = new Set(["node_modules", ".next", ".netlify", ".env.local", "next-env.d.ts", "tsconfig.tsbuildinfo", "pnpm-lock.yaml"]);
22
+ const SKIP = new Set(["node_modules", ".next", ".netlify", ".vercel", ".env.local", "next-env.d.ts", "tsconfig.tsbuildinfo", "pnpm-lock.yaml"]);
23
23
  /** Dotfiles npm won't publish (or that would confuse the monorepo) are stored with a leading underscore. */
24
24
  const RENAMES = { _gitignore: ".gitignore", "_pnpm-workspace.yaml": "pnpm-workspace.yaml" };
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the-smithy/create-hearth-site",
3
- "version": "0.24.1",
3
+ "version": "0.26.0",
4
4
  "description": "Start a new client website powered by Hearth: pnpm create @the-smithy/hearth-site",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -21,6 +21,7 @@
21
21
  "!template/node_modules",
22
22
  "!template/.next",
23
23
  "!template/.netlify",
24
+ "!template/.vercel",
24
25
  "!template/.env.local",
25
26
  "!template/next-env.d.ts",
26
27
  "!template/tsconfig.tsbuildinfo",
@@ -29,9 +30,9 @@
29
30
  ],
30
31
  "devDependencies": {
31
32
  "vitest": "^5.0.3",
32
- "@the-smithy/hearth-schema": "^0.24.1"
33
+ "@the-smithy/hearth-schema": "^0.26.0"
33
34
  },
34
35
  "scripts": {
35
- "test": "node --test \"test/**/*.test.js\" && vitest run test/blocks.test.ts"
36
+ "test": "node --test \"test/**/*.test.js\" && vitest run test/blocks.test.ts test/overrides.test.ts"
36
37
  }
37
38
  }
@@ -1,13 +1,195 @@
1
1
  # Notes for coding agents
2
2
 
3
- Read `CLAUDE.md` in this folder: it holds this project's instructions for every AI coding assistant, then `SITE.md`
4
- for notes about this site.
3
+ This is a client website whose content comes from Hearth, The Smithy's headless CMS. This file is Hearth's and is
4
+ replaced by `pnpm hearth:update`: notes about this particular site go in `SITE.md`, which you should read too.
5
+ `CLAUDE.md` and `GEMINI.md` only point here, for the assistants that look for those names.
5
6
 
6
- In short: Hearth's code (`src/hearth/`, and the files `src/hearth/manifest.json` lists) is shared by every site and
7
- replaced by `pnpm hearth:update`, so never edit it. A site is styled in `src/theme/`: tokens in `theme.css`, and the
8
- Tailwind classes of each element's slot in `index.ts` (every slot and its default is in
9
- `src/hearth/theme/defaults.ts`).
7
+ ## How this site is built: Hearth's code, the site's styling
10
8
 
11
- In a new checkout, run `pnpm hearth:check`, then `pnpm hearth:docs`. The latter also installs the skills Hearth
12
- offers this site in `.claude/skills/`. Read the `SKILL.md` of any that fits your task and follow it (see "Skills" in
13
- `CLAUDE.md`).
9
+ Everything that works with Hearth is already here, and it's the same in every site Hearth runs: every block type,
10
+ the event, fundraiser and donation pages, forms (contact, volunteer sign-up, registration, donations), the
11
+ header and phone menu, the footer, previews, SEO, analytics, redirects and the sitemap. **A site is built by styling
12
+ it, not by writing code.** It has two parts:
13
+
14
+ | Folder | Whose | What's in it |
15
+ | -------------- | ---------- | ---------------------------------------------------------------------------------------------- |
16
+ | `src/hearth/` | Hearth's | All the components and pages, and the theme's slots and defaults. **Never edit it.** |
17
+ | `src/theme/` | The site's | Its look: tokens, slot classes, fonts, wording, custom blocks. **All design work goes here.** |
18
+
19
+ `src/app/` holds one-line route files that point at `src/hearth/pages/`; they're Hearth's too (see
20
+ `src/hearth/manifest.json` for every file Hearth manages). Pages the site needs that Hearth doesn't serve can be
21
+ added in `src/app/` as the site's own.
22
+
23
+ `pnpm hearth:update` brings in a new release of Hearth's code: it replaces the managed files and leaves `src/theme`
24
+ and the site's own files alone. That's how every site gets new blocks and fixes at once, so an edit to a managed file
25
+ is lost at the next update, and stops the update (it refuses to overwrite uncommitted changes there).
26
+
27
+ ## Styling a site
28
+
29
+ Work in this order, and stop at the first step that gets the result:
30
+
31
+ 1. **Tokens** in `src/theme/theme.css`: colors, fonts, content width. Every slot uses these names (`bg-accent`,
32
+ `text-highlight`, `bg-muted`, `border-field`…), so most of a brand is set here. Hearth's tones (default, muted,
33
+ inverse, accent) map onto them.
34
+ 2. **Fonts** in `src/theme/fonts.ts` (next/font), used from the tokens.
35
+ 3. **Slots** in `src/theme/index.ts`. Every element Hearth's code draws takes its Tailwind classes from one slot.
36
+ `src/hearth/theme/defaults.ts` lists every part (`hero`, `cardGrid`, `header`, `form`, `event`…), every slot
37
+ and its default classes: read it before styling. Set only the slots the design changes:
38
+
39
+ ```ts
40
+ export const theme = defineTheme({
41
+ hero: { title: "font-display text-7xl uppercase", inner: (base) => `${base} text-center` },
42
+ button: { primary: "inline-block bg-accent px-6 py-3 font-mono uppercase text-accent-ink hover:bg-ink" },
43
+ stats: { tone: "accent" },
44
+ });
45
+ ```
46
+
47
+ - A string **replaces** the slot's default; a function **extends** it (`(base) => …`). Replace when you change a
48
+ property the default already sets (a radius, a size), since two classes for the same property don't reliably
49
+ resolve in the order written.
50
+ - Each slot styles one element, so slots never fight. States are attributes on the element, so they're plain
51
+ Tailwind variants in the slot: `on-dark:` (on an inverse or accent ground), `data-[size=full]:`,
52
+ `data-highlight:`, `aria-invalid:`, `aria-[current=page]:`, `has-checked:`, `in-data-[…]:` and others; the top
53
+ of `defaults.ts` lists them and each slot's comment says which apply.
54
+ - Slots named `tone` pick a block's ground (default, muted, inverse or accent). `text` holds the words the code
55
+ shows ("Get directions", "Register"…), with `{name}` for what the code fills in. `map.style` is the map's MapLibre
56
+ style; `emberForm` shapes Ember forms. A few slots in `page` are settings: `themeColor` (the browser's color around
57
+ the page), `externalLinks` ("new-tab"), `navMatch` ("exact" for menu links) and `dateStyle` ("long" for event dates).
58
+ - Icons and marks drawn with CSS (`before:content-['✓']`, the FAQ's `+`, link arrows) live in slots too. A card's
59
+ Icon in Hearth arrives as `data-icon="graduation-cap"`, for the site's CSS to draw.
60
+ 4. **Site CSS** under the tokens in `theme.css`: keyframes, background patterns, `@utility` classes your slots use,
61
+ and rules for Hearth's rich text (`.prose`), a block (`[data-block="hero"]`), a page the code serves
62
+ (`[data-page="events"]`, so the same blocks elsewhere stay as they are) or one card (`[data-card="our-history"]`, from
63
+ its title).
64
+ 5. **A page in Hearth** for words the code draws on its own pages: see "Pages at the code's addresses" below.
65
+ 6. **Overrides**, last: `src/theme/overrides.tsx` (see "Overrides" below). An override is the site's own code: updates
66
+ don't reach it, so it falls behind Hearth. Use one only when everything above can't get there, build it from
67
+ `src/hearth/ui.tsx` and the theme, and say why in `SITE.md`.
68
+
69
+ Never copy a component out of `src/hearth/` to restyle it. If a design needs a slot or an option Hearth doesn't have,
70
+ say so: it belongs in Hearth's template for every site (tell the developer to raise it with The Smithy), and an
71
+ override can bridge the gap meanwhile.
72
+
73
+ ### Pages at the code's addresses
74
+
75
+ `/events`, `/fundraisers`, `/donate`, `/donate/thank-you`, `/registered` and the 404 are served by Hearth's code, with
76
+ default words. A published page in Hearth at that address (`events`, `donate/thank-you`, `not-found` for the 404…)
77
+ replaces those words: its blocks, its SEO and its structured data. **So a page's text never needs its own route or an
78
+ override: make the page in Hearth.** `/events` and the fundraiser and donation addresses still need the site's Events
79
+ or Giving feature. The site's `reserved_slugs` in Hearth mustn't list these, since that refuses the page.
80
+
81
+ The thank-you pages and the 404 are never indexable and never in the sitemap or llms.txt. The 404 always answers 404,
82
+ never serves at `/not-found`, and redirects run before it.
83
+
84
+ ### Overrides
85
+
86
+ Everything is optional. A site's file only exports what it uses, and an update never replaces it. They can import
87
+ Hearth's components, `Blocks` too. The types are in `src/hearth/overrides.ts`. They're used everywhere the template draws
88
+ these things:
89
+
90
+ | Export | Replaces | Given |
91
+ | --- | --- | --- |
92
+ | `blocks` | A whole block's component, by type | `{ block }`. An anchor from Hearth keeps working around it |
93
+ | `Logo` | The logo in the header, and in the footer once the `footer.logo` slot has classes | `{ site, place }` |
94
+ | `Footer` | The footer | `{ site }` |
95
+ | `EventPage` | An event's page, at `/events/…` and in collections, so a site needs no route of its own | `{ event }` |
96
+ | `FundraiserPage` | A fundraiser's page | `{ fundraiser }` |
97
+ | `EventCard`, `FundraiserCard` | The cards in lists; each draws an `<li>`, and may be async | `{ event }` or `{ fundraiser }`, and `heading`: `"h2"` or `"h3"`, for its title |
98
+ | `DonateForm` | The donation form | The amounts, `fundraiser`, `fundraisers` (active ones to choose from) and `submitLabel` |
99
+
100
+ `EventPage` and `FundraiserPage` have their structured data drawn around them. To build on Hearth's: `EventContent`,
101
+ `EventRegisterSection` and `EventVolunteerSection` from `components/EventView.tsx`, `FundraiserContent` from
102
+ `components/FundraiserView.tsx`. A site's own routes are for pages Hearth has no address for, not for an event's or
103
+ a fundraiser's page.
104
+
105
+ `own` in the manifest (managed files `pnpm hearth:update` leaves alone) is for a site inside an app, with its own root
106
+ layout, not for keeping a page: it stops that file getting fixes. Such a site imports `src/hearth/hearth.css` alone in
107
+ its own CSS, and `src/hearth/base.css` if it wants Hearth's rich text and focus rings.
108
+
109
+ **Custom blocks** (Settings → Custom blocks in Hearth) are the one place a site writes components: in
110
+ `src/theme/custom-blocks.tsx`, built from `src/hearth/ui.tsx` so they take the theme.
111
+
112
+ ## Staying on the template and up to date
113
+
114
+ Every site Hearth runs has the same code, so a fix or a new block reaches them all at once. That only holds while
115
+ each site stays on the template and on current releases:
116
+
117
+ - **Check for new releases from The Smithy at the start of every piece of work.** `pnpm hearth:check` lists every
118
+ `@the-smithy/*` package and Hearth's code in this site with a newer release on npm, even with no API key. Tell the
119
+ developer what it found.
120
+ - **Update before you build on old code.** `pnpm hearth:update` brings Hearth's code and SDK to the latest release;
121
+ `pnpm add @the-smithy/<name>@latest` does any other Smithy package, such as the Ember SDK. Read the package's
122
+ `CHANGELOG.md`, run the checks below, look at the site, and commit the update on its own, with
123
+ `pnpm-workspace.yaml` if pnpm added the new versions to it.
124
+ - **Don't drift.** A design change goes in `src/theme/`. Don't copy a component out of `src/hearth/`, replace a route
125
+ file, or write a site's own data layer, SEO or forms. A bug or a gap in Hearth's code gets fixed in Hearth's
126
+ template and reaches the site by an update; say so to the developer, and if it can't wait, bridge it with an override
127
+ and note it in `SITE.md`.
128
+ - **After an update, look through `SITE.md`'s overrides** and compare each with the component it replaced in
129
+ `src/hearth/blocks`, since updates don't reach them. Delete any that the theme can now do.
130
+
131
+ ## First steps in a new checkout
132
+
133
+ Do these before writing code, and tell the developer what you found:
134
+
135
+ 1. **Check the setup:** `pnpm hearth:check`. It reports any newer releases from The Smithy (see above), reads
136
+ `.env.local`, connects to Hearth, lists the published pages and the block types they use, and flags types with no
137
+ component. It never prints the API key; you shouldn't either, so don't open `.env.local` to read the key or echo
138
+ it. If it fails, its message says what to fix (missing key, wrong `HEARTH_SITE`, revoked key). Ask the developer to
139
+ fix `.env.local`; don't guess values.
140
+ 2. **Get the full reference:** `pnpm hearth:docs` saves Hearth's API guide (every block and its fields, events,
141
+ forms, previews, SEO) to `hearth-api.md`. Read the parts you need from it, and from
142
+ `node_modules/@the-smithy/hearth/llms.md` for the SDK. It also installs any skills Hearth offers this site (see
143
+ "Skills" below), so run it again when they're missing. These aren't committed, so some assistants' search skips
144
+ them: open them by their exact path, or from the terminal if your tool won't.
145
+ 3. **"Nothing is published"** means every page is a 404 until editors publish in Hearth. That's not a code
146
+ problem: say so, and don't add placeholder content to work around it.
147
+ 4. **Then style the site** as above, checking with `pnpm dev` against the published pages, on a phone width too.
148
+
149
+ ## Skills
150
+
151
+ `pnpm hearth:docs` installs the skills Hearth offers this site (which ones depends on the site) in `.agents/skills/`
152
+ and, as the same copies, in `.claude/skills/`, since Claude Code only reads that one. Each `SKILL.md` says what it's
153
+ for and when to use it. If your assistant doesn't load them by itself, read the ones that fit the task and follow them.
154
+ Use them for that work, design work in particular, rather than starting from scratch. They aren't committed (a
155
+ `.gitignore` in each folder lists them), so a fresh checkout gets them by running `pnpm hearth:docs`; never edit them,
156
+ because the next run replaces them.
157
+
158
+ ## Hosting
159
+
160
+ The site runs on Netlify or Vercel with no code changes: `HOSTING.md` has the steps and the settings for each. Don't
161
+ add code for one host, or an image optimizer; if a host seems to need one, say so to the developer.
162
+
163
+ ## Rules
164
+
165
+ - **Content comes from Hearth.** Don't hard-code page text, menus, contact details or SEO tags. They're edited
166
+ in Hearth and arrive through `src/hearth/client.ts`. The site's code owns design only.
167
+ - **The API key is secret.** Only import `@/hearth/client` from server components, route handlers and server
168
+ actions. Never from a `"use client"` file.
169
+ - **Use `hearthNext.*` for reads.** It caches and tags requests so the publish webhook can refresh them.
170
+ Calling `hearth.*` directly skips the cache.
171
+ - **Keep every option working.** Blocks have tones, image positions, sizes and optional fields; check a restyle
172
+ with each, on light and dark grounds, and check text contrast on every ground (form field borders need 3:1).
173
+ - **No bundled content.** Every page comes from Hearth; an address with no published page is a 404. Don't add
174
+ fallback or sample content to the code.
175
+ - **Previews, analytics and forms come from Hearth's code.** Don't build a preview banner, add Google Analytics or
176
+ other trackers (Hearth's analytics is cookieless; marketing tags need a consent banner and are a separate, opt-in
177
+ feature), or post forms anywhere else. Use `track("name")` from `@the-smithy/hearth/analytics` in the site's own
178
+ code for meaningful actions.
179
+
180
+ ## Checks
181
+
182
+ `pnpm typecheck` and `pnpm build`. The build needs the `.env.local` keys, because it reads content from Hearth.
183
+ `pnpm hearth:check` shows any block type the published content uses without a component (run `pnpm hearth:update`).
184
+ After `pnpm hearth:update`: `pnpm install`, then the checks; a type error in `src/theme/index.ts` means a slot was
185
+ renamed or removed (see `defaults.ts`).
186
+
187
+ <!-- BEGIN:nextjs-agent-rules -->
188
+
189
+ # This is NOT the Next.js you know
190
+
191
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
192
+
193
+ This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
194
+
195
+ <!-- END:nextjs-agent-rules -->
@@ -1,125 +1,3 @@
1
- # Notes for Claude
1
+ The instructions for coding agents in this project are in `AGENTS.md`:
2
2
 
3
- This is a client website whose content comes from Hearth, The Smithy's headless CMS. This file is Hearth's and is
4
- replaced by `pnpm hearth:update`: notes about this particular site go in `SITE.md`, which you should read too.
5
-
6
- ## How this site is built: Hearth's code, the site's styling
7
-
8
- Everything that works with Hearth is already here, and it's the same in every site Hearth runs: every block type,
9
- the event, fundraiser and donation pages, forms (contact, volunteer sign-up, registration, donations), the
10
- header and phone menu, the footer, previews, SEO, analytics, redirects and the sitemap. **A site is built by styling
11
- it, not by writing code.** It has two parts:
12
-
13
- | Folder | Whose | What's in it |
14
- | -------------- | ---------- | ---------------------------------------------------------------------------------------------- |
15
- | `src/hearth/` | Hearth's | All the components and pages, and the theme's slots and defaults. **Never edit it.** |
16
- | `src/theme/` | The site's | Its look: tokens, slot classes, fonts, wording, custom blocks. **All design work goes here.** |
17
-
18
- `src/app/` holds one-line route files that point at `src/hearth/pages/`; they're Hearth's too (see
19
- `src/hearth/manifest.json` for every file Hearth manages). Pages the site needs that Hearth doesn't serve can be
20
- added in `src/app/` as the site's own.
21
-
22
- `pnpm hearth:update` brings in a new release of Hearth's code: it replaces the managed files and leaves `src/theme`
23
- and the site's own files alone. That's how every site gets new blocks and fixes at once, so an edit to a managed file
24
- is lost at the next update, and stops the update (it refuses to overwrite uncommitted changes there).
25
-
26
- ## Styling a site
27
-
28
- Work in this order, and stop at the first step that gets the result:
29
-
30
- 1. **Tokens** in `src/theme/theme.css`: colors, fonts, content width. Every slot uses these names (`bg-accent`,
31
- `text-highlight`, `bg-muted`, `border-field`…), so most of a brand is set here. Hearth's tones (default, muted,
32
- inverse, accent) map onto them.
33
- 2. **Fonts** in `src/theme/fonts.ts` (next/font), used from the tokens.
34
- 3. **Slots** in `src/theme/index.ts`. Every element Hearth's code draws takes its Tailwind classes from one slot.
35
- `src/hearth/theme/defaults.ts` lists every part (`hero`, `cardGrid`, `header`, `form`, `event`…), every slot
36
- and its default classes: read it before styling. Set only the slots the design changes:
37
-
38
- ```ts
39
- export const theme = defineTheme({
40
- hero: { title: "font-display text-7xl uppercase", inner: (base) => `${base} text-center` },
41
- button: { primary: "inline-block bg-accent px-6 py-3 font-mono uppercase text-accent-ink hover:bg-ink" },
42
- stats: { tone: "accent" },
43
- });
44
- ```
45
-
46
- - A string **replaces** the slot's default; a function **extends** it (`(base) => …`). Replace when you change a
47
- property the default already sets (a radius, a size), since two classes for the same property don't reliably
48
- resolve in the order written.
49
- - Each slot styles one element, so slots never fight. States are attributes on the element, so they're plain
50
- Tailwind variants in the slot: `on-dark:` (on an inverse or accent ground), `data-[size=full]:`,
51
- `data-highlight:`, `aria-invalid:`, `aria-[current=page]:`, `has-checked:`, `in-data-[…]:` and others; the top
52
- of `defaults.ts` lists them and each slot's comment says which apply.
53
- - Slots named `tone` pick a block's ground (default, muted, inverse or accent). `text` holds the words the code
54
- shows ("Get directions", "Register"…). `map.style` is the map's MapLibre style; `emberForm` shapes Ember forms.
55
- - Icons and marks drawn with CSS (`before:content-['✓']`, the FAQ's `+`, link arrows) live in slots too.
56
- 4. **Site CSS** under the tokens in `theme.css`: keyframes, background patterns, `@utility` classes your slots use,
57
- and rules for Hearth's rich text (`.prose`) or a block (`[data-block="hero"]`).
58
- 5. **Overrides**, last: `src/theme/overrides.tsx` can replace a whole block's component or draw the header's logo.
59
- An override is the site's own code: updates don't reach it, so it falls behind Hearth. Use one only when slots,
60
- tokens and CSS can't get there, build it from `src/hearth/ui.tsx` and the theme, and say why in `SITE.md`.
61
-
62
- Never copy a component out of `src/hearth/` to restyle it. If a design needs a slot or an option Hearth doesn't have,
63
- say so: it belongs in Hearth's template for every site (tell the developer to raise it with The Smithy), and an
64
- override can bridge the gap meanwhile.
65
-
66
- **Custom blocks** (Settings → Custom blocks in Hearth) are the one place a site writes components: in
67
- `src/theme/custom-blocks.tsx`, built from `src/hearth/ui.tsx` so they take the theme.
68
-
69
- ## First steps in a new checkout
70
-
71
- Do these before writing code, and tell the developer what you found:
72
-
73
- 1. **Check the setup:** `pnpm hearth:check`. It reads `.env.local`, connects to Hearth, lists the published
74
- pages and the block types they use, and flags types with no component. It never prints the API key; you
75
- shouldn't either, so don't open `.env.local` to read the key or echo it. If it fails, its message says what to
76
- fix (missing key, wrong `HEARTH_SITE`, revoked key). Ask the developer to fix `.env.local`; don't guess values.
77
- 2. **Get the full reference:** `pnpm hearth:docs` saves Hearth's API guide (every block and its fields, events,
78
- forms, previews, SEO) to `hearth-api.md`. Read the parts you need from it, and from
79
- `node_modules/@the-smithy/hearth/llms.md` for the SDK. It also installs any skills Hearth offers this site in
80
- `.claude/skills/` (see "Skills" below), so run it again when they're missing.
81
- 3. **"Nothing is published"** means every page is a 404 until editors publish in Hearth. That's not a code
82
- problem: say so, and don't add placeholder content to work around it.
83
- 4. **Then style the site** as above, checking with `pnpm dev` against the published pages, on a phone width too.
84
-
85
- ## Skills
86
-
87
- `pnpm hearth:docs` installs the skills Hearth offers this site in `.claude/skills/` (which ones depends on the
88
- site). Each `SKILL.md` says what it's for and when to use it. Claude Code loads them by itself; other assistants
89
- should read the ones that fit the task and follow them. Use them for that work, design work in particular, rather
90
- than starting from scratch. They aren't committed (`.claude/skills/.gitignore` lists them), so a fresh checkout gets
91
- them by running `pnpm hearth:docs`; never edit them, because the next run replaces them.
92
-
93
- ## Rules
94
-
95
- - **Content comes from Hearth.** Don't hard-code page text, menus, contact details or SEO tags. They're edited
96
- in Hearth and arrive through `src/hearth/client.ts`. The site's code owns design only.
97
- - **The API key is secret.** Only import `@/hearth/client` from server components, route handlers and server
98
- actions. Never from a `"use client"` file.
99
- - **Use `hearthNext.*` for reads.** It caches and tags requests so the publish webhook can refresh them.
100
- Calling `hearth.*` directly skips the cache.
101
- - **Keep every option working.** Blocks have tones, image positions, sizes and optional fields; check a restyle
102
- with each, on light and dark grounds, and check text contrast on every ground (form field borders need 3:1).
103
- - **No bundled content.** Every page comes from Hearth; an address with no published page is a 404. Don't add
104
- fallback or sample content to the code.
105
- - **Previews, analytics and forms come from Hearth's code.** Don't build a preview banner, add Google Analytics or
106
- other trackers (Hearth's analytics is cookieless; marketing tags need a consent banner and are a separate, opt-in
107
- feature), or post forms anywhere else. Use `track("name")` from `@the-smithy/hearth/analytics` in the site's own
108
- code for meaningful actions.
109
-
110
- ## Checks
111
-
112
- `pnpm typecheck` and `pnpm build`. The build needs the `.env.local` keys, because it reads content from Hearth.
113
- `pnpm hearth:check` shows any block type the published content uses without a component (run `pnpm hearth:update`).
114
- After `pnpm hearth:update`: `pnpm install`, then the checks; a type error in `src/theme/index.ts` means a slot was
115
- renamed or removed (see `defaults.ts`).
116
-
117
- <!-- BEGIN:nextjs-agent-rules -->
118
-
119
- # This is NOT the Next.js you know
120
-
121
- This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
122
-
123
- This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
124
-
125
- <!-- END:nextjs-agent-rules -->
3
+ @AGENTS.md
@@ -0,0 +1,3 @@
1
+ The instructions for coding agents in this project are in `AGENTS.md`:
2
+
3
+ @./AGENTS.md
@@ -0,0 +1,88 @@
1
+ # Hosting a Hearth site
2
+
3
+ This site runs on Netlify or Vercel with no code changes. Its pages, webhook, previews and SEO use standard Next.js
4
+ features, and images come straight from Hearth's CDN, so neither host's image optimization is used or billed. The
5
+ sites The Smithy runs are on Netlify.
6
+
7
+ This file is Hearth's and is replaced by `pnpm hearth:update`. Notes about this site's hosting go in `SITE.md`.
8
+
9
+ ## Setting up, on either host
10
+
11
+ Do these in order: the webhook's signing secret only exists once the webhook address is saved.
12
+
13
+ 1. **Create a project from this repo,** with `pnpm-lock.yaml` committed. The host-specific settings are below.
14
+ 2. **Add the environment variables** before the first deploy, because the build reads content from Hearth and fails
15
+ without them. Never put them in `netlify.toml` or anything else in git.
16
+
17
+ | Variable | Value | Where it's needed |
18
+ | --- | --- | --- |
19
+ | `HEARTH_SITE` | The site's address in Hearth, e.g. `acme` | Production and previews |
20
+ | `HEARTH_API_KEY` | A key made for this host (API & webhooks → API keys), so you can revoke it alone. Mark it secret | Production and previews |
21
+ | `HEARTH_WEBHOOK_SECRET` | Added in step 5 | Production |
22
+
23
+ 3. **Deploy,** then connect your domain and choose its primary address (see "Domains").
24
+ 4. **Set the web address in Hearth** to that primary address, under **Settings → Your site → Web address**. Search
25
+ engines are kept out until it's set.
26
+ 5. **Connect the publish webhook.** In **API & webhooks → Webhook and preview**, set **Revalidation webhook** to
27
+ `https://<primary domain>/api/revalidate` and save. A **Signing secret** appears: add it as
28
+ `HEARTH_WEBHOOK_SECRET`, then **deploy again**. Both hosts apply a changed variable only to new deploys.
29
+ 6. **Clear the Preview address** if you set it to localhost. Empty means the live site's `/api/preview`.
30
+ 7. **Check it:** publish a small change in Hearth. It should show on the live site within a few seconds.
31
+
32
+ Without the webhook, published changes still appear, but can take up to an hour.
33
+
34
+ ## Netlify
35
+
36
+ The template's `netlify.toml` sets the build command and Node 22, and Netlify's
37
+ [Next.js adapter](https://docs.netlify.com/build/frameworks/framework-setup-guides/nextjs/overview/) needs no other
38
+ configuration.
39
+
40
+ | Setting | Use | Why |
41
+ | --- | --- | --- |
42
+ | Adapter version | Don't pin it | Netlify then updates it on each build, and tests it with every Next.js release |
43
+ | Function region | Leave on the default, Ohio (`cmh`) | It's the same AWS region as Hearth. Older sites may differ: see Cloud compute → Functions → Region (Pro and up) |
44
+ | `NETLIFY_NEXT_SKEW_PROTECTION` | `true` | Forms use server actions. This keeps a page opened before a deploy able to submit after it |
45
+ | Variable scopes (Pro and up) | Include Builds and Functions | The build and the running site both read Hearth |
46
+
47
+ ## Vercel
48
+
49
+ Vercel detects Next.js and pnpm by itself, so there's no config file, and `netlify.toml` is ignored. If the site
50
+ isn't at the top of its repo, set the project's Root Directory.
51
+
52
+ | Setting | Use | Why |
53
+ | --- | --- | --- |
54
+ | Plan | Pro for commercial work | [Hobby is non-commercial only](https://vercel.com/docs/limits/fair-use-guidelines). Taking payment from visitors, tickets for example, counts; asking for donations doesn't. Treat a site you're paid to build as commercial |
55
+ | `ENABLE_EXPERIMENTAL_COREPACK` | `1` | Builds with the exact pnpm version in `package.json` rather than one [picked from the lockfile](https://vercel.com/docs/package-managers) |
56
+ | Node version | Set `"engines": { "node": "22.x" }` in `package.json` to match Netlify | [`engines` overrides the project setting](https://vercel.com/docs/functions/runtimes/node-js/node-js-versions), and the template's `>=20` names no version. Your own machine then needs Node 22 too |
57
+ | Function region | Leave on the default, Washington, D.C. (`iad1`) | It's in the US East, near Hearth. Pages are served from the CDN, and a region further away still fetches from Hearth in the US |
58
+ | Skew Protection | Leave on | Forms use server actions. It's [on by default](https://vercel.com/docs/skew-protection) for new projects, on Pro and up |
59
+ | Enable access to System Environment Variables | Leave on | The site reads `VERCEL_ENV` to keep preview deployments out of search engines, and Skew Protection needs it |
60
+ | `HEARTH_API_KEY` type | Secret | [Write-only after saving](https://vercel.com/docs/environment-variables/sensitive-environment-variables) |
61
+ | Deployment Protection | Standard | [Protects everything except production domains](https://vercel.com/docs/deployment-protection), so the webhook and Hearth's preview must use the production domain, never a `*.vercel.app` deployment address |
62
+
63
+ **Redeploying doesn't refresh content on Vercel.** The
64
+ [Data Cache](https://vercel.com/docs/caching/runtime-cache/data-cache) that holds Hearth's responses keeps them across
65
+ deployments, and production and previews each have their own. The webhook clears production's when something
66
+ publishes; previews catch up within the hour. To clear it by hand, use CDN → Caches → Purge cache → All content →
67
+ Runtime and Data Cache, but on Hobby and Pro that clears every project in the team.
68
+
69
+ ## Domains
70
+
71
+ Add both the bare domain and `www` to the project, with the one Hearth uses as primary, so the other redirects to it.
72
+ Hearth's web address, the webhook and the primary domain must all be the same address. Netlify and Vercel both
73
+ recommend `www` as primary, Netlify especially when DNS is hosted elsewhere.
74
+
75
+ - **Netlify:** add both under Domain management and set the primary there.
76
+ - **Vercel:** under Settings → Domains, choose Edit on the one to redirect, then **Redirect to**.
77
+
78
+ ## When something's wrong
79
+
80
+ | What you see | Why, and what to do |
81
+ | --- | --- |
82
+ | The build fails with ``Hearth: `apiKey` is required`` | The variables are missing for that deploy's environment, previews included |
83
+ | Published changes don't show | The webhook isn't set, or `HEARTH_WEBHOOK_SECRET` doesn't match Hearth's signing secret. Deploy again after changing it |
84
+ | The webhook gets a 401 and it isn't the secret | On Vercel, it points at a protected deployment address rather than the production domain |
85
+ | Published changes show on production but not a preview | Expected: the webhook only reaches production |
86
+ | A page stays stale on Netlify | Run `curl -D - -o /dev/null -H "x-nf-debug-logging: 1" <url>`. `Debug-Netlify-Cache-Tag` should list `hearth`. If it doesn't, the site's SDK is older than 0.24.1: run `pnpm hearth:update` |
87
+ | "Preview on the website" opens the wrong place or a login | Check **Preview address** in API & webhooks. On Vercel it must be the production domain |
88
+ | A form fails in a page opened before a deploy, and works after a reload | Skew protection is off: see the host's table above |
@@ -41,17 +41,17 @@ without touching your styling. See "Styling the site" below.
41
41
 
42
42
  ## Working with an AI coding assistant
43
43
 
44
- The project comes with instructions for coding agents (`CLAUDE.md`, which Claude Code reads on its own, and
45
- `AGENTS.md` for other assistants). Once your API key is in `.env.local`:
44
+ The project comes with instructions for coding agents in `AGENTS.md`, which Codex, Cursor, Copilot and others read on
45
+ their own. `CLAUDE.md` and `GEMINI.md` point Claude Code and Gemini CLI to it. Once your API key is in `.env.local`:
46
46
 
47
47
  1. **Check the connection yourself, or have the agent do it:** `pnpm hearth:check` confirms the key works and
48
48
  lists the published pages and the block types they use. It never prints the key.
49
49
  2. **Give the agent the full reference:** `pnpm hearth:docs` saves Hearth's API guide to `hearth-api.md` (not
50
- committed), and installs any coding-assistant skills Hearth offers your site in `.claude/skills/`. The agent's
51
- instructions already point it at both.
50
+ committed), and installs any coding-assistant skills Hearth offers your site in `.agents/skills/` and
51
+ `.claude/skills/`. The agent's instructions already point it at both.
52
52
  3. **Then tell it what to build.** Prompts that work well:
53
53
 
54
- - *"Run the first steps in CLAUDE.md and tell me what's published and which blocks the site uses."*
54
+ - *"Run the first steps in AGENTS.md and tell me what's published and which blocks the site uses."*
55
55
  - *"Restyle the site to match the brand: colors `#1F3A5F` and `#C8A15A`, font Lora for headings. Start with
56
56
  the tokens in `src/theme/theme.css`, then the hero, card grid and media-text slots in `src/theme/index.ts`."*
57
57
  - *"Match the current website at https://example.org: compare each published page with the same page there
@@ -76,12 +76,15 @@ Keep the API key out of chat: the agent never needs to see it, only `.env.local`
76
76
  and the page's language all come from Hearth (`siteMetadata`, `generatePageMetadata`, `sitemapWith`, `robots`,
77
77
  `llmsTxtHandler`, `redirectOrNotFound`). Preview deploys
78
78
  and sites hidden in Hearth are kept out of search engines. What's still yours:
79
- - Pages your own code serves (like `/events`) need their own `generateMetadata` and a place in `src/app/sitemap.ts`.
79
+ - `/events`, `/fundraisers`, `/donate`, `/donate/thank-you`, `/registered` and the 404 are served by Hearth's code. A page
80
+ in Hearth at that address (`not-found` for the 404) replaces their words, SEO and all, so make one there rather than
81
+ a page in code.
82
+ - Other pages your own code serves need their own `generateMetadata` and a place in `src/app/sitemap.ts`.
80
83
  - Use `<Img priority>` for the image at the top of a page, and keep one `<h1>` per page (the Hero block renders it).
81
84
  - Give `<Img>` the `crop` of an image that fills a frame (`crop={4 / 3}` for `aspect-[4/3] object-cover`) so it keeps
82
85
  the focal point set in Hearth's media library in view.
83
- - Add both the bare domain and `www` to the site in Netlify, with the one Hearth uses as primary: Netlify then
84
- redirects the other to it.
86
+ - Add both the bare domain and `www` to the site at your host, with the one Hearth uses as primary, so the other
87
+ redirects to it (see `HOSTING.md`).
85
88
 
86
89
  ### Styling the site
87
90
 
@@ -92,7 +95,7 @@ Keep the API key out of chat: the agent never needs to see it, only `.env.local`
92
95
  | `src/hearth/theme/defaults.ts` | Every slot and its default classes: read it to find the slot to change |
93
96
  | `src/theme/fonts.ts` | Web fonts (next/font) |
94
97
  | `src/theme/custom-blocks.tsx` | Components for the site's own block types (Settings → Custom blocks in Hearth) |
95
- | `src/theme/overrides.tsx` | Last resort: replace a whole block's component, or draw the logo |
98
+ | `src/theme/overrides.tsx` | Last resort: replace a whole block's component, the logo, the footer, the event or fundraiser page, their cards or the donation form (see `AGENTS.md`) |
96
99
  | `SITE.md` | Notes about this site's design, for the next person or coding agent |
97
100
 
98
101
  A slot holds the classes of one element, e.g. `hero.title` or `cardGrid.card`. Set a string to replace the default,
@@ -107,7 +110,10 @@ export const theme = defineTheme({
107
110
  ```
108
111
 
109
112
  States are Tailwind variants inside the slot: `on-dark:` for blocks on an inverse or accent ground,
110
- `data-[size=full]:`, `aria-invalid:` and so on (listed at the top of `defaults.ts`).
113
+ `data-[size=full]:`, `aria-invalid:` and so on (listed at the top of `defaults.ts`). Words the code shows are slots under
114
+ `text`, and `page` holds some settings: `themeColor`, `externalLinks`, `navMatch` and `dateStyle`.
115
+
116
+ Editors can name any block in Hearth ("Link to this block"), so a link such as `/#upcoming` jumps to it.
111
117
 
112
118
  ### Where things live
113
119
 
@@ -137,24 +143,11 @@ import { GlobalBlock } from "@/hearth/blocks/global";
137
143
  `hearthNext.getGlobalBlocks()` lists every global block (key, name, type, pop-up, the pages and menu items it's on,
138
144
  the resolved block); `hearthNext.getGlobalBlock(keyOrId)` gets one.
139
145
 
140
- ## Deploying to Netlify
141
-
142
- Do these in order: the webhook's signing secret only exists once the webhook URL is saved.
143
-
144
- 1. **Create the Netlify site** from this repo. `netlify.toml` already sets the build command.
145
- 2. **Add environment variables** in Netlify: `HEARTH_SITE`, and `HEARTH_API_KEY` as a secret. Use a separate key
146
- named "Netlify", so you can revoke one without breaking the other. Set them **before the first deploy**: the
147
- build reads content from Hearth and fails without them. Nothing else is needed; the packages are public on npm.
148
- 3. **Deploy**, and note the site's domain.
149
- 4. **Set the web address in Hearth.** The site's admin sets it under **Settings → Your site → Web address**.
150
- Until then, search engines are kept out.
151
- 5. **Connect the publish webhook.** In **API & webhooks → Webhook and preview**, set **Revalidation
152
- webhook** to `https://<domain>/api/revalidate` and save. A **Signing secret** appears: add it in Netlify as
153
- `HEARTH_WEBHOOK_SECRET`, then **redeploy**, since Netlify only applies new variables on the next deploy.
154
- 6. **Clear the Preview address** if you set it to localhost. Empty means the live site's `/api/preview`.
155
- 7. **Check it:** publish a small change in Hearth. It should show on the live site within a few seconds.
146
+ ## Deploying
156
147
 
157
- Without the webhook, published changes still appear, but can take up to an hour.
148
+ The site runs on Netlify or Vercel with no code changes. **`HOSTING.md` has the steps and the best settings for each.**
149
+ Set `HEARTH_SITE` and `HEARTH_API_KEY` before the first deploy, because the build reads content from Hearth. The
150
+ packages are public on npm, so nothing else is needed to install them.
158
151
 
159
152
  ## When something's wrong
160
153
 
@@ -162,7 +155,7 @@ Without the webhook, published changes still appear, but can take up to an hour.
162
155
  | --- | --- |
163
156
  | "Page not found" on every page | Nothing is published yet, or `HEARTH_SITE` is wrong. Publish the homepage in Hearth; check the address against Hearth's address bar |
164
157
  | Not sure what's wrong | Run `pnpm hearth:check`: it checks `.env.local` and the connection and says what to fix |
165
- | ``Hearth: `apiKey` is required`` (dev or build) | `HEARTH_API_KEY` is missing from `.env.local`, or from Netlify's variables |
158
+ | ``Hearth: `apiKey` is required`` (dev or build) | `HEARTH_API_KEY` is missing from `.env.local`, or from your host's variables |
166
159
  | An `unauthorized` (401) error from Hearth | The key is mistyped, revoked, or belongs to another site. Create a new one for this site |
167
160
  | Published changes don't show locally | Hard refresh (Ctrl+Shift+R). The webhook can't reach your computer |
168
161
  | Published changes don't show live | The webhook isn't set, or `HEARTH_WEBHOOK_SECRET` doesn't match Hearth's signing secret (redeploy after changing it) |
@@ -175,7 +168,15 @@ with a Markdown version to give an AI coding assistant. The SDK also ships `node
175
168
 
176
169
  ## Updating
177
170
 
171
+ **Check for new releases whenever you start work on the site:** `pnpm hearth:check` lists anything from The Smithy
172
+ with a newer release, and works without an API key.
173
+
178
174
  `pnpm hearth:update` updates Hearth's code in the site (`src/hearth/` and the other files `src/hearth/manifest.json`
179
175
  lists) to the latest release, with the packages it needs. Your `src/theme/` and your own files aren't touched. Then
180
176
  `pnpm install`, `pnpm typecheck`, `pnpm build`, and a look at the site. New blocks arrive styled by the defaults and
181
- your tokens; give them slots in the theme if the design needs it.
177
+ your tokens; give them slots in the theme if the design needs it. Update other Smithy packages, such as the Ember SDK,
178
+ with `pnpm add @the-smithy/<name>@latest`.
179
+
180
+ Keep the site on the template: design goes in `src/theme/`, and bugs or gaps in `src/hearth/` get fixed in Hearth's
181
+ template rather than here. If a site has to bridge a gap, its override goes in `src/theme/overrides.tsx` with a note
182
+ in `SITE.md`.
package/template/SITE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # This site
2
2
 
3
3
  Notes for whoever works on this site next, people and coding agents alike: the design's intent, decisions, and
4
- anything unusual. Hearth's own instructions are in `CLAUDE.md` (replaced by `pnpm hearth:update`); this file is the
4
+ anything unusual. Hearth's own instructions are in `AGENTS.md` (replaced by `pnpm hearth:update`); this file is the
5
5
  site's and stays as it is.
6
6
 
7
7
  ## Design
@@ -2,11 +2,12 @@ node_modules/
2
2
  /.next/
3
3
  /out/
4
4
  .netlify
5
+ .vercel
5
6
  .DS_Store
6
7
  *.tsbuildinfo
7
8
  next-env.d.ts
8
9
 
9
- # Keys live here locally and in Netlify's environment variables, never in git.
10
+ # Keys live here locally and in your host's environment variables, never in git.
10
11
  .env*
11
12
  !.env.example
12
13