Skip to content
animorio
IT
Back to the library

The curved-edge transition

Transitions, SVGGSAP, CSS onlyMediumCompressed weight: Demo 6.1 KBGSAP 28 KBFreeDownload the zip

A minimal-style page change on click: a black sheet rises from the bottom with its edge bulging like cloth pushed from below, flattens as it arrives, shows the name of the next page widening, then leaves upwards with its lower edge curved the other way, uncovering the new page.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The curved-edge transition</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <!-- the first page -->
    <section class="page page--a is-on" data-page="a" aria-label="Work">
      <header class="bar">
        <span class="brand">Orrin</span>
        <nav class="nav" aria-label="Pages">
          <a href="#work" aria-current="page">Work</a>
          <a href="#studio" data-go="b">Studio</a>
        </nav>
      </header>
      <div class="hero">
        <p class="kicker" data-rise>Architecture and interiors</p>
        <h1 class="headline">
          <span class="line"><span class="line__in" data-line>Rooms that</span></span>
          <span class="line"><span class="line__in" data-line>hold the light.</span></span>
        </h1>
        <a class="go go--next" href="#studio" data-go="b" data-autoclic data-rise>
          <span class="go__text">Meet the studio</span>
          <span class="go__arrow" aria-hidden="true"><span class="go__shaft"></span><svg class="go__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
        </a>
      </div>
      <footer class="foot" data-rise>
        <span>Houses, shops and quiet rooms</span>
        <span>Since 2011</span>
      </footer>
    </section>

    <!-- the second page -->
    <section class="page page--b" data-page="b" aria-label="Studio" aria-hidden="true">
      <header class="bar">
        <span class="brand">Orrin</span>
        <nav class="nav" aria-label="Pages">
          <a href="#work" data-go="a">Work</a>
          <a href="#studio" aria-current="page">Studio</a>
        </nav>
      </header>
      <div class="hero">
        <p class="kicker" data-rise>The studio</p>
        <h2 class="headline">
          <span class="line"><span class="line__in" data-line>Four people,</span></span>
          <span class="line"><span class="line__in" data-line>one long table.</span></span>
        </h2>
        <a class="go go--back" href="#work" data-go="a" data-autoclic data-rise>
          <span class="go__arrow" aria-hidden="true"><svg class="go__head" viewBox="0 0 8 12"><path d="M6.5 1.5 1.5 6l5 4.5"/></svg><span class="go__shaft"></span></span>
          <span class="go__text">Back to the work</span>
        </a>
      </div>
      <footer class="foot" data-rise>
        <span>A model shop facing north</span>
        <span>Visits by appointment</span>
      </footer>
    </section>

    <!-- the panel: a black sheet drawn as one path, with a hairline on its leading edge, and the name of the next page -->
    <div class="curtain" data-curtain aria-hidden="true">
      <svg class="curtain__svg" data-svg preserveAspectRatio="none">
        <path class="curtain__sheet" data-sheet d="" />
        <path class="curtain__edge" data-edge d="" />
      </svg>
      <p class="curtain__word"><span class="curtain__in" data-word>Studio</span></p>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

34 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The curved-edge transition” from Animorio and fit it into my site

What it does

On opening the page is covered by a black sheet with the studio's name, Orrin, in the middle, narrow and light, widening and gaining weight; then the word rises away and the sheet leaves upwards: its lower edge is not straight, it is a curve whose middle lags behind while the sides run ahead, with a vermilion hairline along the edge. Underneath is the first page, on warm white paper: the two lines of the headline, ‘Rooms that hold the light.’, rise from their slot one after the other, narrowing and gaining weight, then the link with the arrow. On a click on ‘Meet the studio’ the black sheet rises again from the bottom: now its top edge bulges, the middle ahead and the sides behind, like cloth pushed from below; reaching the top it flattens, goes a hair past flat and settles. On the black the name of the next page comes up, ‘Studio’, narrow and thin, and while the sheet stands still it widens to the end of the width axis and turns bold; then it leaves upwards and the sheet moves on, its lower edge curving the other way as it uncovers the studio page, a slightly darker paper grey, whose lines rise one after the other. A click on ‘Back to the work’ makes the same trip backwards, with the name ‘Work’ on the black. With the pointer over the link the arrow's shaft stretches on a spring; the menu items widen and take the accent.

