feat(accessibility): ARIA attributes, responsive modes, keyboard support, list normalisation - #3117
Open
dazgreer wants to merge 7 commits into
Open
feat(accessibility): ARIA attributes, responsive modes, keyboard support, list normalisation#3117dazgreer wants to merge 7 commits into
dazgreer wants to merge 7 commits into
Conversation
…ort, list normalisation - Added ARIA attribute support (aria-label, aria-roledescription, aria-hidden role) across all relevant components - Added responsive-mode to mj-table (“stack" and "scroll”) and to mj-navbar (“block”) to give more accessible layout options for narrow screens - Added normalize-elements attribute to mj-text to normalize rendering of <ul> and <ol> lists. - Updated all default font-size to 16px and line-height to 150% (and auto added mso-line-height of 120%) - Expanded mj-table role attribute to accept "table" in addition to "none" and "presentation". - Added keyboard accessibility and default ARIA attributes to mj-accordion and mj-carousel - Updated mj-divider to use a semantic <hr> - Added various regression tests - Updated docs
- `font-size` now includes `rem` units and `line-height` includes `em` and `rem` - Moved some ARIA tags for better client coverage, following testing - Removed default `aria-hidden` on mj-divider and removed regression test
dazgreer
force-pushed
the
feature/accessibility-support
branch
from
August 5, 2026 15:08
eceb8de to
aec1520
Compare
- Added the width="0" to hide the <hr> in Outlook 2016
- Changed `responsive-mode` option from `block` to `stack` for better consistency of the option across components
There was a problem hiding this comment.
Pull request overview
Adds broad accessibility and responsive-rendering enhancements across MJML components.
Changes:
- Adds ARIA attributes, keyboard focus support, and semantic divider output.
- Adds responsive table/navbar modes and list normalization.
- Standardizes typography defaults and expands regression tests/documentation.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
yarn.lock |
Updates dependency registry URLs. |
packages/mjml/test/text-normalize-elements.test.js |
Tests list normalization. |
packages/mjml/test/table-narrow-mode.test.js |
Tests responsive tables. |
packages/mjml/test/body-font-size.test.js |
Tests body font fallback. |
packages/mjml/test/beautify-output.test.js |
Updates typography expectations. |
packages/mjml/test/aria-attributes.test.js |
Tests ARIA rendering. |
packages/mjml-wrapper/README.md |
Documents wrapper ARIA attributes. |
packages/mjml-text/src/index.js |
Adds normalization and typography defaults. |
packages/mjml-text/README.md |
Documents text changes. |
packages/mjml-table/src/index.js |
Adds responsive modes and ARIA support. |
packages/mjml-table/README.md |
Documents responsive tables. |
packages/mjml-spacer/src/index.js |
Adds default aria-hidden. |
packages/mjml-spacer/README.md |
Documents spacer ARIA behavior. |
packages/mjml-social/src/SocialElement.js |
Updates typography handling. |
packages/mjml-social/src/Social.js |
Updates social defaults. |
packages/mjml-social/README.md |
Documents social typography. |
packages/mjml-section/src/index.js |
Adds section ARIA support. |
packages/mjml-section/README.md |
Documents section ARIA attributes. |
packages/mjml-parser-xml/src/helpers/convertBooleansOnAttrs.js |
Preserves string ARIA values. |
packages/mjml-navbar/src/NavbarLink.js |
Updates link typography. |
packages/mjml-navbar/src/Navbar.js |
Adds responsive and keyboard behavior. |
packages/mjml-navbar/README.md |
Documents navbar changes. |
packages/mjml-image/src/index.js |
Adds image ARIA and font sizing. |
packages/mjml-image/README.md |
Documents image changes. |
packages/mjml-hero/src/index.js |
Adds hero ARIA support. |
packages/mjml-hero/README.md |
Documents hero ARIA attributes. |
packages/mjml-group/src/index.js |
Adds group ARIA support. |
packages/mjml-group/README.md |
Documents group ARIA attributes. |
packages/mjml-divider/src/index.js |
Introduces semantic divider output. |
packages/mjml-divider/README.md |
Documents divider ARIA behavior. |
packages/mjml-column/src/index.js |
Adds column ARIA support. |
packages/mjml-column/README.md |
Documents column ARIA attributes. |
packages/mjml-carousel/src/CarouselImage.js |
Adds slide ARIA and keyboard behavior. |
packages/mjml-carousel/src/Carousel.js |
Adds carousel ARIA and focus styles. |
packages/mjml-carousel/README.md |
Documents carousel accessibility. |
packages/mjml-button/src/index.js |
Updates button typography. |
packages/mjml-button/README.md |
Documents button typography. |
packages/mjml-body/src/index.js |
Adds body font-size fallback. |
packages/mjml-accordion/src/AccordionTitle.js |
Updates title font size. |
packages/mjml-accordion/src/AccordionText.js |
Adds region role and typography. |
packages/mjml-accordion/src/Accordion.js |
Adds ARIA and keyboard focus support. |
packages/mjml-accordion/README.md |
Documents accordion changes. |
Suppressed comments (2)
packages/mjml-accordion/README.md:121
- The role is applied to the accordion table, not a slide container. Update the copied description to identify the accordion container.
| role | string | adds a `role` attribute to the slide container | |
packages/mjml-carousel/README.md:43
- This role is emitted on the main carousel table, not on a slide container. Update the description to identify the carousel container.
| role | string | adds a `role` attribute to the slide container | |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| * in browser environments where cheerio is not available. | ||
| */ | ||
| function normalizeListContent(html, elements) { | ||
| const tagRe = /<(\/?)([a-zA-Z][a-zA-Z0-9]*)(\s[^>]*)?>/g |
Comment on lines
+108
to
+110
| } else if (!t.isClose && t.tagName === 'li' && listStack.length > 0) { | ||
| // Register this <li> with the innermost active list. | ||
| listStack[listStack.length - 1].liItems.push({ |
Comment on lines
+305
to
+313
| <table | ||
| ${this.htmlAttributes({ | ||
| border: '0', | ||
| cellpadding: '0', | ||
| cellspacing: '0', | ||
| class: 'mj-scroll-table-outer', | ||
| width: '100%', | ||
| })} | ||
| > |
Comment on lines
+250
to
+252
| let colIndex = 0 | ||
| return html.replace( | ||
| /(<\/?\s*tr\b[^>]*>)|(<th(?:\s[^>]*)?>)|(<td(\s[^>]*)?>)/gi, |
Comment on lines
+355
to
+357
| role: this.getAttribute('role'), | ||
| 'aria-label': this.getAriaLabel(), | ||
| 'aria-roledescription': this.getAttribute('aria-roledescription'), |
| | padding-left | `px` `%` | accordion text left padding | | | ||
| | padding-right | `px` `%` | accordion text right padding | | | ||
| | padding-top | `px` `%` | accordion text top padding | | | ||
| | role | string | adds a `role` attribute to the slide container | `region` | |
| this.context.globalData.supportOutlookClassic !== false | ||
|
|
||
| const checkbox = `<input type="checkbox" id="${labelKey}" class="mj-menu-checkbox" style="display:none !important; max-height:0; visibility:hidden;" />\n ` | ||
| const checkbox = `<input type="checkbox" id="${labelKey}" class="mj-menu-checkbox" style="display: block !important; position: absolute; opacity: 0; pointer-events: none;" />\n ` |
| > | ||
| <tr> | ||
| <td> | ||
| <div class="mj-scroll-table-inner" role="region" tabindex="0"> |
| chai.expect(img.attr('aria-hidden')).to.equal('true') | ||
| }) | ||
|
|
||
| it('should allow aria-hidden false for decorative images', async function () { |
Comment on lines
+30
to
+31
| | aria-label | string | adds an `aria-label` attribute to the slide container | | | ||
| | aria-roledescription | string | adds an `aria-roledescription` attribute to the slide container | | |
- Added `aria-hidden="true"` to `mj-social-element` when no text is present. Applies to either `<img>` or `<a>` depending if `href` attribute is present. If added to `<a>` then `tabindex="-1"` is also added - Updated `aria-hidden` implementation on `mj-image` to replicate the above except it's not applied by default. Also doesn't show the attribute when set to `false` - Updated `mj-divider` and `mj-spacer` so `aria-hidden` doesn't show the attribute when set to `false` - Updated documentation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's changed
Features
Added ARIA attribute support (aria-label, aria-roledescription, aria-hidden role) across all relevant components
Added responsive-mode to mj-table (“stack" and "scroll”) and to mj-navbar (“block”) to give more accessible layout options for narrow screens
Added normalize-elements attribute to mj-text to normalize rendering of and lists.
Expanded mj-table role attribute to accept "table" in addition to "none" and "presentation".
Added keyboard accessibility and default ARIA attributes to mj-accordion and mj-carousel
Updated mj-divider to use a semantic
<hr>Refactor
Testing
Documentation