Skip to content

No.238 · Reading· New

Stretchtext

One article at three depths. Switch between Gist, Normal and Full and the extra detail grows inside the sentences themselves, washed briefly with a highlighter so you can see what's new, while the paragraph you were reading stays put on screen.

The prompt

Copy this text

Automatic copying is unavailable. Copy the selected text using your device’s copy command.

Written for the variant you picked above: Light

Build a long-form reader that lets the reader choose how deep to go, based on Ted Nelson's stretchtext: the same article written at three depths, where more depth adds clauses and sentences inside the text instead of linking elsewhere. Authoring API: <Stretchtext levels={['Gist', 'Normal', 'Full']} defaultLevel={1} title='How Masar keeps your files'> wrapping ordinary paragraphs and small headings; inside them <More level={1}>…</More> is text that appears from that depth up, and <Less below={1}>…</Less> is shorter wording used only below that depth (so the gist can say 'Deleted files can be recovered for 30 days.' while Normal says it in two sentences). Word counts per depth are computed by walking the same tree that renders. Surface: warm paper with a 14px radius and hairline border, Newsreader at 17–19px with 1.62 line height for the text, small letter-spaced uppercase Hanken Grotesk headings, a 28–38px Newsreader title. A sticky top bar holds a three-part segmented control (each segment shows its name in bold and '96 words' under it; the active segment is a raised paper chip) and '2 min read' on the right. Changing depth: new text appears in place with a pale yellow highlighter wash that fades over 1.2s, and the added length is revealed downward over 320ms with a clip (layout settles at once, so scrolling stays correct). The first paragraph still on screen is held at the same position by adjusting scroll, so expanding text above doesn't push the reader's place away. Paragraphs that still have more to say at a greater depth carry a dotted rust rule in the left margin. Selected variant — Light (light): Palette for this theme (Light): --stx-accent #8a3b12; --stx-ink #1f1c17; --stx-line rgb(31 28 23 / 0.14); --stx-muted #6b6457; --stx-paper #fbf8f1; --stx-seg #f1ebdd; --stx-wash #fde9a3. Treat these as the root colour tokens, one value per role, and name them to suit your code. Select it with the theme option set to "light". Keep every dimension, spacing value, state and motion from the brief unchanged, and keep text at 4.5:1 contrast or better on these surfaces. This selection takes precedence over references to other themes or options in the general brief. Implement this selected appearance; do not add a variant picker or alternative appearances unless requested. Implementation requirements: Use React and TypeScript. Keep the component self-contained, with uniquely scoped CSS and no animation libraries. Include required imports, styles and font setup in the result. Interaction: The segmented control is a native radio group (arrow keys move between depths). While focus is anywhere in the article, ] goes one level deeper and [ one level shallower. onLevelChange(level) reports changes. Motion: New text: a highlighter wash held for the first quarter of 1.2s then faded. Length: when the article grows, a clip-path reveal over 320ms cubic-bezier(.2,.8,.2,1) uncovers the new length; shrinking is immediate. With prefers-reduced-motion or motion={false} the change is immediate and new text keeps a static highlight until the next change, so it can still be found. Accessibility: Hidden text is removed from the DOM rather than visually hidden, so assistive tech reads exactly what is shown. The depth control is a fieldset with a legend and native radios that declare their [ ] shortcuts. A polite live region announces the new depth with its word count and reading time. Responsive behavior: Side padding and type size scale with the viewport; the bar wraps under 30rem and stays sticky at the top of the article while reading. Touch: Tap a depth; scroll position is held the same way as with a mouse. Preserve keyboard access, visible focus and reduced-motion behavior. Keep fictional demo data and simulated service calls in a separate usage example; expose callbacks for real actions and recover from rejected requests. Keep usage-example links inside the demo: use local buttons or prevent navigation. Demonstration links must not navigate to Vitrine routes or open other pages; preserve real URL support in the reusable component itself. Verify the result at narrow and wide viewport sizes, with keyboard input and reduced motion enabled.

Notes

Interaction
The segmented control is a native radio group (arrow keys move between depths). While focus is anywhere in the article, ] goes one level deeper and [ one level shallower. onLevelChange(level) reports changes.
Motion
New text: a highlighter wash held for the first quarter of 1.2s then faded. Length: when the article grows, a clip-path reveal over 320ms cubic-bezier(.2,.8,.2,1) uncovers the new length; shrinking is immediate. With prefers-reduced-motion or motion={false} the change is immediate and new text keeps a static highlight until the next change, so it can still be found.
Accessibility
Hidden text is removed from the DOM rather than visually hidden, so assistive tech reads exactly what is shown. The depth control is a fieldset with a legend and native radios that declare their [ ] shortcuts. A polite live region announces the new depth with its word count and reading time.
Responsive
Side padding and type size scale with the viewport; the bar wraps under 30rem and stays sticky at the top of the article while reading.
On touch
Tap a depth; scroll position is held the same way as with a mouse.

Featured

363 components