Skip to content

Conversation

@mgadewoll
Copy link
Contributor

@mgadewoll mgadewoll commented Jul 28, 2025

Summary

closes #8821

This PR introduces a new component: EuiLiveAnnouncer. This component aims to provide an alternative way (to EuiScreenReaderLive) of handling live region announcements.

Information about the testing scope

It's not feasible for us to test all possible screen reader and browser combinations but we can determine a set that covers the majority of users. Based on the latest screen reader survey we can make some generic assumptions:

If we test the following combinations we can cover ~ 74% of usages:

  • JAWS/Chrome
  • NVDA/Chrome
  • JAWS/Edge (Chromium based)
  • NVDA/Firefox
  • VoiceOver/Safari
Screenshot 2025-07-28 at 09 48 27

The Problem(s)

The limitation of aria-live regions is that they have to be loaded and available in the DOM before content changes are being read out by screen readers. This means that the available component EuiScreenReaderLive currently only reads content updates, but it does not read the content on mount.

EuiScreenReaderLive relies on two sibling aria-live regions which receive content updates in turn. This pattern aims to ensure predictable output even if announcements happen in rapid succession or identical content is passed.
Unfortunately this implementation showed flaws when testing. Especially the oscillating aria-hidden and aria-live attributes (code) cause varying output behaviors across different screen reader and browser combinations. It leads to announcements being skipped at time or not being read at all in some testing scenarios.

🚫 The current implementation results in no content being read at all in VoiceOver/Safari.

The proposed solution(s)

EuiLiveAnnouncer aims to provide support for announcements on mount, e.g. when showing UI based on user interaction. Additionally EuiLiveAnnouncer proposes a different approach to handling aria-live announcements to improve the output support and consistency across screen reader and browser combinations.

Important

EuiLiveAnnouncer is being added as standalone component next to EuiScreenReaderLive to test it while being in Beta status. This way we can adjust and compare behaviors. If the component proves to behave better than EuiScreenReaderLive across the board, the plan would be to replace EuiScreenReaderLive with EuiLiveAnnouncer.

How it's done

  1. Announcements on mount

EuiLiveAnnouncer utilizes a minimal timeout on mount of the component to set the passed content. This means initially the DOM element renders without content and is being updated shortly after with the expected content. This ensures the content is read as an update to the DOM element happened.

  1. Improve consistency of announcements

Instead of using two sibling elements that alternate being updated with content, EuiLiveAnnouncer uses a single DOM element and utilizes React state to set content and trigger re-renders.
Additionally it clears the content after a configurable timeout which ensures that duplicate content can be read as well as preventing duplicate content being available in the DOM for screen reader users to navigate.

Note

Clearing the content did not result in any issues when testing. A message that a screen reader started to read, was being read even after the content was cleared.

Component comparison

Feature EuiLiveAnnouncer EuiScreenReaderLive
Auto-clear message Yes, after configurable timeout (clearAfterMs) No
Announce on mount Yes No
Announce on message change Yes Yes
Focus on message change No (could be added) Yes (focusRegionOnTextChange prop)
Handles rapid updates Yes Yes
Output consistency High Medium
API stability Beta/experimental Stable

Known limitations and issues

Generic

  • 🤷‍♀️ VoiceOver will at times read announcements twice in default Storybook view (it works as expected when viewing stories in standalone page view or actual implementations)

EuiLiveAnnouncer

Issue screen reader recordings
⚠️ Announcements on mount are at times skipped on opening a dialog VoiceOver
Screen.Recording.2025-07-28.at.10.47.27.mov

EuiScreenReaderLive

Issue screen reader recordings
⚠️ Announcements are sometimes skipped All
Screen.Recording.2025-07-28.at.10.40.38.mov
🚫 Announcements not read on mount or change VoiceOver/Safari
Screen.Recording.2025-07-28.at.10.44.27.mov

Why are we making this change?

The new component aims to provide a solution to a missing functionality (live announcements on mount) as well as providing a more consistent live region announcement experience.

Screenshots

Screen recordings
combinations mount & update mount & update in a portalled scope
JAWS/Chromium (Chrome & Edge)
Screen.Recording.2025-07-24.at.19.49.59.mov
Screen.Recording.2025-07-24.at.19.53.54.mov
NVDA/Chrome
Screen.Recording.2025-07-28.at.10.22.16.mov
Screen.Recording.2025-07-28.at.10.27.01.mov
NVDA/Firefox
Screen.Recording.2025-07-24.at.19.57.25.mov
Screen.Recording.2025-07-24.at.19.58.00.mov
VoiceOver/Safari
Screen.Recording.2025-07-24.at.20.00.48.mov
Screen.Recording.2025-07-24.at.20.01.45.mov

Impact to users

🟢 There are no updates needed by consumers.

QA

Test stories:

Added documentation:


  • added unit tests succeed
  • test EuiLiveAnnouncer in different testing scenarios (as mentioned above) and verify that:
    • announcements happen on component mount
    • announcements happen on content update
    • messages are cleared if clearAfterMs is set which means there is no duplicate content in the DOM after the content being cleared
  • compare the behavior of EuiLiveAnnouncer with EuiScreenReaderLive
    • verify that the overall behavior of announcements on content change is the same
    • verify that announcements are more consistent
  • verify that EuiCallOut with announceOnMount is being read on mount
  • verify that EuiCallOut that the screen reader only text added with announceOnMount is being cleared to prevent duplicate DOM content

General checklist

  • Browser QA

    • Checked in both light and dark modes
    • Checked in both MacOS and Windows high contrast modes
    • Checked in mobile
    • Checked in Chrome, Safari, Edge, and Firefox
    • Checked for accessibility including keyboard-only and screenreader modes
  • Docs site QA

  • Code quality checklist

  • Release checklist

    • A changelog entry exists and is marked appropriately.
    • If applicable, added the breaking change issue label (and filled out the breaking change checklist)
  • Designer checklist

    • If applicable, file an issue to update EUI's Figma library with any corresponding UI changes. (This is an internal repo, if you are external to Elastic, ask a maintainer to submit this request)

    Todo

@mgadewoll mgadewoll self-assigned this Jul 28, 2025
@mgadewoll mgadewoll force-pushed the screenreader/8821-announce-on-mount branch 2 times, most recently from 8ce548d to 41eee00 Compare July 28, 2025 09:30
import { EuiCallOut, EuiButton, EuiSpacer } from '@elastic/eui';

