Editorial Layouts

Running Head Folio

A book running-head system — a sticky thin bar showing the current chapter title on the left and a page number derived from the scroll position on the right, with the chapter title flipping like a turning page and the folio incrementing in step at chapter boundaries.

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 running-head-folio.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.

1
40
300
Active edited usageDefault props
<RunningHeadFolio/>

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/running-head-folio.en.json

Or, once registries are configured in components.json, install it as @webberui/running-head-folio.

Usage

Wrap a number of RunningHeadFolioChapter elements in RunningHeadFolio; each chapter's title becomes the chapter name on the running-head bar. If the reading area is a nested scroll container, pass the container ref to container.

import {
  RunningHeadFolio,
  RunningHeadFolioChapter,
} from "@/components/ui/running-head-folio";

export function Reader() {
  const scrollerRef = React.useRef<HTMLDivElement>(null);

  return (
    <div ref={scrollerRef} className="h-[70vh] overflow-y-auto">
      <RunningHeadFolio container={scrollerRef} center="progress" startPage={1}>
        <RunningHeadFolioChapter title="Prologue">
          {/* chapter content */}
        </RunningHeadFolioChapter>
        <RunningHeadFolioChapter title="Chapter One">
          {/* chapter content */}
        </RunningHeadFolioChapter>
      </RunningHeadFolio>
    </div>
  );
}

center switches the middle field: "progress" shows a thin reading-progress line, "title" shows bookTitle, and "none" leaves it blank. The page number's presentation can be customized with format, for example as Roman numerals:

<RunningHeadFolio
  container={scrollerRef}
  center="title"
  bookTitle="Notes on Kinetic Typography"
  format={(p) => `p. ${p}`}
>
  {/* … */}
</RunningHeadFolio>

Props

RunningHeadFolio

PropTypeDefaultDescription
childrenReactNode—Put RunningHeadFolioChapter children here
containerRefObject<HTMLElement | null>—Ref of the nested scroll container; defaults to the window as the scroll container
center"title" | "progress" | "none""progress"Style of the middle field
bookTitleReactNode—The book title in the middle (used when center="title")
startPagenumber1Starting page number
pageHeightnumberone screen height of the containerScroll distance corresponding to one page (px)
barHeightnumber40Height of the running-head bar (px)
format(page: number) => ReactNode(p) => pCustomize how the page number is presented
classNamestring—Class for the outer container
barClassNamestring—Class for the running-head bar (can override the background color)

RunningHeadFolioChapter

PropTypeDefaultDescription
titleReactNode—Chapter name, shown on the running-head bar
childrenReactNode—Chapter content
idstring—Anchor id for the chapter container
classNamestring—Class for the chapter container

How it works

  • Page number derivation: the useScroll progress is mapped onto [startPage, startPage + total pages − 1]; the total page count comes from the total content height divided by pageHeight, and when pageHeight is unspecified it takes one screen height of the scroll container, so "one screen is roughly one page".
  • Current chapter detection: the bottom edge of the running-head bar is the baseline, and the last chapter whose top has crossed that baseline is the current one, so the chapter title switches exactly in step with the reading position.
  • Flip direction: the flip direction recognizes the scroll direction — when incrementing downward the new value flips in from below, and when going back upward it flips away in reverse.
  • Adaptivity: a ResizeObserver watches the content and container size, recomputing the total page count and the current chapter whenever the size changes.

Accessibility

  • The running-head bar is marked role="status" with an aria-label, containing a plain-text summary (which chapter, which page) for assistive technology to announce; the visually duplicated flip content is marked aria-hidden so nothing is announced twice.
  • Each chapter is a native section, and a string title becomes its aria-label.
  • When "reduce motion" is enabled at the system level, the page-flip animation becomes a very short fade, and all other information and layout are fully preserved.