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

[selectors] Proposal for a :hover-only pseudo class #7544

Open
devongovett opened this issue Jul 30, 2022 · 8 comments
Open

[selectors] Proposal for a :hover-only pseudo class #7544

devongovett opened this issue Jul 30, 2022 · 8 comments

Comments

@devongovett
Copy link

devongovett commented Jul 30, 2022

Intro

The current :hover pseudo class has an issue: due to backward compatibility with older sites, it applies on tap on touch devices. This makes sense as it ensures that older sites not designed for compatibility with touch devices are usable (e.g. hover menus). However, it also leads to sub-optimal experiences in many cases, where the hover state "sticks" on tap. Ideally it wouldn't apply at all on touch devices.

The hover/any-hover media queries appear to be a workaround to this. Wrap your :hover selectors in those, and they won't apply on touch devices. However, this does not work correctly on devices with both pointer and touch support, e.g. modern iPads, Windows laptops with touch screens, etc. The media query doesn't update based on the input device the user is currently using, only based on what devices are available. Therefore, sometimes the hover effect either doesn't apply when it should, or applies when it shouldn't depending on the current input device.

Currently, the only way to work around this is to apply classes with JavaScript based on pointer events rather than using :hover. I wrote more details and examples in this blog post.

Proposal

I would like to propose a new :hover-only pseudo class. This would apply only when hovering with a pointer or another device capable of hover, and would not apply when using touch input, stylus input, etc. Updating the existing :hover pseudo class to have this behavior would be a huge breaking change, so I think a new pseudo class is needed here.

While this can be achieved with JavaScript, I think a CSS-only solution would be very nice. Writing the JS from scratch to handle this is quite involved, and likely not as performance optimized as something browsers could implement natively. Due to the complexity, most websites don't implement this, leading to a worse user experience. I think if it were built into the web platform and easier for developers to get right, the UX of hoverable elements across the web could be improved.

@codes001

This comment was marked as off-topic.

@samhed
Copy link

samhed commented Dec 15, 2022

I like this idea. If we want to follow the same naming convention as with the media query pointer: fine, a suggestion is to name this new selector :hover-fine.

@benface
Copy link

benface commented Nov 6, 2023

This is very much needed!

The hover/any-hover media queries appear to be a workaround to this. Wrap your :hover selectors in those, and they won't apply on touch devices. However, this does not work correctly on devices with both pointer and touch support, e.g. modern iPads, Windows laptops with touch screens, etc. The media query doesn't update based on the input device the user is currently using, only based on what devices are available. Therefore, sometimes the hover effect either doesn't apply when it should, or applies when it shouldn't depending on the current input device.

Another reason why the hover / any-hover media queries are insufficient for this is that it is pretty common to apply the same styles for :hover and :focus-visible states, which can be done with :is(:hover, :focus-visible). However, if one wants to do that while preventing the annoying "hover on tap" behavior on mobile, they're forced to duplicate those styles, because the :hover rule should now go in a media query while the :focus-visible one should not. With :hover-only, it is much cleaner and more elegant: :is(:hover-only, :focus-visible).

@GermanJablo
Copy link

Hi @devongovett, thanks for the detailed proposal.

I agree that the behavior of :hover needs to be improved on touch devices, but I'm not sure excluding it from touch events is the right way.

Mice and fingers are different interfaces and therefore there can be different interpretations about what some states such as hover mean on touch devices.

One possible interpretation is that hover should not be possible with touch events as you propose, but I think another worthy of reflection is that hover could be activated while the finger is held down and on the target*.

In other words, something could be considered "hovered" when it has what in react-aria you call isPressed state.

*note: I know that react-aria's onPress event is "reactivated" if the mouse leaves the target and enters again, while this does not happen in the touch case. I suppose that this decision was made under the consideration that the user may want to scroll. A semi-related question then would be whether this pressed/hovered state should not be "reactivated" in touch events also if the user enters and exits the target and there is no scrollable container.

I wouldn't be surprised if you've spent more time on these questions than anyone else, so you probably have good reasons to prefer the behavior you described. If so, I would like to know them :)

@benface
Copy link

benface commented Feb 13, 2024

@GermanJablo – I can't answer for Devon, but a couple thoughts I had reading your reply:

  • I don't think it's possible to change the behavior of :hover on touch devices now. It would break existing websites which rely on the current behavior.
  • The problem here is that we need a way to select more precisely when an element is hovered with a pointer, for styles that should never apply on touch devices. There is a big difference between hovering an element and holding your finger down on it, the main one (IMO) being that clicking on the target is an easy and logical next step to hovering. That is why hover styles make sense, to indicate that you can click. If an element's hover styles were triggered by holding it, you would have to first lift your finger up and then tap the element again, which isn't as intuitive.

@GermanJablo
Copy link

  • I don't think it's possible to change the behavior of :hover on touch devices now. It would break existing websites which rely on the current behavior.

Yes, my proposal did not consist of that. It could be a new pseudo class.

  • The problem here is that we need a way to select more precisely when an element is hovered with a pointer, for styles that should never apply on touch devices. There is a big difference between hovering an element and holding your finger down on it, the main one (IMO) being that clicking on the target is an easy and logical next step to hovering. That is why hover styles make sense, to indicate that you can click. If an element's hover styles were triggered by holding it, you would have to first lift your finger up and then tap the element again, which isn't as intuitive.

Well, to quote myself, "Mice and fingers are different interfaces and therefore there can be different interpretations about what some states such as hover mean on touch devices."

In this case your interpretation is that the hover indicates that it can be clicked/pressed. But as I said, I think another valid interpretation could be that hover indicates that something can be clicked/pressed OR that it is being pressed/clicked.

Personally, if I press a button on my mobile I have an expectation that it will turn a slightly different color while it is being pressed.

I consider it to be feedback from the UI to the user that if he releases his finger from that position, an operation will be performed.

That said, it's also true that what I'm saying could be achieved with a combination of :hover:active (or :hover-only:active if your and Devon's interpretation prevails). Doing what I want would be a little more verbose, but possible.

Again, I don't 100% disagree with your interpretation, I just think there are two valid readings. And in a decision as important as this one, I think they should be considered and discussed.

@benface
Copy link

benface commented Feb 13, 2024

@GermanJablo – What you describe sounds like :active to me, not :hover.

@nmn
Copy link

nmn commented Apr 23, 2024

Thanks @devongovett for starting this issue. I was thinking and complaining about the same issue recently and have my own write up explaining the problem and possible solutions.

Some of it may be a bit repetitive, but I want to share it here so we can discuss some of the details before one us can start a more formal proposal to get this problem solved.


Problem

Due to legacy reasons, :hover on touchscreens works in a way that the only way to achieve "hover effects" on the web is to use Javascript.

The specific problem is that on modern touchscreen OSs, hover interactions "stick". I.e, After touching and letting on an element with :hover styles applied, the effect persists until the user touches a different element on the screen.

Further, the existing @media queries can't be used to conditionally apply styles for devices that may have touchscreens and mouse pointers such as iPads, Android Tablets and many Windows Laptops.

Adobe has a detailed blog post explaining the problem and how they had to resort to using Javascript to solve it.

Desired Outcome

There are two possible desired outcomes:

  1. A way to ignore all hover styles on touch interactions. Developers can instead use :active to apply visual styles for touch interactions. Touch screens usually don't have "hover" interactions and so this would be a reasonable solution.
  2. A :hover that is only active while the touch interaction is active. This is likely the solution that most developers expect. In fact, iOS used to work like this in the past. However, the behaviour was changed to the way it works today so that older websites that show menus and other interactive elements on :hover can be usable on touchscreens.

Proposals

There are at least two possible ways to solve this problem:

1. A new :visual-hover or :hover-only pseudo-class

This is the first solution that comes to mind when thinking about this problem. It is unfortunate that :hover has to remain sticky for legacy reasons. So we can add a new :visual-hover that can be used for applying visual effects that work with all input methods as expected. (Choosing from one of the two desired outcomes above.)

2. A way to "turn-off" persistent hover on touchscreens

When solving other similar problems, such as the 300ms tap-delay on links and buttons, the solution was to apply touch-action: manipulation. Perhaps, any element with touch-action: manipulation should also be opted out of persistent hover styles?


My personal preference

I've been thinking about this a lot recently, and I think the ideal solution would be a way to opt out of the persistent hover behaviour when touch-action: manipulation is applied.

The introduces no new APIs which has many advantages:

  1. It doesn't create a confusion about to different hover pseudo classes
  2. It can be implemented as Progressive Enhancement as older browsers will get the current behaviour.
  3. It follows the pattern of how the tap delay was fixed.

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

No branches or pull requests

7 participants