Vertical Spring Couplet
A spring couplet layout with vertically written upper and lower scrolls plus a top banner — gold on red, revealed stroke by stroke.
writing-mode: vertical-rl lays out the traditional spring couplet form — the upper scroll on the top right, the lower scroll on the bottom left — with a banner across the top and an optional diamond 「福」 character. The background has a red velvet texture and the characters are embossed with a gold gradient; on reveal each character settles like a brush stroke landing (from enlarged and blurred down to sharp and in place), and hovering a scroll makes it flutter slightly like paper.
npx shadcn@latest add https://webberui.com/r/vertical-couplet.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<VerticalCouplet />
Installation
npx shadcn@latest add https://webberui.com/r/vertical-couplet.jsonOr, once registries are configured in components.json, install it as @webberui/vertical-couplet.
Usage
import { VerticalCouplet } from "@/components/ui/vertical-couplet";
<VerticalCouplet
upper="天增歲月人增壽"
lower="春滿乾坤福滿門"
horizontal="四季平安"
showFu
/>By tradition the upper scroll is pasted on the right as you face the door and the lower scroll on the left, and the component lays them out that way — while the DOM order stays in the reading order "banner → upper scroll → lower scroll". The banner reads left to right by modern convention; if you want the traditional right-to-left reading, pass the reversed string.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
upper | string | — | Upper scroll, written vertically top to bottom, visually on the right |
lower | string | — | Lower scroll, visually on the left |
horizontal | string | — | The top banner; omit it and no banner is shown |
showFu | boolean | false | Whether to paste a diagonal diamond 「福」 character between the two scrolls |
stagger | number | 0.1 | Interval between characters as they land stroke by stroke (seconds) |
duration | number | 0.5 | Duration of a single character's animation from suspended to settled (seconds) |
className | string | — | Forwarded to the outer container |
Accessibility
- When the user has "reduce motion" enabled at the system level, the per-character landing and the hover flutter are skipped and the complete couplet is shown statically
- The complete text of each scroll is provided
sr-onlyfor assistive technology, and the split per-character spans are allaria-hidden - The DOM order is the correct reading order (banner → upper scroll → lower scroll), so screen readers are unaffected by the right-to-left visual arrangement
- The text is real DOM text rather than an image, so it can be selected, searched, and translated