ZYVOPMulti-Platform Sync
SeriesAI NewsWhy ZyVOPJoin Discord
LoginGet Started
ZYVOPMulti-Platform Sync

The Developer Publishing Hub. Write once, publish everywhere, and make your work citation-ready with built-in SEO, AEO, and GEO discovery support. Zero reader paywalls.

Content

  • Categories
  • Tags
  • Badges
  • Leaderboard
  • Write Article
  • Newsletter

Company

  • About Us
  • Why ZyVOP
  • Developer API & CLI
  • Write for Us
  • Contact

Connect

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DMCA Policy
  • Code of Conduct

ยฉ 2026 ZyVOP. Developer Publishing Hub.

Zero paywalls ยท Full content ownership
All systems operational
HomeSelection Through the Looking Glass: A 3D Stack Component for Mantine

Selection Through the Looking Glass: A 3D Stack Component for Mantine

Giovambattista Fazioli
Giovambattista Fazioli
Senior Full-stack Engineer, Lead DeveloperSupport
September 4, 2026
4 min read
Selection Through the Looking Glass: A 3D Stack Component for Mantine
#webdev#TypeScript#webcomponents#react

A Mantine component that turns flat item selection into a spatial, 3D card-stack browsing experience โ€” with keyboard, wheel, touch, and click navigation built in.

Introduction

Remember the first time you used macOS Time Machine? That moment when your desktop peeled away into an infinite corridor of snapshots, and navigating through time suddenly felt physical. That spatial metaphor stuck with us. mantine-depth-select brings that same sense of depth to any selection UI in your React application โ€” pricing plans, document versions, onboarding steps, or anything else where browsing through stacked content beats scrolling a flat list. It's a single component with zero extra dependencies, built entirely on Mantine 8's Styles API.

mantine Depth Select

What is DepthSelect?

DepthSelect renders an array of items as a 3D stack of cards. The frontmost card is the active selection; cards behind it recede with progressive scale, vertical offset, opacity, and blur โ€” all configurable per-level. When the user navigates (via keyboard, mouse wheel, trackpad swipe, click, or arrow controls), cards animate smoothly in and out of the stack with CSS transitions.

The component follows Mantine's compound component pattern. Built-in arrow controls appear by default, but you can hide them and wire up your own UI through controlled value/onChange. It supports both string and numeric values, loop mode, and respects prefers-reduced-motion.

โœจ Key Features

3D Perspective Stack

Each card in the stack receives progressive CSS transforms based on its depth. Four props control the effect:

<DepthSelectdata={items}scaleStep={0.1}       // scale reduction per level (default)translateYStep={30}    // vertical offset in px per levelopacityStep={0.15}     // opacity reduction per levelblurStep={1}           // blur in px per levelw={400}h={300}/>

Enter fullscreen mode Exit fullscreen mode

Cards that exit the stack (when navigating forward) animate toward the viewer with a scale-up and fade-out. Cards entering from behind animate in from the deepest visible position.

Multi-Input Navigation

Every common input method works out of the box:

  • Keyboard: ArrowUp/ArrowDown to step through, Home/End to jump

  • Mouse wheel / Trackpad: Scroll over the component to navigate (page scroll is blocked automatically). Disable with withScrollNavigation={false}

  • Touch: Vertical swipe gestures for mobile

  • Click: Click the second card (depth 1) to advance

  • Controls: Built-in arrow buttons with optional label

Built-in Controls with controlsProps

The default controls sit to the right of the stack. You can reposition them, add a label, set a fixed width, change alignment, or swap the icons โ€” all through a single controlsProps object:

<DepthSelectdata={items}controlsPosition="left"controlsProps={{
    w: 100,
    justify: 'end',
    labelFormatter: (item) => `Step ${item.value}`,
    upIcon: <IconChevronUp size={16} />,
    downIcon: <IconChevronDown size={16} />,}}w={400}h={300}/>

Enter fullscreen mode Exit fullscreen mode

Compound Component Pattern

Need full layout control? Set withControls={false} and use DepthSelect.Controls as a child:

<DepthSelect data={items} withControls={false} w={400} h={300}><DepthSelect.Controls labelFormatter={(item) => String(item.value)} /></DepthSelect>

Enter fullscreen mode Exit fullscreen mode

Or skip the built-in controls entirely and build your own navigation with value and onChange.

Loop Mode

Enable loop to let navigation wrap from the last item back to the first (and vice versa). The arrow controls never show a disabled state in loop mode:

<DepthSelect data={items} loop w={400} h={200} />

Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ Getting Started

npm install @gfazioli/mantine-depth-select

Enter fullscreen mode Exit fullscreen mode

Import the styles at the root of your application:

import '@gfazioli/mantine-depth-select/styles.css';

Enter fullscreen mode Exit fullscreen mode

Minimal example:

import { Card, Text } from '@mantine/core';import { DepthSelect, DepthSelectItem } from '@gfazioli/mantine-depth-select';

