Skip to content

🎨 Improved muted text contrast in transactional emails - #30201

Open
si-huynh wants to merge 1 commit into
TryGhost:mainfrom
si-huynh:a11y-email-footer-contrast
Open

🎨 Improved muted text contrast in transactional emails#30201
si-huynh wants to merge 1 commit into
TryGhost:mainfrom
si-huynh:a11y-email-footer-contrast

Conversation

@si-huynh

Copy link
Copy Markdown

closes #29666

Problem

Transactional emails (member magic links, staff invites, password resets, comment/gift notifications, verify-email flows) hardcode #738A94 for muted/footer text. On the white email background that is a 3.63:1 contrast ratio — below the 4.5:1 that WCAG 1.4.3 (AA) requires for regular text. These footers are 11px and often carry the link that matters ("Manage your email preferences", sender info).

Investigation notes — why the issue's screenshot doesn't match current newsletters

The screenshot in #29666 shows a welcome-email footer ("Site © 2026 — Manage your preferences") rendered in #738A94 (sampled pixels ≈ #778993, and the copy matches the old welcome template exactly). That specific footer was already fixed: since the unified email wrapper (#26898), newsletter and welcome email footers render rgba(0,0,0,0.6) over white → #6666665.74:1, which passes AA.

What still fails today is the transactional email suite, which never went through that unification: 38 hardcoded #738A94 occurrences across 24 files. This PR sweeps those, which removes the last shipping source of the failing grey.

Change

#738A94#5B6B73 — same slate hue, one step darker, so the muted-text hierarchy against #3A464C/#15212A body text is preserved:

Surface Before After
White background 3.63:1 ❌ 5.51:1 ✅
#EEF5F8 boxes 3.30:1 ❌ 5.02:1 ✅

Strictly a color-value swap: no markup, sizing, layout, or copy changes. Newsletter/welcome templates (email-rendering/, email-service/email-templates/) are intentionally untouched — they already pass.

Screenshots

Member sign-in email, rendered at both values:

Before/after side by side

Footer close-up:

Footer zoom

Verified end-to-end on a fresh local site through Mailpit:

Real email in Mailpit

Testing

  • grep -ri "738a94" ghost/core/core/server → 0 matches
  • Updated the three unit tests that assert the hex (comments renderer, gift email service, email content generator) and regenerated the 5 affected snapshots — every hunk in the diff is the color swap only
  • Affected suites green: unit + e2e newsletters, members, send-magic-link, legacy authentication, email-previews
  • pnpm check run locally: no failures introduced by this change — the only failing tasks reproduce identically on an untouched main checkout (two timezone-sensitive unit tests on a UTC+7 machine, plus an admin-x-framework typecheck failure)
  • Manually verified via Mailpit on a fresh dev install (screenshot above)

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: f82499f2-c383-4cae-8c7a-ccafba6f4e91

📥 Commits

Reviewing files that changed from the base of the PR and between a509378 and ecb5271.

⛔ Files ignored due to path filters (5)
  • ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/admin/__snapshots__/newsletters.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/e2e-api/members/__snapshots__/send-magic-link.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/legacy/api/admin/__snapshots__/authentication.test.js.snap is excluded by !**/*.snap
  • ghost/core/test/unit/server/services/settings/__snapshots__/settings-bread-service.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (28)
  • ghost/core/core/server/data/importer/email-template.ts
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs
  • ghost/core/core/server/services/comments/email-templates/new-comment.hbs
  • ghost/core/core/server/services/comments/email-templates/report.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs
  • ghost/core/core/server/services/gifts/gift-email-service.ts
  • ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html
  • ghost/core/core/server/services/mail/templates/invite-user.html
  • ghost/core/core/server/services/mail/templates/notification.html
  • ghost/core/core/server/services/mail/templates/reset-password.html
  • ghost/core/core/server/services/mail/templates/welcome.html
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/test/unit/server/services/lib/fixtures/templates/welcome.html

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (10)
Review new or changed service boundaries for explicit dependency ownership,

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs
  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/mail/templates/reset-password.html
  • ghost/core/core/server/services/mail/templates/welcome.html
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/comments/email-templates/new-comment.hbs
  • ghost/core/core/server/services/comments/email-templates/report.hbs
  • ghost/core/core/server/services/mail/templates/notification.html
  • ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html
  • ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs
  • ghost/core/core/server/services/mail/templates/invite-user.html
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
  • ghost/core/core/server/services/gifts/gift-email-service.ts
Review whether tests prove changed behaviour, meaningful error/edge paths, and

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
New source files must be TypeScript: flag new JS files as a required change

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
Review lens: "where does this data become trusted?"

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/core/server/data/importer/email-template.ts
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/gifts/gift-email-service.ts
Prioritise concrete correctness, security, data-integrity, compatibility,

⚙️ CodeRabbit configuration file

Files:

  • ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs
  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/mail/templates/reset-password.html
  • ghost/core/core/server/services/mail/templates/welcome.html
  • ghost/core/test/unit/server/services/lib/fixtures/templates/welcome.html
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
  • ghost/core/core/server/data/importer/email-template.ts
  • ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/comments/email-templates/new-comment.hbs
  • ghost/core/core/server/services/comments/email-templates/report.hbs
  • ghost/core/core/server/services/mail/templates/notification.html
  • ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html
  • ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs
  • ghost/core/core/server/services/mail/templates/invite-user.html
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
  • ghost/core/core/server/services/gifts/gift-email-service.ts
Boot owns service initialization; do not

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs
  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/mail/templates/reset-password.html
  • ghost/core/core/server/services/mail/templates/welcome.html
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/comments/email-templates/new-comment.hbs
  • ghost/core/core/server/services/comments/email-templates/report.hbs
  • ghost/core/core/server/services/mail/templates/notification.html
  • ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html
  • ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs
  • ghost/core/core/server/services/mail/templates/invite-user.html
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
  • ghost/core/core/server/services/gifts/gift-email-service.ts
Type-safe boundaries: Fail only if the PR:

📄 CodeRabbit inference engine (Custom checks)

Files:

  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/core/server/data/importer/email-template.ts
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/gifts/gift-email-service.ts
New standalone services use TypeScript; keep CommonJS only

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/gifts/gift-email-service.ts
New files are TypeScript: Fail if the PR adds a new .js/.jsx/.cjs/.mjs source file, unless it is: a DB

📄 CodeRabbit inference engine (Custom checks)

Files:

  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
Always use `pnpm`, never npm or Yarn.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs
  • ghost/core/core/server/services/members/emails/signup.js
  • ghost/core/core/server/services/mail/templates/reset-password.html
  • ghost/core/core/server/services/mail/templates/welcome.html
  • ghost/core/test/unit/server/services/lib/fixtures/templates/welcome.html
  • ghost/core/core/server/services/members/emails/signup-paid.js
  • ghost/core/core/server/services/members/emails/subscribe.js
  • ghost/core/core/server/services/settings/emails/verify-email.js
  • ghost/core/test/unit/server/services/gifts/gift-email-service.test.js
  • ghost/core/test/unit/server/services/lib/email-content-generator.test.ts
  • ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js
  • ghost/core/core/server/data/importer/email-template.ts
  • ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs
  • ghost/core/core/server/services/members/emails/signin.js
  • ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs
  • ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs
  • ghost/core/core/server/services/auth/session/emails/signin.js
  • ghost/core/core/server/services/comments/email-templates/new-comment.hbs
  • ghost/core/core/server/services/comments/email-templates/report.hbs
  • ghost/core/core/server/services/mail/templates/notification.html
  • ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html
  • ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs
  • ghost/core/core/server/services/mail/templates/invite-user.html
  • ghost/core/core/server/services/members/emails/update-email.js
  • ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs
  • ghost/core/core/server/services/members/import-export/import/email-template.ts
  • ghost/core/core/server/services/newsletters/emails/verify-email.js
  • ghost/core/core/server/services/gifts/gift-email-service.ts
🔇 Additional comments (28)
ghost/core/core/server/data/importer/email-template.ts (1)

216-216: LGTM!

ghost/core/core/server/services/comments/email-templates/new-comment-reply.hbs (1)

177-182: LGTM!

ghost/core/core/server/services/comments/email-templates/new-comment.hbs (1)

177-182: LGTM!

ghost/core/core/server/services/comments/email-templates/report.hbs (1)

172-177: LGTM!

ghost/core/core/server/services/members/emails/signup-paid.js (1)

152-152: LGTM!

ghost/core/core/server/services/members/emails/signup.js (1)

153-158: LGTM!

ghost/core/core/server/services/members/emails/subscribe.js (1)

153-158: LGTM!

ghost/core/core/server/services/members/emails/update-email.js (1)

149-154: LGTM!

ghost/core/core/server/services/members/import-export/import/email-template.ts (1)

208-208: LGTM!

ghost/core/core/server/services/gifts/email-templates/gift-buyer-notice-layout.hbs (1)

53-53: LGTM!

ghost/core/core/server/services/gifts/email-templates/gift-delivery.hbs (1)

90-95: LGTM!

ghost/core/core/server/services/mail/templates/notification.html (1)

103-103: LGTM!

Also applies to: 144-144

ghost/core/core/server/services/mail/templates/reset-password.html (1)

141-141: LGTM!

ghost/core/core/server/services/mail/templates/welcome.html (1)

146-146: LGTM!

ghost/core/core/server/services/staff/email-templates/new-milestone-received.hbs (1)

117-122: LGTM!

ghost/core/core/server/services/auth/session/emails/signin.js (1)

157-157: LGTM!

ghost/core/core/server/services/gifts/gift-email-service.ts (1)

167-167: LGTM!

ghost/core/core/server/services/members/emails/signin.js (1)

197-202: LGTM!

ghost/core/test/unit/server/services/gifts/gift-email-service.test.js (1)

552-552: LGTM!

ghost/core/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs (1)

60-60: LGTM!

ghost/core/core/server/services/gifts/email-templates/gift-reminder.hbs (1)

69-69: LGTM!

ghost/core/core/server/services/mail/templates/invite-user-by-api-key.html (1)

145-145: LGTM!

ghost/core/core/server/services/mail/templates/invite-user.html (1)

146-146: LGTM!

ghost/core/core/server/services/newsletters/emails/verify-email.js (1)

150-155: LGTM!

ghost/core/core/server/services/settings/emails/verify-email.js (1)

131-131: LGTM!

Also applies to: 145-151

ghost/core/test/unit/server/services/comments/comments-service-emails-renderer.test.js (1)

55-55: LGTM!

ghost/core/test/unit/server/services/lib/email-content-generator.test.ts (1)

24-24: LGTM!

ghost/core/test/unit/server/services/lib/fixtures/templates/welcome.html (1)

146-146: LGTM!


Walkthrough

Email footers, notices, links, and one verification URL now use #5B6B73 instead of #738A94. The gift email fallback color uses the same value. Existing email content, links, interpolation, and template structure remain unchanged. Rendering and fallback tests now expect the updated color.

Possibly related PRs

  • TryGhost/Ghost#29680: Updates the same transactional email footer colors across related templates and tests.

Suggested reviewers: 9larsons, kevinansfield

Merge Risk: ⚪ Minimal · up to ecb52

This PR only darkens muted transactional-email text to improve accessibility contrast, without changing markup, layout, sizing, or copy. No actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: improved muted text contrast in transactional emails.
Description check ✅ Passed The description explains the contrast problem, affected email flows, color change, scope, and testing. It is directly related to the changeset.
Linked Issues check ✅ Passed The changes address issue #29666 by replacing the failing muted footer color in transactional email templates with #5B6B73, which the PR reports as meeting WCAG 1.4.3 AA contrast requirements. Related…
Out of Scope Changes check ✅ Passed The changes are limited to transactional email color replacements and corresponding test or fixture expectations. No unrelated production behavior, layout, markup, or copy changes are identified.
Type-Safe Boundaries ✅ Passed PASS — The pull request changes only the color literal in 38 production-server occurrences, plus matching test and snapshot expectations. Diff comparison shows every added/removed source line is ident…
New Files Are Typescript ✅ Passed PASS: Against origin/main, the PR has 33 modified paths and no added or renamed paths. The 10 changed .js files are pre-existing files, so the explicit failure condition is not met.
Full details: Linked Issues check

Explanation

The changes address issue #29666 by replacing the failing muted footer color in transactional email templates with #5B6B73, which the PR reports as meeting WCAG 1.4.3 AA contrast requirements. Related tests and fixtures were updated.

Full details: Type-Safe Boundaries

Explanation

PASS — The pull request changes only the color literal in 38 production-server occurrences, plus matching test and snapshot expectations. Diff comparison shows every added/removed source line is identical after replacing #738A94 with #5B6B73. No boundary reads, validation changes, any, unchecked casts, or TypeScript suppression directives were introduced.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

ref TryGhost#29666

Muted text in transactional emails (magic links, invites, password
resets, comment/gift notifications, verify emails) was hardcoded to
that WCAG 1.4.3 requires for small text. Replaced it with #5B6B73,
which keeps the same slate hue while reaching 5.5:1 on white and
5.0:1 on the #EEF5F8 boxes some templates use.

Newsletter and welcome email footers are intentionally untouched:
since the unified email wrapper (TryGhost#26898) they render rgba(0,0,0,0.6)
over white (#666666, 5.74:1), which already meets AA.
@si-huynh
si-huynh force-pushed the a11y-email-footer-contrast branch from 57015b5 to ecb5271 Compare August 26, 2026 01:12
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@si-huynh

Copy link
Copy Markdown
Author

Rebased onto latest main. The conflict came from #30091, which refactored the gift buyer-notice emails onto a shared gift-buyer-notice-layout partial. Resolved by taking the refactored template and moving the contrast fix into that partial, so gift-delivery-failure and gift-sent-confirmation are both covered from one place — gift-delivery-failure.hbs drops out of this diff as a result.

No source email template retains #738A94 now. Remaining occurrences in the tree are deliberately out of scope: Admin CSS, Portal's gift page, the Casper/test-theme fixtures, and the Koenig bookmark-card snapshot (separate package).

Green locally: the four email unit suites, lint:server, lint:types, plus the four e2e snapshot suites this touches (members, newsletters, send-magic-link, authentication) — all snapshots still match after the rebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[a11y] Email footer text doesn't have enough contrast against the background

1 participant