Skip to main content

Accessibility

How duck-primitives implement WAI-ARIA patterns and what you need to know.

What primitives handle automatically

Dialog / Alert Dialog

  • role="dialog" (or role="alertdialog")
  • aria-labelledby connected to Title
  • aria-describedby connected to Description
  • aria-modal="true" for modal dialogs
  • Focus trap: Tab cycles within the dialog
  • Escape closes the dialog
  • aria-hidden on content behind the dialog
  • Focus restoration to trigger on close
  • Scroll lock on body

Popover / Tooltip / Hover Card

  • aria-expanded on trigger
  • aria-controls linking trigger to content
  • aria-haspopup on trigger
  • Focus management (auto-focus on open, restoration on close)
  • role="menu", role="menuitem", role="menuitemcheckbox", role="menuitemradio"
  • aria-checked on checkbox and radio items
  • Arrow key navigation between items
  • Type-ahead character search
  • Submenu opening on ArrowRight
  • aria-disabled on disabled items

Progress

  • role="progressbar"
  • aria-valuemin, aria-valuemax, aria-valuenow
  • aria-valuetext with human-readable label

What you need to provide

Always include Title and Description

Dialog and Alert Dialog warn in development if no Title is found. Always include them:

<Dialog.Content>
  <Dialog.Title>Required for screen readers</Dialog.Title>
  <Dialog.Description>Optional but recommended</Dialog.Description>
</Dialog.Content>
<Dialog.Content>
  <Dialog.Title>Required for screen readers</Dialog.Title>
  <Dialog.Description>Optional but recommended</Dialog.Description>
</Dialog.Content>

If you want a visually hidden title, use CSS:

<Dialog.Title className="sr-only">Settings dialog</Dialog.Title>
<Dialog.Title className="sr-only">Settings dialog</Dialog.Title>

Provide meaningful labels

Triggers should have descriptive text content or an aria-label:

// Good: text content
<Dialog.Trigger>Open settings</Dialog.Trigger>
 
// Good: aria-label for icon buttons
<Dialog.Trigger aria-label="Open settings">
  <GearIcon />
</Dialog.Trigger>
// Good: text content
<Dialog.Trigger>Open settings</Dialog.Trigger>
 
// Good: aria-label for icon buttons
<Dialog.Trigger aria-label="Open settings">
  <GearIcon />
</Dialog.Trigger>

Respect reduced motion

@media (prefers-reduced-motion: reduce) {
  .overlay, .content {
    animation: none !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .overlay, .content {
    animation: none !important;
  }
}

Keyboard navigation summary

ComponentKeys
DialogEscape close, Tab / Shift+Tab cycle focus
PopoverEscape close, Tab move focus
TooltipEscape close, focus-triggered
MenuArrow keys navigate, Enter / Space activate, Escape close
MenubarArrowLeft / ArrowRight switch menus, ArrowUp / ArrowDown navigate items
ProgressNone (display-only)