How it works

A sheet made of four numbers

The black sheet is a single path in an SVG as big as the page, with its viewBox in real pixels. Four numbers, as shares of the height, describe it: where the top and bottom edges are at the sides, and how far the middle of each bulges (the top one upwards, the bottom one downwards). Each edge is a quadratic Q curve from one side to the other with its control point in the middle, moved by twice the bulge: so the middle of the curve lands exactly where it should.

Redrawn on every frame

GSAP does not animate the path: it animates the object with the four numbers, and on every update a single function rebuilds the d attribute from the page's size at that moment. On resize width and height are read again and the sheet is redrawn at once, so the curve stays right even if the window changes halfway through the trip. The bulge is measured on the height, but never beyond nine tenths of the width: on a tall phone the curve would be too steep.

The edge that bulges and settles

While the sheet rises with power3.inOut, its leading edge bulges with power2.out in the first half of the trip, while the sheet speeds up, and flattens in the second, while it slows down; it arrives a couple of pixels past flat (the middle a hair behind the sides) and straightens with elastic.out. Going out the same holds for the lower edge, which bulges downwards: the middle lags like cloth peeling away.

The name on the black

The next page's name sits in a slot in the middle of the sheet: it rises with expo.out and meanwhile, for the whole pause, Archivo's width axis goes from 62% to 125% and the weight from 300 to 620; then it leaves upwards with power3.in. While the sheet stands still the old page switches off and the new one switches on underneath, its headline lines already lowered into their slot and wide.

The new page's lines

When the sheet is a third of the way out, the lines rise one after the other with expo.out, narrow from 104% to 84% and gain weight; the kicker, the link and the footer follow on a back.out. With reduced motion there is no sheet: the two pages swap with a fade.

Values to change

NameFileDefaultWhat it changes
RISEscript.js0.9The seconds the sheet takes to cover the page.
BULGEscript.js0.16How far the middle of the edge runs ahead, as a share of the page's height: 0 is a straight edge, 0.3 a dome.
HOLDscript.js0.35The seconds the next page's name stays on the black.
LEAVEscript.js0.95The seconds the sheet takes to leave, uncovering the new page.
OVERSHOOTscript.js2How many pixels the edge goes past flat before it settles: 0 just stops.
STAGGERscript.js0.08The seconds between one line of the new page and the next.
SPEEDscript.js1The speed of everything: 2 is twice as fast.

Try them in the Playground

When to use it

To move from one page to another on a quiet site where the type matters: an architect's or a designer's studio, a portfolio, an agency, a furniture brand; between the home page and a section, or from one project to the next. The name on the black says where you are going while the page loads.

On phones

The same transition with the sizes adapted: the edge's bulge is also measured on the width, so on a tall screen the curve stays soft; the headline goes on two smaller lines and the name on the black shrinks with the screen. A tap on the link makes the trip and another makes it backwards.

With reduced motion

Nothing moves and there is no sheet: the old page fades out as the new one fades in, with everything already in place.

Pitfalls

  • Animating the path's d attribute with GSAP would have fixed the window's size at the start of the trip: turning the phone halfway through, the curve would have kept the old size. Four numbers are animated as shares of the height and the path is rebuilt on every frame from the sizes of that moment.
  • With the quadratic curve's control point moved by the wanted bulge, the middle of the edge rose only half as much: the curve passes halfway between the ends and the control point. The control point moves by twice the bulge.
  • Measured only on the height, on a tall phone the edge became a narrow, steep dome: the bulge is measured on the smaller of the height and nine tenths of the width.
  • The name on the black has a CSS transform to stay centred vertically, and GSAP would have wiped it: GSAP moves the span inside, CSS the paragraph around it.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 6.1 KB; the libraries: GSAP 28 KB.

Testing

Tested on 21 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations