Layout

Box model and flex‑like composition.

Layout model

Cascade layout is box-based and uses a Yoga-like flex engine. Every element participates in layout, so you can build complex terminal UIs without manually computing coordinates.

In practice, you will spend most of your time with:

- flexDirection
- justifyContent
- alignItems
- width / height
- flexGrow / flexShrink
- padding / margin
- position + offsets for overlays

Flexbox basics

Use a row container to place panels side-by-side. One panel can be fixed width, the other can grow to fill remaining space.

<box flexDirection="row" width="100%" height="100%" padding={1}>
  <box width={24} border padding={1}>
    <text content="Sidebar" />
  </box>
  <box flexGrow={1} border padding={1}>
    <text content="Main content" />
  </box>
</box>

Use justifyContent to distribute children along the main axis and alignItems along the cross axis.

<box flexDirection="row" justifyContent="space-between" alignItems="center" width="100%" height={3}>
  <text content="Left" />
  <text content="Center" />
  <text content="Right" />
</box>

Sizing (fixed, percentage, grow/shrink)

You can mix fixed and percentage sizing. Use fixed sizes for navigation sidebars and toolbars, and use flex growth for main content.

Guideline: if a child should fill remaining space, set flexGrow to 1. If it should stay fixed, keep flexGrow at 0 and provide an explicit width/height.

<box flexDirection="row" width="100%" height={10}>
  <box width="30%" border />
  <box width="70%" border />
</box>

Positioning and overlays

Use absolute positioning for overlays: toasts, tooltips, modals, floating help. Keep overlays in a dedicated layer (higher zIndex) when you have multiple stacked elements.

<box width="100%" height="100%">
  <box flexGrow={1} border />
  <box position="absolute" right={1} bottom={1} border padding={1} backgroundColor="#24283b">
    <text content="Saved" />
  </box>
</box>

Imperative layout

In imperative code you set the same flex properties directly on renderables. This approach is useful when you want to compute sizes from terminal dimensions.

import { BoxRenderable, TextRenderable } from "@cascadetui/core"

const header = new BoxRenderable(renderer, { height: 3, border: true, alignItems: "center" })
header.add(new TextRenderable(renderer, { content: "LAYOUT DEMO" }))

const content = new BoxRenderable(renderer, { flexGrow: 1, flexDirection: "row" })
const sidebar = new BoxRenderable(renderer, { width: 24, border: true })
const main = new BoxRenderable(renderer, { flexGrow: 1, border: true, marginLeft: 1 })

content.add(sidebar)
content.add(main)

renderer.root.add(header)
renderer.root.add(content)