Skip to main content

Inclusive Component Recipes

Why this chapter matters

Accessibility at scale is won or lost in reusable components. If menus, dialogs, tabs, tables, notifications, cards, tooltips, and theme controls are accessible by default, product teams inherit good behavior. If they are not, every page becomes a separate remediation project.

Inclusive Components provides the source pattern for this chapter: accessibility is not a decorative audit layer. It is interaction design, semantic HTML, keyboard behavior, focus management, naming, state, and announcement discipline encoded in components.

Core mental model

An inclusive component recipe is a reusable contract for semantics, state, focus, keyboard, announcement, layout, and failure behavior.

Each shared component should answer:

  • What native element or role represents this behavior?
  • What is the keyboard model?
  • Where does focus start, move, and return?
  • What is the accessible name?
  • Which states must be exposed?
  • Which changes need announcements?
  • How does it behave with zoom, reduced motion, touch, and screen readers?
  • What must product teams not override?

Source-derived architecture notes

  • Native semantics should be the default. ARIA is a repair tool for cases where native elements cannot express the interaction.
  • Menu buttons, tabs, dialogs, collapsibles, sliders, and data tables need state diagrams before styling decisions.
  • Tooltips and toggletips solve different problems: one supplements a control, the other exposes information users may need to intentionally open and read.
  • Notifications need live-region strategy, priority, dismissal behavior, persistence rules, and avoidance of noisy repeated announcements.
  • Cards are often misused as giant links. The interaction model must preserve meaningful headings, link purpose, focus order, and nested actions.
  • Theme switchers are accessibility features when they respect contrast, user preference, motion preference, and persistence.

Architecture decision framework

ComponentContract defaultEscape hatchTradeoff signal
DialogFocus trap, labelled title, return focusNon-modal panelUse non-modal when background work must remain accessible.
Menu buttonButton trigger and managed item navigationSimple disclosureUse disclosure when choices are links or static content.
TabsRoving focus and panel associationSegmented linksUse links when navigation changes route.
Data tableSemantic table with captions/headersGrid widgetUse grid only for spreadsheet-like editing.
NotificationPolite/assertive live-region policySilent visual statusAnnounce when status affects task completion.

Implementation patterns

Baseline pattern for small teams

Create a component recipe template:

  • purpose and non-purpose
  • semantic structure
  • keyboard model
  • focus rules
  • naming rules
  • state matrix
  • reduced-motion and zoom behavior
  • automated checks
  • manual keyboard and screen reader checklist

Use it for the highest-risk primitives first: dialog, menu/disclosure, tabs, form field, notification, and data table.

Scale pattern for multi-team organizations

Publish recipes as design-system contracts:

  • Storybook examples for every state
  • design annotations for names, roles, focus, and announcement behavior
  • unit tests for state and keyboard logic
  • accessibility tests for roles and names
  • visual regression for zoom and density modes
  • release notes for behavior changes
  • prohibited override list for focus, role, and keyboard behavior

The contract should let product teams style and compose without weakening behavior.

Migration-safe pattern for legacy systems

Audit repeated patterns before fixing pages:

  1. Identify all menu, dialog, tab, table, card, and notification implementations.
  2. Pick one canonical recipe per pattern.
  3. Ship a compatibility wrapper when immediate replacement is risky.
  4. Replace components by user journey, not by folder.
  5. Delete old variants once adoption and regression checks are in place.

Anti-patterns and failure modes

  • Symptom: keyboard users get trapped or lost after closing a dialog. Root cause: focus return was not part of the component contract. Prevention control: focus lifecycle tests.
  • Symptom: screen reader users hear repeated noisy alerts. Root cause: notifications use live regions without priority or deduplication rules. Prevention control: live-region policy and announcement tests.
  • Symptom: a data-heavy table becomes unusable with assistive tech. Root cause: visual layout replaced table semantics. Prevention control: semantic table contract or deliberate grid pattern.
  • Symptom: cards have unclear link purpose. Root cause: entire card clickability hides meaningful actions. Prevention control: explicit heading/link/action structure.
  • Symptom: product teams break accessibility while styling. Root cause: component exposes role, focus, or keyboard internals as casual props. Prevention control: stable behavior API with protected internals.

Verification checklist

  • Every shared primitive documents purpose, semantics, keyboard behavior, focus, names, states, and announcements.
  • Dialogs trap focus only when modal and return focus on close.
  • Menus, tabs, and collapsibles have deterministic keyboard models.
  • Data tables preserve captions, headers, sorting semantics, and responsive behavior.
  • Notifications define live-region priority, persistence, and dismissal.
  • Cards preserve meaningful headings, links, and nested actions.
  • Theme controls respect contrast, persisted preference, and OS/user settings.

Metrics and scorecards

Track leading indicators:

  • number of shared primitives with complete recipes
  • duplicate component variants by pattern
  • keyboard test coverage for primitives
  • manual assistive technology defects by component
  • adoption rate of canonical recipes

Track lagging indicators:

  • accessibility regressions caused by component changes
  • blocked critical flows for keyboard or screen reader users
  • design-system migration delay caused by missing states

