Skip to content

a11y(2.4.6): guard DrawerContent against empty <h2> when title is omitted#3595

Merged
ardiewen merged 4 commits into
mainfrom
wcag/2.4.6-drawer-content-empty-title
Jul 6, 2026
Merged

a11y(2.4.6): guard DrawerContent against empty <h2> when title is omitted#3595
ardiewen merged 4 commits into
mainfrom
wcag/2.4.6-drawer-content-empty-title

Conversation

@rosanusi

Copy link
Copy Markdown
Contributor

Summary

DrawerContent defaulted title to '' and unconditionally rendered <h2>{title}</h2>. When a consumer omitted the prop (two cloud consumers do), the DOM contained an empty heading — no accessible name, failing WCAG 2.4.6 (Headings and Labels, AA) and 4.1.2 (Name, Role, Value).

Fix: wrap the title-wrapper div in {#if title || $$slots['subtitle']} and the <h2> in {#if title}. The element is only emitted when content exists; the subtitle slot continues to render independently.

What changes

-<div class="title-wrapper {position}">
-  <h2>{title}</h2>
-  {#if $$slots['subtitle']}
-    ...
-  {/if}
-</div>
+{#if title || $$slots['subtitle']}
+  <div class="title-wrapper {position}">
+    {#if title}
+      <h2>{title}</h2>
+    {/if}
+    {#if $$slots['subtitle']}
+      ...
+    {/if}
+  </div>
+{/if}

Impact

  • Consumers that pass title= (all 9 existing correct usages): no change in rendered output.
  • Consumers that omit title (the two cloud gaps): empty <h2> removed.
  • Subtitle-only usage: the wrapper still renders when only a subtitle slot is provided.

Test plan

  • Open any drawer that passes a title (Manage Capacity, Bulk Add, Task Queue Fairness, Activity Options, Configure Headers, Keyboard Shortcuts). Confirm <h2> still renders with correct text.
  • Open a drawer with only a subtitle slot (if any). Confirm the wrapper renders but no <h2>.
  • axe-core: no empty heading violations on any drawer.
  • Storybook visual regression: drawer story shows correct layout.

References

A11y-Audit-Ref: 2.4.6-cloud-drawer-content-empty-title

🤖 Generated with Claude Code

…tted

DrawerContent always rendered <h2>{title}</h2> even when title was '' (the
default), producing an empty heading that fails WCAG 2.4.6 (Headings and
Labels, AA) and 4.1.2 (Name, Role, Value).

Wrap the title-wrapper div in {#if title || $$slots['subtitle']} and the <h2>
in {#if title} so the element is only emitted when content exists. The
subtitle slot continues to render independently. Consumers that pass title=
are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment Jul 6, 2026 4:36pm

Request Review

@github-actions github-actions Bot added a11y Accessibility audit PR a11y:broken-ref A11y-Audit-Ref slug not in manifest labels Jun 24, 2026
@rosanusi
rosanusi marked this pull request as ready for review June 25, 2026 15:13
@rosanusi
rosanusi requested a review from a team as a code owner June 25, 2026 15:13
PR #3595 references A11y-Audit-Ref: 2.4.6-cloud-drawer-content-empty-title,
but the slug was absent from scripts/a11y/manifest.yml, so the A11y PR Triage
Action flagged it a11y:broken-ref. Add the entry so the ref resolves.

Scope is universal: the fix lives in the shared Holocene DrawerContent
component in ui, but the empty <h2> only surfaces via cloud consumers that
omit the title prop. Bucket 2 / moderate matches the sibling 2.4.6
datepicker-empty-label entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@github-actions github-actions Bot added a11y:bucket-2 Bucket 2: design-mergeable, HTML / ARIA a11y:sc-2.4.6 and removed a11y:broken-ref A11y-Audit-Ref slug not in manifest labels Jul 6, 2026
… in Storybook

The prior fix removed the .title-wrapper entirely when a drawer has neither a
title nor a subtitle. That wrapper's p-6 was the only source of top padding, so
title-less/subtitle-less drawers rendered flush against the top edge (both live
cloud consumers, connect-drawer and add-replica, hit this path).

Derive `hasHeader` and apply pt-6 to .content only when no header renders, so
titled/subtitled drawers keep identical layout while the header-less case keeps
its top padding. Makes DrawerContent self-contained regardless of which props a
consumer passes.

Add Storybook stories for the previously uncovered permutations: With Subtitle,
Subtitle Only, and No Title or Subtitle.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@ardiewen

ardiewen commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Follow-ups pushed since the original diff:

  • Defensive padding — the empty-<h2> guard also removed .title-wrapper, the sole source of top padding, so a drawer with neither title nor subtitle rendered flush to the top (hits both title-less cloud consumers, connect-drawer + add-replica). Now derives hasHeader and adds pt-6 to .content only when no header renders — titled/subtitled drawers unchanged.
  • Storybook — added With Subtitle, Subtitle Only, and No Title or Subtitle stories (previously only title-only was covered).
  • Manifest — added the 2.4.6-cloud-drawer-content-empty-title entry so the A11y-Audit-Ref resolves (was a11y:broken-ref).

pnpm check + pnpm lint clean. Ships alongside cloud-ui wcag/2.4.6-drawer-content-titles (adds real titles to those two drawers).

@ardiewen
ardiewen merged commit 24eabae into main Jul 6, 2026
18 checks passed
@ardiewen
ardiewen deleted the wcag/2.4.6-drawer-content-empty-title branch July 6, 2026 17:03
temporal-cicd Bot pushed a commit that referenced this pull request Jul 8, 2026
Auto-generated version bump from 2.51.1 to 2.52.0

Bump type: minor

Changes included:
- [`9c6fcd54`](9c6fcd5) Add direct SSO redirect and backend logout (#3590)
- [`feab0d90`](feab0d9) a11y(4.1.3): Toaster role="log" → per-toast role="status"/"alert" for correct AT announcement semantics (#3560)
- [`d7bbfb01`](d7bbfb0) a11y(2.5.8): toast — replace bare close button with IconButton (36×36 px) (#3553)
- [`4efd6ab6`](4efd6ab) a11y(3.3.1): Holocene input primitives — add aria-invalid, aria-describedby, and uniform live-region error announcement (#3554)
- [`0600b700`](0600b70) Use green only (#3599)
- [`94107549`](9410754) Clarify activity pause timeout behavior (#3600)
- [`9f88d5a8`](9f88d5a) Bump node version to 22.23.1 or newer to fix issue from previous security release (#3604)
- [`ca4104ae`](ca4104a) Update reason copy and inputs (#3588)
- [`51db73fa`](51db73f) Update batch operation reason input hint (#3605)
- [`836d7c55`](836d7c5) a11y(2.4.3): route navigation — move focus to the main landmark on afterNavigate, not just scroll (#3538)
- [`4ee95696`](4ee9569) Improve re-renders for large encoded event histories (#3592)
- [`fa5e7aa1`](fa5e7aa) feature/schedules (#3603)
- [`0d9467fd`](0d9467f) Default includeHeartbeatDetails and includeLastFailure to true for getActivityExecution and pollActivityExecution (#3521)
- [`e5bc514e`](e5bc514) fix schedules: duplicate key (#3612)
- [`f1eb489f`](f1eb489) Remove preview badge (#3581)
- [`1e5173b4`](1e5173b) Add existing versions to worker create deployment version form (#3601)
- [`a8552bae`](a8552ba) fix schedules -e.trim not a function (#3613)
- [`0442f847`](0442f84) Fix width of ComputeBadge in VersionTableRow (#3615)
- [`ddbeb54f`](ddbeb54) fix(cloud-nav): animate and stabilize side-nav collapse (#3606)
- [`3cfc684b`](3cfc684) fix - schedules recent/upcoming runs - sort issue (#3616)
- [`92b8875b`](92b8875) Move timestamp out of translate (#3617)
- [`901258af`](901258a) Fix SAA and WF consistency (#3614)
- [`99593d97`](99593d9) DT-4001 - standalone nexus operations (#3496)
- [`f10d1bf1`](f10d1bf) Clamp codeblock to container width (#3621)
- [`0a9d93ce`](0a9d93c) Remove unnecessary bottom-0 (#3623)
- [`9dabb522`](9dabb52) chore(deps-dev): bump vite from 6.4.2 to 6.4.3 (#3610)
- [`5c1854d3`](5c1854d) News Feed (#3596)
- [`340da30f`](340da30) a11y(2.1.1): guard global r/R keydown so text inputs can receive the letter (#3589)
- [`dd97a9d1`](dd97a9d) fix(markdown-editor): disable chromatic snapshot for preview story (#3631)
- [`7db61ab9`](7db61ab) a11y(4.1.2): Table / ProgressBar / Loading primitives — expose aria-busy on data-refresh regions (#3555)
- [`d2111ecf`](d2111ec) a11y(4.1.3): Add live-region wrappers for workflow status, event count, filter counts, and Banner (#3561)
- [`24eabaec`](24eabae) a11y(2.4.6): guard DrawerContent against empty <h2> when title is omitted (#3595)
- [`3552e307`](3552e30) Fix CodeMirror Lezer dependency mismatch (#3632)
- [`7ac37199`](7ac3719) Update defaults to match workflow defaults (#3634)
- [`c1f02865`](c1f0286) Draft fix for link variant handling (#3620)
- [`bbbf2b8c`](bbbf2b8) Add connection status and optional Latest Version column to deployments (DT-4174) (#3618)
- [`76f266f4`](76f266f) DurationInput updates (#3633)
- [`07da53c5`](07da53c) COM-191 - prefill CloudFormation template params in Launch Stack link (#3619)
- [`90946e0b`](90946e0) Schedules - Timeout empty defaults (#3637)
- [`97f9ad39`](97f9ad3) fix(cloud-nav): smooth label fade on side-nav collapse/expand (#3628)
- [`a399ed35`](a399ed3) Improve Search Attribute deletion messaging (#3629)
- [`ad976d94`](ad976d9) Pass through source prop for News Feed Widget (#3638)
- [`6ef84fe3`](6ef84fe) Restrict select-all to exclude workflow status (#3639)
- [`4c3729f8`](4c3729f) Reduce news feed markdown whitespace (#3642)
- [`aa04a9b6`](aa04a9b) feat(nexus-operations): add loading state to standalone nexus operations list page header (#3643)
- [`dce2f804`](dce2f80) chore: remove unused generate-holocene-props script (#3644)
- [`760bc7bc`](760bc7b) Make megaphone icon bigger (#3645)
- [`c047384a`](c047384) refactor(nexus): replace hand-rolled types with @temporalio/proto (#3646)
rossedfort added a commit that referenced this pull request Jul 8, 2026
Auto-generated version bump from 2.51.1 to 2.52.0

Bump type: minor

Changes included:
- [`9c6fcd54`](9c6fcd5) Add direct SSO redirect and backend logout (#3590)
- [`feab0d90`](feab0d9) a11y(4.1.3): Toaster role="log" → per-toast role="status"/"alert" for correct AT announcement semantics (#3560)
- [`d7bbfb01`](d7bbfb0) a11y(2.5.8): toast — replace bare close button with IconButton (36×36 px) (#3553)
- [`4efd6ab6`](4efd6ab) a11y(3.3.1): Holocene input primitives — add aria-invalid, aria-describedby, and uniform live-region error announcement (#3554)
- [`0600b700`](0600b70) Use green only (#3599)
- [`94107549`](9410754) Clarify activity pause timeout behavior (#3600)
- [`9f88d5a8`](9f88d5a) Bump node version to 22.23.1 or newer to fix issue from previous security release (#3604)
- [`ca4104ae`](ca4104a) Update reason copy and inputs (#3588)
- [`51db73fa`](51db73f) Update batch operation reason input hint (#3605)
- [`836d7c55`](836d7c5) a11y(2.4.3): route navigation — move focus to the main landmark on afterNavigate, not just scroll (#3538)
- [`4ee95696`](4ee9569) Improve re-renders for large encoded event histories (#3592)
- [`fa5e7aa1`](fa5e7aa) feature/schedules (#3603)
- [`0d9467fd`](0d9467f) Default includeHeartbeatDetails and includeLastFailure to true for getActivityExecution and pollActivityExecution (#3521)
- [`e5bc514e`](e5bc514) fix schedules: duplicate key (#3612)
- [`f1eb489f`](f1eb489) Remove preview badge (#3581)
- [`1e5173b4`](1e5173b) Add existing versions to worker create deployment version form (#3601)
- [`a8552bae`](a8552ba) fix schedules -e.trim not a function (#3613)
- [`0442f847`](0442f84) Fix width of ComputeBadge in VersionTableRow (#3615)
- [`ddbeb54f`](ddbeb54) fix(cloud-nav): animate and stabilize side-nav collapse (#3606)
- [`3cfc684b`](3cfc684) fix - schedules recent/upcoming runs - sort issue (#3616)
- [`92b8875b`](92b8875) Move timestamp out of translate (#3617)
- [`901258af`](901258a) Fix SAA and WF consistency (#3614)
- [`99593d97`](99593d9) DT-4001 - standalone nexus operations (#3496)
- [`f10d1bf1`](f10d1bf) Clamp codeblock to container width (#3621)
- [`0a9d93ce`](0a9d93c) Remove unnecessary bottom-0 (#3623)
- [`9dabb522`](9dabb52) chore(deps-dev): bump vite from 6.4.2 to 6.4.3 (#3610)
- [`5c1854d3`](5c1854d) News Feed (#3596)
- [`340da30f`](340da30) a11y(2.1.1): guard global r/R keydown so text inputs can receive the letter (#3589)
- [`dd97a9d1`](dd97a9d) fix(markdown-editor): disable chromatic snapshot for preview story (#3631)
- [`7db61ab9`](7db61ab) a11y(4.1.2): Table / ProgressBar / Loading primitives — expose aria-busy on data-refresh regions (#3555)
- [`d2111ecf`](d2111ec) a11y(4.1.3): Add live-region wrappers for workflow status, event count, filter counts, and Banner (#3561)
- [`24eabaec`](24eabae) a11y(2.4.6): guard DrawerContent against empty <h2> when title is omitted (#3595)
- [`3552e307`](3552e30) Fix CodeMirror Lezer dependency mismatch (#3632)
- [`7ac37199`](7ac3719) Update defaults to match workflow defaults (#3634)
- [`c1f02865`](c1f0286) Draft fix for link variant handling (#3620)
- [`bbbf2b8c`](bbbf2b8) Add connection status and optional Latest Version column to deployments (DT-4174) (#3618)
- [`76f266f4`](76f266f) DurationInput updates (#3633)
- [`07da53c5`](07da53c) COM-191 - prefill CloudFormation template params in Launch Stack link (#3619)
- [`90946e0b`](90946e0) Schedules - Timeout empty defaults (#3637)
- [`97f9ad39`](97f9ad3) fix(cloud-nav): smooth label fade on side-nav collapse/expand (#3628)
- [`a399ed35`](a399ed3) Improve Search Attribute deletion messaging (#3629)
- [`ad976d94`](ad976d9) Pass through source prop for News Feed Widget (#3638)
- [`6ef84fe3`](6ef84fe) Restrict select-all to exclude workflow status (#3639)
- [`4c3729f8`](4c3729f) Reduce news feed markdown whitespace (#3642)
- [`aa04a9b6`](aa04a9b) feat(nexus-operations): add loading state to standalone nexus operations list page header (#3643)
- [`dce2f804`](dce2f80) chore: remove unused generate-holocene-props script (#3644)
- [`760bc7bc`](760bc7b) Make megaphone icon bigger (#3645)
- [`c047384a`](c047384) refactor(nexus): replace hand-rolled types with @temporalio/proto (#3646)

Co-authored-by: rossedfort <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-2 Bucket 2: design-mergeable, HTML / ARIA a11y:sc-2.4.6 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants