Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix focus styles showing up when using the mouse #2347

Merged
merged 5 commits into from
Mar 10, 2023
Merged

Conversation

RobinMalfait
Copy link
Member

@RobinMalfait RobinMalfait commented Mar 8, 2023

This PR fixes the annoying issue that focus styles show up even if you used the mouse.

Right now the issue exists whenever we call element.focus() that the element will show the focus related styles. Unfortunately this is something the browser does (even if you did this call in a click handler). There is also no API yet to control this behaviour. There is an experimental API that can be found here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#browser_compatibility

So what are the solutions here?

For starters, you could immediately blur the active element when something receives focus. This would hide the focus ring (because the element won't have focus anymore) but it will degrade the usability of your application. Imagine you are filling out a big form, you tab to an input and fill it in, you tab to the next input and fill it in, and so on. You are now 50 fields further and you use a Listbox component. You select a value, the Listbox closes and focus is restored to the Listbox.Button. Now you want to continue but uh oh it won't work because you just blurred that Listbox.Button and the body now has the focus.

Some people mentioned that you could just blur the input when you are using the mouse, but then you would have to re-focus a nearby input field to continue. While this might be okay, it's not something we want as a default implementation we want the components to feel right and usable.

At the end of the day, we want the element to be focused, we just don't want to show the focus styles when you used the mouse. One day we have more control over when to show the focus ring and when not to, but for now this is not possible.

One solution is that we could expose a focus value in the render prop. We already expose active and selected in components like the Listbox so that you can style your elements based on these values. While this is doable, the issue is that some buttons are not controlled by Headless UI at all. For example when you are using the Dialog component, we will restore focus to the element that opened the Dialog component. This is not an element controlled by Headless UI so you won't be able to style the element based on exposed focus props.

There are other solutions where we directly mutate the element we are restoring the focus to and "delete" the focus related styles. This is not an ideal solution because we don't always control the element.

Another potential solution is by introducing a phantom or proxy button that doesn't have any styles attached to it but can hold the focus, if you then perform any action, it will forward that to the element that should have required focus. This could work, but will have accessibility related issues.

The current behaviour where the focus is restored is also consistent with most other common libraries out there like React Aria, Radix UI and Reakit.

Custom selector

The solution we came up with for this problem is by exposing a new ui-focus-visible variant in the @headlessui/tailwindcss package. This way we can give you control over when and how this should behave. If you always want the focus styles, then you can keep using the focus related styles. If you don't then you could switch to this new variant. In the future, this can hopefully all be handled by the browser but unfortunately for now this isn't the case.

The variant can be used like this:

<button class="ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 focus:outline-none">
  My Button
</button>

The implementation of that variant looks like this:

addVariant(`${prefix}-focus-visible`, ':where([data-headlessui-focus-visible]) &:focus')

Where the prefix is defined as "ui" by default. More info on this prefix and the @headlessui/tailwindcss package can be found here: https://github.com/tailwindlabs/headlessui/tree/main/packages/%40headlessui-tailwindcss

This way the focus styles will only be applied if you used a keyboard, not when using another input method like a mouse or pointer.

As long as you use Headless UI, then you should be able to use the variant wherever you want and the focus styles should show up when using the keyboard, and won't when using the mouse.

This also means that when you open a Dialog, and click outside that the button won't have a focus ring attached, but it will still have the focus. If you now press space or enter then the Dialog will show again.

You can play with an example of this here: https://headlessui-react-git-fix-issue-1694-tailwindlabs.vercel.app/combinations/tabs-in-dialog


If you want to play with this yourself, then you have to install the insiders versions of the Headless UI library for React or Vue, and also the Tailwind CSS plugin:

  • npm install @headlessui/react@insiders
  • npm install @headlessui/vue@insiders
  • npm install @headlessui/tailwindcss@insiders

Fixes: #1694

@vercel
Copy link

vercel bot commented Mar 8, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated
headlessui-react ✅ Ready (Inspect) Visit Preview 💬 Add your feedback Mar 10, 2023 at 0:48AM (UTC)
headlessui-vue ✅ Ready (Inspect) Visit Preview 💬 Add your feedback Mar 10, 2023 at 0:48AM (UTC)

@RobinMalfait RobinMalfait force-pushed the fix/issue-1694 branch 2 times, most recently from 2ae3901 to 7486268 Compare March 8, 2023 17:29
@RobinMalfait RobinMalfait changed the title Fix triggering focus styles when using the mouse Improve focus styles showing up when using the mouse Mar 9, 2023
@RobinMalfait RobinMalfait changed the title Improve focus styles showing up when using the mouse Fix focus styles showing up when using the mouse Mar 9, 2023
The focus was always set, but the ring wasn't showing up. This was also
focusing a ring when the browser decided not the add one.

Let's make the browser decide when to show this or not.
@sgrund14
Copy link

hello! has this update been released yet? not seeing the new variants on @headlessui/tailwindcss: 0.1.3 and @headlessui/react: 1.7.14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Popover enabling :focus-visible state on mouse interaction
2 participants