How to Optimize Tooltip Performance with a Delayed First Show
Use a CSS transition-delay for the first tooltip appearance, then dynamically set transition-duration: 0ms via a data-instant attribute for all subsequent tooltips to eliminate perceived latency.
The emilkowalski/skills repository codifies a battle-tested pattern for toolbar interactions that prevents accidental activations while maintaining snappy perceived performance. This approach applies a brief delay on initial hover to avoid visual noise, then instantly displays neighboring tooltips once the user has engaged with the interface.
Why Delay the First Tooltip Show?
Dense toolbars suffer from accidental activation when users sweep their cursor across the screen. A short delay—typically implemented via transition-delay or a JavaScript timeout—acts as a safety buffer, ensuring tooltips appear only when the user actually pauses on an element.
According to the design engineering guidelines in skills/emil-design-eng/SKILL.md, the first hover should require intentional intent, while subsequent interactions within the same toolbar should feel immediate (lines 47-48).
The Core Pattern: Delayed First Show, Instant Subsequent Displays
The pattern relies on three layers: CSS transitions for the animation, HTML data attributes for state tracking, and minimal JavaScript to toggle between "animated" and "instant" modes.
CSS Implementation
Define the base tooltip with a standard transition using only GPU-accelerated properties, as mandated by the repository's animation standards (skills/review-animations/STANDARDS.md, lines 110-118).
/* Base tooltip – delayed first show */
.tooltip {
transition: transform 125ms ease-out, opacity 125ms ease-out;
transform-origin: var(--transform-origin);
}
/* Initial hidden state */
.tooltip[data-starting-style],
.tooltip[data-ending-style] {
opacity: 0;
transform: scale(0.97);
}
/* Instant tooltip – subsequent shows */
.tooltip[data-instant] {
transition-duration: 0ms;
}
The data-instant attribute overrides the transition duration, eliminating the 125ms fade-in for any tooltip flagged after the first display.
HTML Structure
Mark each tooltip with data-starting-style to handle the entry animation, leaving room for the JavaScript to inject data-instant dynamically.
<button class="btn" data-tooltip-id="save">Save</button>
<div id="tooltip-save" class="tooltip" data-starting-style>Save changes</div>
<button class="btn" data-tooltip-id="delete">Delete</button>
<div id="tooltip-delete" class="tooltip" data-starting-style>Delete item</div>
JavaScript State Management
Track whether the first tooltip has been shown using a simple boolean flag. Once triggered, set data-instant on all tooltips in the group to skip future animations.
const tooltipGroup = document.querySelectorAll('.tooltip');
let firstShown = false;
tooltipGroup.forEach(tooltip => {
tooltip.addEventListener('mouseenter', () => {
tooltip.classList.add('visible');
if (!firstShown) {
firstShown = true;
} else {
tooltip.setAttribute('data-instant', '');
}
});
tooltip.addEventListener('mouseleave', () => {
tooltip.classList.remove('visible');
});
});
This logic ensures the delay applies only once per toolbar session, creating the perception of a faster interface without sacrificing the initial safety buffer.
Performance Benefits of Skipping Subsequent Animations
Removing the transition delay after the first show delivers measurable improvements in perceived performance and rendering efficiency.
Perceived Speed: A 125ms fade-in feels slower than an immediate pop-in, even though the computational cost is minimal. As noted in the repository standards (STANDARDS.md, line 49), instant tooltips after the first interaction make toolbars feel significantly more responsive.
GPU Optimization: By animating only transform and opacity, the browser stays on the compositor thread. Setting transition-duration: 0ms avoids unnecessary style recalculations and paint work for subsequent tooltips.
Reduced Layout Thrashing: Because data-instant modifies only the transition duration—a property that does not trigger layout—the browser skips expensive reflow cycles when rapidly hovering over multiple buttons.
Complete Working Example
Combine the CSS, HTML, and JavaScript patterns into a self-contained toolbar implementation.
<style>
.tooltip {
position: absolute;
pointer-events: none;
opacity: 0;
transform: scale(0.97);
transition: transform 125ms ease-out, opacity 125ms ease-out;
background: #1a1a1a;
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.tooltip.visible {
opacity: 1;
transform: scale(1);
}
.tooltip[data-instant] {
transition-duration: 0ms;
}
.toolbar {
display: flex;
gap: 8px;
}
</style>
<div class="toolbar">
<button class="btn" data-tooltip="tip-bold">Bold</button>
<button class="btn" data-tooltip="tip-italic">Italic</button>
<button class="btn" data-tooltip="tip-link">Link</button>
</div>
<div id="tip-bold" class="tooltip">Bold (Ctrl+B)</div>
<div id="tip-italic" class="tooltip">Italic (Ctrl+I)</div>
<div id="tip-link" class="tooltip">Insert link</div>
<script>
const btns = document.querySelectorAll('.toolbar .btn');
let firstTooltipShown = false;
btns.forEach(btn => {
const tip = document.getElementById(btn.dataset.tooltip);
btn.addEventListener('mouseenter', () => {
tip.classList.add('visible');
if (firstTooltipShown) {
tip.setAttribute('data-instant', '');
}
firstTooltipShown = true;
});
btn.addEventListener('mouseleave', () => {
tip.classList.remove('visible');
});
});
</script>
This implementation follows the Emil‑Design‑Eng skill guidelines exactly, using data-starting-style for entry states and data-instant for subsequent optimizations as defined in skills/emil-design-eng/SKILL.md (lines 52-66).
Summary
- Delay the first show using
transition-delayor JavaScript timeouts to prevent accidental activations when users sweep their cursor across toolbars. - Set
data-instantafter the first tooltip appears to changetransition-durationto0msfor all subsequent tooltips in the group. - Animate only
transformandopacityto remain on the GPU compositor thread and avoid layout or paint recalculations. - Reference source files
skills/emil-design-eng/SKILL.mdandskills/review-animations/STANDARDS.mdfor canonical implementation patterns and performance budgets.
Frequently Asked Questions
What is the optimal delay duration for the first tooltip show?
The repository does not mandate a specific millisecond count, but typical implementations use between 100ms and 150ms for the initial delay. This duration is long enough to distinguish intentional hovers from cursor sweeps, yet short enough to avoid feeling unresponsive. The skills/review-animations/STANDARDS.md file emphasizes keeping all animations under 300ms total (line 49), so ensure your delay plus fade-in duration stays below this threshold.
Why not just disable animations entirely for all tooltips?
Completely removing animations reduces perceived quality and can cause cognitive disorientation when UI elements pop in without transition. The delayed-first-show pattern specifically protects against accidental triggers during cursor movement while preserving the polished feel of an animated entry. Once the user has engaged with the toolbar, instant subsequent displays prioritize speed over aesthetics.
How does the data-instant attribute improve GPU performance?
When a browser encounters transition-duration: 0ms via the data-instant selector, it skips the interpolation calculations and composite layers required for animation. Because the CSS uses only transform and opacity—properties that do not trigger layout or paint—the browser avoids expensive CPU cycles spent recalculating geometry or rasterizing pixels when rapidly hovering between adjacent buttons.
Can this pattern work without JavaScript?
Pure CSS implementations can approximate this behavior using the :hover pseudo-class on a parent container, but they cannot reliably track "first show" state across separate tooltip elements. The JavaScript approach in skills/emil-design-eng/SKILL.md (lines 52-66) requires minimal overhead—typically under 50 lines—and provides precise control over when to inject the data-instant flag, making it the recommended method for production toolbars.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →