CleverCloud / clever-components

Collection of Web Components by Clever Cloud
https://www.clever-cloud.com/doc/clever-components/
Apache License 2.0
220 stars 19 forks source link

Evaluate the use of :focus-visible (and its polyfills) #127

Open hsablonniere opened 4 years ago

hsablonniere commented 4 years ago
hsablonniere commented 4 years ago

Feedbacks from someone :wink:

hsablonniere commented 4 years ago

I also found this: https://github.com/adobe/spectrum-web-components/blob/master/packages/shared/src/focus-visible.ts

hsablonniere commented 4 years ago

Here's a custom "polyfill" someone share with me for components with shadow DOM:

/**
 * Lightweight implementation of focus-visible polyfill (https://github.com/WICG/focus-visible).
 *
 * Elements that should have visible focus, have the .focus-visible class added. In the official
 * spec focus visible differs per user agent, for our purposes it's visible only when using
 * keyboard navigation.
 *
 * See https://github.com/WICG/focus-visible/blob/master/src/focus-visible.js for full spec.
 */import { css } from 'lit-element';
import { listen, listenOnce } from '../utils/event.js';export const focusVisibleStyles = css`
  .focus-visible:focus {
    /* your styles go here */
  }
`;// Set up document listeners to track whether the keyboard is being used.
let hadKeyboardEvent = false;['mousedown', 'pointerdown', 'touchstart'].forEach(name => {
  document.addEventListener(
    name,
    () => {
      hadKeyboardEvent = false;
    },
    true,
  );
});document.addEventListener(
  'keydown',
  () => {
    hadKeyboardEvent = true;
  },
  true,
);/**
 * Observes focus visibility on the given target. This needs to be set up only once per
 * shadowRoot boundary. For general most cases, the FocusVisibileMixin should be used.
 *
 * @param {HTMLElement} target
 * @returns {() => void} Function which removes the event listener.
 */
export function observeFocusVisible(target) {
  return listen(
    target,
    'focusin',
    e => {
      /** @type {HTMLElement} */
      const focusTarget = (e.target);
      // SVG elements don't have a classList
      if (!focusTarget || !focusTarget.classList) {
        return;
      }      // We cannot handle focus events fired from within shadow roots. It should be
      // handled host element.
      if (focusTarget.shadowRoot) {
        return;
      }      if (hadKeyboardEvent) {
        hadKeyboardEvent = false;
        focusTarget.classList.add('focus-visible');        listenOnce(focusTarget, 'blur', () => {
          focusTarget.classList.remove('focus-visible');
        });
      }
    },
    true,
  );
}/**
 * Calls and cleans up observeFocusVisible() for the element's shadowroot or host.
 */
export const FocusVisibleMixin = base =>
  class extends base {
    connectedCallback() {
      if (super.connectedCallback) {
        super.connectedCallback();
      }      this.__unlistenFocusVisible = observeFocusVisible(this.shadowRoot || this);
    }    disconnectedCallback() {
      if (super.disconnectedCallback) {
        super.disconnectedCallback();
      }      if (this.__unlistenFocusVisible) {
        this.__unlistenFocusVisible();
      }
    }
  };
florian-sanders-cc commented 2 years ago

@hsablonniere :focus-visible is on the way (landing in Safari soon, in Safari TP at the moment). In the meantime, we need to decide what we do:

Alsacreation suggests a way to switch back to basic :focus selector if :focus-visible is not supported:

*:focus {
  outline: 6px dashed hotpink;
}

@supports selector(div:focus-visible) {

  /* if :focus-visible is supported, hide :focus style defined right above */
  *:focus:not(:focus-visible) {
    outline-color: transparent;
  }

  /* rely on :focus-visible to provide outline only for keyboard users */
  *:focus-visible {
    outline: 6px dashed hotpink;
  }
}

We can import the code above inside most of our components to avoid duplicating it (especially since it is quite verbose). However if one needs to customize it inside a component, they would need to override both :focus and :focus-visible styles to do so.

What I do not know at the moment is whether one would actually need to customize this and how often they would. What's your opinion on that @Galimede @hsablonniere ?

If you feel like there is a risk of over complicating things, we could simply rely on the :focus and change it to :focus-visible in 1 year or so. (this might actually the best option as long as making focus visible on click is not a problem for you).

florian-sanders-cc commented 1 year ago

We'll consider using the focus-visible when/if we introduce a shared style module that all components import. (see #690) For now, this subject in on standby.