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

Add hover/focus/long-press triggering delays to CSS #9236

Open
mfreed7 opened this issue Aug 24, 2023 · 10 comments
Open

Add hover/focus/long-press triggering delays to CSS #9236

mfreed7 opened this issue Aug 24, 2023 · 10 comments
Labels

Comments

@mfreed7
Copy link

mfreed7 commented Aug 24, 2023

Please see the Popover Hint and Hover explainer for more context:

https://open-ui.org/components/popover-hint.research.explainer/

We're working on an addition to the Popover feature that allows authors to easily and declaratively get hover-triggering of popovers. (And to make sure all input modalities are covered, "hover" includes keyboard focus and also touch screen long-press.) The idea for now is to use the popovertargetaction attribute, with a new value called interest (see this discussion for context on the name) that enables this new mode of triggering. When popovertargetaction=interest on a button that points (via popovertarget=foo) to a popover, that popover will be triggered when one of these things happens:

  • The user mouse-hovers the button for a specified period of time, or
  • The user keyboard-focuses the button for a specified period of time, or
  • The user long-presses the button for a specified period of time

The key phrase above, and the point of this issue, is that the delay should be developer-configurable. Leaving this delay up to the UA leads to issues and missed use cases. For example, the built-in tooltip that shows up as a result of the title attribute has one major, common complaint: the UA-imposed delay is too long. Other, more creative use cases of this feature would also require a customizable hover-delay. For that reason, I'd like to propose that this delay is set in CSS, via a new property:

button[popovertargetaction=interest] {
  popover-show-delay: 0.5s;
}

There is another quite related, yet separate, set of behaviors that happens after the popover is shown. Typical "tooltip" use cases would also like the tooltip to be hidden after the tooltip stops being hovered for a period of time. To make this work, both the invoker (the button above) and the popover should be able to be hovered to maintain the popover in the open state. But once both of them have been de-hovered for a period of time, the popover can be closed. For this, I propose:

[popover] {
  popover-hide-delay: 1s;
}

Note that in the above proposal, the "show" delay is applied to the button that triggers the popover, while the "hide" delay is applied to the popover. That's because those elements are where the action takes place. However, one could make the case that it'd be nice to instead put both properties on one element, and the [popover] would seem to be the natural place to put it. In that case, the above properties could also be combined into a single two-valued property (credit @una for the idea):

[popover] {
  popover-delay: 0.5s 1s; /* first value = show delay, second value = hide delay */
}

Thoughts?

@mfreed7
Copy link
Author

mfreed7 commented Aug 24, 2023

See also these related discussions:

  • Idea to try to re-use transition-delay for part of this proposal. I think it's a very roundabout way to provide the functionality, and can't provide the "hide" side of the behavior. I'd rather make this easy for developers to understand by creating new bespoke properties that can cover the full use case.
  • New proposal in WHATWG (and corresponding explainer) to generalize the triggering behaviors from popovers to many types of elements. I'm supportive of that proposal, and we might want to name the new property for this issue more generally as a result. E.g. instead of popover-delay, perhaps invoker-delay?

@rthrejheytjyrtj545
Copy link

rthrejheytjyrtj545 commented Aug 24, 2023

Can this work at the level of matching selectors by inventing functional forms for them like :focus(visible, 30ms) and adding arguments to forgiving selectors like :where(0 0 0, .3s / :not(:active))?

@mfreed7
Copy link
Author

mfreed7 commented Aug 24, 2023

Can this work at the level of matching selectors by inventing functional forms for them like :focus(visible, 30ms), :hover(3s) or adding arguments to forgiving selectors like :where(0 0 0, 300ms / :target, :active)?

I'd be worried about that approach, because then you'd need a CSS way to actually trigger the popover/behavior. I.e. you'd need :focus(visible,30ms) { trigger-popover-now: yes; }. So far (and as far as I can see it going), that really belongs in the HTML part of this feature, right? Also, I think the feature I've described doesn't have an equivalent pseudo state already. :focus gets the keyboard part, :hover gets the mouse hover part, nothing gets the long-press part, and (most importantly) nothing enforces that you need to use them all together or someone will be broken.

@rthrejheytjyrtj545
Copy link

rthrejheytjyrtj545 commented Aug 24, 2023

If authors want to control popovers regardless of the state provided by :popover-open and default styles, they can already do that, that is not a problem.

then you'd need a CSS way to actually trigger the popover

the display property defines an element’s display type

so far (and as far as I can see it going), that really belongs in the HTML part of this feature, right?

user agents are not required to present HTML documents in any particular way

the feature I've described doesn't have an equivalent pseudo state already

custom selector which is written as a pseudo-class with the given <extension-name>, and represents a :is() selector using the provided <selector-list> as its argument

nothing gets the long-press part

:active pseudo-class applies while any generated box of any element (or pseudo-element) is being actively indicated by a pointing device (in the “down” state), e.g. between the time the user presses the primary mouse button and releases it, or while a finger is pressing on a touchscreen

@tabatkins
Copy link
Member

Yeah, this isn't really doable via Selectors, it's triggering a different behavior that's not CSS controlled. CSS doesn't make something a popover, that's a quality of it imposed by the DOM APIs (or the UA itself in some cases). So that's not a viable approach.


Back to the OP, this seems reasonable.

I agree that it would make the most UX sense to set both the show and hide delays on the same element. I could see it being set on either the button or the popover, tho - as you say, both the popover and the invoker button need to be dehovered before the hide timer starts. Since you're setting the "interest" popover method on the button, and these are both effectively just applying some config to the "interest" method, I think it probably makes the most sense to put them both on the button, then?

One note is that we don't use CSS to configure/control non-CSS concepts much (at all?). Is it needed here, or should it be communicated in the DOM attribute? Why are the delays in CSS, but the indication that you want to use "interest" method is in the DOM? I presume the reason to put the delays into CSS is because they're unlikely to be different per-invoker, so you can reduce a lot of repetitive attribute values by making it a property, but would that similarly apply to the popover method?

(I think CSS should be used for non-CSS concepts a lot more, it's a rich and powerful language both in values and targeting, and authors are intimately familiar with it. I'm just wondering what the reasoning behind this particular slicing of responsibility is.)

@rthrejheytjyrtj545
Copy link

rthrejheytjyrtj545 commented Aug 25, 2023

Also #6719 can be useful in this case, with ::popup it will be possible to bind more properties to the popover associated with a particular button, including the suggested.

@rthrejheytjyrtj545
Copy link

By the way, the name of the issue is a little confusing, it seems to describe the need for a general mechanism for this, the description only mentions the use case for opening or closing popovers.

@mfreed7
Copy link
Author

mfreed7 commented Aug 31, 2023

I agree that it would make the most UX sense to set both the show and hide delays on the same element. I could see it being set on either the button or the popover, tho - as you say, both the popover and the invoker button need to be dehovered before the hide timer starts. Since you're setting the "interest" popover method on the button, and these are both effectively just applying some config to the "interest" method, I think it probably makes the most sense to put them both on the button, then?

Interesting idea to put it on the button. I was initially thinking it made more sense to put them on the popover, since that's the thing that is showing and hiding. But I like the idea of adding both to the button; that also makes it possible to use the same popover invoked via different buttons that each have different delays. I'm not sure what the use case is, but it just feels more flexible which is good.

One note is that we don't use CSS to configure/control non-CSS concepts much (at all?). Is it needed here, or should it be communicated in the DOM attribute? Why are the delays in CSS, but the indication that you want to use "interest" method is in the DOM? I presume the reason to put the delays into CSS is because they're unlikely to be different per-invoker, so you can reduce a lot of repetitive attribute values by making it a property, but would that similarly apply to the popover method?

(I think CSS should be used for non-CSS concepts a lot more, it's a rich and powerful language both in values and targeting, and authors are intimately familiar with it. I'm just wondering what the reasoning behind this particular slicing of responsibility is.)

The reason I proposed using CSS for this is exactly what you said: it's just easier to do it there. I would bet that the overwelmingly-common thing is for all popover invokers on a page to use the same standard delays. So it's easier to do button[interesttarget] {interest-delay: 0.5s 0.5s;} once and not have to worry about adding attributes to all the buttons. The interesttarget attribute is in the DOM because (I think) it's somewhat semantic - it tells you these two elements are linked together via this connection. Also the a11y associations typically happen as a result of the DOM content and not as a result of anything from CSS.

@rthrejheytjyrtj545
Copy link

I would bet that the overwelmingly-common thing is for all popover invokers on a page to use the same standard delays. So it's easier to do button[interesttarget] {interest-delay: 0.5s 0.5s;} once and not have to worry about adding attributes to all the buttons.

Another solution would be to make the proposed properties inheritable so that authors can set them directly to the root.

@mfreed7
Copy link
Author

mfreed7 commented Dec 14, 2023

Hello, I'd like to ping this issue as the result of a good discussion we just had in OpenUI related to the delays discussed on this issue:

openui/open-ui#963 (comment)

There are a number of things discussed there, including the concept of a safe-area triangle. But related to this issue, a few interesting things were discussed. First, it does seem that folks like the idea of allowing CSS to control these delays, independently for the "show" and "hide" actions. In addition, we had a good discussion about specifying the delays themselves. As background, there are several issues with these delays:

  • There likely need to be different delays between mouse, keyboard, and "other" input modalities. E.g. hovering with a mouse might need a longer delay than focusing an element with the keyboard. Things like eye tracking might need a separate way of thinking about delays entirely.
  • There likely need to be different delays between hovering and de-hovering an element. You might want a longer de-hover delay, for example, so it's easy to get to the element before it goes away.
  • It seems that most often, the choice of a particular delay should lie with the user rather than the developer since some folks prefer fast moving interfaces and others prefer more time to get around the screen.
  • Having said the above, the developer also has a preference, since some interfaces are more naturally fast-moving (think of a game console or other fast-action UI) and others are less so (e.g. tooltips on Wikipedia).

Given the above constraints, the new idea is to specify the delays not as actual time values, but as "slow" or "fast", etc. So:

button[interesttarget] {
  interest-delay: [none|slow|normal|fast] [none|slow|normal|fast];
  /* interest-delay: interest-delay loseinterest-delay */
}

It would then be up to the UA to determine how to resolve "slow" into an actual delay value in seconds. The UA could also offer a user preference like "Minimal delays please" which would then reduce the delay value for all settings, or "I want more time" to increase them.

Thoughts? Suggestions?

@astearns astearns added this to Unsorted assorted in Feb 2024 Agenda Jan 30, 2024
@astearns astearns moved this from Unsorted assorted to Tuesday afternoon in Feb 2024 Agenda Feb 11, 2024
@fantasai fantasai removed this from Tuesday afternoon in Feb 2024 Agenda Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants