Skip to content

Commit c594808

Browse files
uzirthapaclaude
andcommitted
fix: Screen reader not announcing Adaptive Card content in stacked layout
Screen readers (Narrator/NVDA) were not announcing Adaptive Card content when focused because stacked layout attachment rows lacked tabIndex and cards without the `speak` property had no aria-label for screen readers to announce. - Add `tabIndex={0}` to stacked layout AttachmentRow for keyboard focus - Add focus-visible styling using existing CSS custom properties - Derive `aria-label` from card text content when `speak` is not set Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cd39b84 commit c594808

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,10 @@ Breaking changes in this release:
363363

364364
### Fixed
365365

366+
- Fixed screen reader (Narrator/NVDA) not announcing Adaptive Card content when focused in stacked layout, by [@uzirthapa](https://github.com/uzirthapa)
367+
- Made stacked layout attachment rows keyboard-focusable with `tabIndex={0}`
368+
- Added focus-visible styling for stacked attachment rows
369+
- Adaptive Cards without `speak` property now derive `aria-label` from visible text content
366370
- Fixed [#5256](https://github.com/microsoft/BotFramework-WebChat/issues/5256). `styleOptions.maxMessageLength` should support any JavaScript number value including `Infinity`, by [@compulim](https://github.com/compulim), in PR [#5255](https://github.com/microsoft/BotFramework-WebChat/issues/pull/5255)
367371
- Fixes [#4965](https://github.com/microsoft/BotFramework-WebChat/issues/4965). Removed keyboard helper screen in [#5234](https://github.com/microsoft/BotFramework-WebChat/pull/5234), by [@amirmursal](https://github.com/amirmursal) and [@OEvgeny](https://github.com/OEvgeny)
368372
- Fixes [#5268](https://github.com/microsoft/BotFramework-WebChat/issues/5268). Concluded livestream is sealed and activities received afterwards are ignored, and `streamSequence` is not required in final activity, in PR [#5273](https://github.com/microsoft/BotFramework-WebChat/pull/5273), by [@compulim](https://github.com/compulim)

packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardHacks/useRoleModEffect.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,22 @@ export default function useRoleModEffect(
3434
adaptiveCard: AdaptiveCard
3535
): readonly [(cardElement: HTMLElement) => void, () => void] {
3636
const modder = useMemo(
37-
() => (_, cardElement: HTMLElement) =>
38-
setOrRemoveAttributeIfFalseWithUndo(
37+
() => (_, cardElement: HTMLElement) => {
38+
// If the card doesn't have an aria-label (i.e. no "speak" property was set),
39+
// derive one from the card's visible text content so screen readers can announce it.
40+
let undoAriaLabel: () => void = () => {};
41+
42+
if (!cardElement.getAttribute('aria-label')) {
43+
const textContent = (cardElement.textContent || '').replace(/\s+/g, ' ').trim();
44+
45+
if (textContent) {
46+
const label = textContent.length > 200 ? textContent.slice(0, 200) + '\u2026' : textContent;
47+
48+
undoAriaLabel = setOrRemoveAttributeIfFalseWithUndo(cardElement, 'aria-label', label);
49+
}
50+
}
51+
52+
const undoRole = setOrRemoveAttributeIfFalseWithUndo(
3953
cardElement,
4054
'role',
4155
// "form" role requires either "aria-label", "aria-labelledby", or "title".
@@ -44,7 +58,13 @@ export default function useRoleModEffect(
4458
cardElement.getAttribute('title')
4559
? 'form'
4660
: 'figure'
47-
),
61+
);
62+
63+
return () => {
64+
undoRole();
65+
undoAriaLabel();
66+
};
67+
},
4868
[]
4969
);
5070

packages/component/src/Activity/AttachmentRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function AttachmentRow(props: AttachmentRowProps) {
3535
const classNames = useStyles(styles);
3636

3737
return (
38-
<div aria-roledescription="attachment" className={classNames['stacked-layout__attachment-row']} role="group">
38+
<div aria-roledescription="attachment" className={classNames['stacked-layout__attachment-row']} role="group" tabIndex={0}>
3939
<ScreenReaderText text={attachedAlt} />
4040
{showBubble ? (
4141
<Bubble

packages/component/src/Activity/StackedLayout.module.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@
5252
.stacked-layout__attachment-row {
5353
margin-block-start: var(--webchat__padding--regular);
5454
width: 100%;
55+
56+
&:focus {
57+
outline: 0;
58+
}
59+
60+
&:focus-visible {
61+
outline: var(--webchat__border-width--transcript-visual-keyboard-indicator)
62+
var(--webchat__border-style--transcript-visual-keyboard-indicator)
63+
var(--webchat__color--transcript-visual-keyboard-indicator);
64+
}
5565
}
5666

5767
&.stacked-layout--no-message .stacked-layout__attachment-list .stacked-layout__attachment-row:first-child {

0 commit comments

Comments
 (0)