const data: DepthSelectItem[] = [{ value: 'first', view: <Card p="lg" withBorder h="100%"><Text>First item</Text></Card> },{ value: 'second', view: <Card p="lg" withBorder h="100%"><Text>Second item</Text></Card> },{ value: 'third', view: <Card p="lg" withBorder h="100%"><Text>Third item</Text></Card> },];

function Demo() {return <DepthSelect data={data} w={400} h={200} />;}

Enter fullscreen mode Exit fullscreen mode

The w and h props define the area where cards live. Cards should use h="100%" to fill the available height.

Use Cases

Props & API

DepthSelect

  • data โ€” DepthSelectItem[] โ€” Array of { value, view } objects. value can be string or number, view is any ReactNode

  • value / defaultValue / onChange โ€” Standard controlled/uncontrolled pattern

  • w / h โ€” Dimensions of the card area (passed to the inner stack container)

  • visibleCards โ€” Number of cards visible in the stack (default: 4)

  • withControls โ€” Show built-in arrow controls (default: true)

  • controlsPosition โ€” "right" (default) or "left"

  • controlsProps โ€” All DepthSelectControlsProps passed to the built-in controls

  • loop โ€” Enable wrap-around navigation (default: false)

  • withScrollNavigation โ€” Enable mouse wheel / trackpad navigation (default: true)

  • transitionDuration โ€” Animation duration in ms (default: 400)

  • scaleStep / translateYStep / opacityStep / blurStep โ€” Per-level depth effect parameters

DepthSelect.Controls

  • labelFormatter โ€” (item: DepthSelectItem) => ReactNode โ€” Custom label between arrows

  • upIcon / downIcon โ€” Custom icons for the arrow buttons

  • justify โ€” Vertical alignment: "start", "center" (default), "end"

  • Plus all Mantine BoxProps (w, h, style, etc.)

๐ŸŽจ Styles API

Selectors: root, stack, card, controls, controlUp, controlDown, controlLabel

Data attributes:

  • data-active โ€” Frontmost (selected) card

  • data-depth="N" โ€” Card depth level (0 = front)

  • data-exited โ€” Card that has animated out

  • data-disabled โ€” Disabled control button

  • data-controls-position โ€” "right" or "left" on root

CSS variables: --ds-transition-duration, --ds-scale-step, --ds-translate-y-step, --ds-opacity-step, --ds-blur-step, --ds-visible-cards

โšก Performance

  • Render window: Only activeIndex - 1 through activeIndex + visibleCards + 1 are in the DOM. A dataset of 100 items renders ~6 nodes

  • Memoized styles: Card CSS is computed once per navigation change via useMemo

  • Targeted will-change: Only visible cards get GPU layer promotion; hidden cards use will-change: auto

  • Non-passive wheel listener: Blocks page scroll without layout thrashing

  • prefers-reduced-motion: Transitions are disabled automatically for users who prefer reduced motion

Live Demo & Documentation

Explore interactive demos โ€” including a configurator with live props, rich card examples, pricing plan selector, version history browser, onboarding wizard, and more:

gfazioli.github.io/mantine-depth-select

Links

  • ๐Ÿ“ฆ npm: @gfazioli/mantine-depth-select

  • ๐Ÿ“– Docs: gfazioli.github.io/mantine-depth-select

  • ๐Ÿ”— GitHub: github.com/gfazioli/mantine-depth-select

  • ๐Ÿงฉ Mantine Extensions: mantine-extensions.vercel.app

Comments (0)

Login to post a comment.

Giovambattista Fazioli
Giovambattista Fazioli

Senior Full-stack Engineer, Lead Developer

Italian Senior Full-stack Engineer and Lead Developer on the Cloud team at Namecheap. I build developer tools, React/Mantine UI components and native macOS apps โ€” mostly open source. I work across TypeScript, Next.js, Go and SwiftUI, and I've been coding since the Commodore/Assembly days. Creator of WP Bones and 25+ Mantine extensions, and maintainer of the Undolog open-source studio.

Support
Subscribe to Giovambattista Fazioli's Newsletter

More from Giovambattista Fazioli

View profile

FinderGit 0.29.0 โ€” open a commit like a folder

Thereโ€™s a moment, reading a commit list, where you want to know one small thing: which files did that one touch?

3 minSep 4

Netfox 0.18.0 โ€” is it my connection, or the internet?

There is a question that comes up every time something feels slow, and until now Netfox could not...

3 minSep 4

Mantine Book โ€” Grab Any Edge, Turn Any Page

A realistic iBooks-style book for React built on Mantine: stack two-sided pages and turn them by...

5 minSep 4

Netfox โ€” The macOS app that never loses track of your network

I built Netfox because every existing answer to "what's actually on my network?" annoyed me. The...

5 minSep 4

Audio That Sees Itself

A Mantine-native audio player for React with waveform visualisation and a live spectrum analyser,...

5 minSep 4