Source Code

<div class="cm-demo">
  <div class="cm-toolbar">
    <button class="cm-tool-btn" title="New file">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
    </button>
    <button class="cm-tool-btn" title="Search">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
    </button>
    <button class="cm-tool-btn cm-coach-target" id="cmExportBtn" title="Export">
      <span class="cm-pulse-ring"></span>
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12M7 8l5-5 5 5"/><path d="M5 21h14"/></svg>
    </button>
    <button class="cm-tool-btn" title="Settings">
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 00.3 1.9l.1.1a2 2 0 11-2.9 2.9l-.1-.1a1.7 1.7 0 00-1.9-.3 1.7 1.7 0 00-1 1.6V21a2 2 0 11-4 0v-.1a1.7 1.7 0 00-1-1.6 1.7 1.7 0 00-1.9.3l-.1.1a2 2 0 11-2.9-2.9l.1-.1a1.7 1.7 0 00.3-1.9 1.7 1.7 0 00-1.6-1H3a2 2 0 110-4h.1a1.7 1.7 0 001.6-1 1.7 1.7 0 00-.3-1.9l-.1-.1a2 2 0 112.9-2.9l.1.1a1.7 1.7 0 001.9.3H9a1.7 1.7 0 001-1.6V3a2 2 0 114 0v.1a1.7 1.7 0 001 1.6 1.7 1.7 0 001.9-.3l.1-.1a2 2 0 112.9 2.9l-.1.1a1.7 1.7 0 00-.3 1.9V9a1.7 1.7 0 001.6 1H21a2 2 0 110 4h-.1a1.7 1.7 0 00-1.6 1z"/></svg>
    </button>
  </div>

  <div class="cm-canvas">
    <p>This mock toolbar demonstrates a "coach mark" — a spotlighted call-out that draws attention to a specific feature the first time a user encounters it.</p>
  </div>

  <div class="cm-coach-tooltip" id="cmCoachTooltip">
    <div class="cm-coach-arrow"></div>
    <div class="cm-coach-tag">New</div>
    <p class="cm-coach-text">Export your data here — download as CSV, JSON, or PDF whenever you need it.</p>
    <button class="cm-coach-dismiss" id="cmDismissBtn">Got it</button>
  </div>
</div>

Welcome Coach Mark Tooltip — Free HTML CSS JS Snippet

Welcome Coach Mark Tooltip · Misc · Plain HTML, CSS & JS · Live preview

What's included

Features

Pulsing ring highlight built purely from a CSS keyframe animation on scale and opacity
Tooltip position computed live from the target button's getBoundingClientRect(), not hardcoded
Automatic horizontal centering under the target with viewport-edge clamping
Speech-bubble arrow pointing from the tooltip up toward the highlighted button
Single "Got it" dismiss action that removes both the tooltip and the pulsing ring
Recalculates position on window resize while the coach mark is visible
Realistic mock toolbar context so the callout is seen in a believable setting
Small, dependency-free implementation easy to drop onto any existing button

About this UI Snippet

Welcome Coach Mark Tooltip — Positioned Feature Callout with Pulsing Ring

Screenshot of the Welcome Coach Mark Tooltip snippet rendered live

Coach marks are small, self-dismissing callouts that highlight one specific UI element the first time a user is likely to notice it — commonly used to introduce a new feature without interrupting the whole page like a modal would. This snippet demonstrates the pattern against a mock toolbar, calling out an "Export" button with a pulsing ring and a positioned tooltip card.

The pulsing ring

A .cm-pulse-ring element sits absolutely positioned around the target button, growing and fading via a CSS @keyframes animation (transform: scale() combined with fading opacity) that loops indefinitely until dismissed — a lightweight, GPU-friendly way to draw the eye without JavaScript-driven animation.

Real-position tooltip placement

Rather than hardcoding the tooltip's coordinates, positionCoachMark() reads the target button's getBoundingClientRect() and centers the tooltip horizontally beneath it, then clamps the resulting left offset against the viewport width so the card never overflows the screen edge — the same technique used by production tooltip and popover libraries, implemented here in a few lines of vanilla JS.

Dismissal removes both the ring and the tooltip

Clicking "Got it" hides the tooltip via a CSS class toggle (letting the opacity/transform transition play) and removes the pulsing ring element outright, so the coach mark disappears for good rather than resuming its animation loop — mirroring how a real onboarding flow marks a coach mark as seen and never shows it again.

Step by step

How to Use

  1. 1
    Load the snippetClick "Welcome Coach Mark Tooltip" in the sidebar to load its HTML, CSS, and JS into the editor panels. The preview updates instantly.
  2. 2
    Edit the codeModify any panel — HTML, CSS, or JS. The preview refreshes as you type. Use Reset in each panel header to restore the original.
  3. 3
    Preview on devicesClick the Mobile (375px), Tablet (768px), or Desktop buttons in the preview header to check responsiveness.
  4. 4
    Export in your formatClick "HTML" to download a standalone file, "JSX" for a React component, "Tailwind" for a React + Tailwind CSS component, "Tailwind HTML" for a standalone HTML file with Tailwind CDN, "Vue" for a Vue 3 SFC with <template>/<script setup>/<style scoped>, or "Angular" for a standalone Angular .component.ts file. "Copy all" copies the full code to clipboard.
  5. 5
    Save your versionClick "Save as", type a name, and press Enter. Your snippet saves to IndexedDB and appears in the Saved tab.

Real-world uses

Common Use Cases

New feature announcements
Draw attention to a newly added button or menu item the first time a user sees it.
Progressive onboarding
Introduce features one at a time as users reach them, instead of a single upfront tour.
Toolbar and icon-button UIs
A natural fit for calling out one icon among several in a dense toolbar.
Reference for positioned tooltips
The getBoundingClientRect()-based centering and clamping logic is reusable for any tooltip or popover.

Got questions?

Frequently Asked Questions

positionCoachMark() reads the target button's bounding rectangle and centers the tooltip's horizontal midpoint under it, placing it a fixed distance below the button's bottom edge, then clamps the left offset so it never runs off either side of the viewport.

A CSS @keyframes rule scales the ring element up while fading its opacity to zero, looping indefinitely via animation: ... infinite — no JS timers are involved in the animation itself.

The tooltip's visible class is removed (triggering its CSS fade/slide transition) and the pulsing ring element is removed from the DOM entirely, so dismissing it is permanent for that session.

Yes — move the .cm-coach-target class and .cm-pulse-ring span to any other button and update the exportBtn reference in the JS; the positioning logic works with any target element.