Skip to main content

Layout

Kraken TUI uses Flexbox for layout, powered by the Taffy layout engine. This provides familiar, powerful constraint-based layout entirely in Rust.

Layout Engine Overview

The Layout Module uses Taffy (v0.9.x) for Flexbox constraint resolution. All layout computation happens in the Native Core with zero overhead from the TypeScript layer.

How Layout Works

1

Constraint Specification

You define layout constraints using familiar CSS Flexbox properties like direction, justifyContent, width, padding, etc.
2

Dirty Propagation

When layout properties change, widgets are marked dirty and the dirty flag propagates up to ancestors.
3

Layout Resolution

On render(), Taffy resolves Flexbox constraints only for dirty subtrees, computing absolute positions and dimensions.
4

Caching

Computed layout is cached. Only dirty subtrees recompute (O(dirty nodes) cost).

Dimension Properties

Control widget size using width and height properties.

Setting Dimensions

import { Box } from "kraken-tui";

const box = new Box();

// Fixed dimensions (cells)
box.setWidth(50);      // 50 terminal cells wide
box.setHeight(20);     // 20 terminal cells tall

// Percentage of parent
box.setWidth("100%");  // Fill parent width
box.setHeight("50%");  // Half of parent height

// Auto (content-based)
box.setWidth("auto");  // Size to content
box.setHeight("auto");

Dimension Units

widget.setWidth(40);    // 40 terminal cells
widget.setHeight(10);   // 10 terminal cells
Use case: Fixed-size components like buttons, headers, sidebars.

Dimension Encoding

Dimensions are parsed into (value, unit) tuples:
// From source: ts/src/style.ts
export function parseDimension(val: string | number): [number, number] {
  if (typeof val === "number") return [val, 1];  // Fixed cells
  
  const s = val.trim().toLowerCase();
  if (s === "auto") return [0, 0];               // Auto
  if (s.endsWith("%")) {
    const n = parseFloat(s);
    return [isNaN(n) ? 0 : n, 2];                // Percentage
  }
  const n = parseFloat(s);
  return [isNaN(n) ? 0 : n, 1];                  // Fixed cells
}

Flexbox Properties

Direction

Control the primary axis of child layout:
const box = new Box({ direction: "column" });
box.setDirection("row");
// Children laid out horizontally: [A][B][C]

Justify Content

Distribute children along the primary axis:
box.setJustifyContent("center");
Children packed at the start of the container.
box.setJustifyContent("start");
// [A][B][C]        ·········
Children packed at the end of the container.
box.setJustifyContent("end");
// ·········        [A][B][C]
Children centered in the container.
box.setJustifyContent("center");
// ····· [A][B][C] ·····
Even spacing between children, edges flush.
box.setJustifyContent("space-between");
// [A]     [B]     [C]
Even spacing around children.
box.setJustifyContent("space-around");
//  · [A] · [B] · [C] ·
Perfectly even spacing including edges.
box.setJustifyContent("space-evenly");
// ·· [A] ·· [B] ·· [C] ··

Align Items

Align children along the cross axis:
box.setAlignItems("center");
Children stretch to fill cross-axis.
box.setAlignItems("stretch");
// Each child fills available height/width
Children aligned to cross-axis start.
box.setAlignItems("start");
// Children top-aligned (column) or left-aligned (row)
Children aligned to cross-axis end.
box.setAlignItems("end");
// Children bottom-aligned (column) or right-aligned (row)
Children centered on cross-axis.
box.setAlignItems("center");
// Children vertically centered (column) or horizontally centered (row)
Children aligned by text baseline.
box.setAlignItems("baseline");
// Text baselines aligned

Gap

Set spacing between children:
box.setGap(1, 2);  // rowGap: 1, columnGap: 2
box.setDirection("column");
box.setGap(2, 0);  // 2 cells between rows
// [A]
// ·
// ·
// [B]
// ·
// ·
// [C]

Spacing Properties

Padding

Inner spacing between widget border and children:
// All sides
box.setPadding(1, 1, 1, 1);  // top, right, bottom, left

// Shorthand patterns
box.setPadding(2, 2, 2, 2);  // Uniform
box.setPadding(1, 2, 1, 2);  // Vertical | Horizontal
box.setPadding(1, 2, 3, 2);  // Top | Horizontal | Bottom
Padding is applied inside the widget’s border. It pushes children inward.

