InteractableBase — and therefore every interactive toolkit component — resolves semantics like this:| Situation | Result |
|---|---|
Clickable, rendered as a plain div | Implicit role="button". |
Rendered as a native element or a component such as a router Link | No role is injected; the element keeps its own semantics. |
An explicit role prop | Always wins. |
Focusable | tabIndex is 0 unless you pass a different tabIndex. |
Not focusable, non- div root | tabIndex is forced to -1 so the native element leaves the tab order. |
interactive={false} on a div root | No role, no tabIndex, no aria-disabled, and no interaction state at all. |
disabled | aria-disabled is set, activation is suppressed, and the element stays focusable so users can still reach it and learn why. |
ariaLabel or the standard aria-label. ariaLabel maps to the rendered aria-label only when the standard prop is absent.disabled with tooltipMode={TooltipMode.DISABLED_CLICK} and tooltipText to explain why the control is unavailable when pressed; the toolkit dismisses the tooltip automatically.| Component | Shipped semantics |
|---|---|
Page | The route root is a role="main" landmark, named from the header text and metadata. Pass role or aria-label to override either. Render one Page per route so there is exactly one main landmark. |
SubNavigationPager, Pager | The pager root is a labeled container — role="group" for SubNavigationPager, role="region" for Pager — with a generic fallback name, so give it an ariaLabel. SubNavigation renders role="tablist" with aria-activedescendant and aria-keyshortcuts, and announces the new page position and label through a polite live region on every section change. |
ListItem | Role resolves to slider, switch, or radio when the row shows that control, otherwise button. Checkable rows set aria-checked; slider rows set aria-valuemin, aria-valuemax, and aria-valuenow. The accessible name is composed from the title, subtitle, timestamp, status indicator, and trailing tag; an aria-label (or ariaLabel) you supply is prepended to that composition rather than replacing it, so use it for a lead-in phrase, not to restate the row. |
Scrubber | role="slider" with aria-orientation, aria-valuemin, aria-valuemax, aria-valuenow, and aria-valuetext. It has a sensible default label; override it with aria-label. Decorative track and handle elements are aria-hidden. |
Switch, RadioButton, SliderBar | Controlled visual indicators. Switch and RadioButton handle no input, take no focus, and are never a tab stop, but still report role="switch" and role="radio" with aria-checked. SliderBar reports role="progressbar" with its range; passing it an onChange opts it into an interactive mode instead — role="slider", a tab stop, and arrow-key stepping by incrementPercentage. Because a role is always announced, putting one of these inside a row that already has switch, radio, or slider semantics produces two nested widgets for one action. Use ListItem’s showSwitch, showRadioButton, or showSlider slots, which move the semantics to the row and hide the indicator, instead of assembling a row by hand. |
ProgressIndicator, ProgressRing, VolumeIndicator, ZoomIndicator | role="progressbar" with aria-valuemin, aria-valuemax, aria-valuenow, and a default label, on a non-focusable element. announceUpdatesForAccessibility adds a polite live region that announces the value as it changes — on by default for ProgressIndicator, off by default for the other three, so opt in when the value is the point of the screen. |
IndeterminateLoader | role="progressbar" with no aria-valuenow, because the progress is unknown, and a default loading label you can override with aria-label. |
VerticalMenu | role="menu" with a default label you can override. Its own buttons and any focusable child with role="menuitem" participate in arrow-key navigation; items that are aria-disabled, aria-hidden, or inert are skipped. |
Modal | Forwards aria-label, falling back to a description composed from its own content. |
Header | Combines the caller-supplied label, or the header text plus metadata, and always appends the avatar’s status description when one is shown. |
IconImage and every icon slot | Rendered aria-hidden. Icons never carry the label; the host component does. |
Tooltip popups | Rendered with role="tooltip" and an accessible label. |
PageTransition | The outgoing page is marked aria-hidden and inert for the duration of the transition. |
| Key | Behavior |
|---|---|
Enter, Space | Activate the focused interactable. The default action is suppressed, press visuals run, and one activation is dispatched. |
Arrow keys | Move focus spatially within FocusNavigationProvider; if no target exists, scroll the nearest scroll container; if neither can move, run rubber-band feedback. |
Tab | Native traversal is preserved by default. It is only suppressed when a FocusNavigationProvider sets blockTabNavigation. |
Escape | Restores focus to the first eligible control when focus has left the provider or rests on its container element, and reaches the application boundary through useBackNavigation. |
Tab navigation. The device uses directional input, but the same build runs in a browser where keyboard and assistive-technology users require a working tab order.title is not always rendered.ScrollView and VerticalList render an unnamed role="region" landmark until the application passes an ariaLabel; Pager and SubNavigationPager otherwise use a generic name.Avatar meaningful alt text unless the image is decorative.role="menuitem" so arrow-key navigation can include them.role="radiogroup" so assistive technology communicates their relationship.TextAppearance classes or TextView’s textStyle. Do not set individual font properties or custom letter spacing on toolkit text; the toolkit owns these values for legibility and consistency.App prefers a locally installed copy and otherwise loads it from the Google Fonts content delivery network at runtime, so no font package or bundled font files are required.useMemo and useCallback in large lists and animated surfaces to avoid unnecessary rendering.useSpringAnimation and write to the DOM from onChange. Its callback is capped at 30 updates per second by default, pauses while the document is hidden, and snaps to the target under reduced motion.foregroundBlendsWithContent or backgroundBlendsWithBackdrop only when the blend needs live pixels because each option adds compositing surfaces.createPreloadableLazyComponent and warm them through RoutePreloadProvider. The provider remains inert until it receives a preloadRoute handler. ReactRouterNavigationProvider mounts the provider and forwards its own preloadRoute. The toolkit queues requests, drains them on an idle callback, and defers them while directional input continues, preventing route warming from competing with scrolling or focus movement.RoutePreloadTarget, useRoutePreloadTarget, or ReactRouterPreloadLink.App, which loads the library stylesheet.Page for the header and content relationship.CornerRadius and TextAppearance scales only.Tab still works.