Editorial Layouts

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

The install command below needs a licence to run; without one it returns 401.

How to install Pro components →See the plans →

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add "https://webberui.com/r/vertical-writing-spread.en.json?t=<install token>"

New project or installation trouble? Start here →

Playground

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

32
0.05
0.7
Active edited usageDefault props
<VerticalWritingSpread/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add "https://webberui.com/r/vertical-writing-spread.en.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">Vertical Order</VerticalColumn>
  <VerticalColumn>The text flows in vertical columnsFlowing, from the rightLeftReads……</VerticalColumn>

  <HorizontalInsert>
    <blockquote>“White space is not blank, but breathing.”</blockquote>
  </HorizontalInsert>

  <VerticalColumn>HorizontalQuotes and images appear throughout, giving your eyes a place to rest……</VerticalColumn>
  <VerticalColumn variant="heading">The layout is the narrative.</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

PropTypeDefaultDescription
childrenReact.ReactNode—Place VerticalColumn and HorizontalInsert in reading order; they are laid out right to left
gapnumber32Horizontal gap between columns (px)
columnHeightnumber | string"100%"Height of the vertical columns; fills the container by default, so the container needs its own fixed height
wheelToHorizontalbooleantrueTurn a vertical wheel into horizontal advancement through the columns, releasing page scrolling at the boundaries
showProgressbooleanfalseShow a horizontal reading progress bar at the bottom
fadeEdgesbooleanfalseFade the left and right edges with a mask, hinting that there is more to scroll
staggernumber0.05Interval between characters as a vertical heading enters (seconds)
durationnumber0.7Duration of a single column's or character's entrance (seconds)
oncebooleantruePlays the reveal animation only the first time it comes into view
ariaLabelstring"Vertical writing spread"accessible label applied to the scrollable region
classNamestring—Appended to the outer scrollable container's className (set the height here)
trackClassNamestring—Appended to the inner horizontal track's className (left and right padding, for instance)

VerticalColumn

PropTypeDefaultDescription
variant"heading" | "body""body"heading is a large vertical heading (a plain string enters character by character), body is vertical body text
childrenReact.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
classNamestring—Appended to the column container's className

HorizontalInsert

PropTypeDefaultDescription
childrenReact.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
classNamestring—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; HorizontalInsert breaks the vertical flow with horizontal-tb to 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
  • wheelToHorizontal maps 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 an aria-label; ArrowDown / Space advance and ArrowUp goes back, switching between smooth and instant scrolling according to reduced motion
  • Character-by-character headings keep an sr-only copy 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)