1 | - [Rationale](#rationale)
|
2 | - [API Proposal](#api-proposal)
|
3 | * [Note: Styling non-native elements which should always match `focus-visible`](#note--styling-non-native-elements-which-should-always-match--focus-visible-)
|
4 | - [Example heuristic](#example-heuristic)
|
5 | - [Implementation Prototype](#implementation-prototype)
|
6 |
|
7 | ## Rationale
|
8 |
|
9 | The status quo, `:focus`, is quite problematic:
|
10 |
|
11 | - Many developers disable the default focus ring in their CSS styles,
|
12 | others attempt to style it in concert with their design.
|
13 | The former often seems to be a result of finding the default focus ring
|
14 | both aesthetically unpleasant and confusing to users
|
15 | when applied after a mouse or touch event and introduces accessibility problems.
|
16 | The latter inevitably creates considerably more of the kind of problem that the former was trying to solve.
|
17 | - Some native elements in some browsers,
|
18 | notably `<button>` in Chrome,
|
19 | have a "magic" focus style which does _not_ apply
|
20 | unless focus was received via a keyboard interaction.
|
21 |
|
22 | To deal with this:
|
23 | - It seems evident that a visual indication of what has focus
|
24 | is only interesting to a user who is using the keyboard
|
25 | to interact with the page.
|
26 | A user using any kind of pointing device
|
27 | would only be interested in what is in focus
|
28 | if they were _just about_ to use the keyboard -
|
29 | otherwise, it is irrelevant and potentially confusing.
|
30 | - Thus, if we only show the focus ring when relevant,
|
31 | we can avoid user confusion
|
32 | and avoid creating incentives for developers to disable it.
|
33 | - A mechanism for exposing focus ring styles
|
34 | only when the keyboard is the user's current input modality
|
35 | gives us this opportunity.
|
36 |
|
37 | ## API Proposal
|
38 |
|
39 | ```css
|
40 | /* override UA stylesheet if necessary */
|
41 | :focus {
|
42 | outline: 0;
|
43 | }
|
44 |
|
45 | /* establish desired focus ring appearance for appropriate input modalities */
|
46 | :focus-visible {
|
47 | outline: 2px solid blue;
|
48 | }
|
49 | ```
|
50 |
|
51 | `:focus-visible` matches native elements that are
|
52 | 1. focussed; and
|
53 | 2. would display a focus ring if only UA styles applied
|
54 |
|
55 | Additionally, `:focus-visible` matches non-native elements as if they were
|
56 | native button elements.
|
57 |
|
58 | ### Note: Styling non-native elements which should always match `focus-visible`
|
59 |
|
60 |
|
61 | This is not currently part of the spec,
|
62 | but a mechanism is needed to explain the ability of native text fields
|
63 | to match `:focus-visible` regardless of how focus arrived on the element.
|
64 |
|
65 | Consider an author creating a custom element, `custom-texty-element`,
|
66 | which they believe should show a focus ring on mouse click.
|
67 | By default, the default `:focus-visible` user agent style
|
68 | will not show a focus ring when this element receives focus via mouse click.
|
69 | However, if the author were to style the element via `:focus`,
|
70 | they could not recreate the browser's default `outline` style reliably:
|
71 |
|
72 | ```css
|
73 | custom-texty-element:focus {
|
74 | outline: ???;
|
75 | }
|
76 | ```
|
77 |
|
78 | Either of the following two new primitives would allow the author to
|
79 | show the default focus ring on click for this element:
|
80 |
|
81 | 1. Add a new keyword value to the outline shorthand that represents whatever the default UA `::focus-visible` is. Then authors can do:
|
82 |
|
83 | custom-texty-element:focus {
|
84 | outline: platform-default-focus-outline-style-foo;
|
85 | }
|
86 |
|
87 | 2. Add a new CSS property that controls "keyboard modality" vs non-"keyboard modality" behavior, e.g.
|
88 |
|
89 | custom-texty-element {
|
90 | show-focus-visible-foo: always | keyboard-only;
|
91 | }
|
92 |
|
93 | _("`-foo`" placeholder indicates that these names are by no means final!)_
|
94 |
|
95 | While either of these primitives would suffice,
|
96 | having both would provide more flexibility for authors.
|
97 |
|
98 | ## Example heuristic
|
99 |
|
100 | The heuristic used to decide the current modality should not be defined
|
101 | normatively. An example heuristic is to update modality on each style recalc: if
|
102 | the most recent user interaction was via the keyboard; and the key press did not
|
103 | include a meta, alt/option, or control key; then the modality is keyboard.
|
104 | Otherwise, the modality is not keyboard.
|
105 |
|
106 | See [the web platform
|
107 | tests](https://github.com/web-platform-tests/wpt/search?utf8=%E2%9C%93&q=focus-visible&type=)
|
108 | which check against the proposed behavior.
|
109 |
|
110 | ## Implementation Prototype
|
111 |
|
112 | The tiny
|
113 | [focus-visible.js](https://www.npmjs.com/package/focus-visible)
|
114 | provides a prototype intended to achieve the goals we are proposing
|
115 | with technology that exists today
|
116 | in order for developers to be able to try it out, understand it and provide feedback.
|
117 | It sets a `.js-focus-visible` class on the body element
|
118 | to provide a way to disable focus styles only when the polyfill is loaded.
|
119 | It also sets a `.focus-visible` class on the active element
|
120 | if the script determines that the keyboard is being used.
|
121 | This attribute is removed on any `blur` event.
|
122 |
|
123 | This allows authors to write rules
|
124 | which show a focus style only when it would be relevant to the user.
|
125 | Note that the prototype does not match the proposed API -
|
126 | it is intended to give developers a feel for the model
|
127 | rather than to provide a high-fidelity polyfill.
|