From 6c8da38be5ec2600d5c52fe00ff4de90053d717e Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sun, 5 Jul 2020 21:50:15 +1000 Subject: [PATCH] Add :focus-visible polyfill (#123) --- .../internal/FocusVisiblePolyfill.js | 33 +++++++++++ src/providers/BasisProvider.js | 2 + src/themes/default/accordion.js | 1 + src/themes/default/button.js | 1 + src/themes/default/dropdown.js | 1 + src/themes/default/index.js | 55 ++++--------------- src/themes/default/link.js | 1 + website/src/themes/website/button.js | 1 + website/src/themes/website/link.js | 1 + 9 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 src/components/internal/FocusVisiblePolyfill.js diff --git a/src/components/internal/FocusVisiblePolyfill.js b/src/components/internal/FocusVisiblePolyfill.js new file mode 100644 index 00000000..4b501b52 --- /dev/null +++ b/src/components/internal/FocusVisiblePolyfill.js @@ -0,0 +1,33 @@ +import { useState, useEffect } from "react"; +import useWindow from "../../hooks/useWindow"; + +function FocusVisiblePolyfill() { + const [isKeyboardMode, setIsKeyboardMode] = useState(false); + const onKeyDown = () => setIsKeyboardMode(true); + const onMouseDown = () => setIsKeyboardMode(false); + const window = useWindow(); + + useEffect(() => { + if (window) { + window.addEventListener("keydown", onKeyDown); + window.addEventListener("mousedown", onMouseDown); + } + + return () => { + if (window) { + window.removeEventListener("keydown", onKeyDown); + window.removeEventListener("mousedown", onMouseDown); + } + }; + }, [window]); + + useEffect(() => { + if (window) { + window.document.body.dataset.basisKeyboardMode = String(isKeyboardMode); + } + }, [window, isKeyboardMode]); + + return null; +} + +export default FocusVisiblePolyfill; diff --git a/src/providers/BasisProvider.js b/src/providers/BasisProvider.js index 061a5f4e..df0355f2 100644 --- a/src/providers/BasisProvider.js +++ b/src/providers/BasisProvider.js @@ -4,6 +4,7 @@ import WindowProvider from "./WindowProvider"; import BreakpointProvider from "./BreakpointProvider"; import LinkProvider from "./LinkProvider"; import { enhanceTheme } from "../utils/theme"; +import FocusVisiblePolyfill from "../components/internal/FocusVisiblePolyfill"; export const ThemeContext = React.createContext(); @@ -19,6 +20,7 @@ function BasisProvider({ return ( + ({ textAlign: "left", ...textStyles(theme)["subtitle2"], ...textStyles(theme)["subtitle2.bold"], + outline: 0, ...theme.focusStyles.focusVisible, }, accordionHeaderContent: { diff --git a/src/themes/default/button.js b/src/themes/default/button.js index b298b3a2..b96fbfa9 100644 --- a/src/themes/default/button.js +++ b/src/themes/default/button.js @@ -10,6 +10,7 @@ export default (theme) => ({ overflow: "hidden", transition: theme.transitions.button, borderRadius: theme.radii[1], + outline: 0, ...theme.focusStyles.focusVisible, "a &": { cursor: "pointer", diff --git a/src/themes/default/dropdown.js b/src/themes/default/dropdown.js index a3e080d6..bbf64add 100644 --- a/src/themes/default/dropdown.js +++ b/src/themes/default/dropdown.js @@ -13,6 +13,7 @@ export default (theme) => ({ margin: 0, border: 0, borderRadius: theme.radii[0], + outline: 0, ...theme.focusStyles.focusVisible, }, dropdownButtonPlaceholder: { diff --git a/src/themes/default/index.js b/src/themes/default/index.js index 096848e8..2d661d6b 100644 --- a/src/themes/default/index.js +++ b/src/themes/default/index.js @@ -200,61 +200,28 @@ theme.shadows = { focus: `0 0 0px ${theme.radii[1]} ${theme.colors.secondary.lightBlue.t80}`, }; +const focusStyle = { + boxShadow: theme.shadows.focus, + // Make sure that the focus style sits above the surrounding elements with normal page flow + position: "relative", + zIndex: theme.zIndices.aboveNormalFlow, +}; + theme.focusStyles = { - // https://github.com/WICG/focus-visible#backwards-compatibility focusVisible: { - // Provide basic, default focus styles. ":focus": { - outline: 0, - boxShadow: theme.shadows.focus, - // Make sure that the focus style sits above the surrounding elements with normal page flow - position: "relative", - zIndex: theme.zIndices.aboveNormalFlow, - }, - // Remove default focus styles for mouse users ONLY if :focus-visible is supported on this platform. - ":focus:not(:focus-visible)": { - boxShadow: "none", - position: "initial", - zIndex: "initial", - }, - // If :focus-visible is supported on this platform, provide enhanced focus styles for keyboard focus. - ":focus-visible": { - boxShadow: theme.shadows.focus, - // Make sure that the focus style sits above the surrounding elements with normal page flow - position: "relative", - zIndex: theme.zIndices.aboveNormalFlow, + '[data-basis-keyboard-mode="true"] &': focusStyle, }, }, focusVisibleAdjacentLabel: { ":focus + label": { - boxShadow: theme.shadows.focus, - // Make sure that the focus style sits above the surrounding elements with normal page flow - position: "relative", - zIndex: theme.zIndices.aboveNormalFlow, - }, - ":focus:not(:focus-visible) + label": { - boxShadow: "none", - position: "initial", - zIndex: "initial", - }, - ":focus-visible + label": { - boxShadow: theme.shadows.focus, - // Make sure that the focus style sits above the surrounding elements with normal page flow - position: "relative", - zIndex: theme.zIndices.aboveNormalFlow, + '[data-basis-keyboard-mode="true"] &': focusStyle, }, }, }; -theme.focusStyles.__keyboardFocus = { - ...theme.focusStyles.focusVisible[":focus"], - ...theme.focusStyles.focusVisible[":focus-visible"], -}; - -theme.focusStyles.__keyboardFocusAdjacentLabel = { - ...theme.focusStyles.focusVisibleAdjacentLabel[":focus + label"], - ...theme.focusStyles.focusVisibleAdjacentLabel[":focus-visible + label"], -}; +theme.focusStyles.__keyboardFocus = focusStyle; +theme.focusStyles.__keyboardFocusAdjacentLabel = focusStyle; export default { ...theme, diff --git a/src/themes/default/link.js b/src/themes/default/link.js index ef5f3754..b32fbdd7 100644 --- a/src/themes/default/link.js +++ b/src/themes/default/link.js @@ -4,6 +4,7 @@ export default (theme) => ({ link: { textDecoration: "none", borderRadius: theme.radii[0], + outline: 0, ...theme.focusStyles.focusVisible, }, "link.light-bg": { diff --git a/website/src/themes/website/button.js b/website/src/themes/website/button.js index 6a14ef8d..afd21855 100644 --- a/website/src/themes/website/button.js +++ b/website/src/themes/website/button.js @@ -10,6 +10,7 @@ export default (theme) => ({ overflow: "hidden", transition: "transform 100ms ease, color 100ms ease, border-color 100ms ease", + outline: 0, ...theme.focusStyles.focusVisible, }, "button:disabled": { diff --git a/website/src/themes/website/link.js b/website/src/themes/website/link.js index 94e38c28..fa2bd3ae 100644 --- a/website/src/themes/website/link.js +++ b/website/src/themes/website/link.js @@ -3,6 +3,7 @@ export default (theme) => ({ display: "inline-block", textDecoration: "none", borderRadius: theme.radii[0], + outline: 0, ...theme.focusStyles.focusVisible, }, "link.light-bg": {