diff --git a/docs/focus-visible.md b/docs/focus-visible.md new file mode 100644 index 00000000..56d76868 --- /dev/null +++ b/docs/focus-visible.md @@ -0,0 +1,70 @@ +# Accessibility focus visible + +One of the core pillars for supporting accessibility (a11y) within a modern web application is through maintaining the state of focus as a user tabs through an interface. The current implementation of this a11y state is via the `:focus` state introduced in CSS Level 2 (Revision 1). + +For a11y reasons, according to the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus#Accessibility_Concerns), it is advised to NEVER remove the browsers default settings without replacing it with a sufficient substitute. + +```css +:focus { outline: none; } +``` + +> Never just remove the focus outline (visible focus indicator) without replacing it with a focus outline that will pass [WCAG 2.1 SC 1.4.11 Non-Text Contrast](https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html) + +## Design concerns + +In contrast to the a11y issue, there is typically a design concern with the `:focus` state as an element within a web app has focus regardless of tab, click or tap. As a result of that, unless there is a requirement to maintain a11y the `:focus` state is typically disabled. + +For projects that have an a11y requirement, this is not an option. This in itself is a large design challenge and one with many opportunities. + +## Selectors Level 4 specification + +In the next selector level specification draft is [9.4. The Focus-Indicated Pseudo-class: :focus-visible](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). + +> The `:focus-visible` pseudo-class applies while an element matches the `:focus` pseudo-class and the UA (User Agent) determines via heuristics that the focus should be made evident on the element. (Many browsers show a “focus ring” by default in this case.) + +-- [:focus-visible](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) + +The purpose of this new CSS spec is to allow for developers to build interfaces that speaks specifically to their special needs audience while not having to adjust the experience for those who do not require additional interaction feedback. This also allows for designers to design very specific interactions for those who require additional interaction feedback. + +## Focus visible browser support + +With [increased browser support](https://caniuse.com/?search=focus-visible) the `:focus-visible` rarely requires polyfill support. Use of this selector is consistent with any CSS pseudo-class. + +```css +:focus-visible { + background-color: var(--auro-color-border-error-on-light); + color: var(--auro-color-base-white); +} +``` + +Something to remember, browser defaults have a similar outline style with `:focus-visible` as with a standard `:focus` selector. + +## Focus visible polyfill + +The Auro WC-Generator supports the focus-visible [polyfill](https://www.npmjs.com/package/focus-visible), based on the proposed CSS pseudo selector, and allows for this functionality for remaining non-supporting browsers. + +> Based on the proposed CSS :focus-visible pseudo-selector, this prototype adds a focus-visible class to the focused element, in situations in which the :focus-visible pseudo-selector should match. + +The polyfill has been tested in a number of situations and has great support from the community and its users. + +## Focus visible, Auro custom elements, native and polyfill support + +It is important to remember that the focus-visible polyfill does NOT reach into the shadow DOM of a custom element. When tabbing through an interface, the `.focus-visible` selector will be placed on the host tag of a custom element. The CSS within the custom element will need to be aware of the outer host's appearance of the `.focus-visible` selector. The following example is how to write a CSS selector that will be inside the shadow DOM, but aware of updates to the host custom element tag. + +```css +:host(.focus-visible) { + .button { + background-color: red; + } +} +``` + +When the `:focus-visible` pseudo-class is available to the browser, the polyfill does not conflict with the `.focus-visible` selector in the DOM. In order to support both formats, it is necessary to duplicate the styles for this interaction. The pseudo-class will be applied to the actual element in the shadow DOM that was tabbed to, versus the outer tag of the custom element, so there is no need to use the `:host()` selector as a reference. + +```css +:focus-visible { + background-color: red; +} +``` + +WCSS has support for the [.focus-visible](https://alaskaairlines.github.io/WebCoreStyleSheets/#accessibility-css) selector as well as the [:focus-visible](https://alaskaairlines.github.iodocs/#core-css-#{$scope}%20*) pseudo-class selector for a baseline experience. diff --git a/template/src/[namespace]-[name].js b/template/src/[namespace]-[name].js index f5b1f06f..4873e3d2 100644 --- a/template/src/[namespace]-[name].js +++ b/template/src/[namespace]-[name].js @@ -15,6 +15,7 @@ import { LitElement, html } from "lit-element"; import "focus-visible/dist/focus-visible.min.js"; import styleCss from "./style-css.js"; import styleCssFixed from './style-fixed-css.js'; +import { isFocusVisibleSupported, isFocusVisiblePolyfillAvailable } from './util'; // See https://git.io/JJ6SJ for "How to document your components using JSDoc" /** @@ -26,9 +27,12 @@ import styleCssFixed from './style-fixed-css.js'; // build the component class class [Namespace][Name] extends LitElement { - // constructor() { - // super(); - // } + constructor() { + super(); + if (!isFocusVisibleSupported() && isFocusVisiblePolyfillAvailable()) { + window.applyFocusVisiblePolyfill(this.shadowRoot); + } + } // function to define props used within the scope of this component static get properties() { @@ -51,7 +55,7 @@ class [Namespace][Name] extends LitElement { // function that renders the HTML and CSS into the scope of the component render() { return html` -
+
`; diff --git a/template/src/style.scss b/template/src/style.scss index c1f63e91..0972a190 100644 --- a/template/src/style.scss +++ b/template/src/style.scss @@ -27,3 +27,18 @@ border: 1px solid var(--auro-color-border-error-on-light); color: var(--auro-color-border-error-on-light); } + +// Selector for native :focus-visible support +:focus-visible { + background-color: var(--auro-color-border-error-on-light); + color: var(--auro-color-base-white); +} + +// Selector for focus-visible polyfill +:host(.focus-visible) { + // requires reference to custom element selector in this example + .testClass { + background-color: var(--auro-color-border-error-on-light); + color: var(--auro-color-base-white); + } +} diff --git a/template/src/util.js b/template/src/util.js new file mode 100644 index 00000000..7a13d0d7 --- /dev/null +++ b/template/src/util.js @@ -0,0 +1,18 @@ +// Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license +// See LICENSE in the project root for license information. + +// --------------------------------------------------------------------- + +export function isFocusVisibleSupported() { + try { + document.querySelector(':focus-visible'); + } catch { + return false; + } + return true; +} + +// https://github.com/WICG/focus-visible#shadow-dom +export function isFocusVisiblePolyfillAvailable() { + return window.applyFocusVisiblePolyfill != null; +}