WebberUI

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.

How to install Pro components →See the plans →

Loading preview…
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.

32
0.05
0.7
<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

PropTypeDefaultDescription
childrenReact.ReactNodePlace 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
classNamestringAppended to the outer scrollable container's className (set the height here)
trackClassNamestringAppended 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.ReactNodeThe 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
classNamestringAppended to the column container's className

HorizontalInsert

PropTypeDefaultDescription
childrenReact.ReactNodeA 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
classNamestringAppended 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)

On this page