@markoradak/folder-card

Animated folder-style cards for React. FLIP spring expansion, 3D lid rotation, SVG notch masks, and a render props API.

npm install @markoradak/folder-card motion

Features

FLIP Spring Animation

Cards spring from grid position to centered dialog using Framer Motion physics. Configurable stiffness and damping.

3D Lid Rotation

Perspective-correct 3D rotation on any edge. Bottom, top, left, right, or auto based on aspect ratio.

SVG Notch Mask

Folder-style tab cutouts at any corner or edge. Dynamic SVG masks with configurable concave and inverted radii.

Render Props API

Full control over card face, detail content, and tab via render props. No opinionated markup or styles imposed.

Stagger Animations

FolderCardItem wraps content for automatic staggered fade-in. Built-in Framer Motion variants included.

CSS Variable Theming

Override --fc-* variables for colors, radii, shadows, and transitions. Automatic dark mode via prefers-color-scheme.


Automatic hinge detection

With hingeSide="auto", the lid rotation axis adapts to the card's aspect ratio.

Add content to the card lid to make it taller. Once it becomes portrait, the hinge automatically switches from bottom to left.

Landscape (bottom)

8 notch positions

Place the folder tab at any corner or edge. Select a position to see the notch move.


Hinge sides

Four directions for lid rotation. Click each card to see it in action.


Content cards

Editorial layouts with hingeSide="left" and notchPosition="bottom-right".


Tune the physics

Each card uses a different spring configuration. Click to feel the difference.


Fade lid

With fadeLid, the lid fades out on open instead of staying visible during 3D rotation.


Code Examples

Basic usage

FolderCardGroup manages state. FolderCard takes renderLid and renderDetail. FolderCardItem adds stagger animations.

import {
  FolderCardGroup,
  FolderCard,
  FolderCardItem,
} from "@markoradak/folder-card";
import "@markoradak/folder-card/styles";

function MyCards() {
  return (
    <FolderCardGroup>
      <FolderCard
        id="project-1"
        renderLid={() => (
          <div className="p-5">
            <h3 className="font-semibold">Project Alpha</h3>
            <p className="text-sm text-muted">Click to expand</p>
          </div>
        )}
        renderDetail={(close) => (
          <div className="p-8">
            <h2 className="text-lg font-semibold">Project Alpha</h2>
            <FolderCardItem>
              <p>Stagger-animated content.</p>
            </FolderCardItem>
            <button onClick={close}>Close</button>
          </div>
        )}
      />
    </FolderCardGroup>
  );
}

Tab notch

Add renderTab for a folder-style tab cutout. 8 positions available (corners and edges) with configurable radii.

<FolderCard
  id="with-tab"
  notchPosition="top-right"
  renderLid={() => <CardFace />}
  renderDetail={(close) => <CardDetail close={close} />}
  renderTab={() => (
    <div className="pr-5 pt-3 pb-2 pl-4">
      <div className="flex size-8 items-center justify-center
        rounded-full border border-border/40
        group-hover:border-foreground/30">
        <ArrowUpRight className="size-3.5 text-muted
          group-hover:text-foreground" />
      </div>
    </div>
  )}
/>

Custom spring

Pass springConfig to FolderCardGroup for bouncy, smooth, or snappy animations. Adjust stiffness and damping.

<FolderCardGroup
  springConfig={{ stiffness: 200, damping: 15 }}
>
  <FolderCard
    id="bouncy-card"
    renderLid={() => <CardFace />}
    renderDetail={(close) => <CardDetail close={close} />}
  />
</FolderCardGroup>

Hinge side + notch position

Combine hingeSide and notchPosition for unique card orientations. The tab cutout and lid rotation work together.

<FolderCard
  id="recipe-card"
  hingeSide="left"
  notchPosition="bottom-right"
  renderLid={() => <RecipeFace />}
  renderDetail={(close) => <RecipeDetail close={close} />}
  renderTab={() => <TabIcon />}
/>

CSS variable theming

Override --fc-* CSS custom properties for colors, radii, shadows, and transitions. Supports light and dark mode.

:root {
  --fc-radius: 20px;
  --fc-card-bg: #ffffff;
  --fc-foreground: #0a0a0a;
  --fc-border: rgba(0, 0, 0, 0.1);
  --fc-muted: rgba(0, 0, 0, 0.45);
  --fc-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
  --fc-shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.12);
}

@media (prefers-color-scheme: dark) {
  :root {
    --fc-card-bg: #111111;
    --fc-foreground: #f5f5f5;
    --fc-border: rgba(255, 255, 255, 0.06);
  }
}