Skip to content
animorio
IT
All components

Consent in one line

Consent, Text, TransitionsGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The cookie banner written as one sentence, in the minimal style: We use cookies for essentials, analytics and marketing. The kinds are words of the sentence and real switches: touched, a word is struck by a thin vermilion line, loses weight and width and hops over the others to the end of the sentence, which finds its new order with FLIP («essentials only, not analytics or marketing»). Once chosen, the sentence closes into a line and a small link is left that says what was chosen.

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, viewport-fit=cover">
  <title>Consent in one line</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>
  <!-- the cookie banner written as one sentence, near the middle of the page (at the bottom on a real site). The kinds of cookies are words of the
       sentence and real switches: struck with a thin line they move to the end («not analytics»), and the sentence
       finds its new order. Once chosen, the sentence closes into a line, and a small link is left to open it again.
       The words are made by script.js, from the values at its top -->
  <div class="consent" data-consent>
    <section class="banner" role="region" aria-label="Cookie consent" data-banner data-autoclic>
      <span class="banner__frame" aria-hidden="true" data-frame></span>
      <p class="sentence" data-sentence></p>
      <div class="actions" data-actions>
        <button class="pill pill--main" type="button" data-accept>
          <span class="pill__bg" aria-hidden="true"></span>
          <span class="pill__label" data-text="Accept all">Accept all</span>
        </button>
        <button class="pill" type="button" data-save>
          <span class="pill__bg" aria-hidden="true"></span>
          <span class="pill__label" data-text="Save choices">Save choices</span>
        </button>
      </div>
    </section>

    <button class="reopen" type="button" hidden data-reopen>
      <span class="reopen__mask"><span class="reopen__text" data-reopen-text>Cookie settings <span class="reopen__state">· <span data-summary>all on</span></span></span></span>
      <span class="reopen__line" aria-hidden="true" data-reopen-line></span>
    </button>

    <p class="sr" role="status" aria-live="polite" aria-atomic="true" data-live></p>
  </div>

  <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.

23 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 “Consent in one line” from Animorio and fit it into my site

What it does

On a white background, in the middle (on a real site it sits at the bottom: just lower --gap), a white capsule with a thin hairline and a barely there shadow. Inside, a sentence in light Archivo: We use cookies for essentials, analytics and marketing., with the three kinds heavier; analytics and marketing have a still grey hairline under them, which says they can be touched. Next to it two pills, a black Accept all and Save choices with a thin edge. When the page opens a point in the middle draws a line of ink, the line opens into the capsule a pixel past its height, and the words rise out of the line one after another, as wide as they should be. With the mouse over a kind the word gains weight and the vermilion line just touches it (over a struck word, the line lifts off a little). Touched, the line strikes it from the left, the word turns grey, lighter and narrower, then hops over the others to the end, where the sentence names it among the refused ones: We use cookies for essentials and analytics, not marketing. The other words slide to their new places, the commas and the joining words (and, only, not, or) fade where they were and rise where they are needed, and the capsule grows or shrinks with the sentence. Both off: We use cookies for essentials only, not analytics or marketing. Touched again, the line lifts off to the right and the word hops back to its place. Essentials cannot be struck: the line starts and springs back, and the word shivers by a pixel. With Save choices or Accept all (which first brings the struck words back) the words fold flat onto their line one after another, the capsule closes into a hairline, the hairline turns to ink and runs down to the size of a small link, out of which rises Cookie settings · essentials, analytics (or all on, or essentials only). The link opens the sentence again as it was.

How it works

The sentence from its switches

The sentence is written from START, ALWAYS and KINDS at every change: the kinds that are on with commas and and (with only when essentials is alone), then, if some are off, a comma, not and the ones off with or. Every piece is an element with a key: a kind is always the same button (role switch, aria-checked), and moves from one part of the sentence to the other; a comma or a joining word is named after the word that comes after it, so when the order changes the old one fades and a new one rises where it is needed, and no word crosses another. Commas and the full stop live inside the word before them: they never part at the end of a line.

FLIP with the type changing

Before the change every piece is measured (getBoundingClientRect, transforms included, so a change halfway starts again from where the words are). Then the new order is put in, every word gets its final weight and width (wght 610 or 300, wdth 100 or 84) and is measured again. Every piece starts moved by the difference and goes back to zero with power3.inOut in RECOMPOSE seconds. While they travel, every word keeps the width it will have at the end and the banner its new size: the struck word slims down on the way, and without this it would have pulled the others along (on a phone it would also have moved the line breaks).

The line, the hop, the fold

The line is a 1.5 pixel span inside the word’s text, always as long as the letters, at the middle of the small letters, measured with measureText: it is drawn with scaleX from the left in STRIKE seconds and lifts off to the right. The word stays put while the line runs, then goes: on the first line it hops over the others (HOP ems up with sine, and a squash to 0.94 with elastic.out when it lands); if it changes line, or would hop into the line above, it folds flat where it was and opens where it goes with back.out, so it never covers the other words.

The capsule that becomes a link

The capsule is a layer of its own, with left, top, width and height tweened in pixels: it follows the sentence when it changes length and, once chosen, closes into a hairline one pixel high (edge and shadow fade with a CSS variable, --sh), turns to ink and runs down to the size of the link, while the words fold with scaleY and the wdth axis at 62. The link rises out of the hairline inside a mask and the hairline is spent from both ends. Opening again does the same backwards, and the capsule opens with back.out, a pixel past and back.

Keys, screen readers and the preview

Tab goes from essentials to analytics and marketing (in the order the sentence names them at that moment) and to the pills; Space or Enter strike and turn back on, and the focus stays on the word even after it has moved. Every change is announced in an aria-live region with the new sentence; closing, the focus moves to the link, opening again to the first kind. The preview’s click (detail 0) plays a round in a GSAP timeline: it strikes marketing, then analytics, turns analytics back on, saves, opens again, accepts all and opens again as it started. A real touch stops the round.

Values to change

NameFileDefaultWhat it changes
STARTscript.js'We use cookies for'The words before the kinds.
ALWAYSscript.js'essentials'The kind that is always on and cannot be struck.
KINDSscript.js['analytics', 'marketing']The kinds that can be struck, in the order the sentence names them.
STRIKEscript.js0.34The seconds the line takes to strike a word, or to lift off it.
RECOMPOSEscript.js0.72The seconds the sentence takes to find its new order.
HOPscript.js0.85How high the word passing over the others hops, in ems.
CLOSEscript.js1How slowly the banner closes into a line and opens again (1 is the base).
WEIGHTSscript.js[330, 610, 300]The weight of the sentence, of a kind that is on and of a struck one (100 to 900).
STRUCK_WIDTHscript.js84The width (wdth axis) of a struck word, 62 to 125.

Try them in the Playground

When to use it

For the cookie consent of a site that wants to say it in a few words, but also for any choice that reads as a sentence: the notifications you get (we’ll email you about orders and news), the data an app shares, the ingredients to leave out of a dish.

On phones

The capsule takes the whole width but twelve pixels each side, the sentence wraps onto two lines and the pills sit under it, side by side, half each. Words changing line fold and open again instead of crossing the others; the same animations, only measured for a finger.

With reduced motion

No hops, no folds, no running lines: the strike appears whole, the new sentence fades in in place, the banner fades out and the link fades in where it was, and the same opening again.

Pitfalls

  • With words changing weight and width, the final size and the size of the moment are not the same: the centred banner shrank while the struck word slimmed down, and the whole sentence slid sideways before it even set off. During the journey every word keeps its final width and the banner its new size.
  • A comma as a piece of its own, with a negative margin to stay close to its word, could wrap alone and hang outside the edge: commas and the full stop live inside the word before them.
  • The same and reused somewhere else in the sentence crossed the kind moving the other way (it read analyticsand): joining words are named after the word that follows, and an old one fades while a new one rises.
  • On a phone the hop over the others ended on the line above and covered We use cookies: away from the first line, and when it changes line, the word folds and opens again in its new place.
  • Tweening the capsule’s edge and shadow as box-shadow strings does not work (the browser writes the colour first and the tween mixes up the numbers): edge and shadow depend on a CSS variable, --sh, going from 1 to 0.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

Tested on 16 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 components