Skip to content

fix: retain copy confirmation for accessibility - #5864

Open
ahonamatata wants to merge 4 commits into
microsoft:mainfrom
ahonamatata:users/asalsa/fix-copy-confirmation-a11y
Open

ahonamatata wants to merge 4 commits into
microsoft:mainfrom
ahonamatata:users/asalsa/fix-copy-confirmation-a11y

Conversation

@ahonamatata

@ahonamatata ahonamatata commented Oct 2, 2026 •

Copy link
Copy Markdown

Fixes #5857

Changelog Entry

  • Fixed Copy confirmation visibility and status announcement retention, resolving #5857

Description

After activating Copy, Web Chat displayed Copied for only 700 ms and removed its polite live-region announcement after about 1 second. Windows Narrator could miss a queued polite announcement during that short window, while sighted users could also miss the visual confirmation.

The copy confirmation now remains visible for 5 seconds and uses a persistent atomic status region portaled directly to the .webchat root. This places the status outside response-labelled message ancestors and the shared transcript live region. An independent ponyfill timeout controls its lifetime, so cleanup does not depend on CSS animation events and unrelated announcements are not retained.

Design

The defect had two independent causes:

  1. The CSS animation hid the visible Copied state after 0.7 seconds.
  2. The shared live-region queue removed the copy announcement after the configured 1-second timeout, allowing Narrator to miss it.

A duration-only CSS change would not make the assistive-technology message more reliable. Increasing the shared live-region timeout would retain unrelated transcript announcements. Rendering the status inside the message row would also add Copied to an ancestor accessible name.

Instead, the Copy button portals a persistent role="status" with aria-atomic="true" to the nearest .webchat root. Each activation replaces a keyed child, making repeated identical Copied messages observable while the status container remains mounted. A 5-second timeout from usePonyfill() resets on every activation, clears on unmount, removes the status content, and restores the visual button state even when animation events are disabled or absent. The existing animation remains an independent visual completion path.

Clipboard behavior, focus, localization, and the shared LiveRegionTwin implementation are unchanged.

Specific Changes

  • Extended the visible Copied confirmation from 0.7 seconds to 5 seconds.
  • Added a persistent atomic status region outside response-labelled ancestors and the shared live region.
  • Added an independent ponyfill timeout with repeated-activation reset and unmount cleanup.
  • Added HTML integration coverage for duration, status semantics and placement, repeated activation, and cleanup without animation events.
  • Updated the hide/show test to assert the 5-second confirmation duration.
  • Updated CHANGELOG.md.

Reproduction on current main

  1. Grant clipboard-write permission and render a copy-enabled markdown activity.
  2. Focus the Copy button and activate it.
  3. After 1 second, inspect the button and assistive-technology status DOM.
  4. On unmodified main, the visible Copied state has ended and the shared live-region announcement has already been removed.

Validation

Validated pushed head fbdede960c67ea41dcd73ebc2e724251e5dfd1a7:

  • npm clean-install --userconfig .npmrc: passed on Windows, installing 1,753 packages without tracked dependency changes.
  • npm run build-browser: passed, building the test harness IIFE and ESM bundles.
  • node_modules/.bin/eslint.cmd --report-unused-disable-directives --max-warnings 0 packages/component/src: passed.
  • Changed-file Prettier checks: passed.
  • git diff --check HEAD^ HEAD: passed.
  • Isolated Linux npm clean-install --userconfig .npmrc && npm run build: passed. CRLF normalization occurred only in the disposable validation copy.
  • Selenium Grid build and startup: passed with two Chrome nodes reported UP.
  • npm test -- --testPathPattern '__tests__/html2/copyButton/behavior\.html$': passed, 1 suite, 1 test, and 3 snapshots in 15.028 seconds.
  • npm test -- --testPathPattern '__tests__/html2/copyButton/behavior\.hideAndShow\.html$': passed, 1 suite, 1 test, and 3 snapshots in 12.114 seconds.
  • npm run precommit:typecheck --workspace ./packages/component: passed in the built isolated Linux environment.
  • Manual Windows Narrator validation: passed for the primary activation. Narrator announced the focused Copy button as Copied after activation.
  • Rapid repeated activation and duplicate-speech behavior were not separately verified manually.
  • Selenium sessions and all disposable containers, networks, volumes, exported outputs, archives, and dependencies were removed after validation. The worktree is clean.

Regression Coverage

  • behavior.html fails on unmodified main after 1 second because the 0.7-second visual confirmation is already hidden and there is no persistent root-level atomic status. It also verifies placement outside aria-labelledby ancestors and the shared live region, repeated activation, and timer cleanup when CSS animation is disabled.
  • behavior.hideAndShow.html fails on unmodified main because the visual Copied confirmation is no longer visible after 1 second. It also verifies return to the normal state after the remaining duration and after Web Chat is hidden and shown.
  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

No user-facing documentation change is required beyond the changelog.

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 068c5ef2-5e03-4d29-8666-f2b34b1bcc92
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 068c5ef2-5e03-4d29-8666-f2b34b1bcc92
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 068c5ef2-5e03-4d29-8666-f2b34b1bcc92
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 068c5ef2-5e03-4d29-8666-f2b34b1bcc92

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The Fluent theme still overrides the confirmation animation to 500 ms, leaving the reported visual issue unresolved there.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Retains visual and assistive copy confirmation for five seconds.

Changes:

  • Adds a persistent atomic status portal with timer cleanup.
  • Extends confirmation duration and adds integration coverage.
  • Updates the changelog.
File Description
ActivityCopyButton.ts Extends legacy animation duration.
ActivityCopyButton.tsx Adds status portal and timeout handling.
ActivityCopyButton.module.css Extends animation and visually hides status.
CHANGELOG.md Records the accessibility fix.
behavior.html Tests status semantics, repetition, and cleanup.
behavior.hideAndShow.html Tests confirmation duration and visibility changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

&.activity-copy-button--copied {
.activity-copy-button__copied-text {
animation: webchat__activity-copy-button__copied-animation 0.7s linear;
animation: webchat__activity-copy-button__copied-animation 5s linear;

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants