FocusVisibleMixin

Shows a focus indication if and only if the keyboard is active

Overview

Purpose: Tracks a component's focus state so that it can render a focus indicator (e.g., a glowing outline) if and only if the user has used the keyboard to interact with the component. This is modeled after the proposed focus-visible feature for CSS.

This mixin works at the beginning of the Elix render pipeline:

eventsmethods → setState → render DOM → post-render

Expects the component to provide:

  • setState method compatible with ReactiveMixin's setState.

Provides the component with:

  • [internal.state].focusVisible member that is true if the element should render focus.

Usage

import FocusVisibleMixin from "elix/src/base/FocusVisibleMixin.js";
class MyElement extends FocusVisibleMixin(HTMLElement) {}

FocusVisibleMixin is appropriate for all components that can receive the keyboard focus but only want to show the focus indicator when the keyboard is actually being used. This is particularly helpful on mobile devices such as phones, where most users don't need or want to see the keyboard focus.

See also KeyboardMixin, which can be used to handle keyboard events.

Example

One
Two
Three
Demo: These custom buttons only show focus for keyboard activity

Heuristic

This mixin sets [internal.state].focusVisible if the document has received a keydown event since the last mousedown event (which includes taps).

Example: if the user presses the Tab key to move to a ListBox element, the ListBox shows a focus indicator. If the user then clicks an item with the mouse, the focus indicator will disappear. If the user then presses an arrow key to move the selection, the ListBox will show a focus indicator once again.

Note: A component can't actually force a focus indicator to appear when it wants, but can only suppress the focus indicator when it doesn't want one. In the above example, when we say that the ListBox "shows a focus indicator, it's more precise to say that it "stops telling the browser it doesn't want a focus indicator". The difference is that the browser may have additional heuristics for deciding when to show a focus indicator or not. There may be cases where the component says it's okay letting a focus indicator appear, but the browser may decide for its own reasons not to render one.

FocusVisibleMixin also tracks focus visibility across changes in window focus. If an element has [internal.state].focusVisible set to true (i.e., it should show focus state) and the window loses focus, then [internal.state].focusVisible will be set to false. If the window later regains focus, then the browser will restore focus to the previously-focused element, and [internal.state].focusVisible will be set to true again.

To see this, use the keyboard to move the focus to one of the buttons in the demo above. Then tab away from that window. The button should drop its visible focus. Then tab back to the window. The button should once again show its visible focus.