A top progress bar fills as the user scrolls down the page.
Attribute
data-scroll-progress
CSS
[data-scroll-progress]{position:fixed;top:0;left:0;height:3px;background:linear-gradient(90deg,#6366f1,#ec4899);animation:scrollProgress linear both;animation-timeline:scroll(root);animation-range:0% 100%;z-index:9999;width:100%;}@keyframes scrollProgress{from{transform:scaleX(0);}to{transform:scaleX(1);}}
Scroll
#162CSS Only
Section Snap Scroll
Sections snap smoothly one at a time when the user scrolls.
Background color interpolates from one color to another as the page is scrolled.
Attribute
data-scroll-color
CSS
[data-scroll-color]{animation:bgColorScroll linear both;animation-timeline:scroll(root);}@keyframes bgColorScroll{from{background-color:#6366f1;}to{background-color:#ec4899;}}
Scroll
#170JS Only
Lazy Background Image
Applies a background image to an element only when it enters the viewport.
Border glows with an animated gradient that rotates around the element.
Attribute
data-grad-border
CSS
[data-grad-border]{position:relative;border-radius:12px;}[data-grad-border]::before{content:'';position:absolute;inset:-2px;border-radius:inherit;background:conic-gradient(#6366f1,#ec4899,#f59e0b,#22c55e,#6366f1);z-index:-1;animation:rotateBorder 3s linear infinite;}@keyframes rotateBorder{to{transform:rotate(360deg);}}
Visual FX
#191CSS + JS
3D Tilt Card
Card tilts in 3D perspective to follow the mouse cursor on hover.
Content reveals via a circle expand from center using clip-path.
Attribute
data-radial-reveal
CSS
[data-radial-reveal]{clip-path:circle(0% at 50% 50%);animation:radialReveal linear both;animation-timeline:view();animation-range:entry 0% entry 30%;}@keyframes radialReveal{to{clip-path:circle(75% at 50% 50%);}}
Animations
#258CSS Only
Wipe Right Reveal
Element is revealed with a left-to-right wipe using clip-path.
Auto-formats number inputs with thousands separators on blur.
Attribute
data-num-format
JS JavaScript
document.querySelectorAll('[data-num-format]').forEach(el=>{el.addEventListener('blur',()=>{const n=parseFloat((el as HTMLInputElement).value.replace(/,/g,''));if(!isNaN(n))(el as HTMLInputElement).value=n.toLocaleString();});});
Text
#273CSS Only
Glassmorphism Panel
Panel with deep glass blur, subtle border, and inner highlight.
Element's hue-rotate animates as the user scrolls past it.
Attribute
data-hue-scroll
CSS
[data-hue-scroll]{animation:hueScroll linear both;animation-timeline:view();}@keyframes hueScroll{from{filter:hue-rotate(0deg);}to{filter:hue-rotate(360deg);}}
Visual FX
#281CSS + JS
Notification Toast
Slides in a toast message from the bottom-right corner.
const tog=document.querySelector('[data-price-toggle]');tog?.addEventListener('change',()=>{const isAnnual=(tog as HTMLInputElement).checked;document.querySelectorAll('[data-price-monthly]').forEach(el=>(el as HTMLElement).style.display=isAnnual?'none':'');document.querySelectorAll('[data-price-annual]').forEach(el=>(el as HTMLElement).style.display=isAnnual?'':'none');});
UI Patterns
#284CSS + JS
Collapsible Section
Toggles a content section open/closed with smooth height animation.
document.querySelectorAll('[data-range-display]').forEach(input=>{const out=document.getElementById(input.dataset.rangeDisplay||'');if(out)out.textContent=(input as HTMLInputElement).value;input.addEventListener('input',()=>{if(out)out.textContent=(input as HTMLInputElement).value;});});
Forms
#292CSS + JS
Multi-step Form
Hides all form steps except the current one, with Next/Back controls.
let cur=0;const steps=[...document.querySelectorAll('[data-step]')];const show=(n:number)=>{steps.forEach((s,i)=>s.classList.toggle('active',i===n));};show(0);document.querySelectorAll('[data-next-step]').forEach(btn=>btn.addEventListener('click',()=>show(Math.min(++cur,steps.length-1))));document.querySelectorAll('[data-prev-step]').forEach(btn=>btn.addEventListener('click',()=>show(Math.max(--cur,0))));
Forms
#293CSS + JS
Real-time Email Validation
Shows a tick/cross icon as the user types to indicate valid email format.
document.querySelectorAll('[data-email-validate]').forEach(el=>{el.addEventListener('input',()=>{const ok=/^[^s@]+@[^s@]+.[^s@]+$/.test((el as HTMLInputElement).value);el.classList.toggle('valid',ok);el.classList.toggle('invalid',(el as HTMLInputElement).value.length>0&&!ok);});});
Forms
#294JS Only
Credit Card Formatter
Auto-formats credit card number input with spaces every 4 digits.
Attribute
data-cc-format
JS JavaScript
document.querySelectorAll('[data-cc-format]').forEach(el=>{el.addEventListener('input',()=>{let v=(el as HTMLInputElement).value.replace(/D/g,'').slice(0,16);(el as HTMLInputElement).value=v.replace(/(.{4})/g,'$1 ').trim();});});
Forms
#295CSS + JS
One Page Scroll Indicator
Dots on the side indicate which section is currently in view.
Disables a form's submit button after first submission to prevent duplicates.
Attribute
data-once-submit
JS JavaScript
document.querySelectorAll('[data-once-submit]').forEach(form=>{form.addEventListener('submit',()=>{form.querySelectorAll('[type=submit]').forEach((btn:Element)=>{(btn as HTMLButtonElement).disabled=true;(btn as HTMLButtonElement).textContent='Sending…';});});});
let idleTimer:any;const reset=()=>{clearTimeout(idleTimer);idleTimer=setTimeout(()=>document.querySelector('[data-idle-modal]')?.classList.add('show'),120000);};['click','keydown','mousemove','scroll'].forEach(ev=>document.addEventListener(ev,reset));reset();
Utility
#299JS Only
Keyboard Shortcut Handler
Listens for a keyboard shortcut and triggers an action.
Wraps any scroll callback in a throttle to limit firing rate.
Attribute
data-throttle-scroll
JS JavaScript
let ticking=false;window.addEventListener('scroll',()=>{if(!ticking){requestAnimationFrame(()=>{document.querySelector('[data-throttle-scroll]')?.dispatchEvent(new Event('throttled-scroll'));ticking=false;});ticking=true;}},{passive:true});
Performance
#303CSS + JS
High Contrast Mode
Adds a toggle for high-contrast mode that respects WCAG AA contrast ratios.
Buttons to increase or decrease the root font size for readability.
Attribute
data-font-size
JS JavaScript
let base=parseFloat(getComputedStyle(document.documentElement).fontSize);document.querySelectorAll('[data-font-size]').forEach(btn=>{btn.addEventListener('click',()=>{base=Math.min(Math.max(base+(btn.dataset.fontSize==='+'?2:-2),12),24);document.documentElement.style.fontSize=base+'px';});});
Accessibility
#305JS Only
Tab Trap in Modal
Traps keyboard focus inside an open modal so tab doesn't escape.
document.querySelectorAll('[data-video-progress]').forEach(video=>{const fill=video.parentElement?.querySelector('[data-video-fill]');(video as HTMLVideoElement).addEventListener('timeupdate',()=>{if(fill)(fill as HTMLElement).style.width=((video as HTMLVideoElement).currentTime/(video as HTMLVideoElement).duration*100)+'%';});});
Media
#307CSS + JS
Image Compare Slider
Drag a divider to compare two images side by side.
document.querySelectorAll('[data-pinch-zoom]').forEach(el=>{let dist=0;el.addEventListener('touchstart',e=>{if(e.touches.length===2)dist=Math.hypot(e.touches[0].clientX-e.touches[1].clientX,e.touches[0].clientY-e.touches[1].clientY);},{passive:true});el.addEventListener('touchmove',e=>{if(e.touches.length===2){const nd=Math.hypot(e.touches[0].clientX-e.touches[1].clientX,e.touches[0].clientY-e.touches[1].clientY);(el as HTMLElement).style.transform=`scale(${Math.min(Math.max(nd/dist,1),3)})`;dist=nd;}},{passive:true});});
Interaction
#310CSS + JS
Context Menu Override
Shows a custom right-click context menu instead of the browser default.
Updates CSS --accent variable site-wide from a color input.
Attribute
data-accent-picker
JS JavaScript
document.querySelectorAll('[data-accent-picker]').forEach(input=>{input.addEventListener('input',()=>document.documentElement.style.setProperty('--accent',(input as HTMLInputElement).value));});
Color & Theme
#312JS Only
Randomize Palette
Generates and applies a random complementary color palette on click.
let lastY=0;window.addEventListener('scroll',()=>{const dir=scrollY>lastY?'down':'up';lastY=scrollY;document.querySelectorAll('[data-dir-gradient]').forEach(el=>{el.classList.toggle('down',dir==='down');el.classList.toggle('up',dir==='up');});},{passive:true});
Color & Theme
#314JS Only
Easter Egg Konami Code
Triggers a hidden animation when the Konami code is entered on keyboard.
document.querySelectorAll('[data-sticky-note]').forEach(el=>{let ox=0,oy=0,down=false;el.style.top=(localStorage.getItem('sn-top')||'100')+'px';el.style.left=(localStorage.getItem('sn-left')||'100')+'px';(el as HTMLElement).contentEditable='true';el.textContent=localStorage.getItem('sn-text')||'Your note…';el.addEventListener('input',()=>localStorage.setItem('sn-text',el.textContent||''));el.addEventListener('mousedown',e=>{down=true;ox=e.clientX-el.getBoundingClientRect().left;oy=e.clientY-el.getBoundingClientRect().top;});document.addEventListener('mousemove',e=>{if(!down)return;(el as HTMLElement).style.left=e.clientX-ox+'px';(el as HTMLElement).style.top=e.clientY-oy+'px';});document.addEventListener('mouseup',()=>{down=false;localStorage.setItem('sn-left',(el as HTMLElement).style.left);localStorage.setItem('sn-top',(el as HTMLElement).style.top);});});
Misc
#318JS Only
QR Code from URL
Generates a QR code SVG for the current page URL using a public API.
Content reveals using an expanding ink-blot circle clip.
Attribute
data-ink-reveal
CSS
[data-ink-reveal]{clip-path:circle(0 at center);animation:inkBlot linear both;animation-timeline:view();animation-range:entry 0% entry 35%;}@keyframes inkBlot{to{clip-path:circle(100% at center);}}
Animations
#326CSS Only
Scroll-Linked Width Bar
A div's width expands from 0 to 100% relative to scroll position.
Attribute
data-scroll-width
CSS
[data-scroll-width]{animation:scrollWidth linear both;animation-timeline:scroll(root);}@keyframes scrollWidth{from{width:0%;}to{width:100%;}}
Scroll
#327CSS + JS
Section Active Class
Adds .active to a section when it's the primary visible section.
const obs=new IntersectionObserver(([e])=>{if(e.isIntersecting){e.target.classList.add('in-view');obs.disconnect();}});document.querySelectorAll('[data-float-cards]').forEach(el=>{[...el.children].forEach((c,i)=>(c as HTMLElement).style.transitionDelay=i*0.08+'s');obs.observe(el);});
Scroll
#329CSS + JS
Spotlight Card
Reveals a radial light effect at the mouse position inside a card.
Attribute
data-spotlight-card
CSS
[data-spotlight-card]{background:radial-gradient(circle 200px at var(--x,-200px) var(--y,-200px),rgba(99,102,241,0.15),transparent 70%);transition:background 0.05s;}
document.querySelectorAll('[data-tag-input]').forEach(input=>{const list=input.parentElement?.querySelector('[data-tag-list]');if(!list)return;input.addEventListener('keydown',e=>{if((e.key===','||e.key==='Enter')&&(input as HTMLInputElement).value.trim()){e.preventDefault();const val=(input as HTMLInputElement).value.trim().replace(',','');const tag=document.createElement('span');tag.className='wm-tag';tag.innerHTML=val+'<button onclick="this.parentElement.remove()">×</button>';list.appendChild(tag);(input as HTMLInputElement).value='';}});});
Forms
#335JS Only
Image AVIF/WebP Picker
Automatically selects the best image format the browser supports.
Attribute
data-best-img
JS JavaScript
document.querySelectorAll('[data-best-img]').forEach(img=>{const avif=img.dataset.bestImgAvif;const webp=img.dataset.bestImgWebp;const fallback=img.dataset.bestImgFallback;const test=(src:string)=>new Promise<boolean>(r=>{const i=new Image();i.onload=()=>r(i.width>0);i.onerror=()=>r(false);i.src=src;});(async()=>{if(avif&&await test(avif))(img as HTMLImageElement).src=avif;else if(webp&&await test(webp))(img as HTMLImageElement).src=webp;else if(fallback)(img as HTMLImageElement).src=fallback;})();});
Performance
#336JS Only
Announce Page Title on Route Change
Re-announces the document title to screen readers on SPA navigation.
document.querySelectorAll('[data-seq-fade]').forEach(el=>{[...el.children].forEach((c,i)=>(c as HTMLElement).style.transitionDelay=i*0.07+'s');new IntersectionObserver(([e])=>{if(e.isIntersecting)el.classList.add('ready');}).observe(el);});
Animations
#355CSS Only
Breathing Glow
Element pulses with a glowing shadow rhythm like breathing.
Adds a radial vignette darkening around the edges of an element.
Attribute
data-vignette
CSS
[data-vignette]{position:relative;}[data-vignette]::after{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,0.6) 100%);pointer-events:none;border-radius:inherit;}
Visual FX
#367CSS Only
Pixel Art Filter
Applies a pixelate effect using CSS image-rendering.
On form submit failure, smoothly scrolls to and focuses the first invalid field.
Attribute
data-scroll-error
JS JavaScript
document.querySelectorAll('[data-scroll-error]').forEach(form=>{form.addEventListener('submit',e=>{const invalid=form.querySelector(':invalid') as HTMLElement|null;if(invalid){e.preventDefault();invalid.scrollIntoView({behavior:'smooth',block:'center'});setTimeout(()=>invalid.focus(),400);}});});
Forms
#378CSS Only
Mosaic Layout
CSS grid mosaic with some cells spanning multiple rows or columns.
Lets users navigate a list of items with arrow keys.
Attribute
data-arrow-nav
JS JavaScript
document.querySelectorAll('[data-arrow-nav]').forEach(list=>{const items=[...list.querySelectorAll<HTMLElement>('[role=option],[data-nav-item]')];list.addEventListener('keydown',e=>{const i=items.indexOf(document.activeElement as HTMLElement);if(e.key==='ArrowDown'){e.preventDefault();items[Math.min(i+1,items.length-1)]?.focus();}if(e.key==='ArrowUp'){e.preventDefault();items[Math.max(i-1,0)]?.focus();}});});
Accessibility
#384CSS Only
Responsive Video
Video element scales to fill its container while maintaining aspect ratio.
Video starts playing when the user hovers and pauses on mouse leave.
Attribute
data-hover-video
JS JavaScript
document.querySelectorAll('[data-hover-video]').forEach(video=>{const v=video as HTMLVideoElement;v.muted=true;video.addEventListener('mouseenter',()=>v.play());video.addEventListener('mouseleave',()=>v.pause());});
Media
#386CSS + JS
Drag & Drop Kanban
Kanban columns that accept card drops and reorder on drag.
document.querySelectorAll('[data-kanban-col]').forEach(col=>{col.addEventListener('dragover',e=>{e.preventDefault();col.classList.add('drag-over');});col.addEventListener('dragleave',()=>col.classList.remove('drag-over'));col.addEventListener('drop',e=>{e.preventDefault();col.classList.remove('drag-over');const id=(e as DragEvent).dataTransfer?.getData('text/plain');const card=document.getElementById(id||'');if(card)col.appendChild(card);});});document.querySelectorAll('[data-kanban-card]').forEach(card=>{(card as HTMLElement).draggable=true;(card as HTMLElement).addEventListener('dragstart',e=>(e as DragEvent).dataTransfer?.setData('text/plain',card.id));});
Interaction
#387CSS + JS
Keyboard Hint Display
Shows relevant keyboard shortcuts in a floating hint box when Ctrl is held.
let fired=false;document.addEventListener('mouseleave',e=>{if(e.clientY<=0&&!fired){fired=true;document.querySelector('[data-exit-popup]')?.classList.add('show');}});document.querySelector('[data-exit-close]')?.addEventListener('click',()=>document.querySelector('[data-exit-popup]')?.classList.remove('show'));
Misc
#394JS Only
Auto Save Draft
Auto-saves textarea content to localStorage every 5 seconds.
Attribute
data-autosave
JS JavaScript
document.querySelectorAll('[data-autosave]').forEach(el=>{const key='autosave-'+el.id;if(localStorage.getItem(key))(el as HTMLTextAreaElement).value=localStorage.getItem(key)||'';setInterval(()=>localStorage.setItem(key,(el as HTMLTextAreaElement).value),5000);});
Misc
#395CSS + JS
Search Autocomplete
Shows a dropdown of matching suggestions as the user types in a search input.
document.querySelectorAll('[data-autocomplete]').forEach(input=>{const list=input.parentElement?.querySelector('[data-ac-list]');const items=input.parentElement?.querySelectorAll('[data-ac-item]');if(!list||!items)return;input.addEventListener('input',()=>{const q=(input as HTMLInputElement).value.toLowerCase();let any=false;items.forEach(item=>{const match=item.textContent?.toLowerCase().includes(q)&&q.length>0;(item as HTMLElement).style.display=match?'':'none';if(match)any=true;});list.classList.toggle('show',any);});document.addEventListener('click',e=>{if(!input.contains(e.target as Node))list.classList.remove('show');});items.forEach(item=>item.addEventListener('click',()=>{(input as HTMLInputElement).value=item.textContent||'';list.classList.remove('show');}));});
UI Patterns
#396CSS Only
Skeleton Avatar Group
Shows stacked avatar skeleton placeholders during load.
Adds a master checkbox that toggles all checkboxes in a group.
Attribute
data-check-all
JS JavaScript
document.querySelectorAll('[data-check-all]').forEach(master=>{const group=document.querySelectorAll<HTMLInputElement>('[data-check-group="'+master.id+'"]');(master as HTMLInputElement).addEventListener('change',()=>group.forEach(c=>c.checked=(master as HTMLInputElement).checked));group.forEach(c=>c.addEventListener('change',()=>(master as HTMLInputElement).checked=[...group].every(x=>x.checked)));});
Forms
#409JS Only
Passive Event Listeners
Marks scroll and touch events as passive for improved scrolling performance.
document.querySelectorAll('[data-360]').forEach(el=>{const frames=el.dataset['360']?.split(',').map(s=>s.trim())||[];let idx=0,down=false,startX=0;el.addEventListener('mousedown',e=>{down=true;startX=e.clientX;el.classList.add('dragging');});document.addEventListener('mouseup',()=>{down=false;el.classList.remove('dragging');});el.addEventListener('mousemove',e=>{if(!down)return;const dx=e.clientX-startX;if(Math.abs(dx)>10){idx=(idx+Math.sign(dx)+frames.length)%frames.length;(el as HTMLImageElement).src=frames[idx];startX=e.clientX;}});});
Media
#413JS Only
Multi-select Checkboxes with Shift
Holding Shift selects a range of checkboxes at once.
Attribute
data-range-select
JS JavaScript
let lastChecked:HTMLInputElement|null=null;document.querySelectorAll<HTMLInputElement>('[data-range-select] input[type=checkbox]').forEach(cb=>{cb.addEventListener('click',e=>{if(e.shiftKey&&lastChecked){const boxes=[...document.querySelectorAll<HTMLInputElement>('[data-range-select] input[type=checkbox]')];const a=boxes.indexOf(lastChecked),b=boxes.indexOf(cb);boxes.slice(Math.min(a,b),Math.max(a,b)+1).forEach(x=>x.checked=cb.checked);}lastChecked=cb;});});
Interaction
#414JS Only
CSS Palette from Image
Extracts dominant colors from an image and sets them as CSS variables.
Attribute
data-palette-from-img
JS JavaScript
document.querySelectorAll('[data-palette-from-img]').forEach(img=>{const canvas=document.createElement('canvas');canvas.width=canvas.height=1;canvas.getContext('2d')?.drawImage(img as HTMLImageElement,0,0,1,1);const[r,g,b]=canvas.getContext('2d')?.getImageData(0,0,1,1).data||[99,102,241];document.documentElement.style.setProperty('--img-accent',`rgb(${r},${g},${b})`);});
Color & Theme
#415CSS + JS
Offline Page Message
Shows a message overlay when the user loses internet connection.
new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('visible');}}),{threshold:0.1}).observe(document.querySelector('[data-fade-img]')!);
Performance
#426JS Only
Focus Lock on Open Drawer
Prevents focus from moving outside an open drawer/side panel.
new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){(e.target as HTMLElement).style.backgroundImage=`url(${(e.target as HTMLElement).dataset.bgLazy})`;e.target.classList.add('loaded');}})).observe(document.querySelector('[data-bg-lazy]')!);
Media
#428JS Only
Proximity Effect
Nearby elements move away from the cursor as if repelled by a magnet.
Attribute
data-repel
JS JavaScript
document.addEventListener('mousemove',e=>{document.querySelectorAll('[data-repel]').forEach(el=>{const r=el.getBoundingClientRect();const dx=e.clientX-(r.left+r.width/2);const dy=e.clientY-(r.top+r.height/2);const dist=Math.hypot(dx,dy);if(dist<80){const force=(80-dist)/80;(el as HTMLElement).style.transform=`translate(${-dx*force*0.5}px,${-dy*force*0.5}px)`;}else{(el as HTMLElement).style.transform='';}});});
Interaction
#429CSS + JS
Invert Mode
Inverts all colors for a quick negative/dark toggle.
function layout(el:HTMLElement){const cols=Math.floor(el.offsetWidth/260)||1;const h=Array(cols).fill(0);const gap=16;[...el.children].forEach((child:Element,i)=>{const col=h.indexOf(Math.min(...h));const c=child as HTMLElement;c.style.left=col*(260+gap)+'px';c.style.top=h[col]+'px';c.style.width='260px';h[col]+=c.offsetHeight+gap;});el.style.height=Math.max(...h)+'px';}document.querySelectorAll<HTMLElement>('[data-js-masonry]').forEach(el=>{layout(el);window.addEventListener('resize',()=>layout(el));});
UI Patterns
#434CSS + JS
Reveal on Scroll with Threshold
Custom threshold percentage for when scroll-reveal triggers (0–1).
document.querySelectorAll('[data-podcast]').forEach(audio=>{const fill=audio.parentElement?.querySelector<HTMLElement>('[data-podcast-fill]');const time=audio.parentElement?.querySelector('[data-podcast-time]');(audio as HTMLAudioElement).addEventListener('timeupdate',()=>{const a=audio as HTMLAudioElement;if(fill)fill.style.width=(a.currentTime/a.duration*100)+'%';if(time)time.textContent=new Date(a.currentTime*1000).toISOString().substr(14,5);});});
Media
#447JS Only
Undo Last Action
Ctrl+Z undoes the last recorded DOM change via a command stack.
Attribute
data-undoable
JS JavaScript
const stack:Array<{el:HTMLElement,html:string}>=[];document.querySelectorAll('[data-undoable]').forEach(el=>{el.addEventListener('input',()=>stack.push({el:el as HTMLElement,html:(el as HTMLElement).innerHTML}));});document.addEventListener('keydown',e=>{if((e.ctrlKey||e.metaKey)&&e.key==='z'){const last=stack.pop();if(last)last.el.innerHTML=last.html;}});
Interaction
#448CSS + JS
Pinned Tooltip Follow
A tooltip sticks to and follows an element as it animates on screen.
let deferredPrompt:any;window.addEventListener('beforeinstallprompt',e=>{e.preventDefault();deferredPrompt=e;document.querySelector('[data-pwa-install]')?.classList.add('show');});document.querySelector('[data-pwa-install]')?.addEventListener('click',()=>{deferredPrompt?.prompt();});
Misc
#451CSS Only
Slide In from Bottom
Element slides up into view from below on scroll entry.
Attribute
data-animate="slide-bottom"
CSS
[data-animate="slide-bottom"]{opacity:0;transform:translateY(60px);animation:slideFromBottom linear both;animation-timeline:view();animation-range:entry 0% entry 30%;}@keyframes slideFromBottom{to{opacity:1;transform:none;}}
Animations
#452CSS Only
Rubber Band
Element stretches and snaps back like a rubber band on hover.
document.querySelectorAll('[data-shake-invalid]').forEach(inp=>{inp.closest('form')?.addEventListener('submit',e=>{if(!(inp as HTMLInputElement).checkValidity()){e.preventDefault();inp.classList.remove('shake');void (inp as HTMLElement).offsetWidth;inp.classList.add('shake');}});});
Forms
#472CSS + JS
Password Show/Hide Toggle
Eye icon toggles password visibility inside the input field.
document.querySelectorAll('[data-pw-toggle]').forEach(inp=>{const btn=document.createElement('button');btn.setAttribute('data-pw-eye','');btn.type='button';btn.innerHTML='👁';inp.parentElement?.appendChild(btn);btn.addEventListener('click',()=>{(inp as HTMLInputElement).type=(inp as HTMLInputElement).type==='password'?'text':'password';});});
Forms
#473CSS + JS
Keyboard Tab Navigation
Users can switch tabs with arrow keys for full keyboard accessibility.
Adds a PiP button to video elements for floating playback.
Attribute
data-pip
JS JavaScript
document.querySelectorAll('[data-pip]').forEach(video=>{const btn=document.createElement('button');btn.textContent='PiP';btn.addEventListener('click',()=>(video as any).requestPictureInPicture?.());video.parentElement?.appendChild(btn);});
Media
#482JS Only
Fullscreen Image on Click
Any image expands to fullscreen using the Fullscreen API on double-click.
Attribute
data-fs-img
JS JavaScript
document.querySelectorAll('[data-fs-img]').forEach(img=>{img.addEventListener('dblclick',()=>(img as any).requestFullscreen?.());});
Media
#483CSS + JS
Click Counter
Counts and displays how many times an element has been clicked.
(async()=>{const nav=navigator as any;if(!nav.getBattery)return;const b=await nav.getBattery();document.querySelectorAll('[data-battery]').forEach(el=>{const upd=()=>el.textContent=`${Math.round(b.level*100)}% ${b.charging?'⚡':''}`;upd();b.addEventListener('levelchange',upd);b.addEventListener('chargingchange',upd);});})();
Misc
#490JS Only
Screen Wake Lock
Prevents the screen from sleeping while a section is visible.
Attribute
data-wake-lock
JS JavaScript
let wl:any=null;const obs=new IntersectionObserver(async([e])=>{if(e.isIntersecting&&!wl){try{wl=await(navigator as any).wakeLock?.request('screen');}catch{}}else if(!e.isIntersecting&&wl){wl.release();wl=null;}});document.querySelectorAll('[data-wake-lock]').forEach(el=>obs.observe(el));
Misc
#491CSS + JS
Flip Number Ticker
Numbers flip like an airport departure board when value changes.
document.querySelectorAll('[data-flip-ticker]').forEach(el=>{const update=(val:string)=>{el.classList.add('flip');setTimeout(()=>{el.querySelector('span')!.textContent=val;el.classList.remove('flip');},200);};el.dataset.flipFn='';(el as any).update=update;});
Animations
#492JS Only
Bubble Up
Small bubble circles float upward from the bottom of an element.
Converts vertical mouse wheel scroll to horizontal on a container.
Attribute
data-wheel-h
JS JavaScript
document.querySelectorAll('[data-wheel-h]').forEach(el=>{el.addEventListener('wheel',e=>{e.preventDefault();(el as HTMLElement).scrollLeft+=e.deltaY;},{passive:false});});
Scroll
#494CSS + JS
Last Seen Section Badge
Shows a 'Continue reading' badge linking back to the last visited section.
document.querySelectorAll('[data-inline-edit]').forEach(el=>{el.setAttribute('contenteditable','true');el.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();(el as HTMLElement).blur();}});el.addEventListener('blur',()=>el.dispatchEvent(new CustomEvent('inline-save',{detail:el.textContent,bubbles:true})));});
UI Patterns
#496CSS Only
Hover Preview Card
Shows a detailed preview card on hover with image and text.
Defers non-critical JS work until the browser is idle.
Attribute
data-idle-defer
JS JavaScript
document.querySelectorAll('[data-idle-defer]').forEach(el=>{const fn=new Function(el.dataset.idleDefer||'');('requestIdleCallback' in window?(requestIdleCallback as Function):setTimeout)(fn);});
Performance
#500CSS + JS
Fun Fact on Idle
Shows a rotating fun fact after the user is idle for 10 seconds.
const facts=(document.querySelector('[data-idle-fact]') as HTMLElement|null)?.dataset.idleFact?.split('|')||['Did you know? CSS is Turing complete!'];let t:any;const pop=document.querySelector('[data-idle-fact-popup]');const reset=()=>{clearTimeout(t);t=setTimeout(()=>{if(pop){pop.textContent=facts[Math.floor(Math.random()*facts.length)];pop.classList.add('show');setTimeout(()=>pop.classList.remove('show'),5000);}},10000);};['click','keydown','mousemove'].forEach(ev=>document.addEventListener(ev,reset));reset();
Misc
#501CSS Only
Elastic Button Press
Button squashes vertically on press and bounces back.
document.querySelectorAll('[data-sig-pad]').forEach(canvas=>{const c=canvas as HTMLCanvasElement;const ctx=c.getContext('2d')!;let drawing=false;ctx.strokeStyle='#6366f1';ctx.lineWidth=2;ctx.lineCap='round';c.addEventListener('pointerdown',e=>{drawing=true;ctx.beginPath();ctx.moveTo(e.offsetX,e.offsetY);});c.addEventListener('pointermove',e=>{if(!drawing)return;ctx.lineTo(e.offsetX,e.offsetY);ctx.stroke();});c.addEventListener('pointerup',()=>drawing=false);});
Media
#519JS Only
Voice Command
Listens for a voice command and triggers an action.
Attribute
data-voice-cmd
JS JavaScript
document.querySelectorAll('[data-voice-cmd]').forEach(el=>{const SpeechRecognition=(window as any).SpeechRecognition||(window as any).webkitSpeechRecognition;if(!SpeechRecognition)return;const rec=new SpeechRecognition();rec.onresult=e=>{const said=e.results[0][0].transcript.toLowerCase();if(said.includes(el.dataset.voiceCmd||''))el.dispatchEvent(new CustomEvent('voice-triggered'));};el.addEventListener('click',()=>rec.start());});
Interaction
#520CSS + JS
Scroll to Reveal Secret
A hidden message reveals after the user scrolls past a specific element.
document.querySelectorAll('[data-smart-date]').forEach(inp=>{inp.addEventListener('blur',()=>{const d=new Date((inp as HTMLInputElement).value);if(!isNaN(d.getTime()))(inp as HTMLInputElement).value=d.toLocaleDateString();});});
Forms
#529CSS Only
Breakout Hero
Hero section overflows container to hit viewport edges.
Video plays when scrolled into view and pauses when out.
Attribute
data-scroll-video
JS JavaScript
const obs=new IntersectionObserver(es=>es.forEach(e=>{const v=e.target as HTMLVideoElement;e.isIntersecting?v.play().catch(()=>{}):v.pause();}),{threshold:0.4});document.querySelectorAll('[data-scroll-video]').forEach(v=>{(v as HTMLVideoElement).muted=true;obs.observe(v);});
Media
#533JS Only
Wheel Color Picker
Mouse wheel changes hue on a colored element.
Attribute
data-wheel-hue
JS JavaScript
document.querySelectorAll('[data-wheel-hue]').forEach(el=>{let hue=200;el.addEventListener('wheel',e=>{e.preventDefault();hue=(hue+Math.sign(e.deltaY)*5+360)%360;(el as HTMLElement).style.background=`hsl(${hue},80%,60%)`;},{passive:false});});
Interaction
#534CSS Only
CSS Logical Properties
Uses inline-start/end instead of left/right for bidirectional layouts.
Applies tilt/parallax only on mobile via deviceorientation.
Attribute
data-gyro-parallax
JS JavaScript
if(window.DeviceOrientationEvent){window.addEventListener('deviceorientation',e=>{const x=(e.gamma||0)/90*10;const y=(e.beta||0)/90*10;document.querySelectorAll('[data-gyro-parallax]').forEach(el=>(el as HTMLElement).style.transform=`translate(${x}px,${y}px)`);});}
Scroll
#543JS Only
Scroll Direction Lock
Locks scroll direction once movement starts, preventing diagonal scroll.
document.querySelectorAll('[data-json-editor]').forEach(el=>{el.addEventListener('blur',()=>{try{const obj=JSON.parse((el as HTMLTextAreaElement).value);(el as HTMLTextAreaElement).value=JSON.stringify(obj,null,2);el.classList.add('valid');el.classList.remove('error');}catch{el.classList.add('error');el.classList.remove('valid');}});});
Forms
#546JS Only
Render Blocking Detector
Warns in console if any synchronous scripts delay page render.
document.querySelectorAll('[data-ambient]').forEach(img=>{const canvas=document.createElement('canvas');canvas.width=canvas.height=1;canvas.getContext('2d')?.drawImage(img as HTMLImageElement,0,0,1,1);const[r,g,b]=canvas.getContext('2d')?.getImageData(0,0,1,1).data||[99,102,241,255];(img as HTMLElement).style.setProperty('--ambient-color',`rgb(${r},${g},${b})`);});
Color & Theme
#550JS Only
Section Time Tracker
Logs how many seconds a user spends with each section visible.