UCDL Accessibility Use Case Definition Language
Contents

Writing Accessibility Use Case Tests with @afixt/usecase-runner

Part 4: Supplementary Keywords

These handle situations beyond the six core interaction keywords.

# Pause execution (use sparingly — prefer wait_for)
# `2000`, `2000ms` and `2s` all work; a bare number means milliseconds
- wait: 2000
- wait: 2s

# Wait for a condition before continuing
- wait_for: text "Loading complete"
- wait_for: button "Submit"

# Navigate directly to a URL
- navigate: 'https://app.example.com/settings'

# Take a screenshot (saved to screenshot_dir)
- screenshot: 'after-form-submit'

# Send raw keyboard input — key names only
- keyboard: 'Tab'
- keyboard: 'Shift+Tab'
- keyboard: 'ArrowDown ArrowDown Enter'
- keyboard: 'Escape'

# Type literal text into whatever currently holds focus
- type: Hello accessible world

# Scroll an element into view
- scroll: to text "Terms and Conditions"
- scroll: to button "Load More"

# Mouse hover over an element (no keyboard equivalent — by design)
- hover: button "Save"
- hover_out: button "Save"

# Add a tester note (no action, appears in reports)
- note: 'Visual layout matches mockup'

# Resize the viewport for the rest of the case
- viewport: mobile
- viewport: 414x896

keyboard vs type. keyboard presses keys. Every whitespace-separated token has to name one — Tab, Enter, Shift+Tab, ArrowDown, F5, or a single character. It is not a way to type words:

# Wrong — these are not key names, and validation rejects it
- keyboard: 'Hello accessible world'
# Right
- focus: field "Title"
- type: Hello accessible world

Use type when there is no nameable field for enter to address — a contenteditable rich-text editor, a canvas-backed editor — or when the page reacts to individual keystrokes. type sends real per-character key events, so live word counts, markdown shortcuts and autocomplete all fire the way they would for a real typist. enter: field "X" value "..." fills the field in one go and is the better choice when you just need a value in an input.

About hover. hover and hover_out are intentionally mouse-only. They exist for tooltip exposure and carousel auto-rotation-pause patterns that gate behavior on pointer hover. Tests using them will fail in keyboard-only profiles by design — pair with a focus-driven companion step (focus: button "X" + a hover-equivalent assertion) when WCAG 1.4.13 also requires keyboard parity.

About viewport. Some controls only exist at some sizes. A navigation menu that collapses to a hamburger below 768px is not hidden at desktop width — the button is not in the accessibility tree at all, so locate: button "Open navigation menu" fails naming a missing element, and the report says nothing about the breakpoint being the reason.

viewport moves the journey to the size the flow is written for:

- viewport: mobile
- locate: button "Open navigation menu"
- activate: button "Open navigation menu"
- verify: visible navigation "Main"

Four presets are available — mobile (375×812), tablet (768×1024), desktop (1280×720) and reflow (320×256) — or give an explicit <width>x<height> such as 414x896.

Prefer a preset. An explicit size stops crossing the boundary it was chosen for the moment the project moves its breakpoints, and nothing in the file says so. reflow is named after WCAG 2.2 SC 1.4.10 rather than after a device: 320 CSS px is what 1280px becomes at 400% zoom, which is the width the success criterion actually names.

That last one pairs with audit, because the delegating keywords resolve against the page as it stands. Auditing at the default size never reaches the viewport-sensitive criteria; auditing at the reflow width does:

- viewport: reflow
- audit: page level "AA"

The size persists for the rest of the case — there is no automatic restore, so set it back explicitly if a later step needs the original width. And a value that is neither a preset nor two in-range integers separated by x is a parse error rather than a silent fall back to a default: a case that reads as though it changed size while running at the previous one is exactly the failure this keyword exists to remove.