diff --git a/.changeset/heavy-trainers-invite.md b/.changeset/heavy-trainers-invite.md new file mode 100644 index 00000000000..a4277847d88 --- /dev/null +++ b/.changeset/heavy-trainers-invite.md @@ -0,0 +1,5 @@ +--- +"@primer/components": major +--- + +Removed `useMouseIntent` in favor of [`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible). With the removal of `useMouseIntent`, the `intent-mouse` class will no longer be added to the ``. Since `:focus-visible` is a relatively new psuedo-class, a polyfill is included. Any focused elements that meet the criteria for `:focus-visible` will also have a `focus-visible` class added to them by the polyfill. diff --git a/package.json b/package.json index 039c4c060b7..906bf92fd75 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@types/styled-system__theme-get": "5.0.1", "classnames": "^2.2.5", "deepmerge": "4.2.2", + "focus-visible": "5.2.0", "polished": "3.5.2", "react-is": "16.10.2", "styled-system": "5.1.2" diff --git a/src/BaseStyles.tsx b/src/BaseStyles.tsx index 42d82f6a18c..c2b5e02271a 100644 --- a/src/BaseStyles.tsx +++ b/src/BaseStyles.tsx @@ -1,27 +1,25 @@ import React from 'react' import styled, {createGlobalStyle} from 'styled-components' import {COMMON, SystemCommonProps, SystemTypographyProps, TYPOGRAPHY} from './constants' -import useMouseIntent from './hooks/useMouseIntent' import {ComponentProps} from './utils/types' const GlobalStyle = createGlobalStyle` * { box-sizing: border-box; } body { margin: 0; } table { border-collapse: collapse; } - body.intent-mouse { - [role="button"]:focus, - [role="tabpanel"][tabindex="0"]:focus, - button:focus, - summary:focus, - a:focus { - outline: none; - box-shadow: none; - } - - [tabindex="0"]:focus, - details-dialog:focus { - outline: none; - } + + [role="button"]:focus:not(:focus-visible):not(.focus-visible), + [role="tabpanel"][tabindex="0"]:focus:not(:focus-visible):not(.focus-visible), + button:focus:not(:focus-visible):not(.focus-visible), + summary:focus:not(:focus-visible):not(.focus-visible), + a:focus:not(:focus-visible):not(.focus-visible) { + outline: none; + box-shadow: none; + } + + [tabindex="0"]:focus:not(:focus-visible):not(.focus-visible), + details-dialog:focus:not(:focus-visible):not(.focus-visible) { + outline: none; } ` @@ -34,7 +32,10 @@ export type BaseStylesProps = ComponentProps function BaseStyles(props: BaseStylesProps) { const {children, ...rest} = props - useMouseIntent() + + // load polyfill for :focus-visible + require('focus-visible') + return ( diff --git a/src/SelectMenu/SelectMenuItem.tsx b/src/SelectMenu/SelectMenuItem.tsx index c160d8861fe..5a1515406f2 100644 --- a/src/SelectMenu/SelectMenuItem.tsx +++ b/src/SelectMenu/SelectMenuItem.tsx @@ -65,7 +65,6 @@ export const listItemStyles = css` // can hover states @media (hover: hover) { - body:not(.intent-mouse) .SelectMenu-item:focus, &:hover, &:active, &:focus { diff --git a/src/__tests__/__snapshots__/SelectMenu.tsx.snap b/src/__tests__/__snapshots__/SelectMenu.tsx.snap index 91d38892f6d..5984466a807 100644 --- a/src/__tests__/__snapshots__/SelectMenu.tsx.snap +++ b/src/__tests__/__snapshots__/SelectMenu.tsx.snap @@ -246,7 +246,6 @@ exports[`SelectMenu right-aligned modal has right: 0px 1`] = ` } @media (hover:hover) { - .c5 body:not(.intent-mouse) .SelectMenu-item:focus, .c5:hover, .c5:active, .c5:focus { diff --git a/src/hooks/useMouseIntent.ts b/src/hooks/useMouseIntent.ts deleted file mode 100644 index 115c130542e..00000000000 --- a/src/hooks/useMouseIntent.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* adapted from: https://github.com/github/github/blob/a959c0d15c29b98c49b881f520c5947fe24eecb9/app/assets/modules/github/behaviors/button-outline.ts */ -import {useEffect} from 'react' - -const useMouseIntent = () => { - useEffect(() => { - let lastActiveElement: Element | null = null - let currentInputIsMouse = false - - function setClass() { - lastActiveElement = document.activeElement - if (document.body) { - document.body.classList.toggle('intent-mouse', currentInputIsMouse) - } - } - - function onKeyDown() { - currentInputIsMouse = false - } - - function onMouseDown() { - currentInputIsMouse = true - if (lastActiveElement === document.activeElement) setClass() - } - // Use mousedown event to make sure outline is remove for holding and dragging - document.addEventListener('mousedown', onMouseDown, {capture: true}) - - document.addEventListener('keydown', onKeyDown, {capture: true}) - - document.addEventListener('focusin', setClass, {capture: true}) - - return () => { - document.removeEventListener('keydown', onKeyDown, {capture: true}) - document.removeEventListener('focusin', setClass, {capture: true}) - document.removeEventListener('mousedown', onMouseDown, {capture: true}) - } - }, []) -} - -export default useMouseIntent diff --git a/src/index.ts b/src/index.ts index f4833e4f27e..7f549326a2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,6 @@ export type {PositionProps, AbsoluteProps, FixedProps, RelativeProps, StickyProp // Hooks export {default as useDetails} from './hooks/useDetails' -export {default as useMouseIntent} from './hooks/useMouseIntent' export {default as useSafeTimeout} from './hooks/useSafeTimeout' export {useOnOutsideClick} from './hooks/useOnOutsideClick' export {useOpenAndCloseFocus} from './hooks/useOpenAndCloseFocus' diff --git a/yarn.lock b/yarn.lock index 19f0e0d1b2f..f2ebc62e55b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7317,6 +7317,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +focus-visible@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.2.0.tgz#3a9e41fccf587bd25dcc2ef045508284f0a4d6b3" + integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== + follow-redirects@^1.0.0: version "1.13.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147"