Pressroom email output: Accessibility Conformance Report

Version: main at 526b67bf (2026-10-09).

Last reviewed .

WCAG edition, based on VPAT® 2.5 (Rev, WCAG 2.2).

Name of product and version

Pressroom newsletter email output: the HTML email and its text/plain part that Pressroom renders for an edition, and the same stored HTML when it is served as a web page (the public archive edition page /archive/{code}, the web view /v/{token} and a shared preview /p/{token}).

Version: main at 526b67bf (2026-10-09).

Report date

2026-10-09. Last reviewed 2026-10-09.

Product description

Pressroom is a multi-tenant newsletter editorial platform. Editors assemble an edition from articles, and Pressroom compiles it through MJML into one email document. Each tenant renders through a shell. This report covers the two shell families that ship today:

  • D10 shell (denver-perfect-10, src/lib/newsletter/templates/tenants/denver-perfect-10/), the only shell where the validator's blocking rules are enforced at generation (ENFORCED_SHELLS in src/lib/newsletter/validate/validator.ts);
  • generic shells (src/lib/newsletter/templates/snippets.ts and templates/shells/*), where the same validator report is attached and logged but does not stop generation unless NEWSLETTER_VALIDATION_ENFORCE=all is set.

Where the two differ, the remark says so.

Contact information

Report accessibility problems through the route on the public statement page /accessibility (the deployment's HELP_URL, or the newsletter's contact address on a newsletter's own host). Issues are tracked in the Pressroom repository (Gitea chris/pressroom).

Notes

  • Self-authored. Nobody outside the project has audited this output. A third-party audit is planned only if a procurement process asks for one (owner decision #1352 D-4).
  • Content is the publisher's. Pressroom controls the template, the markup it generates and the checks it runs. Editors supply headlines, body text, images and alt text. Several criteria therefore read "Partially Supports": the product checks for the failure and blocks sign-off on it, but an editor can override a finding with a reason and ship.
  • Email clients vary. The reader's client (Outlook, Gmail, Apple Mail and others) decides how much of the HTML and CSS is honoured, and it owns keyboard focus, zoom and the reading order of its own interface. Remarks describe what Pressroom emits; they do not certify any client.
  • No real-client or assistive-technology testing. Every client verdict below comes from headless Chromium renders under per-client transforms and the caniemail dataset (2026-09-13 audit). No screen reader was used on the email output.
  • The staff (editorial) web UI is not covered here. It was audited on 2026-09-13 against WCAG 2.2 AA, did not conform, and is being remediated; a separate UI report will follow once that work lands (#1128, #1129, #1190).
  • Cross-reference (2026-10-26, not a re-version of this report): the staff UI and the reader pages are rated in app-acr.md (#2142).

Evaluation methods used

  1. validate-newsletter (src/lib/newsletter/validate/validator.ts, CLI validate/cli.ts). Sixteen rules over the compiled HTML. The accessibility rules are 3.1 text contrast, 3.2 semantic headings, 3.3 layout tables carry role="presentation", 3.4 descriptive link text, 3.5 image alt text (missing, empty without aria-hidden, placeholder, duplicate) and 3.6 numeric image widths. All six are blocking; on the D10 shell a failure stops generation.
  2. QA linters (src/lib/newsletter/qa/), run on every render and shown on Review & QA. Accessibility findings: a11y.alt-*, a11y.heading-order, a11y.vague-link-text, a11y.dark-mode-contrast (computed backgrounds under the shipped dark CSS, dark-css.ts), wcag.1-4-3.contrast, wcag.3-1-1.lang, wcag.2-4-2.title, wcag.1-4-4.font-size (under 12px), wcag.2-4-6.no-headings, wcag.1-3-1.multiple-h1, wcag.1-3-1.table-nesting, wcag.2-5-8.target-size, and rendering.mobile-min-width (360px min-content estimate). Open warnings and fails block the edition's sign-off until an editor resolves them or overrides them with a reason (countOpenQaGate, edition-qa.ts).
  3. Renderer behaviour read from source: generic link labels get an aria-label that starts with the visible text (link-a11y.ts), alt-text rules shared by renderer and editor (alt-text.ts), a generated text/plain part (plain-text.ts), and a sanitizer that drops scripts, iframes, objects and forms (sanitize.ts).
  4. Email client rendering audit, 2026-09-13 (11-audit/email-client-rendering-audit-2026-09-13.md, crops in 11-audit/rendering-audit-2026-09/). Findings F1 (dark mode blanked article text in Apple Mail, 69 of 119 text runs failing AA), F2 (Outlook buttons), F3 (360px overflow) and F4 (:not() dark palette) were fixed in PR #449 on the same day, and the three QA rules it recommended were added. No re-measurement of the 69/119 figure in real clients has been done since.
  5. UI accessibility audit, 2026-09-13 (11-audit/ui-accessibility-audit-2026-09-13.md on branch worktree-audit-ui-accessibility). Out of scope for this report; cited only for the staff-UI status above.

Applicable standards and guidelines

Standard / guidelineIncluded in report
WCAG 2.2 Level AYes
WCAG 2.2 Level AAYes
WCAG 2.2 Level AAANo

Conformance target: WCAG 2.2 Level AA. The output does not yet meet it; see the remarks marked Partially Supports and Does Not Support.

Terms

  • Supports: the functionality of the output has at least one method that meets the criterion without known defects, or meets it with an equivalent facilitation.
  • Partially Supports: some functionality of the output does not meet the criterion.
  • Does Not Support: the majority of the functionality does not meet the criterion.
  • Not Applicable: the criterion is not relevant to the output.

Summary

AAA
Supports135
Partially Supports67
Does Not Support00
Not Applicable1312

One result differs by shell: 1.3.1 (headings enforced on D10, reported on generic). That row counts once, under its weaker result.

WCAG 2.2 report

Table 1: Success Criteria, Level A

CriteriaConformance levelRemarks and explanations
1.1.1 Non-text ContentPartially SupportsEvery <img> must carry alt; decorative images use alt="" with aria-hidden="true" (validator 3.5, QA a11y.alt-missing fail). Placeholder ("image", file names) and duplicate alt text are reported (a11y.alt-redundant, a11y.alt-duplicate, warn). The rules check that alt text exists and is not obviously empty of meaning; they cannot judge whether an editor's alt describes the image, and a warning can be overridden.
1.2.1 Audio-only and Video-only (Prerecorded)Not ApplicableThe output embeds no audio or video; the sanitizer removes iframes and objects. A link to media hosted elsewhere is out of scope.
1.2.2 Captions (Prerecorded)Not ApplicableAs 1.2.1.
1.2.3 Audio Description or Media Alternative (Prerecorded)Not ApplicableAs 1.2.1.
1.3.1 Info and RelationshipsPartially SupportsHeadings are real h1–h3 elements in both shell families (generic shells since #677); validator 3.2 requires an h1 masthead and at least one heading, and QA reports skipped levels, more than one h1, and invalid table nesting (wcag.1-3-1.table-nesting, fail). Layout tables carry role="presentation" (validator 3.3). Enforced at generation on the D10 shell only; on generic shells the same failures are reported and block sign-off but not generation. Lists, quotes and emphasis inside article bodies come from editor content and are not checked.
1.3.2 Meaningful SequenceSupportsSource order is reading order: single-column sections, and multi-column rows (card grid, brief grid) stack in DOM order below 480px. Presentation tables keep screen readers from announcing grid structure.
1.3.3 Sensory CharacteristicsSupportsTemplate copy does not rely on shape, colour or position ("click the red button"). Editor copy is not checked.
1.4.1 Use of ColorPartially SupportsFooter and source-credit links are underlined, and the generic shell's head CSS underlines links. Article links that rely on the editor's body markup, and D10 card and resource rows, are distinguished by layout and button shape rather than underline, and no rule checks link distinguishability.
1.4.2 Audio ControlNot ApplicableNo audio.
2.1.1 KeyboardSupportsThe only interactive elements are links (poll answers are signed links, not forms; the sanitizer drops forms and scripts), which every client makes keyboard-operable.
2.1.2 No Keyboard TrapSupportsNo scripted or embedded content that could hold focus.
2.1.4 Character Key ShortcutsNot ApplicableThe output defines no keyboard shortcuts.
2.2.1 Timing AdjustableNot ApplicableNo time limits.
2.2.2 Pause, Stop, HidePartially SupportsTemplates contain no moving or auto-updating content. An editor-uploaded animated GIF ships as-is; nothing detects animation or limits it to five seconds.
2.3.1 Three Flashes or Below ThresholdPartially SupportsTemplates do not flash. Editor-uploaded animated images are not analysed for flashing.
2.4.1 Bypass BlocksSupportsEach edition is one document with no navigation repeated across pages. Semantic headings (validator 3.2) let screen-reader users move between sections.
2.4.2 Page TitledSupportsMJML writes <title> from <mj-title> (the edition subject); QA wcag.2-4-2.title reports a missing or empty title. Relevant to the web view and archive page; email clients show the subject line instead.
2.4.3 Focus OrderSupportsLinks are in DOM order, which matches visual order (see 1.3.2); no tabindex is emitted.
2.4.4 Link Purpose (In Context)Partially SupportsBare generic link text ("click here", "read more", "learn more" and similar) without an aria-label fails validator 3.4 and QA a11y.vague-link-text (fail), and the renderer adds aria-label="Learn more: <headline>" to template-generated generic labels. Limits: the check is a fixed list of 14 generic phrases, so other vague wording passes; the QA finding can be overridden at sign-off; and the validator rule stops generation only on the D10 shell. Whether editor-written link text describes its destination is not judged.
2.5.1 Pointer GesturesNot ApplicableNo path-based or multipoint gestures.
2.5.2 Pointer CancellationSupportsOnly native links, which activate on the up-event.
2.5.3 Label in NameSupportsGenerated aria-labels begin with the visible link text (link-a11y.ts), so a voice command that names the visible label still matches.
2.5.4 Motion ActuationNot ApplicableNo motion-triggered functionality.
3.1.1 Language of PageSupportsEvery shell writes <html lang="en">: the D10 shell, and since #1509 the generic shells (newsletter, single-content, poll) and the block builder, which put lang="en" on the <mjml> root. A saved template whose root has no lang gets en at render. QA wcag.3-1-1.lang warns when <html> has no lang, an empty one or und (what MJML writes when the root has none). Language is fixed to English; no tenant or newsletter language setting exists. Basis for the rating: all current tenants publish in English; a non-English edition would not meet 3.1.1 until a language setting exists.
3.2.1 On FocusSupportsNothing changes on focus.
3.2.2 On InputNot ApplicableNo form inputs.
3.2.6 Consistent HelpNot ApplicableSingle document, not a set of pages.
3.3.1 Error IdentificationNot ApplicableNo form inputs.
3.3.2 Labels or InstructionsNot ApplicableNo form inputs.
3.3.7 Redundant EntryNot ApplicableNo multi-step process.
4.1.1 ParsingSupportsRemoved from WCAG 2.2 (always satisfied per the 2.2 errata for 2.0 and 2.1). Listed for VPAT completeness.
4.1.2 Name, Role, ValueSupportsLinks have accessible names (text or aria-label, see 2.4.4); images have alt; layout tables are role="presentation"; decorative images and icons are aria-hidden. The preheader is the preview text MJML writes from <mj-preview>, hidden with display:none, so it is not exposed in the email body.

Table 2: Success Criteria, Level AA

CriteriaConformance levelRemarks and explanations
1.2.4 Captions (Live)Not ApplicableNo live media.
1.2.5 Audio Description (Prerecorded)Not ApplicableNo video.
1.3.4 OrientationSupportsFluid layout; nothing locks orientation.
1.3.5 Identify Input PurposeNot ApplicableNo inputs.
1.4.3 Contrast (Minimum)Partially SupportsLight mode: validator 3.1 walks inherited colours and fails text below 4.5:1 (3:1 large); QA wcag.1-4-3.contrast reports the same. Dark mode: since PR #449 the dark palette uses positive surface classes rather than :not(), and QA a11y.dark-mode-contrast measures text against computed backgrounds under the shipped dark CSS. Limits: the dark-mode check is a warning, not a validator rule; clients that partially invert colours (Gmail apps) cannot be modelled and are unverified; text inside images is not measured.
1.4.4 Resize TextSupportsText is real text in fluid tables, so client zoom scales it. QA wcag.1-4-4.font-size reports inline sizes under 12px.
1.4.5 Images of TextPartially SupportsTemplate text is HTML text; the masthead is a logotype image with alt text, which the criterion exempts. Editor-uploaded graphics that contain text (event flyers, for example) are not detected.
1.4.10 ReflowPartially SupportsColumns stack below 480px, and QA rendering.mobile-min-width estimates min-content width against a 360px viewport (the 2026-09 audit found and fixed a 373px overflow). Nothing measures the 320px width the criterion uses, and wide editor content (tables, long URLs) is not checked.
1.4.11 Non-text ContrastPartially SupportsButtons are filled cells with text that passes 1.4.3, but no rule measures a button's or icon's contrast against its surroundings, and focus indicators are drawn by the reader's client.
1.4.12 Text SpacingPartially SupportsNot tested. Templates set line-height inline and avoid fixed-height text containers, but whether user spacing overrides apply is up to the client, and nothing verifies that text survives them.
1.4.13 Content on Hover or FocusNot ApplicableNo hover or focus-triggered content.
2.4.5 Multiple WaysNot ApplicableSingle document. (The public archive, a separate web page, lists past editions.)
2.4.6 Headings and LabelsSupportsSection and article titles are headings with descriptive text; QA wcag.2-4-6.no-headings reports long body text with no h1–h3. No form labels exist.
2.4.7 Focus VisibleSupportsTemplates do not suppress link outlines (outline: none is applied only to images), so the client's focus indicator shows. Its appearance is the client's.
2.4.11 Focus Not Obscured (Minimum)SupportsNo sticky or overlaid content.
2.5.7 Dragging MovementsNot ApplicableNo dragging.
2.5.8 Target Size (Minimum)Partially SupportsD10 buttons and resource rows set min-height of 24px or 44px with cell padding that Outlook honours. QA wcag.2-5-8.target-size reports stand-alone links under 24px tall, but as a warning that can be overridden. Links inside sentences are exempt and are not flagged.
3.1.2 Language of PartsPartially SupportsNo mechanism marks a passage in another language (lang on an element); a bilingual edition would not identify its parts. Not an issue for English-only editions.
3.2.3 Consistent NavigationNot ApplicableSingle document.
3.2.4 Consistent IdentificationNot ApplicableSingle document. Across editions, the template keeps the same labels for the same functions (Unsubscribe, Update preferences, View in browser).
3.3.3 Error SuggestionNot ApplicableNo inputs.
3.3.4 Error Prevention (Legal, Financial, Data)Not ApplicableNo transactions. Unsubscribe and preferences are separate web pages, out of scope.
3.3.8 Accessible Authentication (Minimum)Not ApplicableNo authentication.
4.1.3 Status MessagesNot ApplicableThe output has no status messages; nothing updates after load.

Known limitations

  1. Enforcement differs by shell. Validator failures stop generation only on the D10 shell. Other shells report them and require sign-off, but an editor can override a finding with a reason.
  2. Language is fixed to English (3.1.1). Every shell declares lang="en" (generic shells since #1509); there is no tenant or newsletter language setting to declare another. All current tenants publish in English; a non-English edition would not meet 3.1.1 until a language setting exists.
  3. Editor-supplied content is checked, not guaranteed. Alt text quality, images of text, animated GIFs and flashing, and in-body link styling depend on the editor.
  4. No real-client or screen-reader testing. Client verdicts are simulations (headless Chromium with per-client transforms); Gmail's partial dark-mode inversion is unverified.
  5. Not measured: 320px reflow, text-spacing overrides, non-text contrast, and language of parts.
  6. The staff UI is out of scope and does not yet conform (2026-09-13 audit); a separate report will follow.

This report is self-authored by the Pressroom project. It describes the output as of the version and date above, from the evidence listed under Evaluation methods. It is not a third-party certification.