From ee9ede87532cd0dda3172e1bb076a69a86466c0f Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sun, 4 Oct 2026 20:33:28 -0700 Subject: [PATCH] Let mail wider than the pane scroll sideways The HTML mail root has contain: content, which clips anything that overflows it, and nothing let it scroll. Mail built to a fixed width that cannot shrink (a min-width table, a nowrap cell, a nested fixed layout) was cut off at the pane's edge, with no way to reach the rest. That is worst on a phone, where most marketing mail is wider than the screen. The root now scrolls sideways (overflow-x: auto). Vertical overflow stays clipped, as containment already made it, so no second scrollbar appears. Mail that fits shows no scrollbar. Verified in the browser on the mock with a 1200px min-width table in an 871px pane: on main the right 345px were clipped and unreachable; with this change the root scrolls and the last cell comes into view. Plain fixed-width tables were already squeezed to fit by max-width: 100%, and still are. Not tried by touch on a real phone. No new strings. Fixes #24. Reported by @mbjboon82. --- web/src/lib/text/__tests__/html.test.ts | 13 +++++++++++++ web/src/lib/text/html.ts | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/web/src/lib/text/__tests__/html.test.ts b/web/src/lib/text/__tests__/html.test.ts index b610cf4..6baa3db 100644 --- a/web/src/lib/text/__tests__/html.test.ts +++ b/web/src/lib/text/__tests__/html.test.ts @@ -387,3 +387,16 @@ describe("the containment that mail CSS cannot override", () => { expect(rule).toMatch(/contain\s*:\s*layout/); }); }); + +describe("mail wider than the reading pane", () => { + /* + * The root keeps `contain: content`, which clips what overflows, so without + * a scroll the right side of a min-width table was simply gone, worst on a + * phone (Gitea issue #24). jsdom does no layout, so this pins the rule. + */ + it("scrolls sideways instead of being cut off", () => { + const root = EMAIL_BASE_CSS.match(/\.ihm-email-root \{([^}]*)\}/)?.[1] ?? ""; + expect(root).toMatch(/contain:\s*content/); + expect(root).toMatch(/overflow-x:\s*auto/); + }); +}); diff --git a/web/src/lib/text/html.ts b/web/src/lib/text/html.ts index a2eb1ed..1bdf850 100644 --- a/web/src/lib/text/html.ts +++ b/web/src/lib/text/html.ts @@ -287,7 +287,10 @@ export function sanitizeEditorHtml(input: string): string { export const EMAIL_BASE_CSS = ` :host { display:block; color-scheme: light; } :host(.themed) { color-scheme: inherit; } -.ihm-email-root { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.5; color:#1f2937; background:#fff; padding:16px; border-radius:8px; overflow-wrap:anywhere; word-break:normal; contain: content; } +/* contain: content clips what overflows, so mail built wider than the pane -- + a min-width table, a nowrap cell -- was cut off with no way to reach the + rest. It scrolls sideways instead (Gitea issue #24). */ +.ihm-email-root { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.5; color:#1f2937; background:#fff; padding:16px; border-radius:8px; overflow-wrap:anywhere; word-break:normal; contain: content; overflow-x:auto; overflow-y:hidden; } .ihm-email-root img { max-width:100%; height:auto; } .ihm-email-root img[data-ihm-blocked] { display:inline-block; min-width:16px; min-height:16px; background:#f1f5f9 repeating-linear-gradient(45deg,#e2e8f0 0 6px,#f1f5f9 6px 12px); border:1px dashed #cbd5e1; } .ihm-email-root table { max-width:100%; } -- 2.54.0