Skip to content

Repository files navigation

Broadsheet

Fully customizable text page engine with read, write, and interact modes. Pure JS text layout that bypasses DOM reflow entirely — every character is measured and positioned in JavaScript, then projected to pooled DOM elements at 60fps.

Built On

  • pretext by @chenglou — Pure JS text measurement and layout engine. prepare() does one-time canvas measurement, then layout() is pure arithmetic. The wrap-geometry module (polygon hulls, interval carving, hit testing) is ported from pretext's demo code.
  • agentation (forked at Mikeishiring/agentation) — Visual annotation tool for AI agents. Broadsheet's interact mode adapts agentation's annotation patterns using semantic positions (block ID + char offset) instead of CSS selectors.

Inspired by the pretext dynamic-layout demo.

Features

  • Two-column editorial layout with cursor handoff between columns
  • Obstacle avoidance — text flows around rects, circles, polygons, and image alpha hulls
  • Headline auto-fit via binary search (rejects sizes that break words mid-line)
  • Drop cap — large first character spanning N body lines, measured via binary search
  • Pull quote — italic serif block that text wraps around
  • Draggable obstacles — drag any obstacle and text reflows live at 60fps
  • Image hull extractiongetWrapHull() rasterizes images, scans alpha channels, builds polygon hulls
  • Click-to-rotate — rotate image obstacles, hull updates, text reflows around rotated shape
  • Theme system — parchment (warm editorial) and dark editorial themes with smooth transitions
  • Max-width responsive — editorial layout stays readable on ultra-wide monitors

Architecture

src/
  engine/           # Pure-function layout pipeline
    layout-engine   # computeLayout() — columns, obstacles, cursor handoff
    wrap-geometry   # Ported from pretext: interval carving, hull extraction
    dom-projection  # Imperative pooled-div renderer (bypasses React)
    prepare-cache   # PreparedText cache (text+font -> opaque handle)
  components/       # React layer (manages lifecycle, not text rendering)
    PageCanvas      # ResizeObserver + rAF scheduling -> pool.project()
    HeadlineFitter  # Binary-search headline sizing
    DraggableObstacle, CircleObstacle, ImageObstacle
    DropCap, PullQuote, Atmosphere
  themes/           # Static objects (not CSS variables)
    parchment       # Warm parchment + serif
    dark-editorial  # Dark bg + gold accent
    create-theme    # Deep-merge factory

Key decision: Body text lines are rendered via an imperative DOM pool, not React elements. 200+ absolutely-positioned divs change every frame during resize/drag — React reconciliation is too slow. The pool pattern (grow/shrink divs, reuse nodes) comes directly from pretext's demos.

Getting Started

npm install
npx vite --port 4200

Stack

Vite + React 19 + TypeScript, @chenglou/pretext for layout

License

MIT

About

Pure-JavaScript text layout engine for responsive editorial pages with live reflow, obstacle wrapping, and annotation modes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages