Vize

Focus Visible

Uniform focus-visible data attributes driven by the document interaction modality.

Package @vizejs/ui/focus-visible
Maturity stable
Own the source vize lib pull focus-visible
Requires context, interaction-modality
Aliases focus ring, focus-visible, keyboard focus indicator
Covers CSS :focus-visible, React Aria FocusRing, WCAG 2.4.7 Focus Visible

Usage

import { FocusVisibleProvider } from "@vizejs/ui/focus-visible";

Or copy the source into your project with vize lib pull focus-visible (see Source Distribution).

API

FocusVisibleProvider

Source: src/families/accessibility/focus-visible/focus-visible-provider.vue

Props

Prop Type Default Description
attribute string "data-focus-visible" Attribute set to "true" on the focused descendant while focus should be indicated.
disabled boolean false Stop publishing modality and focus-visible attributes.

Slots

Slot Slot props Description
default FocusVisibleSlotState Subtree whose focused descendants receive the focus-visible attribute.

Exposed

Member Type Description
isFocusVisible boolean Whether a descendant currently shows a focus indicator.
modality FocusVisibleModality | null Latest interaction modality, or null before mount and before any input.
element HTMLDivElement | null Rendered provider element.
focusVisibleElement Element | null Descendant that currently carries the focus-visible attribute.

Behavior

Normative behavior for @vizejs/ui/focus-visible. focus-visible-provider.vue (FocusVisibleProvider) reuses the shared document interaction-modality tracker and publishes uniform data attributes so CSS can style [data-focus-visible] identically across engines, including engines whose :focus-visible heuristics differ. Every row is proven by the named test.

State x input Observable outcome Proven by
keyboard interaction, focus moves to a descendant The descendant gets data-focus-visible="true", the root gets data-vize-modality="keyboard" and data-focus-visible-within. keyboard focus marks the focused descendant and publishes the modality
focus moves between descendants The previous descendant loses the attribute and the new one gains it. keyboard focus marks the focused descendant and publishes the modality
pointer press or pointer focus on a non-text control The mark is removed (or never set) and the root reports data-vize-modality="pointer". pointer focus on buttons is not marked and a pointer press clears the mark
any modality focusing a text entry field Text inputs, textareas, and contenteditable hosts are marked, including after touch (data-vize-modality="touch"). text entry fields are marked for every modality, touch included
focus leaves the subtree The mark and data-focus-visible-within are removed. blur leaving the subtree removes the mark
attribute / disabled The mark uses the configured attribute (renaming moves it); disabled removes every published attribute. custom attribute names and disabled providers
focus already inside the subtree when mounting The provider marks the active descendant on mount. focus already inside the subtree at mount is picked up
useFocusVisible() Inside a provider it reads the provider state; standalone it tracks the document modality; outside a scope it throws VIZE_UI_FOCUS_VISIBLE_SETUP. useFocusVisible reads the provider or tracks the document standalone
heuristics Keyboard and virtual focus, text entry, and unknown modality are indicated; pointer and touch on other controls are not. heuristics mirror browser focus-visible rules
SSR Server markup is byte-identical and carries no modality or focus attributes. renders byte-identical markup without modality or focus attributes
hydration Hydration emits no warnings even when the document already has a modality; attributes appear after mount. hydrates without diagnostics even when the document already has a modality
public types Modality, state, and props are closed contracts. src/families/accessibility/focus-visible/focus-visible.types.test-d.ts
DOM/SSR/Vapor focus-visible-provider.vue compiles in every renderer lane. scripts/check-renderers.ts
Target Public contract
Root part="root", data-vize-ui="focus-visible-provider", data-vize-modality, data-focus-visible-within, data-disabled
Descendant data-focus-visible="true" (or the configured attribute) while focus should be indicated

FocusVisible ships no stylesheet.