[csswg-drafts] [css-ui] Standardize tooltip styling and expose as `::tooltip` (#8930)

LeaVerou has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-ui] Standardize tooltip styling and expose as `::tooltip` ==
Now that popover and anchor positioning are moving forwards, I wonder if it’s time to re-examine standardizing tooltip styling and adding a `::tooltip` pseudo-element so that authors can customize it.

## History

- The earliest such proposal I could find was [this one from 2009](https://lists.w3.org/Archives/Public/www-style/2009Apr/0225.html). It got no opposition, some support, but very little discussion and no resolution.
- [I brought it up again in 2012](https://lists.w3.org/Archives/Public/www-style/2012Apr/0242.html), which generated some more discussion, but the discussion went on tangents and eventually died.
- It was [brought up again in 2017](https://lists.w3.org/Archives/Public/www-style/2017Apr/0052.html), which also didn't generate much discussion (just one message saying [it doesn’t cover all use cases](https://lists.w3.org/Archives/Public/www-style/2017Apr/0053.html)).

Since 6 years have passed since then, it seems like a good time to discuss it again. Maybe this time it will bear fruit. 😁 

## Problem statement

Default UA tooltips are generally seen as inflexible, slow, and not aesthetically pleasing. Their styling is entirely disconnected from the element they describe (e.g. observe that the font size of the span does not affect the font size of the tooltip at all), and their color scheme is set by the OS, not the page (observe how in dark mode, the tooltip is dark, even when the page is actually not).

| MacOS | Windows | MacOS dark mode |
|--------|-----------|-------------------|
<img width="287" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/f57d0ee5-393e-4702-b7b6-980f8f1acf66"> | <img width="301" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/24401527-6412-444d-b97d-d531cc3c4ac2"> | <img width="256" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/0f33ea4d-29e1-4cd4-a8af-1857c2050fa1"> |

For these reasons, nearly all popular websites employ some kind of scripting for custom tooltips. [NPM packages for tooltips](https://www.npmjs.com/search?ranking=popularity&q=tooltip) are in the millions of downloads per week. However, the styling employed by the vast majority of cases is actually pretty simple.

A few examples:

| Google | Facebook | Twitter | GitHub |  Google Docs | Reddit |
|--------|-----------|--------|--------|---------------|-------|
| <img width="150" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/fbf0c214-3ad0-479d-934f-7786959ee18a"> | <img width="137" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/8ca64e81-d00c-455c-ab68-a98f06823498"> | <img width="82" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/166548e4-1d15-49dc-a0b0-327c524d2d94"> | <img width="132" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/34dbc691-2aa1-4971-8fc1-d42c5f575754"> | <img width="104" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/d7ac76ac-3031-4396-8eed-565bda15157c"> | <img width="119" alt="image" src="https://github.com/w3c/csswg-drafts/assets/175836/60ab8d78-7d79-42c9-ae00-a3f683e10fcb"> |

Literally all component libraries include a component for tooltips ([OpenUI research](https://open-ui.org/components/tooltip.research/)), however due to the limitations of components, they have to create an element for something that is conceptually not an element, but a behavior of another element. This results in unpredictable markup and complex selectors (e.g. consider [`<sl-tooltip>`](https://shoelace.style/components/tooltip); selectors now need to account for `sl-tooltip` possibly being anywhere in the tree, consider how a selector like `ul > li > ul > li` would need to be rewritten).

Older scripts depend on processing all `[title]` elements on the page, renaming the attribute to `data-title` or something, and adding event listeners (usually through event delegation). The downside of these scripts is that they don't work in shadow trees, and are often inaccessible.

And of course, there are always the authors that will roll their own, usually also resulting in poor accessibility.

## Goals

- Custom tooltip styling (fonts, backgrounds, colors, shadows etc), including pointers
- Customization of show/hide animations (one of the major issues with the UA tooltips is how long they take to appear)

## Non-goals

- Tooltips with arbitrary HTML content (these can be done with the popover API)

## Proposal

We define a `::tooltip` pseudo-element and standardize the default styling through a UA rule, which could look like this:

```css
::tooltip {
 content: attr(title);
 color: TooltipText;
 background: TooltipBackground;
 font-size: .8rem;
 box-shadow: .1rem .1rem .2rem rgb(0 0 0 / .2);
 transition: 0s 3s visibility; 

 @starting-style {
  visibility: hidden;
 }
}
```

Notes:
- This includes two new [system colors](https://www.w3.org/TR/css-color-4/#css-system-colors): `TooltipText` and `TooltipBackground`, which can be aliased to `ButtonText` and `ButtonFace` when not provided by the OS.
- To allow for pointers, this needs to allow [sub-pseudo-elements](https://www.w3.org/TR/selectors/#sub-pseudo-elements).
- Do we need to restrict the properties that apply to this pseudo-element?
- Right now tooltip contents are set via the `content` property, which can be overridden to something else. Is this useful? I can see it being set to totally unrelated attributes, harming accessibility.

## Issues

### Positioning 

As it currently stands, how the tooltip is positioned is still magic. This does make it easier to implement, but makes certain common styles very difficult: how to add a pointer when you don't know how the tooltip and originating element positions relate? We definitely don't want authors to have to deal with positioning tooltips manually, as that is insanely complicated. Perhaps we should expose some info to them about the relative positions of the tooltip and element that they can use in their styling? Maybe via `env()`? 

I think it's ok if we ship an MVP where pointers are not yet possible (which still covers a large number of use cases), but the design does need to allow for this to become possible in the future. Perhaps by restricting the properties allowed in `::tooltip`

@tabatkins can anchor positioning help here? 

### Customizing display triggers

As it currently stands, what makes the tooltip to be displayed is still magic. While this is fine, especially for an MVP, it would open up a ton of really nice use cases if this was grounded in specific user action pseudo-classes that generate the tooltip or not. Authors could then generate tooltips on `:focus`, `:focus-within`, via interactions on other elements (e.g. `:focus + .foo::tooltip`), or even through entirely custom interactions (by toggling classes via JS).

Perhaps, if we agree that setting the `content` property is what generates the box (like `::before` and `::after`), the default UA styles could look like this:

```css
::tooltip {
 color: TooltipText;
 background: TooltipBackground;
 font-size: .8rem;
 box-shadow: .1rem .1rem .2rem rgb(0 0 0 / .2);
 transition: 0s 3s visibility; 

 @starting-style {
  visibility: hidden;
 }
}

[title]:hover::tooltip {
 content: attr(title);
}
```

The downside is that this couples the display trigger with the content, i.e. if someone wants to override the content, they also need to be up to date with the display triggers used.

We *could* do something like this:

```css
::tooltip {
 content: attr(title);
 color: TooltipText;
 background: TooltipBackground;
 font-size: .8rem;
 box-shadow: .1rem .1rem .2rem rgb(0 0 0 / .2);
 transition: 0s 3s visibility; 
 visibility: hidden;
}

[title]:hover::tooltip {
 visibility: visible;
}
```
But this implies the box is pre-generated and simply shown, which I don't imagine is desirable for implementations.

### `::tooltip` in SVG

Instead of using a `title` attribute, SVG supports a `<title>` element, so when used in SVG, `::tooltip` should cover these tooltips as well. However, there is no way to specify something analogous to `content: attr(title)` that works with that markup pattern, which may be another reason to axe that and have the content be magic (prefixes and suffixes can always be added via `::before` and `::after`).

Perhaps in the future, we could backport this element into HTML, to cater to the use cases that require more rich tooltip content. Though I do wonder what web compat would be like — I suspect there must be some clumsy code out there that assumes there is only a single `<title>` element on the page, and it contains the document title.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/8930 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 7 June 2023 21:20:48 UTC