diff --git a/docs/superpowers/plans/2026-06-26-email-templates-brand-hygiene.md b/docs/superpowers/plans/2026-06-26-email-templates-brand-hygiene.md index f0d3e32..c91e89e 100644 --- a/docs/superpowers/plans/2026-06-26-email-templates-brand-hygiene.md +++ b/docs/superpowers/plans/2026-06-26-email-templates-brand-hygiene.md @@ -71,7 +71,7 @@ email/ invitation.html # MODIFY banned.html # MODIFY test.html # MODIFY - notification.html # MODIFY: keeps body, IMPORTs post-queue-body when applicable + notification.html # MODIFY: keeps {body}; post queue flow supplies rendered post-queue body digest.html # MODIFY: keeps loops; reskinned README.md # MODIFY: partials now real; document IMPORT + literal-hex constraint languages/ @@ -526,7 +526,7 @@ git commit -m "feat(email): convert banned.html to shared partials + brand" ### Task 8: Convert `notification.html` + reskin `post-queue-body.html` -`notification.html` keeps its notification body and imports `post-queue-body.html` for post-queue notifications. +`notification.html` keeps `{body}`. NodeBB's post queue flow renders `post-queue-body.html` server-side into that notification body before the email template renders. **Files:** - Modify: `email/notification.html`, `email/partials/post-queue-body.html` @@ -558,9 +558,9 @@ git commit -m "feat(email): convert banned.html to shared partials + brand"

{content}

``` -- [x] **Step 2: Read `email/notification.html`** (lines ~166–417 are the header/body/footer). Note where it renders the notification body and whether it already `IMPORT`s `post-queue-body.html`. +- [x] **Step 2: Read `email/notification.html`** (lines ~166–417 are the header/body/footer). Note where it renders `{body}`; post-queue content is supplied by NodeBB before template render. -- [x] **Step 3: Apply the Task 6 transform to `notification.html`** — IMPORT header + `[[email:notification.preheader]]` preheader + retained body table (palette/font find/replace) + IMPORT footer. Preserve the `.notification-body` content and any `{{{ if }}}` post-queue branch; if the body renders a post-queue item, keep/add `` inside that branch. Add `style="color: #a8893f;"` to notification body links. +- [x] **Step 3: Apply the Task 6 transform to `notification.html`** — IMPORT header + `[[email:notification.preheader]]` preheader + retained body table (palette/font find/replace) + IMPORT footer. Preserve the `.notification-body` content that renders `{body}`; post-queue content is already rendered into that value by NodeBB before this template runs. Add `style="color: #a8893f;"` to notification body links. - [ ] **Step 4: Guard + build** @@ -709,4 +709,4 @@ Task 12 note: `node scripts/check-emails.js` passed with `Email template check p **Placeholder scan:** Color/font changes given as a concrete find/replace table; `header.html`/`footer.html`/`reset.html`/`post-queue-body.html` given as complete markup; per-template specifics tabulated. Tasks 6–9 reference complete bodies that already exist in the repo (transform, not author-from-scratch) — the engineer reads the existing file and applies the listed mechanical changes. No "TBD"/"handle edge cases". -**Type/name consistency:** Import paths (`emails/partials/header.html`/`footer.html`/`post-queue-body.html`), preheader keys (`[[email:.preheader]]`), the `` marker, and the guard script's regexes all agree across tasks. +**Type/name consistency:** Import paths (`emails/partials/header.html`/`footer.html`), preheader keys (`[[email:.preheader]]`), the `` marker, and the guard script's regexes all agree across tasks. `post-queue-body.html` is rendered by NodeBB's post queue flow into notification body content. diff --git a/docs/superpowers/specs/2026-06-26-email-templates-brand-hygiene-design.md b/docs/superpowers/specs/2026-06-26-email-templates-brand-hygiene-design.md index 98069f4..5512c49 100644 --- a/docs/superpowers/specs/2026-06-26-email-templates-brand-hygiene-design.md +++ b/docs/superpowers/specs/2026-06-26-email-templates-brand-hygiene-design.md @@ -15,9 +15,10 @@ verbatim. Three issues: like they came from the same product as the forum. 2. **Massive duplication.** Each template inlines the full `` (reset CSS, media queries), header, and footer — ~400 lines per file, ~390 of them - identical across all 10. `partials/header.html`, `partials/footer.html`, and - `partials/post-queue-body.html` exist but **nothing imports them** — they are - dead code, and the README wrongly claims they are included. + identical across all 10. `partials/header.html` and `partials/footer.html` + exist but **nothing imports them** — they are dead code, and the README + wrongly claims they are included. `partials/post-queue-body.html` is rendered + by NodeBB's post queue flow into notification body content. 3. **Inbox hygiene gaps.** Every template has an empty `` and no preheader (inbox preview text). These make messages look unfinished in the inbox list and are easy wins under deliverability heuristics. @@ -67,7 +68,7 @@ email/ partials/ header.html # doctype, <head>, brand <style>, letterhead band, open container footer.html # gold hairline, unsubscribe block, closing tags - post-queue-body.html # unchanged in structure; reskinned + post-queue-body.html # NodeBB post queue flow renders this into notification body; reskinned welcome.html # IMPORT header + body block + IMPORT footer verify-email.html reset.html @@ -75,7 +76,7 @@ email/ registration_accepted.html banned.html invitation.html - notification.html # body imports post-queue-body.html when applicable + notification.html # renders {body}; post queue flow supplies rendered post-queue body digest.html # keeps its loops; reskinned test.html ``` diff --git a/email/digest.html b/email/digest.html index 3489004..ddbd262 100644 --- a/email/digest.html +++ b/email/digest.html @@ -4,7 +4,7 @@ <div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;"> ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌</div> <!-- Email Body : BEGIN --> <table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 600px;"> -<!-- 1 Column Text + Button : BEGIN --> +<!-- 1 Column Text : BEGIN --> <tr> <td bgcolor="#fbf7ef"> <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"> diff --git a/scripts/check-emails.js b/scripts/check-emails.js index 2926281..5b6c589 100644 --- a/scripts/check-emails.js +++ b/scripts/check-emails.js @@ -9,13 +9,20 @@ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.html')); const failures = []; for (const f of files) { const src = fs.readFileSync(path.join(dir, f), 'utf8'); + const trimmed = src.trim(); const checks = [ - [/IMPORT emails\/partials\/header\.(html|tpl)/, 'missing header IMPORT'], - [/IMPORT emails\/partials\/footer\.(html|tpl)/, 'missing footer IMPORT'], + [ + trimmed.startsWith('<!-- IMPORT emails/partials/header.html -->'), + 'header IMPORT must be first', + ], + [ + trimmed.endsWith('<!-- IMPORT emails/partials/footer.html -->'), + 'footer IMPORT must be last', + ], [/<!-- preheader -->/, 'missing preheader marker'], ]; - for (const [re, msg] of checks) { - if (!re.test(src)) failures.push(`${f}: ${msg}`); + for (const [check, msg] of checks) { + if (check instanceof RegExp ? !check.test(src) : !check) failures.push(`${f}: ${msg}`); } }