
CSS Underline Draw Hover Effect
Published
30 hand-coded CSS hover effect patterns for text, buttons, cards, images, navigation, and cursor-tracking — text (underline draw, glitch, letter-spacing expand, gradient reveal, split text, neon glow), button (magnetic liquid, border draw, shimmer, fill wipe, 3D press, ripple), card (3D tilt, flip, glassmorphism, spotlight, slide reveal, stack lift), image (zoom pan, color-channel split, distortion, duotone, curtain reveal, Ken Burns), navigation (underline slide, highlight fill, strikethrough link, inline word swap), and cursor-tracking (dot trail, magnetic pull). 24 demos are pure CSS with zero JS; 6 use 30-60 lines of vanilla JavaScript for pointer-tracking — the composition Nielsen Norman Group interaction-cost research validates for reachable affordances without hurting perceived latency. Scoped under .hv-NN for no-collision pasting, prefers-reduced-motion guarded on every animated effect, framework-agnostic (Tailwind arbitrary values + React / Vue / Next mappings documented per demo).
Related6 CSS Custom Cursor Styles36 CSS Button Hover Effects33 CSS Card Hover Effects20 CSS Image Hover Effects

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published
width, height, top, left, margin, padding, border-width, or font-size forces the browser to recalculate the layout of every affected element AND every descendant, on EVERY frame of the animation — that's 60 layouts per second on a 60Hz display, 120 on a 120Hz one. At 4-16ms of layout cost per frame, the main thread saturates fast and the animation drops to ~30fps. This shows up directly in your INP (Interaction to Next Paint) Core Web Vital score — the metric that replaced FID in March 2024. The 30 demos in this collection all animate transform/opacity exclusively; even color-shift hovers use opacity on a sibling overlay instead of animating background-color (which is paint-only but still has cost). Tools like Lighthouse, WebPageTest, SpeedCurve, Calibre, and Chrome's DevTools Performance panel will flag layout-triggering animations as "Recalculate Style" or "Layout" entries — that's the smoking gun. Replace those properties with their transform equivalents (scale, translate, rotate) and the entries disappear from the flame chart..btn:hover, .btn:focus { ... } means keyboard users (Tab) and screen-reader users get the same visual cue desktop pointer users do. WCAG 2.4.7 requires this anyway. (2) For touch-only devices, use the @media (hover: none) media query to either suppress hover styles entirely or replace them with tap-active styles. The pattern: @media (hover: hover) { .btn:hover { ... } } — hover styles only apply on devices that have a real hover. (3) For touch-active feedback, use :active (fires on touchstart) with a slightly shorter transition than your hover version. iOS double-tap-zoom default kicks in on links — set touch-action: manipulation to disable. The 30 demos in this collection use the @media (hover: hover) and (pointer: fine) pattern so touch devices see only the static appearance, not the broken "sticky hover" state that older patterns leave on tap. Related: iOS Safari, Android Chrome, and Samsung Internet all support @media (hover: hover) since 2017.prefers-reduced-motion: reduce. Roughly 35% of adults experience some level of motion sensitivity (vestibular disorders, migraine, simple preference), and they set this OS-level preference (macOS Accessibility → Display → Reduce motion, Windows Settings → Accessibility → Visual effects → Animation, iOS Accessibility → Motion, Android Accessibility → Remove animations). Every demo in this collection wraps its continuous-animation rules in @media (prefers-reduced-motion: reduce) { .hv-NN__... { transition: none; animation: none; } } so users get the static end-state instead of a fade/slide/scale. Three failure patterns most online tutorials skip: (a) animation rules need explicit animation: none; setting transition: none alone doesn't stop keyframe loops. (b) For effects that DEFINE the interaction (e.g., the underline-draw IS the hover) — replace the motion with an instant state change, not nothing. (c) Test in real OS settings, not just DevTools emulation — older Safari versions had reduced-motion-detection bugs. WCAG 2.3.3 (Animation from Interactions) explicitly recommends this; the EU EAA and US Section 508 audits flag missing reduced-motion guards.pointermove on the element, calculate the cursor position relative to the element's bounding box, and translate that into a CSS custom property the element's transform reads. For 3D tilt: --tx and --ty drive rotateY(var(--tx)) rotateX(var(--ty)). For magnetic: --mx and --my drive translate(var(--mx), var(--my)). Library competitor cost comparison: vanilla-tilt.js ships ~6kb gzipped for what's effectively 40 lines of pointermove logic. react-tilt wraps that in React for ~8kb. tilt.js jQuery plugin is ~10kb. GSAP can do magnetic pulls but the runtime is ~20-30kb depending on which plugins you import. Framer Motion + useMotionValue + useTransform achieves the same effect in React with ~40-50kb of runtime. The 6 JS demos here use requestAnimationFrame throttling so the pointermove handler runs at the screen refresh rate, not the (much higher) raw pointer event rate — important for low-end Android. They also use {passive: true} on the pointermove listener so scroll performance isn't blocked.hover: variant + utilities cover the SIMPLE cases brilliantly: hover:bg-blue-600 hover:scale-105 hover:shadow-lg works for 80% of buttons. Where it breaks down: (a) Multi-element coordinated hover — animating an underline pseudo while the link's color shifts requires the underline to be a child or pseudo, and Tailwind utilities can't target ::before directly without arbitrary-variant syntax (verbose). (b) Pointer-tracking effects — Tailwind has no way to read pointer coordinates; you need JS regardless. (c) Keyframe-based effects (shimmer, neon pulse, ripple expand) — Tailwind 3.4+ added animate-[...] arbitrary values but defining @keyframes still requires CSS. (d) Multi-state choreography — when hover staggers multiple children with delays (Demo 05 Split Text), transition-delay via transition-delay-[100ms] works but the per-child stagger needs nth-child selectors that Tailwind doesn't expose as utilities. For these cases, drop into a @layer components rule with the custom selector / keyframe / nth-child math, then call it with @apply for the parts Tailwind CAN handle. The 30 demos here are written as raw CSS so they drop into any setup (Tailwind, vanilla, CSS Modules, styled-components, emotion, Vue scoped, Astro <style>) — copy the entire .hv-NN block into your stylesheet.will-change: transform applied to dozens of elements at once — promotes them all to GPU layers, exhausting GPU memory, causing per-frame composite cost to balloon. Use will-change only on elements about to be animated; remove it once the animation completes. (c) Massive box-shadow on hover (e.g., box-shadow: 0 0 100px rgba(0,0,0,0.3)) — repaints the entire shadow area on every frame. Solution: use filter: drop-shadow() which is GPU-accelerated, OR pre-render the shadow as a pseudo-element with opacity transition. The 30 demos in this collection: 95+ Performance score on Pixel 5 baseline in Lighthouse mobile profile, all use transform/opacity only, all use will-change sparingly (only on the specific element being animated). Audit your own implementation with Chrome DevTools Performance panel, Lighthouse, or WebPageTest — look for "Layout" or "Recalculate Style" entries during hover.20 hand-coded CSS animated buttons — neon glow, ripple, 3D press, liquid fill, jelly bounce, shine sweep, animated border, moving gradient CTA, text flip, submit success state, add-to-cart progress, download icon, hamburger-to-close, toggle switch, loading spinner inside button, next/prev arrow nav, and ghost button background reveal. Half pure CSS, half lightweight JS for production interactions.
11 pure CSS animation-timeline demos built on the W3C scroll() and view() spec — reading progress bar, view-timeline fade & reveal, shrinking sticky header, stacking cards, horizontal scroll section, parallax hero, container shadow indicators, reverse-scroll columns, text scrim reveal, cover card to sticky header, and image zoom / clip-path wipe. Zero JS, off the main thread, Core Web Vitals safe. Copy-paste ready.
25 hand-coded CSS background animations for SaaS hero sections, developer tool documentation, agency portfolios, gaming and Web3 landing pages, seasonal e-commerce campaigns, and dark-mode dashboards. Covers full-screen gradient shifts, animated mesh gradients, aurora borealis glow, cursor spotlight follow, interactive water ripples, dot grid and diagonal stripe patterns, SVG grain and noise texture overlays, topographic contour lines, layered SVG waves, plasma and lava lamp fluids, bokeh light blur, particle constellation networks, matrix digital rain, synthwave 3D perspective grids, starfield warp speed, CRT scanline overlays, fog and mist drift, morphing organic blobs, floating glassmorphism orbs, halftone dot patterns, starry night skies, floating geometric shapes, rising bubbles, and falling snow. 20 are pure CSS with zero JavaScript; the 5 that need it use vanilla canvas or pointer tracking with no dependencies. Every background animates compositor-only properties so it never blocks the main thread, is scoped under a .bga-NN prefix for no-collision pasting, guards prefers-reduced-motion, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.