Skip to content
← Inventory

SwatchFan

A fanned stack of {@link Swatch} tiles — the unified material-fan primitive that merges the retired CardFan (fixed 3-image editorial fan) and StackedSwatchFan (interactive N-tile splay) into one component. Pass 3 items with interaction="breathe" to reproduce CardFan's Figma geometry (variant spread/stack, white 3px mount via bordered); pass N items with interaction="hover-fan" for the interactive splay-on-hover with pointer tilt. bordered={false} swaps the white mount for the 1px hairline tile.

Clay
Terracotta
Sage

Usage

import { SwatchFan, SwatchFanImageProps, SwatchFanInteraction, SwatchFanItem, SwatchFanProps, SwatchFanSize, SwatchFanVariant } from "@desk/ui/components/swatch-fan";

Best practices

  • Feed 3 items for the breathe/none card engine — its Figma geometry, z-order, and variant morphing are built around a fixed left/center/right trio; the hover-fan engine is the N-tile path.

  • Needs ReducedMotionProvider; it already collapses the mount fan-out, hover nudge, and splay under reduced motion (snapping to the settled frame), so don't gate the interaction yourself.

  • bordered is the mount-as-a-prop: true = white 3px card mount, false = 1px hairline — don't hand-roll either border in a wrapper.

Props

PropTypeDefaultDescription
animateOnMountboolean

Animate cards fanning out on mount (card engine — breathe/none).

borderedbooleantrue

Mount as a prop. true (default) = white 3px card mount (matches CardFan + the live MB Bank app); false = 1px hairline (the retired StackedSwatchFan raised look).

cardBorderWidthnumber3

Border width for each material card (card engine).

cardRadiusnumber20

Corner radius for each material card (card engine).

cardShadowstring

Shadow applied to each material card (card engine).

classNamestring
fanOnHoverbooleantrue

Own the fan state via hover/focus (default) or drive it via fanned (hover-fan engine).

fannedboolean

Controlled fan state (hover-fan engine) — e.g. a sibling CTA triggers it.

hoveredboolean

Controlled hover/open state for parent-driven compositions (card engine).

hoverRotatenumber1

Extra side-card rotation while hovered (card engine).

hoverShiftnumber3

Extra outward shift for side cards while hovered (card engine).

interactionSwatchFanInteraction"breathe"

Motion/geometry engine: breathe (CardFan micro-nudge, default), hover-fan (StackedSwatchFan splay + pointer tilt), or none (static).

items(required)SwatchFanItem[]

Tiles to fan. The card engine (breathe/none) maps the first three to left/center/right; the hover-fan engine stacks the first few (matching its motion transforms).

labelstring

Accessible label applied as the fan's aria-label (hover-fan engine).

mountDelaynumber

Delay before the mount fan-out starts, in seconds (card engine).

onFannedChange(fanned: boolean) => void

Fires when the hover-fan opens or closes, with the next fanned state.

overlapnumber

Override the horizontal overlap between adjacent cards, px (card engine).

renderImage(props: SwatchFanImageProps) => ReactNode

Escape hatch for framework-optimized images (e.g. next/image).

rotationDegnumber8

Side-card rotation in degrees (card engine).

sizeSwatchFanSize"xl"

Tile size — a SwatchTileSize scale step or pixel number.

styleCSSProperties

Inline styles applied to the hover-fan root.

wrapperSizenumber

Override the square wrapper size for rotated side cards, px (card engine).

variantSwatchFanVariant

Layout variant (card engine): stack (default) or spread.

Also exports

  • interfaceSwatchFanImageProps
  • typeSwatchFanInteraction
  • interfaceSwatchFanItem
  • interfaceSwatchFanProps
  • typeSwatchFanSize
  • typeSwatchFanVariant

Composition

Builds on

Swatch