Chinese Localization

CJK Vertical Nav

A side navigation bar in vertical Chinese writing — writing-mode vertical typesetting, with an ink line marking the current item.

A side navigation bar in vertical Chinese writing (writing-mode: vertical-rl), with the columns running right to left like the table of contents of a classical book. The current item is marked by an ink-brush vertical line on its right, which glides between sections on a spring animation via layoutId; on hover the letter spacing widens slightly and the text color deepens. Pair it with an IntersectionObserver to track the article's scroll position.

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 cjk-vertical-nav.en

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.

Active edited usageDefault props
<CjkVerticalNav/>

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/cjk-vertical-nav.en.json

Or, once registries are configured in components.json, install it as @webberui/cjk-vertical-nav.

Usage

Uncontrolled usage: it maintains the current item itself and switches on click.

import { CjkVerticalNav } from "@/components/ui/cjk-vertical-nav";

<CjkVerticalNav
  items={[
    { id: "prologue", label: "Prologue" },
    { id: "history", label: "Tea History" },
    { id: "tasting", label: "The Way of Tea Tasting" },
  ]}
  defaultActiveId="prologue"
/>

Controlled usage: pair it with an IntersectionObserver to track the section being scrolled through and write the result back to activeId, and the ink line follows along automatically.

const [activeId, setActiveId] = React.useState("prologue");

React.useEffect(() => {
  const observer = new IntersectionObserver(
    (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) setActiveId(entry.target.id);
      }
    },
    { rootMargin: "-40% 0px -55% 0px" },
  );
  document
    .querySelectorAll("section[id]")
    .forEach((el) => observer.observe(el));
  return () => observer.disconnect();
}, []);

<CjkVerticalNav
  items={items}
  activeId={activeId}
  onSelect={(id) => {
    setActiveId(id);
    document.getElementById(id)?.scrollIntoView({ behavior: "smooth" });
  }}
/>;

Props

PropTypeDefaultDescription
itemsCjkVerticalNavItem[]—The navigation item list (id and label), arranged right to left in vertical reading order
activeIdstring—Controlled mode: the id of the currently active item
defaultActiveIdstringfirst itemInitial active item id in uncontrolled mode
onSelect(id: string) => void—Callback when an item is selected
ariaLabelstring"Chapter Guide"Accessible name for the nav landmark
classNamestring—Forwarded to the outermost nav

Accessibility

  • The outermost element is a nav landmark with an aria-label, so screen readers can jump straight to the navigation region
  • The current item is marked aria-current="true", so assistive technology knows which section you are in
  • Items are native buttons supporting Tab focus and Enter/Space activation, with a clear focus-visible outline
  • When the user has "reduce motion" enabled at the system level, the ink line changes position instantly with no spring movement animation