export default () => {
const [isShown, setIsShown] = useState(false);
Copy link
Member

Choose a reason for hiding this comment

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

Great thinking on making this false by default :D

Copy link
Member

@tkajtoch tkajtoch left a comment

Choose a reason for hiding this comment

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

Code changes look great! I followed the QA instructions and tested with VoiceOver + Chrome on macOS and NVDA + Chrome on Windows. EuiLiveAnnouncer feels so much better! 🎉

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

cc @mgadewoll

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

cc @mgadewoll

@mgadewoll mgadewoll merged commit 87ec4f0 into elastic:main Jul 31, 2025
5 checks passed
weronikaolejniczak added a commit to elastic/kibana that referenced this pull request Aug 13, 2025
- `@elastic/eui`: `v106.2.0` ⏩ `v106.3.0`
- `@elastic/eui-theme-borealis`: `v3.3.0` ⏩ `v3.3.1`
- `@elastic/eslint-plugin-eui`: `v2.2.1` ⏩ `v2.3.0`

[Questions? Please see our Kibana upgrade
FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)

## Package updates

### `@elastic/eui`

#### [`v106.3.0`](https://github.com/elastic/eui/releases/v106.3.0)

- Added thumbUp and thumbDown icons
([#8937](elastic/eui#8937))
- Removed obsolete IE-specific CSS properties
([#8940](elastic/eui#8940))
- Updated `EuiComboBox` to allow multiline options by disabling
virtualization (`rowHeight="auto"`)
([#8934](elastic/eui#8934))

**Bug fixes**

- Fixed interactive content in header cells on `EuiDataGrid` not being
correctly removed from the tab order
([#8938](elastic/eui#8938))
- Fixed an issue where the validity state of `EuiFieldNumber` did not
update when the `isInvalid` prop value changed
([#8952](elastic/eui#8952))
- Reverted a font-size change for `xs` buttons back to `12px`
([#8930](elastic/eui#8930))
- Fixed unexpected results for the SCSS function
`lineHeightFromBaseline`
([#8922](elastic/eui#8922))
- Fixed `euiDataGridRow--selected` not applying on `EuiDataGrid` whith
`stripes={true}` ([#8925](elastic/eui#8925))
- Fixed `euiDataGridRow--marked` and `euiDataGridRow--selected` applying
hover styling on `EuiDataGrid` when `stripes={true}`
([#8925](elastic/eui#8925))

**Accessibility**

- Added a screen reader help text for entered interactive cells of
`EuiDataGrid` to provide information about exiting cells
([#8938](elastic/eui#8938))
- Added a new beta `EuiLiveAnnouncer` component which supports
`aria-live` announcements on mount
([#8916](elastic/eui#8916))
- Added `announceOnMount` prop on `EuiCallOut` to support announcing its
content on mount ([#8916](elastic/eui#8916))

### `@elastic/eui-theme-borealis`

####
[`v3.3.1`](https://www.npmjs.com/package/@elastic/eui-theme-borealis/v/3.3.1)

**Bug fixes**

- Updated shared theme SCSS imports to ensure expected results for the
SCSS function `lineHeightFromBaseline`
([#8922](elastic/eui#8922))

### `@elastic/eslint-plugin-eui`

####
[`v2.3.0`](https://www.npmjs.com/package/@elastic/eslint-plugin-eui/v/2.3.0)

- Added `EuiFlyoutResizable` to `require-aria-label-for-modals` check
([#8946](elastic/eui#8946))
- Added new `no-unnamed-radio-group` rule.
([#8929](elastic/eui#8929))
- Fixed attributes comparison issue in the `consistent-is-invalid-props`
rule. ([#8920](elastic/eui#8920))
fkanout pushed a commit to fkanout/kibana that referenced this pull request Aug 14, 2025
- `@elastic/eui`: `v106.2.0` ⏩ `v106.3.0`
- `@elastic/eui-theme-borealis`: `v3.3.0` ⏩ `v3.3.1`
- `@elastic/eslint-plugin-eui`: `v2.2.1` ⏩ `v2.3.0`

[Questions? Please see our Kibana upgrade
FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)

## Package updates

### `@elastic/eui`

#### [`v106.3.0`](https://github.com/elastic/eui/releases/v106.3.0)

- Added thumbUp and thumbDown icons
([elastic#8937](elastic/eui#8937))
- Removed obsolete IE-specific CSS properties
([elastic#8940](elastic/eui#8940))
- Updated `EuiComboBox` to allow multiline options by disabling
virtualization (`rowHeight="auto"`)
([elastic#8934](elastic/eui#8934))

**Bug fixes**

- Fixed interactive content in header cells on `EuiDataGrid` not being
correctly removed from the tab order
([elastic#8938](elastic/eui#8938))
- Fixed an issue where the validity state of `EuiFieldNumber` did not
update when the `isInvalid` prop value changed
([elastic#8952](elastic/eui#8952))
- Reverted a font-size change for `xs` buttons back to `12px`
([elastic#8930](elastic/eui#8930))
- Fixed unexpected results for the SCSS function
`lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))
- Fixed `euiDataGridRow--selected` not applying on `EuiDataGrid` whith
`stripes={true}` ([elastic#8925](elastic/eui#8925))
- Fixed `euiDataGridRow--marked` and `euiDataGridRow--selected` applying
hover styling on `EuiDataGrid` when `stripes={true}`
([elastic#8925](elastic/eui#8925))

**Accessibility**

- Added a screen reader help text for entered interactive cells of
`EuiDataGrid` to provide information about exiting cells
([elastic#8938](elastic/eui#8938))
- Added a new beta `EuiLiveAnnouncer` component which supports
`aria-live` announcements on mount
([elastic#8916](elastic/eui#8916))
- Added `announceOnMount` prop on `EuiCallOut` to support announcing its
content on mount ([elastic#8916](elastic/eui#8916))

### `@elastic/eui-theme-borealis`

####
[`v3.3.1`](https://www.npmjs.com/package/@elastic/eui-theme-borealis/v/3.3.1)

**Bug fixes**

- Updated shared theme SCSS imports to ensure expected results for the
SCSS function `lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))

### `@elastic/eslint-plugin-eui`

####
[`v2.3.0`](https://www.npmjs.com/package/@elastic/eslint-plugin-eui/v/2.3.0)

- Added `EuiFlyoutResizable` to `require-aria-label-for-modals` check
([elastic#8946](elastic/eui#8946))
- Added new `no-unnamed-radio-group` rule.
([elastic#8929](elastic/eui#8929))
- Fixed attributes comparison issue in the `consistent-is-invalid-props`
rule. ([elastic#8920](elastic/eui#8920))
NicholasPeretti pushed a commit to NicholasPeretti/kibana that referenced this pull request Aug 18, 2025
- `@elastic/eui`: `v106.2.0` ⏩ `v106.3.0`
- `@elastic/eui-theme-borealis`: `v3.3.0` ⏩ `v3.3.1`
- `@elastic/eslint-plugin-eui`: `v2.2.1` ⏩ `v2.3.0`

[Questions? Please see our Kibana upgrade
FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)

## Package updates

### `@elastic/eui`

#### [`v106.3.0`](https://github.com/elastic/eui/releases/v106.3.0)

- Added thumbUp and thumbDown icons
([elastic#8937](elastic/eui#8937))
- Removed obsolete IE-specific CSS properties
([elastic#8940](elastic/eui#8940))
- Updated `EuiComboBox` to allow multiline options by disabling
virtualization (`rowHeight="auto"`)
([elastic#8934](elastic/eui#8934))

**Bug fixes**

- Fixed interactive content in header cells on `EuiDataGrid` not being
correctly removed from the tab order
([elastic#8938](elastic/eui#8938))
- Fixed an issue where the validity state of `EuiFieldNumber` did not
update when the `isInvalid` prop value changed
([elastic#8952](elastic/eui#8952))
- Reverted a font-size change for `xs` buttons back to `12px`
([elastic#8930](elastic/eui#8930))
- Fixed unexpected results for the SCSS function
`lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))
- Fixed `euiDataGridRow--selected` not applying on `EuiDataGrid` whith
`stripes={true}` ([elastic#8925](elastic/eui#8925))
- Fixed `euiDataGridRow--marked` and `euiDataGridRow--selected` applying
hover styling on `EuiDataGrid` when `stripes={true}`
([elastic#8925](elastic/eui#8925))

**Accessibility**

- Added a screen reader help text for entered interactive cells of
`EuiDataGrid` to provide information about exiting cells
([elastic#8938](elastic/eui#8938))
- Added a new beta `EuiLiveAnnouncer` component which supports
`aria-live` announcements on mount
([elastic#8916](elastic/eui#8916))
- Added `announceOnMount` prop on `EuiCallOut` to support announcing its
content on mount ([elastic#8916](elastic/eui#8916))

### `@elastic/eui-theme-borealis`

####
[`v3.3.1`](https://www.npmjs.com/package/@elastic/eui-theme-borealis/v/3.3.1)

**Bug fixes**

- Updated shared theme SCSS imports to ensure expected results for the
SCSS function `lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))

### `@elastic/eslint-plugin-eui`

####
[`v2.3.0`](https://www.npmjs.com/package/@elastic/eslint-plugin-eui/v/2.3.0)

- Added `EuiFlyoutResizable` to `require-aria-label-for-modals` check
([elastic#8946](elastic/eui#8946))
- Added new `no-unnamed-radio-group` rule.
([elastic#8929](elastic/eui#8929))
- Fixed attributes comparison issue in the `consistent-is-invalid-props`
rule. ([elastic#8920](elastic/eui#8920))
qn895 pushed a commit to qn895/kibana that referenced this pull request Aug 26, 2025
- `@elastic/eui`: `v106.2.0` ⏩ `v106.3.0`
- `@elastic/eui-theme-borealis`: `v3.3.0` ⏩ `v3.3.1`
- `@elastic/eslint-plugin-eui`: `v2.2.1` ⏩ `v2.3.0`

[Questions? Please see our Kibana upgrade
FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)

## Package updates

### `@elastic/eui`

#### [`v106.3.0`](https://github.com/elastic/eui/releases/v106.3.0)

- Added thumbUp and thumbDown icons
([elastic#8937](elastic/eui#8937))
- Removed obsolete IE-specific CSS properties
([elastic#8940](elastic/eui#8940))
- Updated `EuiComboBox` to allow multiline options by disabling
virtualization (`rowHeight="auto"`)
([elastic#8934](elastic/eui#8934))

**Bug fixes**

- Fixed interactive content in header cells on `EuiDataGrid` not being
correctly removed from the tab order
([elastic#8938](elastic/eui#8938))
- Fixed an issue where the validity state of `EuiFieldNumber` did not
update when the `isInvalid` prop value changed
([elastic#8952](elastic/eui#8952))
- Reverted a font-size change for `xs` buttons back to `12px`
([elastic#8930](elastic/eui#8930))
- Fixed unexpected results for the SCSS function
`lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))
- Fixed `euiDataGridRow--selected` not applying on `EuiDataGrid` whith
`stripes={true}` ([elastic#8925](elastic/eui#8925))
- Fixed `euiDataGridRow--marked` and `euiDataGridRow--selected` applying
hover styling on `EuiDataGrid` when `stripes={true}`
([elastic#8925](elastic/eui#8925))

**Accessibility**

- Added a screen reader help text for entered interactive cells of
`EuiDataGrid` to provide information about exiting cells
([elastic#8938](elastic/eui#8938))
- Added a new beta `EuiLiveAnnouncer` component which supports
`aria-live` announcements on mount
([elastic#8916](elastic/eui#8916))
- Added `announceOnMount` prop on `EuiCallOut` to support announcing its
content on mount ([elastic#8916](elastic/eui#8916))

### `@elastic/eui-theme-borealis`

####
[`v3.3.1`](https://www.npmjs.com/package/@elastic/eui-theme-borealis/v/3.3.1)

**Bug fixes**

- Updated shared theme SCSS imports to ensure expected results for the
SCSS function `lineHeightFromBaseline`
([elastic#8922](elastic/eui#8922))

### `@elastic/eslint-plugin-eui`

####
[`v2.3.0`](https://www.npmjs.com/package/@elastic/eslint-plugin-eui/v/2.3.0)

- Added `EuiFlyoutResizable` to `require-aria-label-for-modals` check
([elastic#8946](elastic/eui#8946))
- Added new `no-unnamed-radio-group` rule.
([elastic#8929](elastic/eui#8929))
- Fixed attributes comparison issue in the `consistent-is-invalid-props`
rule. ([elastic#8920](elastic/eui#8920))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EuiScreenReaderLive] Support announcements on mount

3 participants