Hover Preview
Overview
Hover a keyword in body text and a preview card floats out next to the pointer. The card content is drawn in pure CSS.
Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Hover Preview to your project in your colors.
Where to use it
Animated headlines, testimonials, emphasis copy and large-type layouts.
Install
With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.
$ npm i -g @8pt-dev/cli && 8pt add hover-preview
mcp › Use 8pt MCP to call get_component("hover-preview")
Code
Source for Hover Preview. Motion uses only transform and opacity and respects the Reduce motion setting.
<div class="x-hp">
<p>Good UI starts with <a data-k="grid">grid systems</a>, uses <a data-k="type">typography</a> to build hierarchy, then <a data-k="ease">motion curves</a> to bring it to life.</p>
<div class="x-hp__card" aria-hidden="true">
<div class="x-hp__art" data-art="grid"></div>
<div class="x-hp__art" data-art="type">Aa</div>
<div class="x-hp__art" data-art="ease"><svg viewBox="0 0 100 60"><path d="M4 56 C 44 56, 50 4, 96 4" fill="none" stroke="currentColor" stroke-width="2.5"/></svg></div>
</div>
</div>.x-hp{position:relative;width:min(460px,100%);color:var(--d-fg,#fff)}
.x-hp p{margin:0;font:500 clamp(17px,2.2vw,22px)/1.7 var(--d-font,system-ui,sans-serif);letter-spacing:-.01em;color:var(--d-muted,#a0a0ab)}
.x-hp a{color:var(--d-fg,#fff);font-weight:700;text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--d-accent,#4D74FF) 70%,transparent);text-underline-offset:5px;cursor:default}
.x-hp__card{position:absolute;left:0;top:0;width:180px;height:120px;border-radius:14px;overflow:hidden;background:#0c0c14;border:1px solid rgba(255,255,255,.12);box-shadow:0 24px 50px -20px rgba(0,0,0,.7);opacity:0;pointer-events:none;transform:translate(0,0) scale(.9);transition:opacity .2s;will-change:transform;z-index:3}
.x-hp__card.on{opacity:1}
.x-hp__art{position:absolute;inset:0;display:none;place-items:center;color:#fff}
.x-hp__art.on{display:grid}
.x-hp__art[data-art=grid]{background:linear-gradient(#4D74FF33 1px,transparent 1px) 0 0/16px 16px,linear-gradient(90deg,#4D74FF33 1px,transparent 1px) 0 0/16px 16px,radial-gradient(circle at 30% 30%,#4b3bff,#0c0c14 70%)}
.x-hp__art[data-art=type]{font:700 64px/1 Georgia,serif;background:linear-gradient(135deg,#1b1b2a,#0c0c14)}
.x-hp__art[data-art=ease]{padding:18px;background:linear-gradient(135deg,#0d2230,#0c0c14);color:#7cf3ff}
.x-hp__art svg{width:100%;height:100%}const init = (root) => {
const box = root.querySelector('.x-hp'), card = box.querySelector('.x-hp__card'), arts = card.querySelectorAll('.x-hp__art');
let x = 0, y = 0, tx = 0, ty = 0, s = .9, ts = .9, raf = 0;
const tick = () => {
x += (tx - x) * .2; y += (ty - y) * .2; s += (ts - s) * .2;
card.style.transform = `translate(${x}px, ${y}px) scale(${s})`;
raf = Math.abs(tx - x) + Math.abs(ty - y) + Math.abs(ts - s) > .05 ? requestAnimationFrame(tick) : 0;
};
box.querySelectorAll('a').forEach(a => {
a.addEventListener('pointerenter', () => { arts.forEach(el => el.classList.toggle('on', el.dataset.art === a.dataset.k)); card.classList.add('on'); ts = 1; });
a.addEventListener('pointerleave', () => { card.classList.remove('on'); ts = .9; });
});
…Subscribe to Builder to see the full source.See pricing