From e94b782f8eb7eb71bee0b5723acb5c8a8800d0dd Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 15 Oct 2026 03:18:58 +0200 Subject: [PATCH] fix: keep mail lines a readable length on wide screens --- src/lib/__tests__/email-html.test.ts | 10 ++++++++++ src/lib/email-html.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/__tests__/email-html.test.ts b/src/lib/__tests__/email-html.test.ts index 3bdd28a7..c69f3bf6 100644 --- a/src/lib/__tests__/email-html.test.ts +++ b/src/lib/__tests__/email-html.test.ts @@ -8,6 +8,7 @@ import { hasRemoteContent, prepareEmailHtml, wrapPlainTextEmail, + wrapEmailHtml, hasNativeDarkMode, sniffImageMime, TRANSPARENT_BLOCKED_PIXEL, @@ -253,3 +254,12 @@ describe('sniffImageMime', () => { expect(sniffImageMime(new Uint8Array([1, 2, 3]), 'application/octet-stream')).toBe('application/octet-stream'); }); }); + +describe('reading width on a wide pane', () => { + it('caps HTML and plain-text bodies only above the tablet breakpoint', () => { + const html = wrapEmailHtml('

Hello

'); + expect(html).toMatch(/@media \(min-width: 840px\) \{\s*body \{ max-width: 720px;/); + const plain = wrapPlainTextEmail('Hello'); + expect(plain).toContain('@media (min-width: 840px) { body { max-width: 80ch; } }'); + }); +}); diff --git a/src/lib/email-html.ts b/src/lib/email-html.ts index 37020889..8cd28df1 100644 --- a/src/lib/email-html.ts +++ b/src/lib/email-html.ts @@ -449,6 +449,11 @@ body { word-wrap: break-word; overflow-wrap: break-word; } +/* A wide reading pane (tablet): keep lines readable instead of running + 150+ characters across. Phones never reach the breakpoint. */ +@media (min-width: 840px) { + body { max-width: 720px; margin-left: auto; margin-right: auto; } +} /* Only force the cap on images and tables that set no max-width of their own: an !important 100% also overrides a sender's inline max-width, so an image sized by max-width + max-height + width:100% grows to the pane width while @@ -676,7 +681,7 @@ export function wrapPlainTextEmail( const { isDark = true, font = 'sans' } = options; const cleaned = stripDangerousTags(innerHtml); - const bg = isDark ? '#09090b' : '#ffffff'; + const bg = isDark ? '#0a0a0a' : '#ffffff'; const fg = isDark ? '#fafafa' : '#1a1a1a'; const link = isDark ? '#60a5fa' : '#1a73e8'; const fontFamily = font === 'mono' @@ -701,6 +706,8 @@ body { } a { color: ${link}; text-decoration: underline; } details > summary::-webkit-details-marker { display: none; } +/* A wide reading pane (tablet): about 80 characters a line. */ +@media (min-width: 840px) { body { max-width: 80ch; } } `; return `