Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Screen Readers

Beverly brand mark

Screen-reader support depends on semantics, not decoration. A polished interface is not accessible if a screen reader cannot understand the structure, names, or state of the controls.

Core requirements

Beverly projects its semantic tree through AccessKit. Bound validation errors create assertive error regions and invalid/error-description relationships. Notifications have live-region semantics, and applications can request an announcement explicitly:

#![allow(unused)]
fn main() {
use beverly::primitives::semantic::{AccessibilityAnnouncement, AnnouncementPriority};

world.write_message(AccessibilityAnnouncement {
    message: "Changes saved".to_owned(),
    priority: AnnouncementPriority::Polite,
});
}

Install SemanticPlugin (included by BeverlyPlugin) before sending messages. Each nonempty message gets its own accessible region, so repeated identical messages are not silently discarded. Regions remain in the tree for one second. Use assertive announcements for urgent errors and polite announcements for ordinary status changes. This adapter does not establish that a specific screen reader announced a message; verify that on the supported platform.

The application should provide:

  • clear text labels for controls and actions
  • a meaningful reading order for panels and cards
  • obvious state cues for selected, disabled, expanded, or alert surfaces
  • consistent names for repeated controls, especially navigation items and form fields

Semantic structure

A Beverly interface should favor meaningful hierarchy over visual-only grouping. The app shell, cards, nav items, forms, and dialogs should all expose clear structure so screen-reader users can navigate with confidence.

When a control has a dynamic state, that state should be announced or discoverable. For example:

  • selected or active tabs
  • expanded or collapsed panels
  • disabled controls
  • validation states and error summaries
  • busy or loading content

Naming and labeling

Labels should be concise, specific, and consistent. Generic names like “Button” or “Open” are not enough when several actions appear in the same interface. A user should be able to understand the action without seeing the visual layout.

This matters for:

  • action buttons in toolbars
  • form inputs and filters
  • modal close or confirmation controls
  • status surfaces and notifications

Example: labeled form controls with state

#![allow(unused)]
fn main() {
use bevy::prelude::*;
use beverly::prelude::*;

fn build_form(mut commands: Commands) {
    commands.spawn((
        NodeBundle::default(),
        BeverlyStack::vertical(),
    )).with_children(|parent| {
        parent.spawn(BeverlyInput::new("Email address")
            .placeholder("[email protected]")
            .required(true));

        parent.spawn(BeverlyButton::secondary("Send invite"));
    });
}
}

The text label gives the field a clear accessible name, and the button label provides a precise meaning for assistive technologies.

Accessibility first, polish second

Glass surfaces, gradients, and motion effects can all be beautiful, but they should never obscure meaning. The semantic layer is what makes the interface understandable to every user, regardless of input method or assistive technology.