Skip to content
animorio
IT
All components

The redacted permissions

Consent, SVG, Text, TransitionsGSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The card where a running app asks to read your health data and shows what it would see: Sunday’s run, the heart rate at rest, the sleep, the weight. What you switch off is blacked out right where it is: the letters swell, heavier and wider, and their ink runs along the words until they are one wet bar, which then dries; switched back on, the ink draws back into the letters. On sharing, the app’s yellow runs like a highlighter over the values it will read, and the track in its icon grows a stretch for each one.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <title>The redacted permissions</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">
    <!-- the card where a running coach app asks to read your health data. Each kind of data shows what the app
         would see; switched off, the words swell into a bar of wet ink that dries, switched on the ink draws back
         into the letters. Shared, the app's yellow runs like a highlighter over the values left in clear, the
         track in its icon grows, and the card says what was shared. The rows are made by script.js, from KINDS -->
    <section class="permit" aria-labelledby="permit-title" data-permit data-autoclic>
      <header class="permit__head">
        <span class="app" aria-hidden="true" data-app>
          <svg class="app__mark" viewBox="0 0 48 48" focusable="false">
            <g transform="rotate(-24 24 24)">
              <path class="app__lap app__lap--ghost" d="M24 36H30A12 12 0 0 0 30 12H18A12 12 0 0 0 18 36Z" />
              <path class="app__lane" d="M24 30.5H30A6.5 6.5 0 0 0 30 17.5H18A6.5 6.5 0 0 0 18 30.5Z" />
              <path class="app__lap" d="M24 36H30A12 12 0 0 0 30 12H18A12 12 0 0 0 18 36Z" data-route />
            </g>
          </svg>
        </span>
        <h2 class="permit__title" id="permit-title" data-title>Share your health data with <span data-app-name>Pacenote</span></h2>
        <p class="permit__text" data-text><span class="permit__line"><span data-app-name>Pacenote</span> plans your runs from what you share.</span> <span class="permit__line">What you keep stays on this phone, blacked out.</span></p>
      </header>

      <ul class="kinds" data-kinds></ul>

      <div class="actions" data-actions>
        <div class="actions__row">
          <button class="btn btn--quiet" type="button" data-needed>Only what’s needed</button>
          <button class="btn btn--quiet" type="button" data-all>Allow all</button>
        </div>
        <button class="btn btn--main" type="button" data-share><span>Share with <span data-app-name>Pacenote</span></span></button>
      </div>
      <div class="actions actions--done" hidden data-done>
        <p class="done__note">
          <svg class="done__mark" viewBox="0 0 20 20" aria-hidden="true" focusable="false"><path d="M5 10.5l3.2 3.2L15 6.6" /></svg>
          <span data-note>Shared just now. Change it any time.</span>
        </p>
        <button class="btn btn--quiet btn--big" type="button" data-change>Change what’s shared</button>
      </div>

      <p class="sr" role="status" aria-live="polite" aria-atomic="true" data-live></p>
    </section>
  </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.

38 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 redacted permissions” from Animorio and fit it into my site

What it does

On a white background a white card 404 pixels wide, with a soft shadow. At the top Pacenote’s icon, a square of ink with a yellow running track seen at an angle, and the title Share your health data with Pacenote; under it, two lines: Pacenote plans your runs from what you share. What you keep stays on this phone, blacked out. Then four rows of data, each with its name and what the app would see, written large: Workouts, 8.4 km run on Sunday, labelled Required; Heart rate, 58 bpm at rest; Sleep, 6 h 40 min last night; Body weight, 71.4 kg this morning, each with its switch. At the bottom Only what’s needed and Allow all, and the black button Share with Pacenote. When the page opens the card rises gently and the three values that are not needed are blacked out one after another: nothing goes out unless you let it. Blacking out is a single gesture: the letters get heavier and wider, then the ink spreads round each of them and runs along the value from left to right, closing the holes in the letters and the gaps between the words, until a black bar is left with the bumps of the tall letters and of those that hang below the line. The fresh ink is a little lighter and bluish and has a shine along its top edge, which dries in just over a second; the edges wave a little, like ink on paper. Turning a switch on (or tapping the row) the ink draws back into the letters, from the left again, and the letters settle a touch thinner before they are themselves again; the knob runs stretching and turns to ink on the yellow track. Only what’s needed blacks out everything that is not needed, one row after another; Allow all uncovers everything. With the mouse over a row you see what a click would do: clear words get a little heavier, as if the ink were about to come, and the ink over hidden words draws in at its edges without letting them through. Share with Pacenote: the switches step aside and the app’s yellow runs like a chisel tipped highlighter over each value it will read, one after another; with each stroke the track in the icon grows its stretch and the icon gives a jolt. The title becomes Pacenote is connected, the lines say what it reads and what stays blacked out on the phone, and at the bottom Shared just now and Change what’s shared, which takes the yellow away and puts the switches back.

How it works

The letters that swell

