Linkvoorbeeld bij hover New
Links in a blog text show a small page preview on hover or keyboard focus: photo, address, title and one line of context. Opens after a short delay, stays open while you move onto the card, closes with Esc and always stays inside the text area. For internal links in blogs, knowledge bases and case stories.
Verbouwen in 2026: waar begin je?
Begin bij wat het meeste oplevert. Lees hoe we een badkamer in Delft in drie weken vernieuwden, wat een hybride warmtepomp in Zwolle kostte en waarom Fysio Oost in Utrecht koos voor één open behandelruimte.
How to use it
- Paste the HTML where the components should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Linkvoorbeeld bij hover · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="lvh">
<div class="lvh__art">
<h3 class="lvh__h">Verbouwen in 2026: waar begin je?</h3>
<p class="lvh__p">Begin bij wat het meeste oplevert. Lees hoe we <a class="lvh__a" href="#linkvoorbeeld-hover" data-i="0">een badkamer in Delft</a> in drie weken vernieuwden, wat <a class="lvh__a" href="#linkvoorbeeld-hover" data-i="1">een hybride warmtepomp in Zwolle</a> kostte en waarom <a class="lvh__a" href="#linkvoorbeeld-hover" data-i="2">Fysio Oost in Utrecht</a> koos voor één open behandelruimte.</p>
</div>
<div class="lvh__card" id="lvh-card" role="tooltip" hidden>
<img class="lvh__img" src="https://neddev.com/lab-media/linkvoorbeeld-hover-1.webp" alt="" width="800" height="436">
<div class="lvh__body"><p class="lvh__url"></p><p class="lvh__t"></p><p class="lvh__x"></p></div>
</div>
</div>
</div>/* Linkvoorbeeld bij hover · NedDev · eigen werk. */
.lvh{position:absolute;inset:0;padding:16px 18px;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;background:#fff;text-align:left;overflow:hidden}
.lvh *{box-sizing:border-box}
.lvh__art{max-width:400px}
.lvh__h{margin:0 0 8px;font-size:17px;line-height:1.2;font-weight:700;letter-spacing:-.015em}
.lvh__p{margin:0;font-size:13.5px;line-height:1.6;color:#2a2a2a}
.lvh__a{color:#0a0a0a;font-weight:600;text-decoration:underline;text-decoration-color:#F5480F;text-decoration-thickness:2px;text-underline-offset:3px;border-radius:2px}
.lvh__a:hover{background:#fde6dc}
.lvh__a:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.lvh__card{position:absolute;left:0;top:0;width:224px;background:#fff;border:1px solid #0a0a0a;border-radius:8px;overflow:hidden;box-shadow:0 14px 28px -14px rgba(10,10,10,.45);opacity:0;transform:translateY(4px);transition:opacity .16s,transform .16s;pointer-events:auto;z-index:5}
.lvh__card.is-on{opacity:1;transform:none}
.lvh__card:not(.is-on){pointer-events:none}
.lvh__card[hidden]{display:none}
.lvh__img{display:block;width:100%;height:78px;object-fit:cover;background:#f4f3f1}
.lvh__body{padding:8px 10px 10px}
.lvh__url{margin:0;font-size:12px;color:#5b5b5b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lvh__t{margin:2px 0 0;font-size:13px;line-height:1.25;font-weight:700}
.lvh__x{margin:3px 0 0;font-size:12px;line-height:1.35;color:#3a3a3a}
@media (prefers-reduced-motion:reduce){.lvh__card{transition:none;transform:none}}// Linkvoorbeeld bij hover · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w = root.querySelector('.lvh'), card = w.querySelector('.lvh__card'), img = card.querySelector('.lvh__img');
var uEl = card.querySelector('.lvh__url'), tEl = card.querySelector('.lvh__t'), xEl = card.querySelector('.lvh__x');
var base = img.getAttribute('src').replace(/linkvoorbeeld-hover-1\.webp$/, '');
var P = [
['voorbeeld.nl/projecten/badkamer-delft', 'Badkamer in Delft, klaar in drie weken', 'Inloopdouche, zwevend meubel en vloerverwarming.', 'linkvoorbeeld-hover-1.webp'],
['voorbeeld.nl/blog/hybride-warmtepomp-kosten', 'Wat kost een hybride warmtepomp?', 'Rekenvoorbeeld uit Zwolle: € 4.850, na subsidie € 2.725.', 'linkvoorbeeld-hover-2.webp'],
['voorbeeld.nl/projecten/fysio-oost-utrecht', 'Fysio Oost: één open behandelruimte', 'Van vier kleine kamers naar één lichte ruimte.', 'linkvoorbeeld-hover-3.webp']
];
P.forEach(function (p) { var i = new Image(); i.src = base + p[3]; });
var links = [].slice.call(w.querySelectorAll('.lvh__a')), cur = null, tOpen = 0, tClose = 0;
var rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
function place(a) {
var W = w.clientWidth, H = w.clientHeight, wr = w.getBoundingClientRect();
var rs = a.getClientRects(), r = rs[0];
var cw = card.offsetWidth, ch = card.offsetHeight;
var left = r.left - wr.left + r.width / 2 - cw / 2;
left = Math.max(6, Math.min(W - cw - 6, left));
var below = rs[rs.length - 1].bottom - wr.top + 6, above = r.top - wr.top - ch - 6, top;
if (below + ch <= H - 6) top = below; else if (above >= 6) top = above; else top = Math.max(6, H - ch - 6);
card.style.left = left + 'px'; card.style.top = top + 'px';
}
function show(a) {
clearTimeout(tClose);
var p = P[+a.dataset.i];
if (cur !== a) {
img.src = base + p[3]; uEl.textContent = p[0]; tEl.textContent = p[1]; xEl.textContent = p[2];
if (cur) cur.removeAttribute('aria-describedby');
}
cur = a; a.setAttribute('aria-describedby', 'lvh-card');
card.hidden = false; place(a);
requestAnimationFrame(function () { card.classList.add('is-on'); });
}
function hide() {
clearTimeout(tOpen); card.classList.remove('is-on');
if (cur) cur.removeAttribute('aria-describedby');
cur = null;
tClose = setTimeout(function () { if (!cur) card.hidden = true; }, rm ? 0 : 170);
}
links.forEach(function (a) {
a.addEventListener('mouseenter', function () { clearTimeout(tOpen); clearTimeout(tClose); tOpen = setTimeout(function () { show(a); }, cur ? 0 : 300); });
a.addEventListener('mouseleave', function () { clearTimeout(tOpen); tClose = setTimeout(hide, 160); });
a.addEventListener('focus', function () { show(a); });
a.addEventListener('blur', function () { hide(); });
a.addEventListener('click', function (e) { e.preventDefault(); });
});
card.addEventListener('mouseenter', function () { if (cur) clearTimeout(tClose); });
card.addEventListener('mouseleave', function () { tClose = setTimeout(hide, 160); });
w.addEventListener('keydown', function (e) { if (e.key === 'Escape' && cur) { hide(); } });
// voorbeeldstand in het Lab: het eerste kaartje staat open tot de eerste interactie
if (w.clientWidth >= 380 && matchMedia('(hover: hover)').matches) show(links[0]);
w.addEventListener('pointerenter', function once() { w.removeEventListener('pointerenter', once); if (document.activeElement !== links[0]) hide(); });
}
init(document.querySelector('.jouw-container'));
More components
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →