Accessibility
Pyzed apps can be used from the keyboard and respect the motion preference of the system. Screen readers are not supported yet: see Screen readers for why and what comes next.
Moving between fields with Tab
Section titled “Moving between fields with Tab”Tab moves the keyboard focus to the next field of the window and
Shift+Tab to the previous one, in the order they appear in view().
After the last field it goes back to the first. Fields are text_inputs, text_editors and
code_editors (and the components built on them, like ui.input or ui.search), dropdowns
(pz.pick_list and ui.select) and the controls wrapped in pz.focusable: the library’s
ui.switch, ui.checkbox, ui.slider, ui.segmented and ui.radio_group. Each window cycles
through its own fields only.
A code_editor with tab_size keeps Tab to indent, like a code editor. Editors without it
pass the key on, so Tab leaves them.
on_key sees the key first ("tab" or "shift-tab"). If it returns something (a message or
a Command), the app handled it and the focus stays; returning None lets the focus move.
Turn the behavior off for a whole app or window with tab_focus = False:
class App(pz.App): tab_focus = False # Tab does nothing unless on_key handles it
def on_key(self, combo): if combo == "tab" and self.completion_open: return self.accept_completion()Command.focus_next() and Command.focus_previous() do the same from code, and
Command.focus(id) jumps to a specific field (pass id= to the control).
Controls with the keyboard focus
Section titled “Controls with the keyboard focus”A click or Tab gives a control the focus, and it keeps it after you use it, like a
text field. A ring in the theme’s focus color shows around it once you use the keys (a click
alone draws none, like a browser’s :focus-visible); a dropdown lights its border instead.
| Control | Keys while it has the focus |
|---|---|
ui.select, pz.pick_list |
↓/↑ (and →/←) pick the next or previous option without opening the menu, Home/End the first and last, letters jump to the option that starts with them. Enter, Space, Alt+↓ or F4 open the menu; there ↑/↓ move, Enter picks and Esc closes. |
ui.switch |
Space or Enter flip it. |
ui.checkbox |
Space toggles it (a mixed one becomes checked). |
ui.segmented, ui.radio_group |
The arrows pick the previous or next choice, skipping disabled ones; Home/End the first and last. |
ui.slider |
The arrows move one step, with Shift (or Page Up/Page Down) ten; Home/End go to min/max. |
ui.number_field |
↑/↓ step it (ten with Shift), Home/End go to min/max. |
The keys a focused control uses do not reach the app’s on_key, so ↓ on a dropdown
does not also scroll a list or move a selection elsewhere. Every other key (shortcuts like
Ctrl+S) still does.
Your own controls get the same with pz.focusable(content, on_key, keys=[...]): while it has the
focus, the combos in keys call on_key(combo) and nothing else sees them.
def stars(self): t = self.theme row = pz.row([pz.icon("star", color=t.warning if i < self.rating else t.text_muted) for i in range(5)], spacing=2)
def rate(combo): self.rating = max(0, min(5, self.rating + (1 if combo == "right" else -1)))
return pz.focusable(row, rate, keys=["left", "right"], id="rating", ring_color=self.theme.border_focused)Reduce motion
Section titled “Reduce motion”When the system asks apps to reduce motion, Pyzed turns its animations off: every
animate(), transition(), presence(), window transition and animated scroll shows its
final state at once. The setting is read when the app starts:
| System | Setting |
|---|---|
| macOS | System Settings → Accessibility → Display → Reduce motion |
| Windows | Settings → Accessibility → Visual effects → Animation effects (off) |
| Linux (GNOME) | Settings → Accessibility → Reduce animation (enable-animations) |
| Linux (KDE) | System Settings → Animation speed set to Instant |
pz.system_reduced_motion() returns what the system says (None if it can’t tell). Your
app can still decide:
pz.set_reduced_motion(True) # always off, e.g. from your app's settingspz.set_reduced_motion(False) # always onpz.set_reduced_motion(None) # follow the system again (the default)The PYZED_REDUCED_MOTION environment variable (1 or 0) overrides both. Screenshots always
show the resting state. See Animation.
Screen readers
Section titled “Screen readers”Pyzed draws its interface with Iced, which paints every widget itself on the GPU instead of using the native controls of the system. Screen readers (VoiceOver, Narrator, NVDA, Orca) need an accessibility tree that describes those widgets, and Iced 0.14 does not produce one. The usual way to provide it in Rust is AccessKit, and we evaluated adding it to Pyzed for 1.1:
- Iced 0.14 has no AccessKit support. Neither
icednoriced_winitdepend on it, and winit 0.30 leaves accessibility to adapters such asaccesskit_winit. - The window is owned by Iced.
accesskit_winitmust be created before the window is shown and receive every window event; Iced’s event loop gives no place to do either. The platform adapters (accesskit_windowsandaccesskit_macossubclass the native window,accesskit_unixtalks to AT-SPI) could be attached from the raw window handle, but that is three separate integrations, each tested with a real screen reader. - Widgets don’t describe themselves. Iced’s widget operations report text, text fields, scrollables and focus, but no roles (button, checkbox, slider), states or actions (“press”), so Pyzed would have to build the tree from its own widget descriptions and keep it in sync with the layout every frame.
That makes it a project of its own, not something to ship half done in a minor release. Accessibility is on Iced’s roadmap and forks of Iced already ship AccessKit, so the plan is:
- Follow Iced upstream and adopt its accessibility support when it lands; Pyzed’s widgets map one to one to Iced’s, so they would gain it with little extra work.
- Meanwhile, make the keyboard path complete (focus for buttons and other controls when Iced allows it) and keep labels in the UI text rather than only in icons.
If screen reader support matters for your app, please say so in the issues: it helps decide priorities.