morphcard

Introduction

morphcard animates a card into a full detail screen and back, with the card's title, name and badge flying into the header.

Tap a delivery below. The card grows into the detail screen, the route and company fly into the header, and the rest of the page settles in. Back plays a shorter version that ends on the card.

state: closed

morphcard is a small TypeScript library for this one transition. You give it three elements (the detail sheet, the list behind it, an optional scrim) and mark the parts that should travel with data-morph. It measures both ends when a transition starts and plays it with the Web Animations API.

What you get

  • A single surface. The sheet is clipped to the card's rectangle and the clip opens to full screen. Text inside is never scaled as a bitmap, so it stays sharp.
  • Shared elements. Anything marked data-morph="key" on the card and on the sheet flies between the two. Text that wraps differently on each side crossfades while it flies.
  • A separate close. Closing takes 300 ms against 400 ms for opening. The big heading hands over to the card's own text in the first 90 ms.
  • Interruptions. Close during an open and the running animations turn around from where they are.
  • Safe fallbacks. No card on screen, no destination, or reduced motion: it fades instead of flying.
  • No dependencies. 36 kB of unminified ESM, about 11 kB gzipped, plus an optional React hook in morphcard/react.

Watch it slowed down

Open and back at a quarter speed, phone size.

The same at normal speed on a desktop layout:

1280 × 800, normal speed.

The anatomy page breaks this recording down step by step, with every duration and easing.

Browser support

morphcard uses Element.animate, animates clip-path: inset() and sets the inert attribute. inert is the newest of these: Chrome and Edge 102, Firefox 112, Safari 15.5 (caniuse). The automated tests run in Chromium only so far.

Status

Version 0.1. The API is small and may still change before 1.0. The package is not on npm yet; install it from GitHub as shown in Getting started.

On this page