Skip to content

Accessibility

Some of the people using your app can’t see its window, or can’t use a mouse. They use a screen reader, which says what’s on screen and lets them act on it from the keyboard: VoiceOver on macOS, Narrator on Windows.

Tessel draws every control itself, so to the system a window would be one picture. Tessel therefore tells the system what’s in it: every control and text, what it’s called, what it holds, and what can be done with it. Your app does this without any code of yours.

ViewWhat a screen reader is told
TextIts words.
ButtonA button, called by its title. It can be pressed.
TextFieldA text field, called by its placeholder, with its text. A secure one is a password field, and its text isn’t said.
ToggleA switch, called by its label, on or off.
PickerA group of options, and which one is chosen.
TabViewIts tabs, which one is chosen, and the page that shows.
Slider, StepperIts number, with the least and the most. It can be moved up and down.
DisclosureGroupA button called by its title, open or closed.
CodeEditorA text area with its text.
IconA picture called by its name: Icon(.folderOpen) is “folder open”.
Image, Canvas, Scene3DA picture or a drawing, called what you say (see below).
MarkdownA document with its text.
A view with .onTapA button, called by the text in it (or its icon’s name).

Stacks and other views that only arrange things aren’t mentioned: what’s in them is.

A control that’s .disabled is said to be. A view that’s .hidden isn’t there.

A screen reader can only say what a view is called if something says it. Three kinds of view need you:

  • An icon that’s pressed, whose name doesn’t say what it does. Icon(.trash) is called “trash”, which is close; Icon(.more) isn’t.
  • A slider or stepper, which has no title of its own.
  • A picture or a drawing that matters: a chart, a photo.

Say it with .accessibilityLabel:

Icon(.more).onTap { showMenu() }.accessibilityLabel("More actions")
Slider(value: volume, from: 0, to: 1).accessibilityLabel("Volume")
Canvas { g in drawChart(g) }.accessibilityLabel("Sales by month: highest in June")

A view with a .tooltip and no label is called by its tooltip, so an icon button with a tooltip is already fine.

Everything works without a mouse: Tab goes from control to control, Space or Enter presses a button, and the arrows choose in a picker, a tab view, a slider or a stepper. See The keyboard and controls.

For that, use a Button for what’s pressed. A view with .onTap is a button to a screen reader, but Tab doesn’t stop on it.

Turn the screen reader on and try the app with your eyes closed: Cmd+F5 starts VoiceOver on a Mac, and Ctrl+Win+Enter starts Narrator on Windows.

To see what’s said without one, the accessibility script command prints it:

Terminal window
TESSEL_SCRIPT='accessibility' tessel run settings.tsl
Window
Text = "Settings"
TextField "Name" = "Ada"
Switch "Loud" (on)
Options
Option "Small" (chosen)
Option "Large"
Slider "Volume" = 0.5 (0 to 1)
Button "More actions"

A Button or Slider with nothing after it has no name: give it a label. The command works in tests too, so a test can keep the names there.

  • Text, piece by piece. A screen reader gets a field’s whole text, but can’t yet move through it by character or word, or type into it through its own commands (typing with the keyboard works as always).
  • Menus drawn in the window (context menus, and the menu bar on Windows) aren’t described. The menu bar on macOS is the system’s own, and is.
  • Android. Apps there don’t tell TalkBack what’s on screen yet.
  • This was checked on macOS, through the interface VoiceOver uses. It’s built for Windows too, and hasn’t been tried with Narrator.