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
Constraint Specification
You define layout constraints using familiar CSS Flexbox properties like direction, justifyContent, width, padding, etc.
Dirty Propagation
When layout properties change, widgets are marked dirty and the dirty flag propagates up to ancestors.
Layout Resolution
On render(), Taffy resolves Flexbox constraints only for dirty subtrees, computing absolute positions and dimensions.
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
Fixed (Cells)
Percentage
Auto
widget . setWidth ( 40 ); // 40 terminal cells
widget . setHeight ( 10 ); // 10 terminal cells
Use case: Fixed-size components like buttons, headers, sidebars.widget . setWidth ( "100%" ); // Fill parent width
widget . setHeight ( "50%" ); // Half parent height
Use case: Responsive layouts that adapt to terminal size.widget . setWidth ( "auto" ); // Size to content
widget . setHeight ( "auto" );
Use case: Content-driven sizing like text blocks.
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" });
row
column
row-reverse
column-reverse
box . setDirection ( "row" );
// Children laid out horizontally: [A][B][C]
box . setDirection ( "column" );
// Children laid out vertically:
// [A]
// [B]
// [C]
box . setDirection ( "row-reverse" );
// Children laid out horizontally reversed: [C][B][A]
box . setDirection ( "column-reverse" );
// Children laid out vertically reversed:
// [C]
// [B]
// [A]
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]
box . setDirection ( "row" );
box . setGap ( 0 , 3 ); // 3 cells between columns
// [A] ··· [B] ··· [C]
box . setGap ( 1 , 2 ); // rowGap: 1, columnGap: 2
// Mixed spacing for grid-like layouts
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
// └─────────────────┘
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
Use Percentage for Responsiveness
Percentage widths/heights adapt to terminal size automatically. main . setWidth ( "100%" ); // Fills available space
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
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 );
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