├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── demo └── index.html ├── spec ├── focusring.bs └── focusring.html ├── src └── keyboard-modality.js └── w3c.json /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the Web Platform Incubator Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To contribute, you must join 5 | the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request's body or in subsequent comments. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors under the 2 | [W3C Software and Document 3 | License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). Contributions to 4 | Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Deprecated:** Please look at https://github.com/wicg/focus-ring/ instead. 2 | 3 | Archived content below. 4 | 5 | --- 6 | 7 | Based on a conversation between Alice Boxhall, Brian Kardell and Marcy Sutton, this prototype attaches/manages metadata in the form of a `modality` attribute to the body, as a way to allow authors to experiment with adapting style based on the user's _active_ input modality (i.e., how they are interacting with the UI _right now_). 8 | 9 | [Demo](https://alice.github.io/modality/demo) 10 | 11 | ## Rationale 12 | 13 | There are many instances in which it would be useful for authors to understand the user's current interaction modality and be able to adapt the UI with better accomodations. The motivating example is `:focus` where the status quo is quite problematic: 14 | 15 | - The default focus ring is not based on a single :focus rule as some might expect, not all things which can receive focus receive a ring in all cases. Adding such a rule is always currently problematic, but it's also exceptionally common. 16 | - Many developers disable the default focus ring in their CSS styles, others attempt to style it in concert with their design. The former often seems to be a result of finding the default focus ring both aesthetically unpleasant and confusing to users when applied after a mouse or touch event and introduces accessibility problems. The latter inevitably creates considerably more of the kind of problem that the former was trying to solve. 17 | 18 | To deal with this: 19 | - It seems evident that a visual indication of what has focus is only interesting to a user who is using the keyboard to interact with the page. A user using any kind of pointing device would only be interested in what is in focus if they were _just about_ to use the keyboard - otherwise, it is irrelevant and potentially confusing. 20 | - Thus, if we only show the focus ring when relevant, we can avoid user confusion and avoid creating incentives for developers to disable it. 21 | - A mechanism for exposing focus ring styles only when the keyboard is the user's current input modality gives us this opportunity. 22 | 23 | ## API Proposal 24 | 25 | ```css 26 | /* override UA stylesheet if necessary */ 27 | :focus { 28 | outline: none; 29 | } 30 | 31 | /* establish desired focus ring appearance for appropriate input modalities */ 32 | :focusring { 33 | outline: 2px solid blue; 34 | } 35 | ``` 36 | 37 | :focusring matches native elements that are 38 | 1. focussed; and 39 | 2. would display a focus ring if only UA styles applied 40 | 41 | Additionally, :focusring matches non-native elements as if they were 42 | native button elements. 43 | 44 | ## Example heuristic 45 | 46 | The heuristic used to decide the current modality should not be defined 47 | normatively. An example heuristic is to update modality on each style recalc: 48 | if the most recent user interaction was via the keyboard; and less than 100ms 49 | has elapsed since the last input event; then the modality is keyboard. Otherwise, 50 | the modality is not keyboard. 51 | 52 | ## Implementation Prototype 53 | 54 | The tiny [keyboard-modality.js](http://alice.github.io/modality/src/keyboard-modality.js) provides a prototype intended to achieve the goals we are proposing with technology that exists today in order for developers to be able to try it out, understand it and provide feedback. Simply speaking, it sets a `modality=keyboard` attribute on `body` if the script determines that the keyboard is being used. Similarly, the attribute is removed if the script determines that the user is no longer using the keyboard. This allows authors to write rules which consider the input modality and style appropriately. Note that the prototype does not match the proposed API - it is intended to give developers a feel for the model rather than to provide a high-fidelity polyfill. 55 | 56 | It also simulates how the default UA styles would be adjusted by appending the following style as the first rule in the page, which disables the focus ring _unless_ `modality` is set to `keyboard`: 57 | 58 | ```html 59 | body:not([modality=keyboard]) :focus { 60 | outline: none; 61 | } 62 | ``` 63 | 64 | (This is added in a ` 24 | 25 | 30 | 31 | 32 |

Keyboard-only focus test

33 |

34 | This page contains various html controls and demonstrates a proposal for only drawing a focus ring when the keyboard is being used. 35 | Navigate through the interactive elements various ways (using mouse, switch to keyboard, try it on a touch device). You should see a green focus indicator (or comment out green focus ring style to see default focus ring) on elements below differently based on whether you interact with them via keyboard or mouse. If you are using the mouse and get lost, at any time you can tab/shift-tab to find your focus indicator. 36 |

37 |

Editable text

38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 |
Contenteditable with textbox role
46 |
47 |

Buttons

48 |
49 | 50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 | 63 |
64 |

Selects

65 | 72 | 79 | 86 |

Other

87 | 90 | 93 |
94 | 95 |
96 |
97 | 98 |
99 |
100 | 101 |
102 |
103 | 104 |
105 |
106 | 107 |
108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /spec/focusring.bs: -------------------------------------------------------------------------------- 1 |
 2 | Title: Input Modality
 3 | Status: w3c/ED
 4 | Work Status: Exploring
 5 | ED: http://github.com/WICG/modality/modality.html
 6 | Shortname: modality
 7 | Level: 1
 8 | Editor: Alice Boxhall, Google, aboxhall@google.com
 9 | Abstract: This specification describes an extension to the :focus pseudoclass that allows developers to change focus behavior based on the input modality of the user interaction that led to the focus occurring.
10 | Repository: WICG/modality
11 | 
12 | 13 | Introduction {#introduction} 14 | ============================ 15 | 16 | Focus rings highlight the current focus location. Particularly during keyboard 17 | interactions, focus rings provide important feedback to the user by showing the 18 | current interaction point. This is particularly important for accessibility use 19 | cases where non-keyboard interactions are unavailable. 20 | 21 | User agents employ a set of heuristics to decide when to show a focus ring. 22 | For example, typically, selecting an element via the keyboard results in a 23 | focus ring being displayed, while selecting an element via a mouse interaction 24 | does not. 25 | 26 | Unfortunately, focus rings are not easily stylable. While the :focus pseudo-class 27 | has been available for some time, this matches every focussed element rather than 28 | just those which the user agent chooses to highlight. 29 | 30 | This specification introduces a new pseudo-class, '':focusring'', that matches an 31 | element only when the user agent would choose to show a focus ring for that 32 | element. 33 | 34 |
35 | 36 | To change the focus ring style to a dotted red outline, without changing the 37 | behavior of when a ring is shown: 38 | 39 |
40 | :focusring {
41 |   outline: 1px dotted red;
42 | }
43 | 
44 | 45 |
46 | 47 | The '':focusring'' pseudo-class {#the-focusring-pseudo-class} 48 | ============================================================= 49 | 50 | The focusring pseudo-class, :focusring, 51 | is a dynamic user action pseudo-class. 52 | 53 | For native interaction elements, :focusring applies when the user agent would 54 | choose to display a focusring in the absence of UA or author stylesheets. 55 | 56 | For other elements, :focusring applies according to a heuristic that may 57 | match the behavior of the UA for native button elements. An example heuristic 58 | is included non-normatively in the appendix. 59 | 60 |
61 | For example, most UAs choose to display focusrings on native text fields 62 | whenever they are focussed, and on native buttons only when selected via 63 | the keyboard. 64 |
65 | 66 |
67 | Authors who build non-native interactive elements that are text-field like 68 | in nature should consider adding a '':focus'' rule to that element to 69 | ensure a focusring is displayed whenever focussed, rather than only when 70 | the heuristic matches. 71 |
72 | 73 | Appendix A: Example Heuristic for non-native elements {#appendix-a} 74 | =================================================================== 75 | 76 | This appendix is non-normative. 77 | 78 | A user agent might choose to use the following heuristic for deciding when 79 | to apply '':focusring'' to elements that are not native interaction elements: 80 | 81 | When focussing an element: 82 | * if the last interaction was a keyboard event, and the interaction 83 | was less than 100ms in the past, apply '':focusring''. 84 | 85 | When unfocussing an element: 86 | * stop applying '':focusring''. 87 | 88 | When handling a keyboard event: 89 | * if a non-native element is focussed, apply '':focusring'' to that element. 90 | -------------------------------------------------------------------------------- /spec/focusring.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Input Modality 6 | 7 | 1178 | 1179 | 1189 | 1218 | 1255 | 1301 | 1363 |

Jump to Table of Contents Pop Out Sidebar

1364 |
1365 |

1366 |

Input Modality

1367 |

Editor’s Draft,

1368 |
1369 |
1370 |
This version: 1371 |
http://github.com/WICG/modality/modality.html 1372 |
Issue Tracking: 1373 |
GitHub 1374 |
Editor: 1375 |
(Google) 1376 |
1377 |
1378 |
1379 | 1385 |
1386 |
1387 |

Abstract

1388 |
1389 |

This specification describes an extension to the :focus pseudoclass that allows developers to change focus behavior based on the input modality of the user interaction that led to the focus occurring.

1390 |
1391 |
1392 | 1413 |
1414 |

1. Introduction

1415 |

Focus rings highlight the current focus location. Particularly during keyboard 1416 | interactions, focus rings provide important feedback to the user by showing the 1417 | current interaction point. This is particularly important for accessibility use 1418 | cases where non-keyboard interactions are unavailable.

1419 |

User agents employ a set of heuristics to decide when to show a focus ring. 1420 | For example, typically, selecting an element via the keyboard results in a 1421 | focus ring being displayed, while selecting an element via a mouse interaction 1422 | does not.

1423 |

Unfortunately, focus rings are not easily stylable. While the :focus pseudo-class 1424 | has been available for some time, this matches every focussed element rather than 1425 | just those which the user agent chooses to highlight.

1426 |

This specification introduces a new pseudo-class, :focusring, that matches an 1427 | element only when the user agent would choose to show a focus ring for that 1428 | element.

1429 |
1430 | 1431 |

To change the focus ring style to a dotted red outline, without changing the 1432 | behavior of when a ring is shown:

1433 |
:focusring {
1434 |   outline: 1px dotted red;
1435 | }
1436 | 
1437 |
1438 |

2. The :focusring pseudo-class

1439 |

The focusring pseudo-class, :focusring, 1440 | is a dynamic user action pseudo-class.

1441 |

For native interaction elements, :focusring applies when the user agent would 1442 | choose to display a focusring in the absence of UA or author stylesheets.

1443 |

For other elements, :focusring applies according to a heuristic that may 1444 | match the behavior of the UA for native button elements. An example heuristic 1445 | is included non-normatively in the appendix.

1446 |
For example, most UAs choose to display focusrings on native text fields 1447 | whenever they are focussed, and on native buttons only when selected via 1448 | the keyboard.
1449 |
Authors who build non-native interactive elements that are text-field like 1450 | in nature should consider adding a :focus rule to that element to 1451 | ensure a focusring is displayed whenever focussed, rather than only wen 1452 | the heuristic matches.
1453 |

Appendix A: Example Heuristic for non-native elements

1454 |

This appendix is non-normative.

1455 |

A user agent might choose to use the following heuristic for deciding when 1456 | to apply :focusring to elements that are not native interaction elements:

1457 |

When focussing an element:

1458 | 1463 |

When unfocussing an element:

1464 | 1468 |

When handling a keyboard event:

1469 | 1473 |
1474 |
1475 |

Conformance

1476 |

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. 1477 | The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” 1478 | in the normative parts of this document 1479 | are to be interpreted as described in RFC 2119. 1480 | However, for readability, 1481 | these words do not appear in all uppercase letters in this specification.

1482 |

All of the text of this specification is normative 1483 | except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

1484 |

Examples in this specification are introduced with the words “for example” 1485 | or are set apart from the normative text with class="example", like this:

1486 |
This is an example of an informative example.
1487 |

Informative notes begin with the word “Note” 1488 | and are set apart from the normative text with class="note", like this:

1489 |

Note, this is an informative note.

1490 |
1491 | 1620 |

Index

1621 |

Terms defined by this specification

1622 | 1625 |

Terms defined by reference

1626 | 1633 |

References

1634 |

Normative References

1635 |
1636 |
[RFC2119] 1637 |
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119 1638 |
1639 |

Informative References

1640 |
1641 |
[SELECTORS-4] 1642 |
Selectors Level 4 URL: https://drafts.csswg.org/selectors-4/ 1643 |
1644 | 1652 | 1709 | -------------------------------------------------------------------------------- /src/keyboard-modality.js: -------------------------------------------------------------------------------- 1 | /* https://github.com/alice/modality */ 2 | document.addEventListener("DOMContentLoaded", function() { 3 | var hadKeyboardEvent = false, 4 | keyboardModalityWhitelist = [ "input:not([type])", 5 | "input[type=text]", 6 | "input[type=number]", 7 | "input[type=date]", 8 | "input[type=time]", 9 | "input[type=datetime]", 10 | "textarea", 11 | "[role=textbox]", 12 | "[supports-modality=keyboard]"].join(","), 13 | isHandlingKeyboardThrottle, 14 | matcher = (function () { 15 | var el = document.body; 16 | if (el.matchesSelector) 17 | return el.matchesSelector; 18 | if (el.webkitMatchesSelector) 19 | return el.webkitMatchesSelector; 20 | if (el.mozMatchesSelector) 21 | return el.mozMatchesSelector; 22 | if (el.msMatchesSelector) 23 | return el.msMatchesSelector; 24 | console.error("Couldn't find any matchesSelector method on document.body."); 25 | }()), 26 | disableFocusRingByDefault = function () { 27 | var css = "body:not([modality=keyboard]) :focus { outline: none; }", 28 | head = document.head || document.getElementsByTagName("head")[0], 29 | style = document.createElement("style"); 30 | 31 | style.type = "text/css"; 32 | style.id = "disable-focus-ring"; 33 | if (style.styleSheet) { 34 | style.styleSheet.cssText = css; 35 | } else { 36 | style.appendChild(document.createTextNode(css)); 37 | } 38 | 39 | head.insertBefore(style, head.firstChild); 40 | }, 41 | focusTriggersKeyboardModality = function (el) { 42 | var triggers = false; 43 | if (matcher) { 44 | triggers = matcher.call(el, keyboardModalityWhitelist) && matcher.call(el, ":not([readonly]"); 45 | } 46 | return triggers; 47 | }; 48 | 49 | disableFocusRingByDefault(); 50 | 51 | document.body.addEventListener("keydown", function() { 52 | hadKeyboardEvent = true; 53 | if (isHandlingKeyboardThrottle) { 54 | clearTimeout(isHandlingKeyboardThrottle); 55 | } 56 | isHandlingKeyboardThrottle = setTimeout(function() { 57 | hadKeyboardEvent = false; 58 | }, 100); 59 | }, true); 60 | 61 | document.body.addEventListener("focus", function(e) { 62 | if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) 63 | document.body.setAttribute("modality", "keyboard"); 64 | }, true); 65 | 66 | document.body.addEventListener("blur", function() { 67 | document.body.removeAttribute("modality"); 68 | }, true); 69 | }); -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": ["80485"] 3 | , "contacts": ["yoavweiss"] 4 | , "shortName": "modality" 5 | } --------------------------------------------------------------------------------