Transitions✨ Premium

Zoom Transition

You step into the content: the overview grows to × 2.5 while fading out as the detail comes in from × 0.3 to × 1, both contents changing scale together over 0.7 s. CSS + 0.9 KB of JavaScript, click, Enter or Space, a simple fade in place when the user reduces motion.

CSSZoomPremium

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →

3 usage examples

Stadium map to chosen stand — a rugby club's ticketing — Zoom Transition example 1

① Stadium map to chosen stand — a rugby club's ticketing

WhenA fan who wants to take his two sons to the November 14 game taps the East stand on the stadium map: the view zooms into its 18 blocks, with prices by category.
WhyThe overview grows and fades while the stand arrives: you step into the chosen area instead of changing pages.
Settings384 × 220 px frame; transform-origin: 86% 50% on view A so the zoom heads for the East stand, outlined in white; state B as 18 blocks colored by price category (sold-out blocks grayed), "pitch side" strip; the three category prices below the frame.
Class photo to one pupil's portrait — a school photographer's parent area — Zoom Transition example 2

② Class photo to one pupil's portrait — a school photographer's parent area

WhenIn the evening, a mother opens the class 5B group photo and taps Léon's face: the photo zooms in to his individual portrait, with its price.
WhyThe zoom tells that you go from the group to the child: the parent keeps the link between both images.
SettingsGroup photo drawn on three rows, transform-origin: 68% 54% set on the chosen pupil (same purple sweater as in the portrait); head-and-shoulders portrait on a studio background; formats and prices below the frame.
Garden map to plot 7 — a community garden app — Zoom Transition example 3

③ Garden map to plot 7 — a community garden app

WhenOdile wonders whether it is her turn to water: on the map of the 24 plots she taps number 7, which zooms in to show its vegetables and the watering schedule she shares with Karim.
WhyGrowing then replacing keeps the sense of "looking closer"; the plot arrives from the map.
Settings6 × 4 grid map, plot 7 in orange; transform-origin: 13% 40% set on it; state B with the four planted beds and the watering week (drops on scheduled days); garden messages below the frame.

How it works

The .zmt-frame (280 × 180 px, 16 px corners, overflow: hidden) stacks two .zmt-states with inset: 0, sharing a 0.7 s cubic-bezier(.4, 0, .2, 1) transition on opacity and scale. Overview A (a grid of six thumbnails on slate) sits at scale(1), opaque. Detail B (violet gradient, "ZOOM" title) waits shrunk to a third and transparent: opacity: 0; transform: scale(.3).

When .active toggles, A goes to opacity: 0; scale(2.5) and B arrives at opacity: 1; scale(1). Both contents grow together: A seems to rush toward the eye and dissolve, B rises from the background to take its place — the zoom-in of a map or a gallery. Measured in Chromium 151: 250 ms after the click, A is at × 1.73 and 0.51 opacity, B at × 0.64 and 0.49 (Firefox 153: × 1.69 / 0.54 and × 0.62 / 0.46); at 1.1 s, A at × 2.5 (700 px wide, clipped by the frame) and transparent, B at × 1 and opaque, a state still unchanged at a last reading 3 s later. The second click zooms back out. Same result in WebKit 26.5.

The enlargement starts from A's center (the default transform-origin). Moving that point onto the clicked area changes the story: you no longer step in "in the middle" but into the chosen stand, plot or photo; the FAQ gives the line to add. On the script side, a few lines per .zmt-frame: .active toggled (mouse, Enter, Space), aria-pressed and aria-hidden updated; everything is prefixed zmt-.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, both states stay at scale 1 (transform: none) and only a 0.15 s fade remains (measured in Chromium and Firefox: no scaling, change complete within 260 ms). A zoom rushing toward the eye is one of the most uncomfortable motions for sensitive people.
  • Keyboard and screen readers: the frame behaves as a toggle button (Enter, Space, aria-pressed) and the hidden state is not read, as in Slide Transition (fx-0696).
  • Visible way back: in a real interface, add a "Back to the map" button in state B; people do not always guess that a second click on the detail brings the overview back.
  • Text during the zoom: enlarged × 2.5 and half transparent, A's text becomes unreadable; put nothing in A that must be read during the change.

Browser compatibility

CSS (transform: scale(), opacity, transition, inset) + 0.9 KB of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26.5: zoom in, state B held, zoom out on the second click; keyboard and reduced motion measured in Chromium and Firefox. Scale and opacity are animated by the compositor, with no layout recalculation; at × 2.5, A's text is rendered enlarged and may look slightly soft during the change, which is expected. inset sets the floor (Chrome 87, Firefox 66, Safari 14.1).

Chrome 87+✓ Full
Firefox 66+✓ Full
Safari 14.1+✓ Full
Edge 87+✓ Full
Mobile iOS 14.5+✓ Full
Android Chrome 87+✓ Full

Without JavaScript, the overview stays on screen. Without inset, write top: 0; right: 0; bottom: 0; left: 0 on .zmt-state.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="zmt-stage">
  <div class="zmt-frame" role="button" tabindex="0" aria-pressed="false">
    <div class="zmt-state zmt-state-a">
      <div class="zmt-grid" aria-hidden="true">…</div>
      <span class="zmt-label">…</span>
    </div>
    <div class="zmt-state zmt-state-b" aria-hidden="true">…</div>
  </div>
  <span class="zmt-hint">…</span>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Zoom Transition, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Zoom point (CSS — transform-origin on .zmt-state-a) center (50% 50%) Set it on the area you enter from: 86% 50% for an item on the right. To set it on click under the pointer, add the line given in the FAQ to the script.
Zoom strength (CSS — scale(2.5) and scale(.3)) A up to × 2.5, B from × 0.3 × 1.6 and × 0.7 for a soft change between two related screens; × 4 and × 0.1 for a real dive into a map.
Duration and curve (CSS — transition on .zmt-state) 0.7 s, cubic-bezier(.4, 0, .2, 1) 0.5 s for a gallery; an ease-in curve on A and ease-out on B stress the feeling of passing through.
Contents (HTML — .zmt-state-a, -b) thumbnail grid and "ZOOM" title A is the overview (map, grid, group photo), B the detail. Opaque background on both so neither shows through the other.
Frame size (CSS — .zmt-frame) 280 × 180 px The frame clips A as it grows; resize it freely, the scales are relative.

FAQ

Before adding .active, compute the click position in the frame and write it on A: var r = frame.getBoundingClientRect(); a.style.transformOrigin = ((e.clientX - r.left) / r.width * 100) + '% ' + ((e.clientY - r.top) / r.height * 100) + '%';. The zoom-in then heads for that point, and the zoom-out leaves from it.
No intermediate layer steps in here: the two contents themselves change scale, the overview by growing, the detail by arriving from afar. You keep the feeling of entering the image, with no pass through a solid color.
Yes: put the image as A's background (background: url(…) center / cover) or as an <img> filling the state. At × 2.5 it is shown enlarged for 0.7 s; an image 2.5 times the frame size stays sharp during the change.