DEV Community

Cover image for Mantine Flip - Compound Components, Touch Gestures, Spring Physics, and More
Giovambattista Fazioli
Giovambattista Fazioli

Posted on

Mantine Flip - Compound Components, Touch Gestures, Spring Physics, and More

The biggest update yet: seven new features, zero breaking changes, and a much better experience on mobile and for accessibility.

Introduction

@gfazioli/mantine-flip is a Mantine component that turns any two views into a smooth 3D flip card — perfect for settings panels, sign-in/sign-up toggles, credit card UIs, and widget grids. This release brings compound components for a clearer API, touch swipe support for mobile, a spring easing curve for natural-feeling animations, lazy rendering for performance, and proper accessibility — all while staying fully backward-compatible with existing code.

What's New

Compound Components: Flip.Front and Flip.Back

Until now, Flip relied on child order to distinguish the front from the back face. That worked, but it wasn't immediately obvious which child was which. You can now use named compound components for a more explicit, self-documenting API:

<Flip h={200} w={400}>
  <Flip.Front>
    <Paper p="lg" withBorder>
      <Text>Front face</Text>
      <Flip.Target>
        <Button>Show back</Button>
      </Flip.Target>
    </Paper>
  </Flip.Front>
  <Flip.Back>
    <Paper p="lg" withBorder>
      <Text>Back face</Text>
      <Flip.Target>
        <Button>Show front</Button>
      </Flip.Target>
    </Paper>
  </Flip.Back>
</Flip>
Enter fullscreen mode Exit fullscreen mode

The classic two-children approach still works exactly as before — no migration required.

Touch Swipe Support

Flip cards are a natural fit for mobile, and now they behave like it. Enable swipeable to let users flip the card with a swipe gesture. The swipe direction automatically matches the direction prop, and you can fine-tune sensitivity with swipeThreshold:

<Flip swipeable swipeThreshold={60} direction="horizontal">
  {/* ... */}
</Flip>
Enter fullscreen mode Exit fullscreen mode

The implementation uses passive touch event listeners for zero scroll-blocking overhead.

Spring Easing

CSS transitions don't have to feel mechanical. Set easing="spring" and the flip gains a physics-based bounce powered by a CSS linear() curve — no JavaScript animation library needed:

<Flip easing="spring">
  {/* ... */}
</Flip>
Enter fullscreen mode Exit fullscreen mode

The spring curve is carefully tuned: a quick overshoot followed by a gentle settle, giving the flip a tactile, almost physical quality. Compare it side by side with the default ease-in-out in the live docs.

Lazy Back Rendering

If you're rendering a grid of flip cards, the back faces are invisible but still in the DOM — potentially holding heavy content like forms, images, or data tables. The new lazyBack prop defers back-face rendering until the user actually flips the card for the first time:

<Flip lazyBack>
  {/* Front: always rendered */}
  {/* Back: rendered only after the first flip */}
</Flip>
Enter fullscreen mode Exit fullscreen mode

This can significantly reduce initial DOM size and improve time-to-interactive in card-heavy layouts.

Disabled State

Need to temporarily prevent flipping? The disabled prop blocks all interactions — Flip.Target clicks and programmatic toggleFlip calls are both ignored:

<Flip disabled>
  {/* ... */}
</Flip>
Enter fullscreen mode Exit fullscreen mode

Flip.Target automatically receives data-disabled and aria-disabled attributes, so you can style the disabled state with plain CSS:

[data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
Enter fullscreen mode Exit fullscreen mode

onTransitionEnd Callback

Know exactly when the flip animation finishes. The callback fires once the flip container's own CSS transform transition completes (bubbled transitions from children are ignored) — useful for focusing an input on the back face, fetching data, or chaining animations:

<Flip onTransitionEnd={() => backInputRef.current?.focus()}>
  {/* ... */}
</Flip>
Enter fullscreen mode Exit fullscreen mode

Expanded Type Exports

FlipProps, FlipTargetProps, FlipFrontProps, and FlipBackProps are now exported directly from the package, making it easier to type wrapper components and custom abstractions.

Bug Fixes

CSS Variable Fallbacks Fixed

CSS fallback values in var() expressions were incorrectly quoted (e.g., var(--flip-perspective, '1000px')). Quoted fallbacks are invalid CSS and were silently ignored in some browsers, causing the component to render with no perspective or transition. All fallbacks are now unquoted and valid.

Direction Change No Longer Desyncs State

Changing direction, directionFlipIn, or directionFlipOut at runtime previously caused several edge cases: the visual rotation and logical flipped state could go out of sync, and changing direction while not flipped could block subsequent flips. All direction-change edge cases are now handled correctly, including proper behavior in controlled mode (where the parent owns the flipped prop).

Controlled Mode + lazyBack Fixed

Using <Flip flipped={true} lazyBack> on initial render left the back face blank. The component now correctly initializes the lazy-back ref from the effective flipped value.

Safari Backface Bleed-Through

Added -webkit-backface-visibility: hidden to fix the back face showing through on Safari and older WebKit browsers. Also added transform-style: preserve-3d on the root element to prevent 3D flattening in certain layout contexts.

Improvements

Accessibility

The component is now screen-reader friendly out of the box:

  • aria-live="polite" on the root announces flip state changes
  • Front/back faces use the inert attribute to control focusability — the non-visible face becomes completely non-interactive, avoiding the common aria-hidden warning when a focused element is inside a hidden container
  • Flip.Target renders aria-disabled when the flip is disabled

Performance

  • Swipe touch listeners use a stable ref and are only re-attached when swipeable, direction, or swipeThreshold change — not on every flip
  • will-change: transform on the flip container hints the browser to promote the element to its own compositing layer
  • lazyBack (see above) reduces initial DOM weight for card grids — and disabling it at runtime immediately mounts the back face

Interactive Configurator

The docs configurator now uses a numeric slider for the perspective control (range 0–5000, step 100) instead of a plain text input, and includes the new "Spring" option in the easing dropdown. Try it live to feel the difference each parameter makes.

New Props at a Glance

Prop Type Default Description
disabled boolean false Blocks all flip interactions
lazyBack boolean false Defers back-face rendering until first flip
swipeable boolean false Enables touch swipe gestures
swipeThreshold number 50 Minimum swipe distance (px) to trigger flip
easing `string \ 'spring'` 'ease-in-out'
onTransitionEnd () => void Called when flip animation completes

Installation & Update

# Install
npm install @gfazioli/mantine-flip
# or
yarn add @gfazioli/mantine-flip
Enter fullscreen mode Exit fullscreen mode

Import styles at your app root:

import '@gfazioli/mantine-flip/styles.css';
Enter fullscreen mode Exit fullscreen mode

This release has no breaking changes — update and all existing code continues to work as-is.

Compatibility

  • Mantine >=7.0.0
  • React 18.x or 19.x

Links

Top comments (0)