IV Space and line

Star on GitHubFeedback
IV Space and lineNative HTML and a hookot-expand

Scroll expand

A plate cropped by four corner marks that open with the scroll, from a small mark to the full measure, and stop when you stop.

Report an issue with Scroll expand

Example

Scroll down: the plates open as far as you go, and close when you go back.

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 01Principles, the first

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 02From the horizon
Shut, a mark

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 01Shut
Half open

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 01Half
Open

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 01Open
Horizon, shut

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 02Shut
Horizon, half open

Space
does the
layout.

A hairline appears only where space alone can’t hold two things apart.

Plate 02Half

Install

Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base, corners, and meta along.

npx shadcn@latest add https://thedirectors.agency/ui/r/scroll-expand.json

Usage

scroll-expand-example.tsx
import { ScrollExpand } from "@/components/ui/scroll-expand"function Plate({ small = false }: { small?: boolean }) {return (<div className="grid gap-(--ot-space-5) py-(--ot-space-6)"><p className={small ? "ot-f" : "ot-fff"} style={{ margin: 0 }}>Space<br />does the<br />layout.</p><p className="ot-pp max-w-[18rem] justify-self-end text-pretty" style={{ margin: 0, color: "var(--ot-pencil)" }}>A hairline appears only where space alone can&rsquo;t hold two things apart.</p></div>)}export default function Example() {return (<div className="grid w-full gap-(--ot-space-9)"><p className="ot-pp" style={{ margin: 0, color: "var(--ot-pencil)" }}>Scroll down: the plates open as far as you go, and close when you go back.</p><ScrollExpand caption={["Plate 01", "Principles, the first"]}><Plate /></ScrollExpand><ScrollExpand variant="horizon" caption={["Plate 02", "From the horizon"]}><Plate /></ScrollExpand></div>)}

Contract

  • Underneath: native, plus a hook that reads the scroll from the real layout; made of corners and meta.
  • Anatomy: <figure class="ot-expand" data-variant="horizon"> holding .ot-expand-frame (a ot-corners) round .ot-expand-plate (the content), then figcaption.ot-expand-caption with a ot-meta row: the caption's words, then .ot-expand-share (aria-hidden). The hook sets --p, 0 to 1.
  • mark (the default): after the printer's crop marks and the frame of SHAPES / GRADIENTS. The plate is cropped (a clip-path) to a small square in the middle (--ot-expand-mark, --ot-space-8), and the four corner marks, --ot-space-4 long, stand a step (--ot-space-2) outside the crop, as crop marks stand outside the trim. As the page scrolls the crop opens to the whole plate. The caption's row is as wide as the crop (never narrower than 20rem or its space), so its hairlines draw out as it opens, the rule that carries the date and the name; the row ends on the share of the measure the crop has reached, in pencil tabular figures (0.42), as SPECTRA sets 0.13.
  • Scrubbed: --p is 0 as the plate's top comes in at the foot of the view and 1 once its middle is at the middle of the view (or as far as the page can scroll). It is read from the real layout in a frame asked for by the scroll event, which runs after a smooth scroller (Lenis) has moved the page, so the crop and the page move in the same frame. Nothing eases: it stops when you stop and closes when you scroll back. progress pins it.
  • horizon: after Eclipse, the disc standing on one line. The crop starts as a hairline across the whole measure (the horizon, drawn in --ot-rule-strong while it's shut, and gone by a quarter open) and opens up and down from it; the caption stays full width.
  • The whole plate is in the flow and read from the start; only the view of it is cropped. Nothing to focus. Reduced motion, or no script: it stands open. It is symmetric, so right to left changes nothing. Forced colours: the marks and the horizon in CanvasText.

Props

Props of Scroll expand
PropTypeDefaultWhat it does
childrenReactNodeWhat the plate holds. It is all in the flow and read from the start; only the view of it is cropped.
captionReactNode | ReactNode[]The words of the meta row under the plate, held apart by hairlines. The row is as wide as the crop, so its hairlines draw out as it opens; the share of the measure reached (0.42) closes it, in pencil.
variant"mark" | "horizon""mark"mark: the crop opens from a small square in the middle to the whole plate, its corner marks a step outside it like a printer's crop marks. horizon: after Eclipse; the crop starts as a hairline across the whole measure and opens up and down from it, the line fading as it goes.
progressnumberPins how far it has opened, 0 to 1, instead of following the scroll.
--ot-expand-markCSS lengthvar(--ot-space-8)The size of the mark it opens from.

Craft

Where its move comes from

Where its move comes from
ComponentReferenceMove
Scroll expandThe printer's crop marks; SHAPES / GRADIENTS frameCrop marks open from a small mark to the measure as you scroll; the caption's rule draws out with them
Horizon scroll expandEclipseThe plate opens up and down from a hairline horizon

How it moves

How it moves
ComponentArticulationWhat moves
Scroll expandRubato (the reader's tempo)The crop and its marks open only as far as the scroll has gone, forwards or back