Vertical Writing Spread
A Chinese/Japanese vertical writing editorial layout built with CSS writing-mode — vertical columns flow right to left with horizontal pull quotes and images interleaved between them, text reveals character by character from top to bottom on entry, and horizontal scrolling advances the column order.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/vertical-writing-spread.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<VerticalWritingSpread />
Installation
npx shadcn@latest add "https://webberui.com/r/vertical-writing-spread.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/vertical-writing-spread.
Usage
Use VerticalWritingSpread as the container and place vertical VerticalColumn and horizontal HorizontalInsert elements inside it in reading order. The layout flows right to left, so the first column sits at the far right; the container needs a fixed height (the vertical columns fill it):
import {
VerticalWritingSpread,
VerticalColumn,
HorizontalInsert,
} from "@/components/ui/vertical-writing-spread";
<VerticalWritingSpread className="h-[320px]" showProgress fadeEdges>
<VerticalColumn variant="heading">直排的秩序</VerticalColumn>
<VerticalColumn>正文以直欄流動,自右向左閱讀……</VerticalColumn>
<HorizontalInsert>
<blockquote>「留白不是空白,而是呼吸。」</blockquote>
</HorizontalInsert>
<VerticalColumn>橫排引文與圖片穿插其間,成為視線的停頓……</VerticalColumn>
<VerticalColumn variant="heading">版面即敘事</VerticalColumn>
</VerticalWritingSpread>Vertical columns reveal character by character from top to bottom as they come into view. wheelToHorizontal is on by default, so a vertical wheel turns into horizontal advancement through the columns, releasing page scrolling automatically once either end is reached.
Props
VerticalWritingSpread
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Place VerticalColumn and HorizontalInsert in reading order; they are laid out right to left |
gap | number | 32 | Horizontal gap between columns (px) |
columnHeight | number | string | "100%" | Height of the vertical columns; fills the container by default, so the container needs its own fixed height |
wheelToHorizontal | boolean | true | Turn a vertical wheel into horizontal advancement through the columns, releasing page scrolling at the boundaries |
showProgress | boolean | false | Show a horizontal reading progress bar at the bottom |
fadeEdges | boolean | false | Fade the left and right edges with a mask, hinting that there is more to scroll |
stagger | number | 0.05 | Interval between characters as a vertical heading enters (seconds) |
duration | number | 0.7 | Duration of a single column's or character's entrance (seconds) |
once | boolean | true | Plays the reveal animation only the first time it comes into view |
ariaLabel | string | "Vertical writing spread" | accessible label applied to the scrollable region |
className | string | — | Appended to the outer scrollable container's className (set the height here) |
trackClassName | string | — | Appended to the inner horizontal track's className (left and right padding, for instance) |
VerticalColumn
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "heading" | "body" | "body" | heading is a large vertical heading (a plain string enters character by character), body is vertical body text |
children | React.ReactNode | — | The column content; passing a string to a heading lets it reveal character by character |
writingMode | "vertical-rl" | "vertical-lr" | "vertical-rl" | Direction of the vertical writing |
className | string | — | Appended to the column container's className |
HorizontalInsert
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | A horizontal block interleaved between vertical columns: a pull quote, an image, or a caption |
align | "start" | "center" | "end" | "center" | Vertical alignment within the vertical flow |
className | string | — | Appended to the block container's className |
How it works
- Vertical writing is built with CSS
writing-mode: vertical-rl, so the body text descends vertically and wraps automatically into multiple vertical lines;HorizontalInsertbreaks the vertical flow withhorizontal-tbto carry horizontal pull quotes and images - The layout runs right to left: the track uses
flex-row-reverse, the first column sits at the far right, and it scrolls to the far right automatically on mount, matching the Chinese and Japanese reading direction - Entrance detection is driven by IntersectionObserver (with the scroll container as the root); headings reveal character by character from top to bottom with a stagger, and body columns are revealed top-down via
clip-path wheelToHorizontalmaps the vertical wheel onto horizontal advancement, so scrolling down moves the column order forward (to the left); once either end is reached, page scrolling is released rather than locked
Accessibility
- The scrollable region is a focusable
role="region"with anaria-label;ArrowDown/Spaceadvance andArrowUpgoes back, switching between smooth and instant scrolling according to reduced motion - Character-by-character headings keep an
sr-onlycopy of the complete text, and the split graphemes are hidden from assistive technology (aria-hidden), so what is announced is complete words and phrases - When the user has "reduce motion" enabled at the system level, the vertical writing layout and horizontal scrolling are preserved and only the reveal and stagger animations are disabled (the content appears in full immediately)
Pull Quote Lift
When a sentence inside a paragraph scrolls into the viewport, a shared-element animation lifts it out and scales it up into a full-width pull quote, dropping it back inline when it leaves the viewport.
Chaptered Report Shell
An annual-report / white-paper page shell — a sticky chapter rail generated automatically from the content, giant chapter numbers cross-fading between chapters, full-bleed color-block transitions between sections, and a dual reading-progress indicator.