Improve screen reader accessibility of auth pages (#31236)

* Improve screen reader accessibility of auth pages

Using a combination of auto-focus + aria-live to ensure content is read as the states progress

For https://element-io.atlassian.net/browse/PSB-971

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix double landmark

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2025-11-14 12:46:15 +00:00 committed by GitHub
parent 333bec33ee
commit ac0a91be9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 33 additions and 18 deletions

View File

@ -129,6 +129,7 @@ test.describe("Login", () => {
await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible();
// Start the login process
await expect(axe).toHaveNoViolations();
await page.getByRole("link", { name: "Sign in" }).click();
// first pick the homeserver, as otherwise the user picker won't be visible
@ -148,8 +149,6 @@ test.describe("Login", () => {
await selectHomeserver(page, homeserver.baseUrl);
await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
// Disabled because flaky - see https://github.com/vector-im/element-web/issues/24688
// cy.percySnapshot("Login");
await expect(axe).toHaveNoViolations();
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -167,11 +167,11 @@ we don't have an account and should hide them. No account == no guest account ei
<div class="mx_Parent">
<a href="https://element.io" target="_blank" rel="noopener">
<img src="$logoUrl" alt="" class="mx_Logo" />
<img src="$logoUrl" alt="$brand" class="mx_Logo" />
</a>
<h1 class="mx_Header_title">_t("welcome_to_element")</h1>
<!-- XXX: Our translations system isn't smart enough to recognize variables in the HTML, so we manually do it -->
<h4 class="mx_Header_subtitle">_t("powered_by_matrix_with_logo")</h4>
<h2 class="mx_Header_subtitle">_t("powered_by_matrix_with_logo")</h2>
<div class="mx_ButtonGroup">
<div class="mx_ButtonRow">
<a href="#/login" class="mx_ButtonParent mx_ButtonSignIn mx_Button_iconSignIn">

View File

@ -14,5 +14,5 @@ interface Props {
}
export default function AuthBody({ flex, className, children }: PropsWithChildren<Props>): JSX.Element {
return <main className={classNames("mx_AuthBody", className, { mx_AuthBody_flex: flex })}>{children}</main>;
return <div className={classNames("mx_AuthBody", className, { mx_AuthBody_flex: flex })}>{children}</div>;
}

View File

@ -89,9 +89,14 @@ export default class AuthPage extends React.PureComponent<React.PropsWithChildre
<div className="mx_AuthPage" style={pageStyle}>
<div className={modalClasses} style={modalStyle}>
{modalBlur}
<div className="mx_AuthPage_modalContent" style={modalContentStyle}>
<main
className="mx_AuthPage_modalContent"
style={modalContentStyle}
tabIndex={-1}
aria-live="polite"
>
{this.props.children}
</div>
</main>
</div>
<AuthFooter />
</div>

View File

@ -26,6 +26,7 @@ export default class Welcome extends React.PureComponent<EmptyObject> {
}
const replaceMap: Record<string, string> = {
"$brand": SdkConfig.get("brand"),
"$riot:ssoUrl": "#/start_sso",
"$riot:casUrl": "#/start_cas",
"$matrixLogo": MATRIX_LOGO_HTML,

View File

@ -120,9 +120,11 @@ exports[`<MatrixChat /> Multi-tab lockout waits for other tab to stop during sta
class="mx_AuthPage_modalBlur"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; filter: blur(40px);"
/>
<div
<main
aria-live="polite"
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; border-radius: 8px; background: rgba(255, 255, 255, 0.59);"
tabindex="-1"
>
<div
class="mx_Welcome"
@ -166,7 +168,7 @@ exports[`<MatrixChat /> Multi-tab lockout waits for other tab to stop during sta
</div>
</div>
</div>
</div>
</main>
</div>
<footer
class="mx_AuthFooter"
@ -238,9 +240,11 @@ exports[`<MatrixChat /> with a soft-logged-out session should show the soft-logo
class="mx_AuthPage_modalBlur"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; filter: blur(40px);"
/>
<div
<main
aria-live="polite"
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; border-radius: 8px; background: rgba(255, 255, 255, 0.59);"
tabindex="-1"
>
<div
class="mx_AuthHeader"
@ -280,7 +284,7 @@ exports[`<MatrixChat /> with a soft-logged-out session should show the soft-logo
</div>
</div>
</div>
<main
<div
class="mx_AuthBody"
>
<h1>
@ -341,8 +345,8 @@ exports[`<MatrixChat /> with a soft-logged-out session should show the soft-logo
Clear all data
</div>
</div>
</main>
</div>
</main>
</div>
<footer
class="mx_AuthFooter"

View File

@ -9,9 +9,11 @@ exports[`CompleteSecurity Allows verifying with another device if one is availab
class="mx_AuthPage_modal"
style="position: relative;"
>
<div
<main
aria-live="polite"
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; border-radius: 8px;"
tabindex="-1"
>
<div
class="mx_Dialog_border _glass_sepwu_8"
@ -127,7 +129,7 @@ exports[`CompleteSecurity Allows verifying with another device if one is availab
</div>
</div>
</div>
</div>
</main>
</div>
<footer
class="mx_AuthFooter"
@ -175,9 +177,11 @@ exports[`CompleteSecurity Allows verifying with recovery key if one is available
class="mx_AuthPage_modal"
style="position: relative;"
>
<div
<main
aria-live="polite"
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; border-radius: 8px;"
tabindex="-1"
>
<div
class="mx_Dialog_border _glass_sepwu_8"
@ -282,7 +286,7 @@ exports[`CompleteSecurity Allows verifying with recovery key if one is available
</div>
</div>
</div>
</div>
</main>
</div>
<footer
class="mx_AuthFooter"

View File

@ -13,9 +13,11 @@ exports[`<AuthPage /> should match snapshot 1`] = `
class="mx_AuthPage_modalBlur"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; filter: blur(40px);"
/>
<div
<main
aria-live="polite"
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; border-radius: 8px; background: rgba(255, 255, 255, 0.59);"
tabindex="-1"
/>
</div>
<footer