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 <noreply@anthropic.com>
@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) <noreply@anthropic.com>
@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) <noreply@anthropic.com>
@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
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