Cursor met label New
Over project cards the cursor turns into a round "View project" label, over a draggable strip into "Drag". Only on devices with a mouse (hover: hover and pointer: fine); on touch and keyboard they stay normal links with a focus ring. requestAnimationFrame with a light lag, no lag with reduced motion. Works in every browser.
Schoolplein EdeBakkerij ZeistTandarts AmersfoortLoods VeenendaalWoonboot UtrechtPraktijk Baarn
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.
<!-- Cursor met label · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="cul">
<div class="cul__cards">
<a class="cul__card cul__card--a" href="#cursor-label" data-cursor="Bekijk project"><span class="cul__k">Woonhuis · Bussum</span><b>Villa Spiegel</b></a>
<a class="cul__card cul__card--b" href="#cursor-label" data-cursor="Bekijk project"><span class="cul__k">Kantoor · Den Bosch</span><b>De Pettelaar</b></a>
</div>
<div class="cul__strip" data-cursor="Sleep" tabindex="0" role="region" aria-label="Meer projecten, horizontaal scrollbaar">
<span class="cul__chip">Schoolplein Ede</span><span class="cul__chip">Bakkerij Zeist</span><span class="cul__chip">Tandarts Amersfoort</span><span class="cul__chip">Loods Veenendaal</span><span class="cul__chip">Woonboot Utrecht</span><span class="cul__chip">Praktijk Baarn</span>
</div>
<div class="cul__cur" aria-hidden="true"><span class="cul__lbl"></span></div>
</div>
</div>/* Cursor met label · NedDev · eigen werk. */
.cul{position:absolute;inset:0;display:flex;flex-direction:column;gap:10px;padding:12px;background:#fff;font-family:'Switzer',system-ui,sans-serif;overflow:hidden}
.cul__cards{flex:1;display:grid;grid-template-columns:1fr 1fr;gap:10px;min-height:0}
.cul__card{position:relative;display:flex;flex-direction:column;justify-content:flex-end;gap:2px;padding:12px;border-radius:4px;text-decoration:none;color:#fff;overflow:hidden}
.cul__card--a{background:linear-gradient(160deg,#7a6a55,#3b3127)}
.cul__card--b{background:linear-gradient(160deg,#4d5c66,#1c252b)}
.cul__card b{font-size:17px;letter-spacing:-.02em}
.cul__k{font:500 10px 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;opacity:.85}
.cul__card:focus-visible,.cul__strip:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.cul__card::after{content:'';position:absolute;inset:0;background:#0a0a0a;opacity:0;transition:opacity .2s}
.cul__card:hover::after{opacity:.12}
.cul__strip{display:flex;gap:8px;overflow-x:auto;scroll-snap-type:x proximity;scrollbar-width:none;padding:2px;user-select:none}
.cul__strip::-webkit-scrollbar{display:none}
.cul__strip.is-drag{scroll-snap-type:none}
.cul__chip{flex:none;scroll-snap-align:start;padding:12px 14px;border:1px solid #0a0a0a;border-radius:4px;font-size:13px;font-weight:600;background:#f4f3f1;color:#0a0a0a}
.cul__cur{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;z-index:4}
.cul__lbl{position:absolute;left:0;top:0;display:grid;place-items:center;width:84px;height:84px;margin:-42px 0 0 -42px;border-radius:50%;background:#F5480F;color:#0a0a0a;font:600 11px/1.15 'Switzer',sans-serif;text-align:center;padding:10px;box-sizing:border-box;transform:scale(0);transition:transform .22s cubic-bezier(.3,.7,.2,1)}
.cul.is-show .cul__lbl{transform:scale(1)}
.cul__lbl.is-drag{background:#0a0a0a;color:#fff;width:64px;height:64px;margin:-32px 0 0 -32px}
.cul--on .cul__card,.cul--on .cul__strip{cursor:none}
@media (prefers-reduced-motion:reduce){.cul__lbl{transition:none}}// Cursor met label · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
root=root.querySelector('.cul')||root;
var strip=root.querySelector('.cul__strip');
/* slepen met de muis in de strook (touch scrolt native) */
var dn=false,sx=0,sl=0,moved=0;
strip.addEventListener('pointerdown',function(e){if(e.pointerType!=='mouse')return;dn=true;moved=0;sx=e.clientX;sl=strip.scrollLeft;strip.classList.add('is-drag');strip.setPointerCapture(e.pointerId);});
strip.addEventListener('pointermove',function(e){if(!dn)return;moved=Math.abs(e.clientX-sx);strip.scrollLeft=sl-(e.clientX-sx);});
strip.addEventListener('pointerup',function(){dn=false;strip.classList.remove('is-drag');});
strip.addEventListener('pointercancel',function(){dn=false;strip.classList.remove('is-drag');});
if(!matchMedia('(hover: hover) and (pointer: fine)').matches)return;
root.classList.add('cul--on');
var cur=root.querySelector('.cul__cur'),lbl=root.querySelector('.cul__lbl'),rm=matchMedia('(prefers-reduced-motion: reduce)').matches;
var tx=0,ty=0,x=0,y=0,raf=0;
function tick(){x+=(tx-x)*(rm?1:.22);y+=(ty-y)*(rm?1:.22);cur.style.transform='translate3d('+x.toFixed(1)+'px,'+y.toFixed(1)+'px,0)';if(Math.abs(tx-x)>.3||Math.abs(ty-y)>.3)raf=requestAnimationFrame(tick);else raf=0;}
root.addEventListener('pointermove',function(e){if(e.pointerType!=='mouse')return;var r=root.getBoundingClientRect();tx=e.clientX-r.left;ty=e.clientY-r.top;
var t=e.target.closest('[data-cursor]');if(t){if(!root.classList.contains('is-show')){x=tx;y=ty;}lbl.textContent=t.getAttribute('data-cursor');lbl.classList.toggle('is-drag',t===strip);root.classList.add('is-show');}else root.classList.remove('is-show');
if(!raf)raf=requestAnimationFrame(tick);});
root.addEventListener('pointerleave',function(){root.classList.remove('is-show');});
}
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 →