{"schemaVersion":"1.0","type":"Article","slug":"mantine-parallax-v3-from-tilt-card-to-interactive-card-system-ukjo4","url":"https://api.zyvop.com/mantine-parallax-v3-from-tilt-card-to-interactive-card-system-ukjo4","title":"Mantine Parallax v3 — From Tilt Card to Interactive Card System","subtitle":null,"tldr":"Spring physics, gyroscope, glare effects, compound layers, keyboard accessibility, and a 60fps...","keywords":["performance","react","showdev","ui"],"entities":["Giovambattista Fazioli","Senior Full-stack Engineer, Lead Developer","performance","react","showdev","ui","ZyVOP"],"keyTakeaways":["Spring physics, gyroscope, glare effects, compound layers, keyboard accessibility, and a 60fps performance rewrite — all in one major release.","Introduction Version 3.0.0 of @gfazioli/mantine-parallax is the biggest update since the package was first published.","What started as a simple Apple TV-style tilt card has evolved into a full-featured interactive card system with physically-based animations, mobile device support, and first-class accessibility."],"headings":["Introduction","What's New","Parallax.Layer — Compound Component for Multi-Depth Parallax","Spring Physics Animation","Glare Reflection Effect","Dynamic Shadow Effect","Gyroscope Support","Keyboard Accessibility","Touch Support","Mantine radius Prop","Rotation Control Props","Transition Customization","onRotationChange Callback","prefers-reduced-motion Support","Performance Rewrite","Bug Fixes","Breaking Changes","1. New glare style selector","2. New --parallax-radius CSS variable","3. New exports and factory type changes","Migration from contentParallax to Parallax.Layer","New Props at a Glance","Getting Started","Links"],"outboundLinks":["https://gfazioli.github.io/mantine-parallax/","https://www.npmjs.com/package/@gfazioli/mantine-parallax","https://github.com/gfazioli/mantine-parallax","https://mantine-extensions.vercel.app/"],"contentText":"Spring physics, gyroscope, glare effects, compound layers, keyboard accessibility, and a 60fps performance rewrite — all in one major release. Introduction Version 3.0.0 of @gfazioli/mantine-parallax is the biggest update since the package was first published. What started as a simple Apple TV-style tilt card has evolved into a full-featured interactive card system with physically-based animations, mobile device support, and first-class accessibility. Under the hood, the rendering pipeline has been completely rewritten to deliver smooth 60fps updates with zero unnecessary React re-renders. This release adds 13 new features, fixes 5 bugs, and includes 4 performance/architecture improvements — while staying fully compatible with Mantine 8.x and React 19. What's New Parallax.Layer — Compound Component for Multi-Depth Parallax The new Parallax.Layer compound component replaces the legacy contentParallax + cloneElement approach. Each layer moves independently based on a depth multiplier, giving you precise control over parallax intensity per element. &lt;Parallax&gt;&lt;Parallax.Layer depth={1}&gt; &lt;Text&gt;Background — subtle movement&lt;/Text&gt;&lt;/Parallax.Layer&gt;&lt;Parallax.Layer depth={3}&gt; &lt;Title&gt;Foreground — dramatic movement&lt;/Title&gt;&lt;/Parallax.Layer&gt;&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Layers automatically consume rotation state via React Context — no prop drilling, no cloneElement magic. The legacy contentParallax prop still works but Parallax.Layer is the recommended approach going forward. Spring Physics Animation Enable physically-based movement with overshoot and oscillation using a damped harmonic oscillator. The card feels alive — it bounces, settles, and responds organically to cursor movement. &lt;ParallaxspringEffectspringStiffness={150} // higher = snappierspringDamping={12} // higher = less bounce&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Spring parameters can be changed mid-animation — new values take effect immediately with no residual momentum from previous settings. Glare Reflection Effect A glare band follows the cursor across the card surface, simulating light reflecting off a glossy surface. &lt;ParallaxglareEffectglareColor=\"rgba(255, 255, 255, 0.4)\"glareMaxOpacity={0.4}glareSize={30}glareOverlay&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Dynamic Shadow Effect A shadow that shifts opposite to the card rotation, creating the illusion of a light source from above. The shadow updates at 60fps during hover and smoothly transitions back to zero on leave. &lt;ParallaxshadowEffectshadowColor=\"rgba(0, 0, 0, 0.4)\"shadowBlur={30}shadowOffset={0.8}&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Gyroscope Support On mobile devices, the card tilts based on physical device orientation via the DeviceOrientation API. On iOS 13+, permission is requested on the first user interaction — with built-in guards against duplicate permission prompts. &lt;ParallaxgyroscopeEnabledgyroscopeSensitivity={1}&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Keyboard Accessibility Arrow keys tilt the card, Escape resets it. When enabled, the component adds proper ARIA attributes (tabIndex, role=\"group\", aria-roledescription, aria-label) for screen reader support. &lt;ParallaxkeyboardEnabledkeyboardStep={5} // degrees per key press&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Touch Support Touch interactions are enabled by default. The card responds to finger movement on mobile, with touch-action: none on the outer container to prevent scroll interference. &lt;Parallax touchEnabled&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Mantine radius Prop Standard Mantine radius prop for controlling border radius via the --parallax-radius CSS variable. Supports theme tokens, CSS strings, or pixel numbers. &lt;Parallax radius=\"md\"&gt;...&lt;/Parallax&gt;&lt;Parallax radius={16}&gt;...&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Rotation Control Props Fine-tune how the card responds to interaction: &lt;ParallaxresetOnLeave={false} // card holds its tilt after mouse leaveinvertRotation // card tilts away from cursormaxRotation={15} // clamp rotation to ±15 degreeshoverScale={1.05} // subtle scale-up on hover/&gt; Enter fullscreen mode Exit fullscreen mode Transition Customization Control the transition timing for enter/leave animations: &lt;ParallaxtransitionDuration={300} // mstransitionEasing=\"ease-out\" // any CSS easing function/&gt; Enter fullscreen mode Exit fullscreen mode onRotationChange Callback Subscribe to rotation changes for building synchronized UI — debug overlays, companion shadows, linked animations: &lt;ParallaxonRotationChange={({ rotateX, rotateY, isHovering }) =&gt; { console.log(`Tilt: ${rotateX.toFixed(1)}° × ${rotateY.toFixed(1)}°`);}}&gt;{children}&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode prefers-reduced-motion Support The component automatically respects the OS-level reduced motion setting. All transitions, hover effects, spring animations, and touch/gyroscope interactions are disabled when active — no extra configuration needed. Performance Rewrite The internal architecture was rewritten from the ground up: Native events + requestAnimationFrame: Replaced the useMouse hook (which triggered a React re-render on every pixel of mouse movement) with native onMouseMove/onTouchMove events throttled via requestAnimationFrame. Rotation updates are batched at 60fps with zero React re-renders during interaction. GPU-friendly shadow updates: box-shadow CSS transitions — which are not GPU-accelerated and cause expensive repaints — are no longer applied during active hover. The shadow updates directly via RAF at 60fps. The CSS transition is only used on mouse leave for a smooth fade-out. Mid-hover deactivation: If disabled or prefers-reduced-motion changes to true while the card is being hovered, the component now properly deactivates instead of continuing to process events. Bug Fixes Light effect persistence: The light gradient overlay no longer disappears from the DOM after mouse leave, preventing a flash when re-entering the card. Background position conflict: Setting backgroundPosition without a backgroundImage no longer causes CSS shorthand/longhand conflicts with Mantine's bg prop in React 19. Shadow during spring return: The dynamic shadow now follows the card rotation during spring return animation instead of snapping to zero instantly. Spring parameter hot-swap: Changing springStiffness or springDamping mid-animation uses the new values immediately and resets accumulated velocity. Gyroscope permission dedup: Rapid taps no longer trigger multiple permission prompts or register duplicate deviceorientation listeners. Breaking Changes 1. New glare style selector ParallaxStylesNames now includes 'glare' as a fourth selector. If you have exhaustive classNames or styles overrides: // Before&lt;Parallax classNames={{ root: '...', content: '...', light: '...' }} /&gt; // After — add the glare key&lt;Parallax classNames={{ root: '...', content: '...', light: '...', glare: '...' }} /&gt; Enter fullscreen mode Exit fullscreen mode 2. New --parallax-radius CSS variable The root element's border-radius is now controlled by a CSS variable. If you were overriding border-radius via styles, the CSS variable takes precedence — use the radius prop instead: // Before&lt;Parallax style={{ borderRadius: 16 }}&gt; // After &lt;Parallax radius={16}&gt; Enter fullscreen mode Exit fullscreen mode 3. New exports and factory type changes ParallaxFactory now includes vars (ParallaxCssVariables) and staticComponents ({ Layer: typeof ParallaxLayer }). New exports: ParallaxLayer, ParallaxLayerProps, ParallaxProvider, useParallaxContext, ParallaxContextValue, ParallaxCssVariables. Migration from contentParallax to Parallax.Layer The legacy contentParallax prop still works, but Parallax.Layer is the recommended approach: // Before (legacy)&lt;Parallax contentParallax contentParallaxDistance={2}&gt;&lt;div&gt;Child 1&lt;/div&gt;&lt;div&gt;Child 2&lt;/div&gt;&lt;/Parallax&gt; // After (recommended)&lt;Parallax&gt;&lt;Parallax.Layer depth={2}&gt; &lt;div&gt;Child 1&lt;/div&gt;&lt;/Parallax.Layer&gt;&lt;Parallax.Layer depth={4}&gt; &lt;div&gt;Child 2&lt;/div&gt;&lt;/Parallax.Layer&gt;&lt;/Parallax&gt; Enter fullscreen mode Exit fullscreen mode Parallax.Layer gives you explicit control over each element's depth and works with any children — no cloneElement limitations. New Props at a Glance Prop Type Default Description springEffect boolean false Enable spring physics animation springStiffness number 150 Spring snappiness springDamping number 12 Spring bounce reduction gyroscopeEnabled boolean false Enable device orientation tilt gyroscopeSensitivity number 1 Gyroscope rotation multiplier keyboardEnabled boolean false Enable arrow key interaction keyboardStep number 5 Degrees per key press shadowEffect boolean false Enable dynamic shadow shadowColor MantineColor 'rgba(0,0,0,0.4)' Shadow color shadowBlur number 30 Shadow blur radius shadowOffset number 0.8 Shadow offset multiplier glareEffect boolean false Enable glare reflection glareColor MantineColor 'rgba(255,255,255,0.4)' Glare color glareMaxOpacity number 0.4 Maximum glare opacity glareSize number 30 Glare band size glareOverlay boolean true Render glare above content radius MantineRadius — Border radius (theme tokens or px) touchEnabled boolean true Enable touch interactions hoverScale number 1 Scale factor on hover resetOnLeave boolean true Reset rotation on leave invertRotation boolean false Tilt away from cursor maxRotation number — Clamp rotation degrees transitionDuration number 300 Transition time in ms transitionEasing string 'ease-out' CSS easing function onRotationChange function — Rotation change callback Getting Started # Install or update npm install @gfazioli/mantine-parallax@3 # or yarn add @gfazioli/mantine-parallax@3 Enter fullscreen mode Exit fullscreen mode Import the styles in your app entry point: import '@gfazioli/mantine-parallax/styles.css'; Enter fullscreen mode Exit fullscreen mode Basic usage with the new features: import { Parallax } from '@gfazioli/mantine-parallax'; function InteractiveCard() {return ( &lt;Parallax radius=\"md\" springEffect glareEffect shadowEffect p={24} bg=\"dark.7\" &gt; &lt;Parallax.Layer depth={1}&gt; &lt;Text c=\"dimmed\"&gt;Background layer&lt;/Text&gt; &lt;/Parallax.Layer&gt; &lt;Parallax.Layer depth={3}&gt; &lt;Title c=\"white\"&gt;Foreground layer&lt;/Title&gt; &lt;/Parallax.Layer&gt; &lt;/Parallax&gt;);} Enter fullscreen mode Exit fullscreen mode Links npm package Documentation &amp; Live Demos GitHub Repository Mantine Extensions Hub","contentHash":"sha256:b0f1c11c24734d5952a7dc8191bf3850ceb13f05e69aa1e7f97ba07f6cce3997","authorName":"Giovambattista Fazioli","authorUrl":"https://api.zyvop.com/author/giovambattista","authorSameAs":["https://gfazioli.github.io","https://github.com/gfazioli","https://x.com/gfazioli","https://www.linkedin.com/in/giovambattistafazioli/"],"category":null,"tags":["performance","react","showdev","ui"],"audience":"Readers researching performance","tone":"Professional, senior full-stack engineer, lead developer perspective","readingTimeMinutes":7,"wordCount":1486,"faqs":null,"primaryTopic":"performance","publishedAt":"2026-09-04T09:24:43.730Z","updatedAt":"2026-09-04T09:24:43.730Z","canonicalUrl":"https://dev.to/undolog/mantine-parallax-v3-from-tilt-card-to-interactive-card-system-5d7o"}