
E-Commerce Product Spotlight Card
Published
15 hand-coded CSS 3D tilt hover cards you can copy-paste into any project — e-commerce product spotlight, glassmorphism parallax team card, SaaS pricing tier, holographic NFT collectible with conic-gradient foil, pop-out character mascot, dark tech grid with border-glow-follows-cursor, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and one Pure CSS touch-friendly tilt (no JS, uses :has() + nine invisible hover zones). Every demo uses the same architecture: Vanilla JS writes four CSS custom properties (--rx/--ry for rotation, --mx/--my for glare position); ALL the rendering stays in CSS on the GPU via transform: perspective() rotateX() rotateY() with transform-style: preserve-3d cascading so inner layers float at their own translateZ() depth. Touch and accessibility guards are non-negotiable: every demo gates the tilt behind @media (hover: hover) and (pointer: fine) AND @media (prefers-reduced-motion: no-preference), so touch users and motion-sensitive users always get the fully-styled static card. Modern platform features throughout: oklch() palettes, color-mix() tints, @property-registered custom properties, conic-gradient holo foil, backdrop-filter glassmorphism, :has() for the pure-CSS variant, aspect-ratio for image slots. Scoped under .thc-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related33 CSS Card Hover Effects23 CSS Flip Cards20 CSS Cards

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published
translateZ(), and a specular glare chases the cursor across the surface. Every demo in this collection uses the same architecture: JavaScript does exactly one job — measure the pointer's position and write four CSS custom properties on the card element (--rx/--ry for rotation, --mx/--my for glare position). ALL the visual rendering stays in CSS: transform: perspective(1100px) rotateX(var(--rx)) rotateY(var(--ry)) on the card, plus transform-style: preserve-3d cascading through the child tree so each element can float at its own depth via translateZ(). Because transforms are compositor-friendly, the animation runs on the GPU thread — not the main thread — so it stays 60fps even on mid-tier mobile, doesn't block INP, and doesn't cause layout shift (Core Web Vitals CLS stays 0). Every demo is graded on four axes: (1) MODERN CSS — @property-typed custom properties, oklch() palettes, color-mix() tints, conic-gradient holo foil, backdrop-filter glass, :has() for the Pure CSS variant. (2) TOUCH + ACCESSIBILITY — every demo gates the tilt behind @media (hover: hover) and (pointer: fine) AND @media (prefers-reduced-motion: no-preference), so touch users and motion-sensitive users always get the fully-styled static card. Interactive cards are real <a>/<button> elements or tabindex="0" with visible :focus-visible rings; decorative layers are aria-hidden. (3) PERFORMANCE — transform-only rendering, no layout thrash, and pointer events + getBoundingClientRect() per move (never cached — cached rects break on scroll or resize). (4) DROP-IN PORTABILITY — every demo themes from 3-6 CSS custom properties on the .thc-NN root; retune the palette by changing 3-6 tokens without touching the tilt math.pointermove, get the card's bounding rect once, calculate the pointer's normalized position (0-1 across width and height), map that to rotation degrees with a MAX constant (10-14° reads premium, 20°+ reads playful), and write the result to CSS custom properties. On pointerleave, drop an is-live class that re-arms a slow spring transition and reset the properties to 0. All the visual work — perspective, rotation, per-layer translateZ, glare gradient — lives in CSS. Compared to vanilla-tilt.js: our approach is ~15 lines vs their 800+, ships zero KB of library dependency, uses modern pointer events (works with pen and touch input correctly), and integrates cleanly with your CSS custom-property design tokens. Compared to tilt.js (the jQuery-era version): our approach doesn't require jQuery, uses transform not top/left (compositor-friendly vs layout-thrashing), and honors prefers-reduced-motion out of the box. Compared to react-parallax-tilt: our approach works in vanilla HTML, any framework, or SSR — no hydration required, no useState/useEffect dance, no client:only directive.if (!matchMedia('(hover: hover) and (pointer: fine)').matches) return;. Touch users have no pointer to hover with; forcing tilt via touchmove would either fight the browser's native scroll or feel jittery. Touch users get the fully-styled STATIC card exactly as it renders at rest — same background, same layers, same button. (2) REDUCED-MOTION GUARD — if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;. Vestibular-disorder users have opted out of motion at the OS level (Windows Reduce Motion, macOS Reduce Motion, iOS Reduce Motion); WCAG 2.3.3 requires we honor that. Same outcome as touch users — static, fully styled card. (3) FOCUS GUARD — every interactive card is a real <button>, <a>, or tabindex="0" with a visible :focus-visible outline. Screen readers announce the card name via aria-label; decorative layers (glare overlays, badges, product mockups) carry aria-hidden="true" so they don't clutter the accessibility tree. This trio is not optional — a tilt effect that only works with a mouse-and-fine-pointer and traps focus is a WCAG 2.1 conformance failure and a bad user experience for the 25-40% of your audience on touch devices. Every demo in this collection ships all three guards by default.conic-gradient painted with oklch() stops that runs the full spectrum, positioned absolutely inside the card with pointer-events: none; mix-blend-mode: color-dodge; opacity: 0.6. The conic-gradient's from angle is bound to a @property-registered custom property so it can animate smoothly, and the whole overlay's position tracks the same --mx/--my variables as the glare. When the card tilts, the holographic rainbow shifts across the surface exactly like a real Pokémon holo card catches light from different angles — because the physics is analogous (angle-dependent reflection). The trick to making it feel expensive rather than tacky: keep the opacity under 0.7, use mix-blend-mode: color-dodge (not screen or overlay — dodge preserves the underlying palette better), and register the conic angle with @property --holo-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg } so animations interpolate smoothly. This exact recipe is what OpenSea NFT card previews, web3 collectible marketplaces, and the growing category of digital trading-card sites use. Baseline requirements: @property (Chrome 85+, Safari 16.4+, Firefox 128+), mix-blend-mode universal, conic-gradient universal since 2020.scale(1.05) at rest plus a stronger accent glow — the middle tier that most SaaS wants users to pick. (3) BILLING-CADENCE toggle (monthly/annual) as a real <input type="checkbox"> or <input type="radio"> pair with :checked ~ .prices CSS selector sibling combinator — the price updates without JavaScript. Every price is font-variant-numeric: tabular-nums so digits don't jiggle on toggle. Feature-list bullets use display: grid; grid-template-columns: 20px 1fr for icon + text alignment (subgrid on newer browsers for cross-tier alignment). The CTA button on each tier translates to translateZ(30-40px) so it floats above the card face — the visual affordance that says 'this is the primary action.' This is the exact pattern Stripe (stripe.com/pricing), Linear (linear.app/pricing), Vercel (vercel.com/pricing), Notion (notion.so/pricing), and Airtable ship. Combine with the shimmer skeleton loader pattern (CSS Shimmer Skeleton Cards Demo 04) for the loading state while pricing data fetches from your backend.card.style.setProperty('--rx', value + 'deg'). CSS reads them via transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)). The 0deg fallback in var() means the card renders correctly even before JS attaches (no flash of un-tilted content). To make the transitions feel physical rather than robotic: two CSS classes with different transition durations. Default: transition: transform 0.6s cubic-bezier(0.2, 0.9, 0.3, 1.15) — the springy return-to-rest curve when the pointer leaves. Active (class is-live, added during pointer movement): transition: transform 0.08s linear — near-instant so the tilt feels responsive to the pointer. The JS toggles is-live on pointermove and removes it on pointerleave. Optional upgrade: register the properties with @property --rx { syntax: '<angle>'; inherits: false; initial-value: 0deg } — this makes them animatable via @keyframes too, and prevents 'accidentally set to a string' bugs by making the browser type-check. Baseline: @property is Baseline widely-available since 2024 (Chrome 85, Safari 16.4, Firefox 128).::before pseudo-element positioned inset: -1px on the card, with background: radial-gradient(150px circle at var(--mx) var(--my), oklch(0.72 0.15 160), transparent 50%). The gradient is a small bright disc positioned at the pointer's location (bound to the same --mx/--my variables as the glare), rendered BEHIND the card face via z-index: -1 or ::before stacking order. Because the gradient is a fixed-size disc following the pointer, the visible portion looks exactly like a light source ILLUMINATING the card's edge from wherever the cursor is. Combined with a backdrop-filter: blur(20px) on the card face, the glow reads as a real physical light interacting with a glass surface. This is the pattern that Linear, Vercel, and Framer all ship on their dark-themed feature cards. Two gotchas: (1) the ::before element MUST have pointer-events: none or it will eat clicks; (2) the color-mix tint on the gradient stop should stay under 60% saturation — a 100%-saturated glow reads as neon and cheapens the effect.<div> elements positioned absolutely over the card face) and the :has() selector to detect which zone the cursor is in. Each zone triggers a different pre-defined tilt pose: top-left zone rotates +12° X / +12° Y, center zone stays flat, bottom-right rotates -12° X / -12° Y, etc. The transitions blend between poses smoothly, so the card appears to follow the cursor even though it's actually snapping between 9 fixed states. Combined with a longer transition duration (~200ms) the illusion is nearly indistinguishable from JS-driven continuous tilt. Baseline requirements: :has() is Baseline widely-available (Chrome 105+, Safari 15.4+, Firefox 121+). Trade-offs vs the JS approach: (a) resolution is limited to 9 poses per card — smooth but not pixel-perfect pointer tracking; (b) no glare position tracking (you can add a slower CSS-only radial-gradient shift via @keyframes but it won't chase the cursor); (c) works with keyboard focus too if you replace the hover zones with tabindex elements (bonus accessibility win). For copy-paste sites, gaming portfolios, and any surface where 'no framework, no library, no build step' is the constraint — this is the right recipe.useEffect (React), onMount (Svelte / Vue), or <script> tag (Astro). The pattern that keeps it framework-clean: the entire tilt logic is scoped by document.querySelector('.thc-NN .thc-NN__card'), so if you rename the class prefix to match your framework's conventions (BEM, CSS Modules, styled-components), just update the selector. React specifics: wrap the JS in a useEffect(() => {...}, []) and add cleanup in the return to remove event listeners. Next.js App Router: works in Client Components ('use client' directive) — the demo won't work in Server Components because there's no window/document. Svelte / SvelteKit: use bind:this={cardEl} instead of querySelector for the ref. Astro: works out of the box with a <script> tag; the tilt initialization runs after hydration. Because the effect is pure Vanilla JS with no dependencies, you avoid the class of 'tilt library conflicts with framework SSR' bugs that plague vanilla-tilt.js, react-parallax-tilt, and Framer Motion tilt patterns.24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.
22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.
25 hand-coded CSS button groups: segmented controls, split buttons, pill radios, toggle groups, filter chips, floating action buttons, pricing toggles, Web3 wallet-connect grids, AI model pickers, icon toolbars — with Popover API, offset-path, View Transitions, @property, :has(), and every modern CSS primitive. Zero dependencies vs Radix/shadcn/Aceternity, WCAG 2.2 AA accessible, MIT licensed.