DioxusLabs / DioxusLabs/blitz

Keyboard interaction: `:focus-visible` never matches, focus does not repaint, Enter/Space activate nothing

Open
#839 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
4.1k
Forks
203
Avg merge
8h 58m
Merged PRs (30d)
112

Description

## Problem

Keyboard use of a Blitz app is effectively broken. Three separate things, filed
together because each one hides the next: focus rings never render, focus changes
do not trigger a frame, and no element can be activated from the keyboard. I
found them in that order while trying to make a component library keyboard
accessible.

## Environment

- blitz `main` (`a50cb897`)
- dioxus `0.8.0-alpha.1`, dioxus-native, `vello` renderer
- Arch Linux, kernel 7.1.9, Intel RPL-S, Mesa

## Minimal reproduction

```html

button:focus-visible { outline: 2px solid red; }

click me
```

1. Press Tab. No outline appears.
2. Move the mouse without clicking. Still no outline.
3. With the button focussed, press Enter, then Space. The click handler never
runs.

Driving the same document headlessly, reading the focussed node's computed
outline, counting `request_redraw()` calls and counting handler invocations:

```
after Tab focus on , outline BorderStyle(None) BorderSideWidth(3px)
redraws requested by the Tab: 0
after Enter clicks: 0
after Space clicks: 0
```

So focus does move (`focus_next_node()` runs and the button takes focus), but
the `:focus-visible` rule never matches, no frame is requested, and neither key
activates anything. For comparison, the same measurement with all three fixed
reads `BorderStyle(Solid) BorderSideWidth(2px)`, 1 redraw, and 1 then 2 clicks.

## Cause

For the missing outline, `match_non_ts_pseudo_class` in
`packages/blitz-dom/src/stylo.rs` has `NonTSPseudoClass::FocusVisible => false`,
so the selector can never match. The document already tracks
`ElementState::FOCUSRING` for this, set and cleared by `Node::focus` / `Node::blur`.

Matching it against `FOCUSRING` alone would make `:focus-visible` behave like
`:focus`, since `FOCUSRING` is currently set on pointer-driven focus too.
Browsers gate this on the last interaction modality rather than per element: a
key press arms the ring, a click disarms it, and any focus change including a
programmatic `element.focus()` inherits whatever is armed.

For the missing repaint, focus is a document-side state change only. No mutation
reaches the shell, and `blitz-shell`'s `KeyboardInput` arm requests no redraw,
unlike the pointer paths.

For activation, `handle_key_or_input_event` in `events/keyboard.rs` handles Tab
traversal and text input editing, and nothing else. There is no activation path
at all, so ``, ``, checkable inputs and `` are all
mouse-only.

## Possible solution

Some pointers, in case they are useful. `Node::synthetic_click_event(mods)`
already exists and dispatching it through the event driver queue reaches both
script handlers and the click default action, where checkbox toggling, ``
expansion and link navigation already live. Calling `request_redraw()` from
`set_focus_to` and `clear_focus` covers programmatic focus as well as Tab.
Tracking the modality needs a flag somewhere on the document, armed on key events
and disarmed on pointer events (pointer movement is not an interaction).

One thing to decide: HTML puts a button's Space activation on key up so that
holding Space does not repeat, while Enter activates on key down.

## AI disclaimer

This report was investigated and written by AI (Claude), on bugs I hit and
verified myself. I have a working local patch for all three and am happy to open
a PR if you want it.

Contributor guide

Open the contributing guide

Research direction

Start with packages/blitz-dom/src/stylo.rs, events/keyboard.rs, Node::focus/blur, set_focus_to, clear_focus, and blitz-shell's KeyboardInput handling. Trace focus state, redraw requests, and the event driver queue from Tab, pointer, Enter, and Space input. Done means focus-visible follows modality, focus repaints, and the appropriate elements activate with the documented key timing.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
accessibility, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.