Each value is SVG text with one tspan per letter (the outline of HTML text, -webkit-text-stroke, has pointed corners and grows spikes when thickened), and each letter has its own amount of ink, from 0 to 1. In the first half the letter gains weight (Archivo’s wght axis from 600 to 900) and all the way through width (wdth from 88 to 112), so the letters after it move along and the ink seems to spread. The real ink is the letter’s outline, in the same colour, with round corners (stroke-linejoin round): an outline two radii wide is the letter grown by a disc of that radius in every direction, and grown enough it closes the holes in the letters and the gaps.

The body of the bar

Under the letters there is a band through the middle of the small letters, as thick at each letter as that letter is inked: the letters’ middles come from getStartPositionOfChar and getEndPositionOfChar on every frame (the letters widen), and the edge is a closed Catmull-Rom curve, round at the ends. While the ink runs, the band thins towards the front; at rest, the swollen letters stick out of it a little where they are tall or hang below the line, and those are the bumps.

The wet ink

Fresh ink has a colour between --ink and --wet (WET says how much) and a shine: a blurred white line along the band’s top edge, broken like the reflection of a window; in DRY seconds it dries. While there is ink the group goes through a filter with a low frequency feTurbulence and a feDisplacementMap as strong as the ink, which makes the edges wave; clean words are drawn without the filter.

The switches and the choices

The switches are real buttons with role switch and aria-checked, named after their row, and the whole row can be tapped. Switching off, the letters ink up one after another in SWEEP seconds with back.out; switching on, the ink goes back to zero with a back.out that takes it below zero, and for a moment the letters are under their weight. Only what’s needed and Allow all change the rows one after another, 0.13 seconds apart; with the mouse, over a clear row the weight goes up by 110, over a blacked out one the ink goes down to 0.9.

The confirmation

Share: the switches fade away to the right, and over each shared value a yellow band runs in MARK seconds, from the start of the letters to as far as it has got, a little uneven and cut at a slant at the ends like a highlighter, under the letters. At the end of each stroke the track in the icon (stroke-dashoffset) reaches the share of data shared, and the icon squashes and comes back with elastic.out. The title and the lines fade with a touch of blur, change, and their height slides from the old to the new while they are invisible, so the card does not jump.

Keys, screen readers and the preview

Tab goes from the switches to the buttons, Space or Enter turn them on and off, and the focus ring shows only to who uses the keys. Each row tells the screen reader its value, or that it is blacked out and stays on the phone; the final choice and the quick choices are announced in an aria-live region, and the focus moves to Change what’s shared and then back to Share. The preview’s click (detail 0) uncovers everything, blacks out what is not needed, turns heart rate on, shares, opens again, and so on; if the card is still moving, the click waits.

Values to change

NameFileDefaultWhat it changes
APPscript.js'Pacenote'The name of the app that asks, in the title, the button and the confirmation.
KINDSscript.js[{ name: 'Workouts', value: '8.4 km run on Sunday', needed: true }, …]The data the app asks for: the name, what it would see and whether it cannot do without it (needed: no switch, never blacked out).
SWELLscript.js1How far the ink spreads round the letters: 0.6 tight, 1.6 blotchy.
SWEEPscript.js0.62The seconds the ink takes to run along a value, and to draw back.
WETscript.js0.75How much the fresh ink shines (0: matte from the start).
DRYscript.js1.3The seconds the ink takes to dry.
MARKscript.js0.42The seconds the app’s yellow takes to run over a value it will read.

Try them in the Playground

When to use it

For any app that asks to see personal data: health, contacts, calendar, location, photos, accounts; for the link between two services that shows what will pass from one to the other; for a privacy page, or a form that lets you hide its delicate fields before sending it.

On phones

The card takes the width of the screen less the margins (358 pixels at 390) and stays the same; the whole row is a target for the finger, the switches are 48 by 30, and the two small buttons narrow their letters a little (the width axis) so that Only what’s needed fits in half the row. The hint on hover is there only with a mouse.

With reduced motion

The card does not rise and the ink is already there; switching on or off, the ink appears or goes at once with a short fade, without running, without the shine and without the knob stretching. On sharing, the yellow fades in on all the values together, the track in the icon is at its point straight away and the words change with a fade.

Pitfalls

  • Holding the lines’ room still (min-height) so that the card would not jump left a gap under the title when the confirmation was shorter: the height of the title and of the lines slides from the old to the new while the words are invisible.
  • A shine on the wet ink made with a light (feSpecularLighting on the blurred alpha) greyed the whole bar: on a flat surface the specular light is never zero. The shine is a blurred white line drawn along the bar’s top edge, and it goes out as the ink dries.
  • A dense noise in the feDisplacementMap made the edges furry; with a low frequency (0.07 by 0.16) they wave like ink on paper.
  • Copies of the values flying into the icon passed over the title and the lines, and for a moment covered the text: the confirmation stays on the values themselves (the highlighter’s yellow) and on the track in the icon.
  • In an inline-flex button the space before a span disappears (Share withPacenote): the label sits in a span of its own.
  • The preview’s click arrived while the opening ink was still running and was lost: if the card is moving, the click waits until it is still.

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