Draw the plain tooltip without Tailwind

Plan step 36. <x-tooltip> renders data-md-tooltip and data-md-side on
the bubble and data-md-tooltip-anchor on a standalone trigger's wrapper;
tooltip.css draws PlainTooltipTokens' inverse-surface bubble, its anchor
placement and flips per side, and the fade on the fast effects spring
with @starting-style.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 14:11:37 +02:00
co-authored by Claude Opus 5
parent 6c7414d60e
commit 147dbcf041
6 changed files with 130 additions and 24 deletions
+80
View File
@@ -0,0 +1,80 @@
/*
* <x-tooltip>: M3's plain tooltip, a short label for a control in the inverse surface.
*
* The bubble is a `popover="manual"` in the top layer, so no `overflow: hidden` parent clips it,
* placed by CSS anchor positioning on its `data-md-side` and flipping to the other side where the
* window has no room. PlainTooltipTokens and TooltipDefaults (androidx Compose Material 3,
* Apache-2.0): `inverse-surface` container with the extra-small corner, `body-small` supporting
* text in `inverse-on-surface`, 8px either side and 4px above and below, 200px wide at most, 4px
* from its anchor. It never takes the pointer, so it can stand over the control it names.
*
* It fades in and out on the fast effects spring; `allow-discrete` keeps `display` and `overlay`
* alive until the fade out has been seen, and `@starting-style` gives the fade in a start.
* resources/js/tooltip.js shows and hides it.
*/
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@layer material.components {
[data-md-tooltip-anchor] {
display: inline-flex;
}
[data-md-tooltip] {
inset: auto;
margin: 0;
max-inline-size: 200px;
overflow: visible;
border: 0;
border-radius: var(--md-sys-shape-corner-xs);
padding-block: var(--md-sys-measurement-space50);
padding-inline: var(--md-sys-measurement-space100);
background-color: var(--md-sys-color-inverse-surface);
color: var(--md-sys-color-inverse-on-surface);
font: var(--md-sys-typescale-body-sm);
letter-spacing: var(--md-sys-typescale-body-sm-tracking);
font-variation-settings: normal;
text-align: center;
white-space: normal;
pointer-events: none;
opacity: 0;
transition-property: opacity, display, overlay;
transition-duration: var(--md-sys-motion-effects-fast-duration);
transition-timing-function: var(--md-sys-motion-effects-fast);
transition-behavior: allow-discrete;
&:popover-open {
opacity: 1;
@starting-style {
opacity: 0;
}
}
}
[data-md-tooltip]:is([data-md-side='top'], [data-md-side='bottom']) {
margin-block: var(--md-sys-measurement-space50);
position-try-fallbacks: flip-block;
}
[data-md-tooltip]:is([data-md-side='left'], [data-md-side='right']) {
margin-inline: var(--md-sys-measurement-space50);
position-try-fallbacks: flip-inline;
}
[data-md-tooltip][data-md-side='top'] {
position-area: top;
}
[data-md-tooltip][data-md-side='bottom'] {
position-area: bottom;
}
[data-md-tooltip][data-md-side='left'] {
position-area: left;
}
[data-md-tooltip][data-md-side='right'] {
position-area: right;
}
}