View transitions

Five ways to move between two views. All the choreography is CSS — the only JavaScript on this page toggles one class per stage (8 lines, inline, at the bottom). Steal the class flip for your router.

1. Fade-through

Out fast, in late with a 96% scale — the gap sells the switch.

Inboxpanel A
Archivepanel B
View CSS
/* Base rule = quick fade OUT. Incoming state rules carry
   the delayed fade IN — symmetric both ways for free. */
.ft .pane { transition: opacity .12s ease-in,
                        transform .12s ease-in; }
.ft .pane-b { opacity: 0; transform: scale(.96); }
.ft.show-b .pane-a { opacity: 0; }
.ft.show-b .pane-b {
  opacity: 1;
  transform: none;
  transition: opacity .24s ease-out .1s,
              transform .24s ease-out .1s;
}
.ft:not(.show-b) .pane-a {
  transition: opacity .24s ease-out .1s,
              transform .24s ease-out .1s;
}

2. Shared axis (horizontal)

Both panels travel the same axis — spatial continuity.

Step 1panel A
Step 2panel B
View CSS
.sa .pane {
  transition: opacity .25s ease,
              transform .3s cubic-bezier(.4, 0, .2, 1);
}
/* incoming waits at +48px, outgoing exits to -48px */
.sa .pane-b        { opacity: 0; transform: translateX(48px); }
.sa.show-b .pane-a { opacity: 0; transform: translateX(-48px); }
.sa.show-b .pane-b { opacity: 1; transform: none; }

3. Expand from card

transform-origin makes the detail view grow out of the card.

Projectspanel A
morphpanel B — detail view
View CSS
.ex .pane-b {
  opacity: 0;
  transform: scale(0.22);
  /* the whole trick: grow FROM the card's position */
  transform-origin: 15% 20%;
  transition: opacity .25s ease,
              transform .35s cubic-bezier(.2, 0, 0, 1);
  border-radius: 10px;   /* card corners while small */
}
.ex.show-b .pane-b {
  opacity: 1;
  transform: scale(1);
  border-radius: 0;      /* full-bleed when expanded */
}

4. Circle reveal

clip-path circle() uncovers the panel from the corner.

Light modepanel A
Dark modepanel B
View CSS
/* Panel B is always rendered — the clip just hides it.
   150% radius guarantees full coverage at any aspect. */
.cr .pane-b {
  clip-path: circle(0% at 88% 84%);
  transition: clip-path .5s cubic-bezier(.4, 0, .2, 1);
}
.cr.show-b .pane-b {
  clip-path: circle(150% at 88% 84%);
}

5. Crossfade with scale

Opposite scale directions add depth to a plain crossfade.

Overviewpanel A
Detailspanel B
View CSS
.cf .pane { transition: opacity .3s ease,
                        transform .3s ease; }
/* incoming grows 96 -> 100, outgoing keeps going 100 -> 104:
   one panel pushes THROUGH the other */
.cf .pane-b        { opacity: 0; transform: scale(.96); }
.cf.show-b .pane-a { opacity: 0; transform: scale(1.04); }
.cf.show-b .pane-b { opacity: 1; transform: scale(1); }