Skip to contentAsheeUIAsheeUI

Accessibility

Roles, states, announcements and keyboard behaviour, what the components implement and what a page inherits.

AsheeUI targets WCAG 2.2 Level AA. This page describes the behaviour the framework guarantees, so a consumer can rely on it instead of re-implementing it.

What the framework handles for you

Guarantee Behaviour
Accessible names A field names its control, and a component that shows only an icon takes a label for assistive technology
Description and message A field links its description and its validation message to the element that holds the value
Focus in dialogs Modal and Drawer move focus into the dialog when it opens, keep it there while open, and return it to the element that was focused before
Overlays render in a portal Dialogs, menus and tooltips escape overflow and stacking contexts, and the backdrop is decoration rather than an unnamed control
Body scroll A dialog locks the page behind it through one reference-counted mechanism
State changes A notification is announced; an alert at the error or warning intent interrupts, while info and success wait for a pause
Reduced motion Automatic motion in Marquee and Carousel stops when the operating system asks for it
Focus movement Components move focus only when the interaction requires it, and an inline message never steals it

Keyboard behaviour

Component family Keys
Button, Link, Switch, Radio Tab reaches the control; Enter or Space activates it
Dropdown, MultiSelect, Autocomplete Arrow keys open the list and move between options, Home and End jump to the edges, Enter selects, Escape dismisses, typing matches an option, and focus returns to the trigger when the list closes
Calendar Arrow keys move by day and by week, PageUp and PageDown move by month, Enter selects a day, Escape closes the popover
Tabs Arrow keys move between tabs with a single tab stop, Home and End jump to the edges
Accordion Enter or Space expands and collapses the section
Modal, Drawer Escape dismisses; focus cannot leave the dialog while it is open
Tooltip Appears on focus as well as on hover, and is exposed as the trigger's description
Navbar The control that opens the mobile panel reports its state with aria-expanded and points at the panel with aria-controls; Escape closes the panel and returns focus to the control
PinInput Typing moves to the next box, Backspace clears the box behind the caret, the arrow keys move between boxes, and Home and End jump to the edges

Composite widgets (the selection family, the calendar, tabs, radio groups) use a single tab stop with arrow navigation inside it, which is what the platform and ARIA conventions ask for.

Structure

  • A Breadcrumb is a named navigation landmark over an ordered list, and the current location is text rather than a link.
  • A Pagination marks the current page with aria-current, and its controls are real buttons or links.
  • A Table renders table semantics, not a grid of divs.
  • A collapsed Accordion panel is hidden from assistive technology and out of the tab order.
  • A Sidebar exposes the active item with aria-current and follows its own collapsed state.
  • A decorative element (a spinner, a skeleton, an icon beside a label) is hidden from assistive technology, and the information it stands for is announced by the element that carries it.
  • A Page shell has one main landmark (PageContent), a header and a footer, and a SidebarLayout renders its navigation column in a named aside, so the regions of a page are navigable rather than anonymous.
  • A Navbar is a header landmark that contains a named navigation landmark, and a Footer gives every navigation group, the legal row and the social row their own name, so a reader can jump to the column they want.
  • A Hero, CTA or FeatureGrid band is a section and is named by the heading inside it, and the framework does not invent a heading for a band that has none.
  • An excluded feature line in a PricingCard says so in words as well as in style, and a recommended plan carries a badge rather than relying on colour.
  • A CopyButton announces the result through a status region, because a change of button label alone is not announced, and focus stays on the control.
  • An EmptyState carries no live-region role of its own. A state that replaces content after an asynchronous result passes role="status" or role="alert", so a change is announced and a state rendered with the page is not.

Theming without a flash

The theme is applied before the first paint, so a consumer never sees the wrong theme appear and then change. That is done with an inline script that runs while the document is parsed, which means two things for a consumer:

  • a server-rendered application gets the theme applied before any content paints;
  • a client-only application has the theme and its colours applied by the provider before the first paint.

Theme system

Theme colours are CSS custom properties (--ashee-background, --ashee-foreground, --ashee-primary, and so on). Component styles read those variables, so a theme change is a variable change rather than a stylesheet swap.

Forced-colors and high-contrast modes are respected because the framework never hard-codes a palette colour: every colour resolves through the configuration.

Known limits in this release

Limit Detail
Right-to-left layouts Not supported in this release. The components assume a left-to-right writing direction.
Automated rule checks The framework's own accessibility tests are behavioural and role based. No rule-based checker runs in the suite, so a consumer running one may see findings about their own markup.
Browser-dependent focus containment Focus containment in a dialog and the absence of a collapsed panel from the tab order are implemented directly; a browser-based test is the only way to observe them, which the framework's DOM-based suite cannot do.
Mobile navigation in a page shell SidebarLayout stacks its navigation column above the content on a narrow screen instead of hiding it behind an off-canvas drawer. An off-canvas drawer needs a focus trap and a way back, which belongs in a deliberate component rather than in a layout.
Paste position in PinInput The value is a dense code, so a paste into a box beyond the characters already filled lands at the end of them rather than leaving gaps.

Reporting an accessibility problem

Open an issue with the component, the assistive technology or browser, and what you expected to happen. A reproduction that starts from one of the components in this documentation is the fastest path to a fix.