Deep component recipes

Dialog recipe

Contract areaRequirement
SemanticsDialog has an accessible name from a visible title or explicit label.
Focus entryOpening moves focus to the dialog or first meaningful control.
Focus containmentModal dialogs keep focus inside while open. Non-modal panels do not pretend to be modal.
Focus returnClosing returns focus to the trigger or a logical successor if the trigger disappeared.
Escape behaviorEscape closes dismissible dialogs; destructive or blocking flows need explicit policy.
BackgroundInert or hidden background content is not reachable to keyboard or assistive tech.
Error stateValidation errors inside the dialog are announced and focused predictably.

Architectural decision: decide whether the component is a modal dialog, non-modal panel, popover, or route. Many inaccessible dialogs are actually route-level tasks squeezed into overlays.

Menus are for commands or choices with menu-like keyboard behavior. Disclosures are for showing and hiding content. Navigation lists are often just lists of links. Do not use a menu pattern because it looks compact.

PatternUse whenAvoid when
Menu buttonactions are command-like and need arrow-key navigationlinks are normal page navigation
Disclosurea button reveals related contentchoices require command semantics
Select/listboxuser chooses one value from a controlled setchoices are links or rich actions

Contract requirements:

  • trigger is a button
  • expanded/collapsed state is exposed
  • keyboard behavior is documented
  • focus movement is deterministic
  • outside click and Escape behavior match product expectation
  • disabled items are handled consistently

Tabs recipe

Tabs are not just styled buttons. They bind a tablist, tabs, and tab panels.

Decide:

  • automatic activation or manual activation
  • whether panels are preloaded, lazy, or persistent
  • how focus moves with arrow keys
  • whether route changes should use links instead of tabs
  • whether hidden panels remain in DOM and why

Failure mode: tab UI that changes route, resets state, and behaves like navigation while exposing itself as a widget. If the browser back button matters, links or route-aware tabs may be the better architecture.

Data table recipe

Data tables need semantic structure before responsive styling:

  • caption or surrounding heading explains purpose
  • column and row headers are explicit
  • sort state is exposed
  • pagination and filtering are keyboard reachable
  • empty, loading, and error states preserve table context
  • responsive collapse does not destroy relationships between header and cell
  • virtualized tables keep assistive technology behavior in mind

Do not promote a table into an ARIA grid unless users need spreadsheet-like cell navigation or editing. A grid has a much heavier keyboard contract.

Notification recipe

Notifications need priority and persistence policy:

TypeAnnouncementPersistence
Successpolite or no announcement if already obviousshort-lived unless needed for confirmation
Warningpolite, visible until understooddismissible or tied to next action
Errorassertive only when urgentpersistent until resolved or dismissed
Background syncpolite, deduplicatedvisible status if user action depends on it

Avoid dumping every toast into an assertive live region. That creates noise and can interrupt task flow.

Card recipe

Cards often combine heading, image, summary, primary link, and secondary actions. The architecture question is whether the card is:

  • an article preview
  • a product tile
  • a selectable object
  • a command surface
  • a container for separate actions

Each shape has different semantics. Avoid nested interactive elements inside a giant clickable card. Prefer a clear heading link plus explicit secondary actions. The focus order should match visual importance.

Tooltip and toggletip recipe

Tooltips are supplemental labels or hints. They should not contain essential interactive content. Toggletips are user-invoked disclosures for information that may need to be read, copied, or explored.

PatternTriggerContentKeyboard expectation
Tooltiphover/focusshort nonessential textappears on focus, dismisses predictably
Toggletipbuttonuseful explanatory contentopens/closes by button, focus remains understandable
Popover/dialogbuttoninteractive contentexplicit focus and dismissal contract

Theme switcher recipe

A theme switcher should account for:

  • system preference
  • stored user preference
  • contrast requirements
  • reduced motion
  • forced colors or high-contrast modes
  • SSR/first paint mismatch
  • accessible label that names the next or current state clearly

Theme is not only brand. It can be an accessibility setting.

Design-system enforcement model

For every recipe, enforce behavior at three layers:

LayerEnforcement
Designannotated Figma/design specs for names, states, and focus
Codeprotected primitive internals, tests, lint rules, and examples
Releaseaccessibility checks, manual keyboard review, migration notes

The strongest design systems do not ask every product engineer to become an accessibility specialist. They make correct behavior hard to accidentally remove.

Exercises

  1. Write a recipe for one existing dialog: labels, focus entry, focus trap, close behavior, and return focus.
  2. Compare a menu button and disclosure pattern in your product. Decide which behavior is actually needed.
  3. Audit one card grid for link purpose, heading structure, nested actions, and focus order.
  4. Define a notification policy for success, warning, error, and background sync states.

Source Lens

This chapter is synthesized from:

  • Inclusive Components - toggle buttons, menus, tooltips, theme switchers, tabs, collapsibles, sliders, notifications, data tables, modal dialogs, and cards.
  • Advanced Front-End Development - component thinking, styling, accessibility, forms, testing, and design-system practice.
  • WCAG-oriented material already present in this guide - component-level contracts and critical-flow verification.