Margin

Outer spacing between widget border and siblings:
// All sides
box.setMargin(1, 1, 1, 1);  // top, right, bottom, left

// Shorthand patterns
box.setMargin(2, 2, 2, 2);  // Uniform
box.setMargin(1, 0, 1, 0);  // Vertical spacing only
Margin is applied outside the widget’s border. It pushes siblings away.

Padding vs Margin

// Padding: space inside border
const padded = new Box();
padded.setBorderStyle("single");
padded.setPadding(2, 2, 2, 2);
// ┌───────────┐
// │           │ ← 2 cells padding
// │  Content  │
// │           │
// └───────────┘

// Margin: space outside border
const spaced = new Box();
spaced.setBorderStyle("single");
spaced.setMargin(2, 2, 2, 2);
//
//   ┌─────┐     ← 2 cells margin around
//   │ Box │
//   └─────┘
//

Layout Examples

Three-Column Layout

const container = new Box({ direction: "row", gap: 1 });

const sidebar = new Box();
sidebar.setWidth(20);

const main = new Box();
main.setWidth("100%");  // Fill remaining space

const aside = new Box();
aside.setWidth(20);

container.append(sidebar);
container.append(main);
container.append(aside);

// [Sidebar][─────Main Content─────][Aside]
//    20         (flexible)            20

Centered Dialog

const overlay = new Box({ direction: "column" });
overlay.setWidth("100%");
overlay.setHeight("100%");
overlay.setJustifyContent("center");
overlay.setAlignItems("center");

const dialog = new Box();
dialog.setWidth(40);
dialog.setHeight(10);
dialog.setBorderStyle("rounded");
dialog.setPadding(1, 2, 1, 2);

overlay.append(dialog);

// ·············································
// ·············································
// ·········╭────────────────────╮·············
// ·········│      Dialog        │·············
// ·········╰────────────────────╯·············
// ·············································
const app = new Box({ direction: "column" });
app.setWidth("100%");
app.setHeight("100%");

const header = new Box();
header.setHeight(3);
header.setWidth("100%");

const content = new Box();
content.setWidth("100%");
content.setHeight("100%");  // Fill remaining

const footer = new Box();
footer.setHeight(1);
footer.setWidth("100%");

app.append(header);
app.append(content);
app.append(footer);

// ┌─────────────────┐
// │     Header      │ 3 cells
// ├─────────────────┤
// │                 │
// │     Content     │ Fill
// │                 │
// ├─────────────────┤
// │     Footer      │ 1 cell
// └─────────────────┘

Performance Characteristics

Typical: O(dirty nodes) — only changed subtrees recompute.Worst case: O(all nodes) — when root layout properties change.Optimization: Dirty flags + Taffy’s internal caching minimize redundant computation.
Flexbox resolution is O(n) for typical trees. Complex constraint interactions (percentage + min/max bounds) may increase cost.Mitigation: Keep nesting depth reasonable (< 10 levels typical).
Terminal resize marks root dirty, triggering full layout recomputation. This is acceptable as resize events are infrequent.
For typical dashboards (10–100 widgets), layout computation is sub-millisecond. For stress cases (1,000+ widgets), layout may approach the 16ms frame budget but remains acceptable.

Layout Best Practices

1

Use Percentage for Responsiveness

Percentage widths/heights adapt to terminal size automatically.
main.setWidth("100%");  // Fills available space
2

Fixed Size for UI Chrome

Headers, sidebars, toolbars should use fixed dimensions.
header.setHeight(3);    // Always 3 cells tall
sidebar.setWidth(20);   // Always 20 cells wide
3

Gap over Margin for Children

Use gap instead of per-child margins for consistent spacing.
// Good: Consistent gap
container.setGap(1, 1);

// Avoid: Per-child margins
child1.setMargin(0, 0, 1, 0);
child2.setMargin(0, 0, 1, 0);
4

Minimize Deep Nesting

Keep widget tree depth reasonable (< 10 levels) for optimal layout performance.

Next Steps

Styling

Add colors and text decoration

Widgets

Learn about widget hierarchy

Build docs developers (and LLMs) love