Pure TailwindMIT licensed

Tailwind Schematic Hotspot Tooltip with Pulsing CAD Callouts

For product diagrams and exploded views: four pulsing red discs pin a wireframe drawing, and hovering or focusing one reveals a numbered callout with part name, prose and a monospaced spec grid. The port's win is peer — the CSS original's .hot:hover + .leader + .note chain broke the moment anything was inserted between the two elements; peer-hover: compiles to a general sibling combinator and does not.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="ttp-01 w-full min-h-svh flex items-center justify-center px-6 py-8 bg-ttp01-stage font-ttp01-sans text-ttp01-ink bg-[linear-gradient(var(--color-ttp01-grid)_1px,transparent_1px),linear-gradient(90deg,var(--color-ttp01-grid)_1px,transparent_1px)] bg-[size:24px_24px]">
  <div class="relative w-full max-w-[760px] aspect-[16/10] bg-ttp01-paper border border-ttp01-edge before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:bg-[linear-gradient(var(--color-ttp01-grid2)_1px,transparent_1px),linear-gradient(90deg,var(--color-ttp01-grid2)_1px,transparent_1px)] before:bg-[size:18px_18px]">
    <span class="absolute top-1.5 left-1.5 w-3 h-3 border border-ttp01-tick border-r-0 border-b-0"></span>
    <span class="absolute top-1.5 right-1.5 w-3 h-3 border border-ttp01-tick border-l-0 border-b-0"></span>
    <span class="absolute bottom-1.5 left-1.5 w-3 h-3 border border-ttp01-tick border-r-0 border-t-0"></span>
    <span class="absolute bottom-1.5 right-1.5 w-3 h-3 border border-ttp01-tick border-l-0 border-t-0"></span>

    <div class="absolute top-3 left-4 right-4 z-2 flex justify-between font-ttp01-mono text-[10px] tracking-[0.15em] uppercase text-ttp01-meta">
      <span>DWG-0142-A · Camera / Exploded View</span>
      <span class="text-right leading-[1.6]">Scale 1:1 · Rev 04<br>14.MAY.2026</span>
    </div>

    <div class="absolute inset-y-[50px] inset-x-[60px] flex items-center justify-center">
      <svg class="w-full h-full overflow-visible" viewBox="0 0 800 460" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
        <rect x="200" y="160" width="400" height="200" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <rect x="340" y="130" width="80" height="30" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <rect x="355" y="120" width="50" height="10" stroke="#1c2330" stroke-width="1" fill="none"/>
        <path d="M200 160 L160 180 L160 340 L200 360 Z" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <circle cx="450" cy="260" r="90" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <circle cx="450" cy="260" r="68" stroke="#1c2330" stroke-width="1" fill="none"/>
        <circle cx="450" cy="260" r="46" stroke="#1c2330" stroke-width="0.8" fill="none"/>
        <circle cx="450" cy="260" r="24" stroke="#1c2330" stroke-width="0.6" fill="none"/>
        <circle cx="260" cy="200" r="22" stroke="#1c2330" stroke-width="1" fill="none"/>
        <circle cx="260" cy="200" r="3" fill="#1c2330"/>
        <circle cx="540" cy="200" r="18" stroke="#1c2330" stroke-width="1" fill="none"/>
        <rect x="320" y="190" width="40" height="28" stroke="#1c2330" stroke-width="0.8" fill="none"/>
        <line x1="200" y1="380" x2="600" y2="380" stroke="#1c2330" stroke-width="0.5" stroke-dasharray="4,4"/>
        <text x="400" y="398" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="10" fill="#1c2330">142mm</text>
      </svg>

      <button type="button" aria-describedby="ttp01-note-1" aria-label="Callout 01, optical assembly" class="peer/h1 group absolute top-[56%] left-[56.25%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-1" role="tooltip" class="absolute top-[4%] right-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h1:visible peer-hover/h1:opacity-100 peer-hover/h1:delay-120 peer-focus-visible/h1:visible peer-focus-visible/h1:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">01</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Optical Assembly</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">35mm <em class="italic text-ttp01-red">f/1.4</em> prime</div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Twelve-element design across nine groups. Aspherical front and rear, two ED elements mid-stack.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Glass</dt><dd class="m-0 text-ttp01-red">12 / 9</dd>
          <dt class="text-ttp01-key">Coating</dt><dd class="m-0 text-ttp01-red">Nano-V</dd>
          <dt class="text-ttp01-key">Weight</dt><dd class="m-0 text-ttp01-red">410g</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-2" aria-label="Callout 02, shutter dial" class="peer/h2 absolute top-[25%] left-[32.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-2" role="tooltip" class="absolute top-[4%] left-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h2:visible peer-hover/h2:opacity-100 peer-hover/h2:delay-120 peer-focus-visible/h2:visible peer-focus-visible/h2:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">02</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Shutter Dial</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">Mechanical <em class="italic text-ttp01-red">command</em> dial</div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Detented brass with knurled edge. Range 1/8000s to 30s plus bulb. Locks with a quarter-press.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Steps</dt><dd class="m-0 text-ttp01-red">1/3 EV</dd>
          <dt class="text-ttp01-key">Travel</dt><dd class="m-0 text-ttp01-red">300°</dd>
          <dt class="text-ttp01-key">Cycles</dt><dd class="m-0 text-ttp01-red">500k</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-3" aria-label="Callout 03, accessory port" class="peer/h3 absolute top-[30%] left-[47.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-3" role="tooltip" class="absolute top-[4%] left-1/2 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h3:visible peer-hover/h3:opacity-100 peer-hover/h3:delay-120 peer-focus-visible/h3:visible peer-focus-visible/h3:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">03</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Accessory Port</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">ISO 518 <em class="italic text-ttp01-red">hot shoe</em></div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Standard mount with X-sync at 1/250s. Center pin plus four data contacts for TTL flash control.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Sync</dt><dd class="m-0 text-ttp01-red">1/250s</dd>
          <dt class="text-ttp01-key">Pins</dt><dd class="m-0 text-ttp01-red">1+4</dd>
          <dt class="text-ttp01-key">Hot</dt><dd class="m-0 text-ttp01-red">12V</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-4" aria-label="Callout 04, hand grip" class="peer/h4 absolute top-[56%] left-[22.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-4" role="tooltip" class="absolute bottom-[4%] left-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h4:visible peer-hover/h4:opacity-100 peer-hover/h4:delay-120 peer-focus-visible/h4:visible peer-focus-visible/h4:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">04</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Hand Grip</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">Magnesium <em class="italic text-ttp01-red">undergrip</em></div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Cast magnesium core wrapped in pressed cowhide. Conceals battery, card slot and focus-recall trigger.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Wrap</dt><dd class="m-0 text-ttp01-red">Cowhide</dd>
          <dt class="text-ttp01-key">Core</dt><dd class="m-0 text-ttp01-red">MgAl</dd>
          <dt class="text-ttp01-key">Battery</dt><dd class="m-0 text-ttp01-red">7.4 Wh</dd>
        </dl>
      </div>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-ttp01-stage: #f4f3ee;
  --color-ttp01-stage: oklch(0.96 0.005 95);
  --color-ttp01-paper: #fbfaf6;
  --color-ttp01-paper: oklch(0.98 0.006 95);
  --color-ttp01-ink: #1c2330;
  --color-ttp01-ink: oklch(0.26 0.03 265);
  --color-ttp01-body: #4a505c;
  --color-ttp01-body: oklch(0.44 0.02 265);
  --color-ttp01-label: #585e6b;
  --color-ttp01-label: oklch(0.48 0.02 265);
  --color-ttp01-meta: #5b616e;
  --color-ttp01-meta: oklch(0.49 0.02 265);
  --color-ttp01-key: #5f6472;
  --color-ttp01-key: oklch(0.51 0.02 265);
  --color-ttp01-edge: #c7cad1;
  --color-ttp01-edge: oklch(0.83 0.008 265);
  --color-ttp01-tick: #9aa0ab;
  --color-ttp01-tick: oklch(0.69 0.012 265);
  --color-ttp01-dash: #b0b5be;
  --color-ttp01-dash: oklch(0.76 0.01 265);
  --color-ttp01-grid: rgba(28, 35, 48, 0.06);
  --color-ttp01-grid2: rgba(28, 35, 48, 0.05);
  --color-ttp01-red: #c1422c;
  --color-ttp01-red: oklch(0.52 0.16 32);
  --font-ttp01-sans: 'Inter', system-ui, sans-serif;
  --font-ttp01-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttp01-serif: Georgia, 'Times New Roman', serif;
  --animate-ttp01-pulse: ttp01-pulse 2.4s ease-out infinite;
}

@keyframes ttp01-pulse {
  0% {
    transform: scale(0.6);
    opacity: 0.8;
  }

  100% {
    transform: scale(2);
    opacity: 0;
  }
}

.ttp-01 {
  width: 100%;
  min-height: 100svh;
}
/* Under 560px the percentage-pinned notes are stacked by max-[560px]:static;
   the flow container needs to allow that, and the ticks go. */

@media (max-width: 560px) {
  .ttp-01 [role="tooltip"] {
    margin-top: 8px;
  }
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-ttp01-stage: #f4f3ee;
  --color-ttp01-stage: oklch(0.96 0.005 95);
  --color-ttp01-paper: #fbfaf6;
  --color-ttp01-paper: oklch(0.98 0.006 95);
  --color-ttp01-ink: #1c2330;
  --color-ttp01-ink: oklch(0.26 0.03 265);
  --color-ttp01-body: #4a505c;
  --color-ttp01-body: oklch(0.44 0.02 265);
  --color-ttp01-label: #585e6b;
  --color-ttp01-label: oklch(0.48 0.02 265);
  --color-ttp01-meta: #5b616e;
  --color-ttp01-meta: oklch(0.49 0.02 265);
  --color-ttp01-key: #5f6472;
  --color-ttp01-key: oklch(0.51 0.02 265);
  --color-ttp01-edge: #c7cad1;
  --color-ttp01-edge: oklch(0.83 0.008 265);
  --color-ttp01-tick: #9aa0ab;
  --color-ttp01-tick: oklch(0.69 0.012 265);
  --color-ttp01-dash: #b0b5be;
  --color-ttp01-dash: oklch(0.76 0.01 265);
  --color-ttp01-grid: rgba(28, 35, 48, 0.06);
  --color-ttp01-grid2: rgba(28, 35, 48, 0.05);
  --color-ttp01-red: #c1422c;
  --color-ttp01-red: oklch(0.52 0.16 32);
  --font-ttp01-sans: 'Inter', system-ui, sans-serif;
  --font-ttp01-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttp01-serif: Georgia, 'Times New Roman', serif;
  --animate-ttp01-pulse: ttp01-pulse 2.4s ease-out infinite;
}

@keyframes ttp01-pulse {
  0% {
    transform: scale(0.6);
    opacity: 0.8;
  }

  100% {
    transform: scale(2);
    opacity: 0;
  }
}

.ttp-01 {
  width: 100%;
  min-height: 100svh;
}
/* Under 560px the percentage-pinned notes are stacked by max-[560px]:static;
   the flow container needs to allow that, and the ticks go. */

@media (max-width: 560px) {
  .ttp-01 [role="tooltip"] {
    margin-top: 8px;
  }
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • One brittle :hover + + chain becomes peer-hover: on the note. The CSS original kept dead <svg class="leader"> elements in the DOM purely so the adjacent-sibling chain still resolved; the port deletes them, because peer compiles to ~.
  • Named peers (peer/h1 … peer/h4) are what let four independent trigger/tip pairs share one positioned parent without cross-talk — unnamed peer would have every note answer every hotspot.
  • UI change from the CSS original: hotspots are now <button> elements with an 44px hit area and aria-describedby, the label and key greys were raised from 0.5–0.6 alpha to solid tokens at roughly 5.6:1, and body copy went 10.5px → 11px. The original failed SC 1.4.3, SC 2.1.1 and SC 1.4.13 together.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Schematic Hotspot Tooltip with Pulsing CAD Callouts
Source: https://codefronts.com/snippets/tailwind-css-tooltips/tailwind-schematic-hotspot-tooltip-pulsing-cad-callouts/

For product diagrams and exploded views: four pulsing red discs pin a wireframe drawing, and hovering or focusing one reveals a numbered callout with part name, prose and a monospaced spec grid. The port's win is peer — the CSS original's .hot:hover + .leader + .note chain broke the moment anything was inserted between the two elements; peer-hover: compiles to a general sibling combinator and does not.
## HTML
```html
<section class="ttp-01 w-full min-h-svh flex items-center justify-center px-6 py-8 bg-ttp01-stage font-ttp01-sans text-ttp01-ink bg-[linear-gradient(var(--color-ttp01-grid)_1px,transparent_1px),linear-gradient(90deg,var(--color-ttp01-grid)_1px,transparent_1px)] bg-[size:24px_24px]">
  <div class="relative w-full max-w-[760px] aspect-[16/10] bg-ttp01-paper border border-ttp01-edge before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:bg-[linear-gradient(var(--color-ttp01-grid2)_1px,transparent_1px),linear-gradient(90deg,var(--color-ttp01-grid2)_1px,transparent_1px)] before:bg-[size:18px_18px]">
    <span class="absolute top-1.5 left-1.5 w-3 h-3 border border-ttp01-tick border-r-0 border-b-0"></span>
    <span class="absolute top-1.5 right-1.5 w-3 h-3 border border-ttp01-tick border-l-0 border-b-0"></span>
    <span class="absolute bottom-1.5 left-1.5 w-3 h-3 border border-ttp01-tick border-r-0 border-t-0"></span>
    <span class="absolute bottom-1.5 right-1.5 w-3 h-3 border border-ttp01-tick border-l-0 border-t-0"></span>

    <div class="absolute top-3 left-4 right-4 z-2 flex justify-between font-ttp01-mono text-[10px] tracking-[0.15em] uppercase text-ttp01-meta">
      <span>DWG-0142-A · Camera / Exploded View</span>
      <span class="text-right leading-[1.6]">Scale 1:1 · Rev 04<br>14.MAY.2026</span>
    </div>

    <div class="absolute inset-y-[50px] inset-x-[60px] flex items-center justify-center">
      <svg class="w-full h-full overflow-visible" viewBox="0 0 800 460" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
        <rect x="200" y="160" width="400" height="200" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <rect x="340" y="130" width="80" height="30" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <rect x="355" y="120" width="50" height="10" stroke="#1c2330" stroke-width="1" fill="none"/>
        <path d="M200 160 L160 180 L160 340 L200 360 Z" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <circle cx="450" cy="260" r="90" stroke="#1c2330" stroke-width="1.5" fill="none"/>
        <circle cx="450" cy="260" r="68" stroke="#1c2330" stroke-width="1" fill="none"/>
        <circle cx="450" cy="260" r="46" stroke="#1c2330" stroke-width="0.8" fill="none"/>
        <circle cx="450" cy="260" r="24" stroke="#1c2330" stroke-width="0.6" fill="none"/>
        <circle cx="260" cy="200" r="22" stroke="#1c2330" stroke-width="1" fill="none"/>
        <circle cx="260" cy="200" r="3" fill="#1c2330"/>
        <circle cx="540" cy="200" r="18" stroke="#1c2330" stroke-width="1" fill="none"/>
        <rect x="320" y="190" width="40" height="28" stroke="#1c2330" stroke-width="0.8" fill="none"/>
        <line x1="200" y1="380" x2="600" y2="380" stroke="#1c2330" stroke-width="0.5" stroke-dasharray="4,4"/>
        <text x="400" y="398" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="10" fill="#1c2330">142mm</text>
      </svg>

      <button type="button" aria-describedby="ttp01-note-1" aria-label="Callout 01, optical assembly" class="peer/h1 group absolute top-[56%] left-[56.25%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-1" role="tooltip" class="absolute top-[4%] right-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h1:visible peer-hover/h1:opacity-100 peer-hover/h1:delay-120 peer-focus-visible/h1:visible peer-focus-visible/h1:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">01</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Optical Assembly</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">35mm <em class="italic text-ttp01-red">f/1.4</em> prime</div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Twelve-element design across nine groups. Aspherical front and rear, two ED elements mid-stack.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Glass</dt><dd class="m-0 text-ttp01-red">12 / 9</dd>
          <dt class="text-ttp01-key">Coating</dt><dd class="m-0 text-ttp01-red">Nano-V</dd>
          <dt class="text-ttp01-key">Weight</dt><dd class="m-0 text-ttp01-red">410g</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-2" aria-label="Callout 02, shutter dial" class="peer/h2 absolute top-[25%] left-[32.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-2" role="tooltip" class="absolute top-[4%] left-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h2:visible peer-hover/h2:opacity-100 peer-hover/h2:delay-120 peer-focus-visible/h2:visible peer-focus-visible/h2:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">02</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Shutter Dial</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">Mechanical <em class="italic text-ttp01-red">command</em> dial</div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Detented brass with knurled edge. Range 1/8000s to 30s plus bulb. Locks with a quarter-press.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Steps</dt><dd class="m-0 text-ttp01-red">1/3 EV</dd>
          <dt class="text-ttp01-key">Travel</dt><dd class="m-0 text-ttp01-red">300°</dd>
          <dt class="text-ttp01-key">Cycles</dt><dd class="m-0 text-ttp01-red">500k</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-3" aria-label="Callout 03, accessory port" class="peer/h3 absolute top-[30%] left-[47.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-3" role="tooltip" class="absolute top-[4%] left-1/2 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h3:visible peer-hover/h3:opacity-100 peer-hover/h3:delay-120 peer-focus-visible/h3:visible peer-focus-visible/h3:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">03</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Accessory Port</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">ISO 518 <em class="italic text-ttp01-red">hot shoe</em></div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Standard mount with X-sync at 1/250s. Center pin plus four data contacts for TTL flash control.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Sync</dt><dd class="m-0 text-ttp01-red">1/250s</dd>
          <dt class="text-ttp01-key">Pins</dt><dd class="m-0 text-ttp01-red">1+4</dd>
          <dt class="text-ttp01-key">Hot</dt><dd class="m-0 text-ttp01-red">12V</dd>
        </dl>
      </div>

      <button type="button" aria-describedby="ttp01-note-4" aria-label="Callout 04, hand grip" class="peer/h4 absolute top-[56%] left-[22.5%] -m-5.5 w-11 h-11 grid place-items-center rounded-full bg-transparent border-0 p-0 cursor-help focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ttp01-red">
        <span class="relative block w-4 h-4"><span class="absolute inset-[5px] rounded-full bg-ttp01-red"></span><span class="absolute inset-0 rounded-full border border-ttp01-red animate-ttp01-pulse motion-reduce:animate-none"></span></span>
      </button>
      <div id="ttp01-note-4" role="tooltip" class="absolute bottom-[4%] left-0 w-[190px] z-10 invisible opacity-0 pointer-events-none bg-ttp01-paper border border-ttp01-ink px-3.5 py-3 font-ttp01-mono text-[11px] leading-[1.6] shadow-[4px_4px_0_rgba(28,35,48,0.08)] transition-[opacity,visibility] duration-300 peer-hover/h4:visible peer-hover/h4:opacity-100 peer-hover/h4:delay-120 peer-focus-visible/h4:visible peer-focus-visible/h4:opacity-100 max-[560px]:static max-[560px]:w-full">
        <div class="font-ttp01-serif italic font-medium text-xl leading-none text-ttp01-red mb-[3px]">04</div>
        <div class="text-[10px] tracking-[0.15em] uppercase text-ttp01-label mb-1.5">— Hand Grip</div>
        <div class="font-ttp01-serif text-[15px] font-medium leading-tight mb-1.5">Magnesium <em class="italic text-ttp01-red">undergrip</em></div>
        <div class="font-ttp01-sans text-[11px] leading-[1.5] text-ttp01-body">Cast magnesium core wrapped in pressed cowhide. Conceals battery, card slot and focus-recall trigger.</div>
        <dl class="mt-2 pt-1.5 border-t border-dashed border-ttp01-dash grid grid-cols-[54px_1fr] gap-x-2 gap-y-[3px] text-[10px]">
          <dt class="text-ttp01-key">Wrap</dt><dd class="m-0 text-ttp01-red">Cowhide</dd>
          <dt class="text-ttp01-key">Core</dt><dd class="m-0 text-ttp01-red">MgAl</dd>
          <dt class="text-ttp01-key">Battery</dt><dd class="m-0 text-ttp01-red">7.4 Wh</dd>
        </dl>
      </div>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-ttp01-stage: #f4f3ee;
  --color-ttp01-stage: oklch(0.96 0.005 95);
  --color-ttp01-paper: #fbfaf6;
  --color-ttp01-paper: oklch(0.98 0.006 95);
  --color-ttp01-ink: #1c2330;
  --color-ttp01-ink: oklch(0.26 0.03 265);
  --color-ttp01-body: #4a505c;
  --color-ttp01-body: oklch(0.44 0.02 265);
  --color-ttp01-label: #585e6b;
  --color-ttp01-label: oklch(0.48 0.02 265);
  --color-ttp01-meta: #5b616e;
  --color-ttp01-meta: oklch(0.49 0.02 265);
  --color-ttp01-key: #5f6472;
  --color-ttp01-key: oklch(0.51 0.02 265);
  --color-ttp01-edge: #c7cad1;
  --color-ttp01-edge: oklch(0.83 0.008 265);
  --color-ttp01-tick: #9aa0ab;
  --color-ttp01-tick: oklch(0.69 0.012 265);
  --color-ttp01-dash: #b0b5be;
  --color-ttp01-dash: oklch(0.76 0.01 265);
  --color-ttp01-grid: rgba(28, 35, 48, 0.06);
  --color-ttp01-grid2: rgba(28, 35, 48, 0.05);
  --color-ttp01-red: #c1422c;
  --color-ttp01-red: oklch(0.52 0.16 32);
  --font-ttp01-sans: 'Inter', system-ui, sans-serif;
  --font-ttp01-mono: 'JetBrains Mono', ui-monospace, monospace;
  --font-ttp01-serif: Georgia, 'Times New Roman', serif;
  --animate-ttp01-pulse: ttp01-pulse 2.4s ease-out infinite;
}

@keyframes ttp01-pulse {
  0% {
    transform: scale(0.6);
    opacity: 0.8;
  }

  100% {
    transform: scale(2);
    opacity: 0;
  }
}

.ttp-01 {
  width: 100%;
  min-height: 100svh;
}
/* Under 560px the percentage-pinned notes are stacked by max-[560px]:static;
   the flow container needs to allow that, and the ticks go. */

@media (max-width: 560px) {
  .ttp-01 [role="tooltip"] {
    margin-top: 8px;
  }
}
```

How this works

The hover chain becomes a peer relationship. The CSS version reveals its note with .tp-01__hot:hover + .tp-01__leader + .tp-01__note — a chain so brittle that the source ships dead <svg> elements purely to keep it resolving. The Tailwind port deletes them: peer on the button, peer-hover: and peer-focus-visible: on the note, and the note may now sit anywhere after its trigger.

The trigger became a button. Each hotspot is <button type="button" aria-describedby> with a min-h-11 min-w-11 invisible hit area around a 16px visual disc, so the callout is reachable by keyboard and by thumb — the CSS original was an unfocusable <div>.

Everything else is arbitrary values done deliberately. Percentage pin positions (top-[56%] left-[56.25%]), the flat 4px offset shadow, the dashed hairline over the spec grid: none of these are design-scale values, and none of them belong in @theme.

Make it yours

  • Recolour the schematic red in one place: --color-ttp01-red drives the disc, the ring, the italic numeral and the spec values. A drafting navy (#1c4a7a) or blueprint inversion reads equally credible.
  • Radius is deliberately 0 — drawings are square. Soften the paper only (rounded-[4px]) and leave the notes flat if you need a hybrid docs look.
  • Swap JetBrains Mono for IBM Plex Mono or Space Mono without touching sizes; the 10px/11px steps were tuned around a 0.55em cap height.

Gotchas — read before shipping

  • peer only reaches FOLLOWING siblings. Keep each note after its hotspot in source order — moving it before is the one refactor that silently kills the reveal.
  • Percentage-pinned notes inside an aspect-[16/10] paper overflow below ~520px. The port adds max-[560px]: variants that drop the notes to a static stack.
  • An infinite pulse fails SC 2.2.2 if the page runs longer than five seconds without a control; motion-reduce:animate-none is the minimum, and the ring degrades to a static outline.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

aspect-ratio and :focus-visible are Baseline; the floor quoted is Tailwind v4's own generated sheet.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Plain CSSno Tailwind surface

Search CodeFronts

Loading…