feat: refine MJML templates to match westside-landing visual language #304

Merged
forgejo_admin merged 2 commits from 303-mjml-visual-refinement into main 2026-04-03 20:56:55 +00:00

Summary

Aligns the four core MJML email templates with the westside-landing site's signature visual elements: red left-border cards, headline red underlines, uppercase bold headlines with tight letter-spacing, and footer red top-borders.

Changes

  • templates/email/includes/brand.mjml — Added mj-class tokens (red-border-col, bordered-content) for the 4px red left-border card pattern. Added mj-style block with .headline-underline (red 3px bottom border mirroring landing h2::after) and .footer-border (red 2px top border mirroring landing footer).
  • templates/email/action.mjml — Body content wrapped in mj-group with 4px red border column + content column (won't stack on mobile). Headline gets headline-underline class, text-transform: uppercase, letter-spacing: -0.02em. Footer uses footer-border class replacing the gray divider.
  • templates/email/notification.mjml — Same treatment as action: red left-border card for body, headline underline, footer red border.
  • templates/email/announcement.mjml — All three section blocks wrapped in red left-border cards. Headline underline and footer red border applied.

Test Plan

  • npm run build:email compiles all 4 templates without errors
  • pytest tests/test_jersey_reminder.py — 23/23 tests pass (verifies compiled HTML contains brand colors, table layout, button presence, placeholder replacement)
  • jersey-reminder.mjml intentionally untouched per scope

Review Checklist

  • Only modified brand.mjml, action.mjml, notification.mjml, announcement.mjml
  • jersey-reminder.mjml untouched
  • No Python files modified
  • npm run build:email compiles cleanly
  • All 23 tests pass
  • Red left-border uses mj-group (no CSS border-left on td)
  • Brand tokens are reusable mj-class definitions
  • Landing site CSS visual language: red left-border cards, h2 red underline, footer red top-border, uppercase bold buttons

Closes #303

## Summary Aligns the four core MJML email templates with the westside-landing site's signature visual elements: red left-border cards, headline red underlines, uppercase bold headlines with tight letter-spacing, and footer red top-borders. ## Changes - **`templates/email/includes/brand.mjml`** — Added `mj-class` tokens (`red-border-col`, `bordered-content`) for the 4px red left-border card pattern. Added `mj-style` block with `.headline-underline` (red 3px bottom border mirroring landing `h2::after`) and `.footer-border` (red 2px top border mirroring landing footer). - **`templates/email/action.mjml`** — Body content wrapped in `mj-group` with 4px red border column + content column (won't stack on mobile). Headline gets `headline-underline` class, `text-transform: uppercase`, `letter-spacing: -0.02em`. Footer uses `footer-border` class replacing the gray divider. - **`templates/email/notification.mjml`** — Same treatment as action: red left-border card for body, headline underline, footer red border. - **`templates/email/announcement.mjml`** — All three section blocks wrapped in red left-border cards. Headline underline and footer red border applied. ## Test Plan - `npm run build:email` compiles all 4 templates without errors - `pytest tests/test_jersey_reminder.py` — 23/23 tests pass (verifies compiled HTML contains brand colors, table layout, button presence, placeholder replacement) - jersey-reminder.mjml intentionally untouched per scope ## Review Checklist - [x] Only modified brand.mjml, action.mjml, notification.mjml, announcement.mjml - [x] jersey-reminder.mjml untouched - [x] No Python files modified - [x] `npm run build:email` compiles cleanly - [x] All 23 tests pass - [x] Red left-border uses mj-group (no CSS border-left on td) - [x] Brand tokens are reusable mj-class definitions ## Related Notes - Landing site CSS visual language: red left-border cards, h2 red underline, footer red top-border, uppercase bold buttons ## Related Closes #303
feat: refine MJML templates to match westside-landing visual language
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline failed
fe152e30a0
Align email templates with the landing site's signature design elements:
red left-border cards (via mj-group 4px column), headline red underline,
uppercase bold headlines with tight letter-spacing, and footer red
top-border. Brand includes now define reusable mj-class tokens and
CSS classes so all templates share the visual vocabulary.

Closes #303

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: inline column and button attrs for MJML v5 beta compatibility
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline failed
a3f9b6b0a3
mj-class tokens from mj-include do not propagate in MJML v5 beta.
Inline background-color, width, padding on red left-border columns
and full button styling on action/announcement templates. Removes
non-functional mj-class definitions from brand.mjml with explanatory
comment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

QA Review

Findings (fixed in a3f9b6b)

Critical: MJML v5 beta mj-include does not propagate mj-class or mj-attributes defaults

The initial commit used mj-class="red-border-col" and mj-class="bordered-content" tokens defined in brand.mjml. These compiled to columns with no background color (the 4px red border was invisible) and 50/50 width split instead of 4px/536px. Similarly, mj-button defaults from brand.mjml compiled with MJML's built-in defaults (#414141, border-radius: 3px) instead of the brand tokens.

Root cause: MJML v5 beta does not apply mj-attributes or mj-class definitions from files loaded via mj-include. Only mj-style CSS classes propagate correctly (they inject into <style>).

Fix applied:

  • Inlined background-color="#d42026", width="4px", padding="0" directly on red border columns
  • Inlined padding="16px 20px", width="536px" on content columns
  • Inlined full button styling on action.mjml and announcement.mjml
  • Removed non-functional mj-class definitions from brand.mjml with explanatory comment
  • css-class="headline-underline" and css-class="footer-border" still work (CSS injection)

Verified

  • npm run build:email compiles cleanly
  • 23/23 tests pass
  • Compiled HTML shows background-color: #d42026 on 4px column td
  • Compiled HTML shows background: #d42026, font-weight: 800, text-transform: uppercase, border-radius: 4px on buttons
  • jersey-reminder.mjml untouched
  • No Python files modified

VERDICT: APPROVED

## QA Review ### Findings (fixed in a3f9b6b) **Critical: MJML v5 beta `mj-include` does not propagate `mj-class` or `mj-attributes` defaults** The initial commit used `mj-class="red-border-col"` and `mj-class="bordered-content"` tokens defined in `brand.mjml`. These compiled to columns with no background color (the 4px red border was invisible) and 50/50 width split instead of 4px/536px. Similarly, `mj-button` defaults from `brand.mjml` compiled with MJML's built-in defaults (`#414141`, `border-radius: 3px`) instead of the brand tokens. **Root cause:** MJML v5 beta does not apply `mj-attributes` or `mj-class` definitions from files loaded via `mj-include`. Only `mj-style` CSS classes propagate correctly (they inject into `<style>`). **Fix applied:** - Inlined `background-color="#d42026"`, `width="4px"`, `padding="0"` directly on red border columns - Inlined `padding="16px 20px"`, `width="536px"` on content columns - Inlined full button styling on action.mjml and announcement.mjml - Removed non-functional `mj-class` definitions from brand.mjml with explanatory comment - `css-class="headline-underline"` and `css-class="footer-border"` still work (CSS injection) ### Verified - [x] `npm run build:email` compiles cleanly - [x] 23/23 tests pass - [x] Compiled HTML shows `background-color: #d42026` on 4px column td - [x] Compiled HTML shows `background: #d42026`, `font-weight: 800`, `text-transform: uppercase`, `border-radius: 4px` on buttons - [x] jersey-reminder.mjml untouched - [x] No Python files modified **VERDICT: APPROVED**
Author
Owner

PR #304 Review

DOMAIN REVIEW

Tech stack: MJML email templates (HTML email markup). No Python, no backend changes.

Scope verification: 4 files changed, all under templates/email/. jersey-reminder.mjml intentionally untouched per scope. No Python files modified. Clean scope.

MJML-specific observations:

  1. mj-group for red left-border card -- Good approach. Using mj-group prevents the border column from stacking on mobile, which is the correct MJML pattern for a visual accent that must stay side-by-side. The comment in brand.mjml correctly documents the MJML v5 beta limitation that prevents mj-class tokens from propagating via mj-include, justifying the inline repetition.

  2. CSS classes via mj-style -- .headline-underline and .footer-border are correctly centralized in brand.mjml and applied via css-class in each template. This is the right abstraction layer for styles that CAN be shared via CSS.

  3. Hardcoded width="536px" on content columns -- This assumes 600px container minus 4px border column minus padding. Fragile if container width changes, but acceptable for email templates where 600px is a universal standard.

  4. display: inline-block !important; width: auto !important; on .headline-underline td -- This makes the red underline only span the text width rather than the full column. Intentional design choice matching the landing h2::after pattern.

BLOCKERS

None.

  • No new functionality requiring new tests (visual-only template refinements, 23/23 existing tests pass per PR body)
  • No user input handling
  • No secrets or credentials
  • No auth/security code

NITS

  1. Redundant button attributes in action.mjml and announcement.mjml -- The CTA buttons inline background-color="#d42026", color="#ffffff", border-radius="4px", font-size="16px", font-weight="800", inner-padding="14px 32px" which are already set as defaults in brand.mjml's <mj-button> attributes block. Only text-transform="uppercase" and letter-spacing="0.05em" are new. Consider adding those two properties to the brand.mjml <mj-button> defaults and removing the redundant inline attributes from both templates. This reduces drift risk if the brand button style ever changes.

  2. Red left-border card repetition -- The 5-line mj-group pattern appears 5 times across 3 templates. The MJML v5 beta mj-class limitation is documented and understood. If/when MJML v5 stabilizes mj-class propagation from includes, this could be collapsed into a single mj-class token in brand.mjml.

SOP COMPLIANCE

  • Branch named after issue: 303-mjml-visual-refinement follows {issue-number}-{kebab-case-purpose}
  • PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related all present
  • Related references issue: Closes #303
  • Related references plan slug -- no plan slug referenced (acceptable if this is standalone board work, not plan-driven)
  • No secrets committed
  • No unnecessary file changes (scope is tight to the 4 template files)
  • Commit messages are descriptive (PR title is clean)

PROCESS OBSERVATIONS

Low-risk visual refinement. Template-only changes with no runtime behavior change. Existing test suite validates compiled HTML structure. Change failure risk is minimal -- worst case is a visual rendering difference in email clients, which is a cosmetic issue not a functional one.

VERDICT: APPROVED

## PR #304 Review ### DOMAIN REVIEW **Tech stack**: MJML email templates (HTML email markup). No Python, no backend changes. **Scope verification**: 4 files changed, all under `templates/email/`. jersey-reminder.mjml intentionally untouched per scope. No Python files modified. Clean scope. **MJML-specific observations**: 1. **mj-group for red left-border card** -- Good approach. Using `mj-group` prevents the border column from stacking on mobile, which is the correct MJML pattern for a visual accent that must stay side-by-side. The comment in brand.mjml correctly documents the MJML v5 beta limitation that prevents `mj-class` tokens from propagating via `mj-include`, justifying the inline repetition. 2. **CSS classes via mj-style** -- `.headline-underline` and `.footer-border` are correctly centralized in brand.mjml and applied via `css-class` in each template. This is the right abstraction layer for styles that CAN be shared via CSS. 3. **Hardcoded `width="536px"`** on content columns -- This assumes 600px container minus 4px border column minus padding. Fragile if container width changes, but acceptable for email templates where 600px is a universal standard. 4. **`display: inline-block !important; width: auto !important;`** on `.headline-underline td` -- This makes the red underline only span the text width rather than the full column. Intentional design choice matching the landing `h2::after` pattern. ### BLOCKERS None. - No new functionality requiring new tests (visual-only template refinements, 23/23 existing tests pass per PR body) - No user input handling - No secrets or credentials - No auth/security code ### NITS 1. **Redundant button attributes in action.mjml and announcement.mjml** -- The CTA buttons inline `background-color="#d42026"`, `color="#ffffff"`, `border-radius="4px"`, `font-size="16px"`, `font-weight="800"`, `inner-padding="14px 32px"` which are already set as defaults in brand.mjml's `<mj-button>` attributes block. Only `text-transform="uppercase"` and `letter-spacing="0.05em"` are new. Consider adding those two properties to the brand.mjml `<mj-button>` defaults and removing the redundant inline attributes from both templates. This reduces drift risk if the brand button style ever changes. 2. **Red left-border card repetition** -- The 5-line mj-group pattern appears 5 times across 3 templates. The MJML v5 beta mj-class limitation is documented and understood. If/when MJML v5 stabilizes mj-class propagation from includes, this could be collapsed into a single `mj-class` token in brand.mjml. ### SOP COMPLIANCE - [x] Branch named after issue: `303-mjml-visual-refinement` follows `{issue-number}-{kebab-case-purpose}` - [x] PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related all present - [x] Related references issue: `Closes #303` - [ ] Related references plan slug -- no plan slug referenced (acceptable if this is standalone board work, not plan-driven) - [x] No secrets committed - [x] No unnecessary file changes (scope is tight to the 4 template files) - [x] Commit messages are descriptive (PR title is clean) ### PROCESS OBSERVATIONS Low-risk visual refinement. Template-only changes with no runtime behavior change. Existing test suite validates compiled HTML structure. Change failure risk is minimal -- worst case is a visual rendering difference in email clients, which is a cosmetic issue not a functional one. ### VERDICT: APPROVED
forgejo_admin deleted branch 303-mjml-visual-refinement 2026-04-03 20:56:55 +00:00
Sign in to join this conversation.
No description provided.