Skip to content

feat(accessibility): ARIA attributes, responsive modes, keyboard support, list normalisation - #3117

Open
dazgreer wants to merge 7 commits into
feature/dark-mode-supportfrom
feature/accessibility-support
Open

feat(accessibility): ARIA attributes, responsive modes, keyboard support, list normalisation#3117
dazgreer wants to merge 7 commits into
feature/dark-mode-supportfrom
feature/accessibility-support

Conversation

@dazgreer

@dazgreer dazgreer commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

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

  • Updated all default font-size to 16px and line-height to 150% (and auto added mso-line-height of 120%) [BREAKING CHANGE]

Testing

  • Added various regression tests

Documentation

  • Updated docs with new attributes

dazgreer added 2 commits July 22, 2026 12:32
…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
@dazgreer dazgreer changed the title Feature/accessibility support feat(accessibility): ARIA attributes, responsive modes, keyboard support, list normalisation Jul 22, 2026
- `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
dazgreer force-pushed the feature/accessibility-support branch from eceb8de to aec1520 Compare August 5, 2026 15:08
- 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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
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.

2 participants