WebberUI

Woodtype Flush Block

A woodtype flush heading — each line of a multi-line heading is solved onto the variable font's wdth axis so it stretches flush to both edges of the container, and on reveal the lines spring open from an extremely narrow width to the solved width, one after another.

Loading preview…
npx shadcn@latest add https://webberui.com/r/woodtype-flush-block.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

200
0.34
0.09
0.9
0.32
<WoodtypeFlushBlock />

Installation

npx shadcn@latest add https://webberui.com/r/woodtype-flush-block.json

Or, once registries are configured in components.json, install it as @webberui/woodtype-flush-block.

Usage

import { WoodtypeFlushBlock } from "@/components/ui/woodtype-flush-block";

<WoodtypeFlushBlock
  text={"THE\nWOODTYPE\nREVIVAL"}
  className="font-black uppercase leading-[0.92]"
  lineClassName="text-6xl"
/>

Split lines with the \n newline character, or pass an array through lines instead:

<WoodtypeFlushBlock
  lines={["THE", "WOODTYPE", "REVIVAL"]}
  className="font-black uppercase"
/>

For the wdth axis to genuinely take effect, the container's typeface must be a variable font with a width (wdth) axis (for example Roboto Flex, Recursive, or Fraunces). If the typeface does not support that axis, the component automatically falls back to horizontal scaling to make up the difference, and still keeps the flush alignment and the spring-open effect.

Props

PropTypeDefaultDescription
textstringHeading text, split into lines by the \n newline character
linesstring[]Specify each line's text directly (takes precedence over text)
as"h1"–"h6" | "p" | "div""h2"The semantic tag to render
wdthMinnumber25Lower bound of the wdth axis solve (the narrowest value when it cannot fill the width)
wdthMaxnumber200Upper bound of the wdth axis solve (the widest value when it overshoots)
entryWdthnumberwdthMinThe extremely narrow wdth value the reveal starts from
entryCompressnumber0.34Additional horizontal compression ratio at the start of the reveal, returning to 1 once sprung open
delaynumber0Overall delay before the reveal starts (seconds)
staggernumber0.09Interval between lines springing open in sequence (seconds)
durationnumber0.9Duration of one line's spring-open animation (seconds)
bouncenumber0.32Amount of bounce as a line springs open (0–1)
oncebooleantruePlays only the first time the element enters the viewport
lineClassNamestringAppended to each line's class (commonly used to set the font size)

How it works

  • Solving for flush lines: the most captivating thing about woodtype composition is that lines with different character counts are all stretched flush to both edges. The component creates one hidden measurement span per line, measures its actual width at different wdth values, then binary-searches for the wdth value that fills the container's content width exactly — a short line (few characters) needs a wider width, a long line (many characters) needs a narrower one, so each line naturally ends up with a different wdth.
  • Pixel-level compensation: a variable font's wdth axis has its own upper and lower bounds, so an extremely short or long line may not fill or may overshoot on width alone. After solving, the component makes up the last few pixels with a residual scaleX to guarantee every line is truly flush; if the typeface has no wdth axis at all, scaleX achieves the flush alignment on its own and the effect still holds.
  • Spring-open reveal: on entering the viewport, each line starts from entryWdth (an extremely narrow width) plus the extra compression from entryCompress, like a slender column of woodtype, and springs open to the solved width in sequence according to stagger; bounce controls the spring-back feel. The width is driven by the CSS variable --wb-wdth, and font-variation-settings reflects its changes live.
  • Responsive re-solving: a ResizeObserver watches the container width, and the solve runs once more after document.fonts.ready (when the web fonts have loaded); when the container changes width, every line smoothly re-justifies. The measurement and solving both complete inside a layout effect before the first paint, avoiding layout flashes.
  • Typesetting advice: a heavy weight (font-black), uppercase, and tight leading best convey the dense, stacked feel of a woodtype poster; set the font size on lineClassName and keep shared styles such as weight, tracking, and colour on the container's className.

Accessibility

  • When the user has "reduce motion" enabled at the system level, the flush heading is presented directly at its solved width with no spring-open reveal animation
  • The complete heading text lives in sr-only so a screen reader reads it once; both the visual layer and the hidden measurement layer are aria-hidden decorative copies and are never read twice
  • The heading renders with a semantic tag (h2 by default, adjustable through as), preserving the document outline hierarchy

On this page