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"