Skip to content

fix(ui): Ensure hidden passworld field is hidden from a11y tree#8899

Merged
alexcarpenter merged 8 commits into
mainfrom
carp/hidden-password-aria
Jun 18, 2026
Merged

fix(ui): Ensure hidden passworld field is hidden from a11y tree#8899
alexcarpenter merged 8 commits into
mainfrom
carp/hidden-password-aria

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 17, 2026

Copy link
Copy Markdown
Member

Description

The hidden instant-password row was still present in the accessibility tree, causing screen readers to announce a password field that isn't available to users visually.

Fix: replace the old CSS-based hiding (absolute positioning + opacity/height tricks) with display: none. This removes the row from the a11y tree natively without needing inert — which would have blocked programmatic interaction and broke the cache-components integration test (Playwright's fill silently no-ops on inert elements).

BEFORE AFTER
Screenshot 2026-06-17 at 2 19 23 PM Screenshot 2026-06-17 at 2 19 45 PM

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Improved accessibility for “instant password” fields by fully hiding the row from layout and assistive technology when the field is not shown, preventing unintended interaction.
  • Tests

    • Added accessibility-focused tests to confirm the row is not visible when hidden and renders correctly when autofill is simulated.
  • Chores

    • Added a patch release entry for the accessibility improvement.

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 18, 2026 2:59pm
swingset Ready Ready Preview, Comment Jun 18, 2026 2:59pm

Request Review

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cd5b434

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@clerk/ui Patch
@clerk/chrome-extension Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

InstantPasswordRow in SignInStart.tsx now removes the hidden password row from the accessibility tree using aria-hidden and refined CSS hiding with absolute positioning, zero opacity/height, overflow hidden, and disabled pointer events. A patch changeset for @clerk/ui documents this accessibility improvement, and a new test suite validates the visibility behavior and autofill detection.

Changes

Accessibility fix for hidden password input

Layer / File(s) Summary
InstantPasswordRow accessibility improvements and changeset
packages/ui/src/components/SignIn/SignInStart.tsx, .changeset/cold-dodos-lay.md
InstantPasswordRow applies aria-hidden to Form.ControlRow when show is false and refines the sx styling with absolute positioning, zeroed opacity/height, overflow: hidden, pointerEvents: none, and negative marginTop to fully remove the row from interaction and layout. The input preserves tabIndex={-1} suppression in the hidden state. A corresponding @clerk/ui patch changeset documents the accessibility change.
Test validation of password row visibility and autofill
packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx
New test suite Instant password field a11y verifies that the empty password field's form row is not visible via not.toBeVisible(), mocks the autofill detection signal by simulating getComputedStyle reporting onAutoFillStart animation, and confirms the row becomes visible with "Forgot password" text rendered when autofill is triggered.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • clerk/javascript#8733: Both PRs modify the SignInStart InstantPasswordRow behavior, with this PR removing hidden rows from the accessibility tree via aria-hidden and improved CSS hiding, and the related PR handling "Forgot password?" action flow when the row is visible.

Poem

🐇 A row hides away, with aria-hidden true,
Absolute positioning keeps it out of view!
No screen-reader finds it, no keyboard can reach,
When concealed with CSS, it's beyond a11y's reach—
A patch-sized victory for accessibility! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: ensuring hidden password fields are excluded from the accessibility tree, which directly matches the primary objective of this accessibility bug fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-18T15:00:26.193Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 1
🔴 Breaking changes 1
🟡 Non-breaking changes 0
🟢 Additions 0

Warning
1 breaking change(s) detected - Major version bump required

🔴 Breaking changes index (1)

Every breaking change, up front. Full diffs are in the package sections below.

Package Subpath Change
@clerk/shared ./inert ./inert

@clerk/shared

Current version: 4.19.0
Recommended bump: MAJOR → 5.0.0

Subpath ./inert

🔴 Breaking Changes (1)

Changed: ./inert

Subpath export ./inert was removed


Report generated by Break Check

Last ran on cd5b434.

@pkg-pr-new

pkg-pr-new Bot commented Jun 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8899

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8899

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8899

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8899

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@8899

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8899

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8899

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8899

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8899

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8899

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8899

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8899

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8899

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8899

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8899

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8899

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8899

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8899

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8899

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8899

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8899

commit: cd5b434

@alexcarpenter alexcarpenter marked this pull request as draft June 17, 2026 18:24
inert blocks programmatic event dispatch, which broke the cache-components
integration test (Playwright fill with force:true silently no-ops on inert
elements, causing sign-in to submit without a password).

display:none achieves the same a11y goal — removes the row from the
accessibility tree — without blocking Playwright's CDP-level interaction.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx (1)

576-577: 🏗️ Heavy lift

Add one browser-level integration test for non-mocked autofill path.

These tests are good for component behavior, but they currently rely on mocked autofill signals. A single Playwright coverage point for real browser autofill would reduce regression risk for this specific display:none change.

Also applies to: 588-589, 591-591, 609-613

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx` around
lines 576 - 577, The existing tests in the SignInStart.test.tsx file for the
"Instant password field a11y" describe block are component-level tests that use
mocked autofill signals. Add a browser-level integration test using Playwright
that tests real browser autofill behavior without mocking, specifically to
verify that the display:none CSS change properly hides the instant password
field from the accessibility tree during actual autofill in a real browser
environment. This integration test should complement the existing component
tests and provide regression coverage for this specific a11y behavior change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/components/SignIn/SignInStart.tsx`:
- Line 737: In the SignInStart.tsx file, the conditional styling using display:
'none' on line 737 prevents browser autofill detection because major browsers
intentionally skip autofilling inputs in display: none containers. Replace the
display: 'none' hiding method with an alternative CSS approach that keeps the
element visible to the browser's autofill engine (such as visibility: 'hidden',
height: 0 with overflow: 'hidden', or positioning off-screen). Alternatively, if
autofill detection should be intentionally disabled, remove the autofill
detection logic from the InstantPasswordRow component and add test coverage
documenting this new behavior. Clarify in the PR whether autofill functionality
is intended to be disabled or preserved.

---

Nitpick comments:
In `@packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx`:
- Around line 576-577: The existing tests in the SignInStart.test.tsx file for
the "Instant password field a11y" describe block are component-level tests that
use mocked autofill signals. Add a browser-level integration test using
Playwright that tests real browser autofill behavior without mocking,
specifically to verify that the display:none CSS change properly hides the
instant password field from the accessibility tree during actual autofill in a
real browser environment. This integration test should complement the existing
component tests and provide regression coverage for this specific a11y behavior
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 8cc893dc-e6a4-47fd-b934-b949c3965bdf

📥 Commits

Reviewing files that changed from the base of the PR and between 7736ab8 and 8c1cc92.

📒 Files selected for processing (2)
  • packages/ui/src/components/SignIn/SignInStart.tsx
  • packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx

Comment thread packages/ui/src/components/SignIn/SignInStart.tsx Outdated
Replace display:none with off-screen CSS + aria-hidden so the browser
autofill engine can still detect and fill the hidden password input.
Also restores tabIndex=-1 to block keyboard focus. Updates tests to
assert on aria-hidden rather than toBeVisible(), since CSS-in-JS styles
are not reflected in jsdom computed styles.
@alexcarpenter alexcarpenter force-pushed the carp/hidden-password-aria branch from 77ff758 to cd5b434 Compare June 18, 2026 14:57
@alexcarpenter alexcarpenter merged commit 325fc43 into main Jun 18, 2026
47 checks passed
@alexcarpenter alexcarpenter deleted the carp/hidden-password-aria branch June 18, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants