Skip to content

Box

The primary layout component. Uses Yoga (flexbox) for layout.

Import

tsx
import { Box } from "inkx"

Usage

tsx
<Box flexDirection="row" padding={1} borderStyle="single">
  <Text>Hello</Text>
  <Text>World</Text>
</Box>

Props

Layout - Flex Direction

PropTypeDefaultDescription
flexDirection"row" | "column" | "row-reverse" | "column-reverse""column"Main axis direction
flexWrap"wrap" | "nowrap" | "wrap-reverse""nowrap"Whether to wrap children
gapnumber0Gap between children (in both directions)

Layout - Flex Item

PropTypeDefaultDescription
flexGrownumber0How much to grow relative to siblings
flexShrinknumber1How much to shrink relative to siblings
flexBasisnumber | string-Initial size before grow/shrink

Layout - Alignment

PropTypeDefaultDescription
alignItems"flex-start" | "flex-end" | "center" | "stretch" | "baseline""stretch"Cross-axis alignment of children
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline""auto"Override parent's alignItems
alignContent"flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around"-Alignment of wrapped lines
justifyContent"flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly""flex-start"Main-axis alignment of children

Sizing

PropTypeDefaultDescription
widthnumber | string-Fixed width or percentage (e.g., "50%")
heightnumber | string-Fixed height or percentage
minWidthnumber | string-Minimum width
minHeightnumber | string-Minimum height
maxWidthnumber | string-Maximum width
maxHeightnumber | string-Maximum height

Spacing - Padding

PropTypeDefaultDescription
paddingnumber0Padding on all sides
paddingTopnumber0Top padding
paddingBottomnumber0Bottom padding
paddingLeftnumber0Left padding
paddingRightnumber0Right padding
paddingXnumber0Horizontal padding (left + right)
paddingYnumber0Vertical padding (top + bottom)

Spacing - Margin

PropTypeDefaultDescription
marginnumber0Margin on all sides
marginTopnumber0Top margin
marginBottomnumber0Bottom margin
marginLeftnumber0Left margin
marginRightnumber0Right margin
marginXnumber0Horizontal margin
marginYnumber0Vertical margin

Position

PropTypeDefaultDescription
position"relative" | "absolute" | "sticky""relative"Positioning mode
stickyTopnumber0Offset from top when sticky (only with position="sticky")
stickyBottomnumber-Offset from bottom when sticky (only with position="sticky")

Display

PropTypeDefaultDescription
display"flex" | "none""flex"Whether to display the element

Border

PropTypeDefaultDescription
borderStyle"single" | "double" | "round" | "bold" | "singleDouble" | "doubleSingle" | "classic"-Border style
borderColorstring-Border color
borderTopbooleantrueShow top border
borderBottombooleantrueShow bottom border
borderLeftbooleantrueShow left border
borderRightbooleantrueShow right border

Style (Colors and Text Formatting)

PropTypeDefaultDescription
backgroundColorstring-Fill the entire box area with a background color
colorstring-Text color for child content
boldbooleanfalseBold text
dimbooleanfalseDimmed (faint) text
dimColorbooleanfalseAlias for dim (Ink compatibility)
italicbooleanfalseItalic text
underlinebooleanfalseUnderlined text
strikethroughbooleanfalseStrikethrough text
inversebooleanfalseSwap foreground/background colors

Box vs Text backgroundColor

Unlike Ink, inkx's Box supports backgroundColor directly. The background fills the entire computed layout area, so you don't need Text elements with spaces to create filled regions.

Overflow (inkx Only)

PropTypeDefaultDescription
overflow"visible" | "hidden" | "scroll""visible"Overflow behavior
scrollTonumber-Child index to keep visible (for overflow="scroll")

Callbacks

PropTypeDefaultDescription
onLayout(layout: ComputedLayout) => void-Called when layout is computed

The ComputedLayout type:

ts
interface ComputedLayout {
  x: number // X position relative to root
  y: number // Y position relative to root
  width: number // Computed width in columns
  height: number // Computed height in rows
}

Border Styles Reference

StyleExampleCharacters Used
single┌─┐
│ │
└─┘
Light box-drawing characters
double╔═╗
║ ║
╚═╝
Double-line box-drawing
round╭─╮
│ │
╰─╯
Rounded corners, single lines
bold┏━┓
┃ ┃
┗━┛
Heavy/bold box-drawing
singleDouble╓─╖
║ ║
╙─╜
Single horizontal, double vertical
doubleSingle╒═╕
│ │
╘═╛
Double horizontal, single vertical
classic+-+
| |
+-+
ASCII characters only

Examples

Row Layout

tsx
<Box flexDirection="row" gap={2}>
  <Text>Left</Text>
  <Text>Middle</Text>
  <Text>Right</Text>
</Box>

Output:

Left  Middle  Right

Column Layout

tsx
<Box flexDirection="column">
  <Text>Line 1</Text>
  <Text>Line 2</Text>
  <Text>Line 3</Text>
</Box>

Output:

Line 1
Line 2
Line 3

Equal Width Columns

tsx
<Box flexDirection="row">
  <Box flexGrow={1} borderStyle="single">
    <Text>Column 1</Text>
  </Box>
  <Box flexGrow={1} borderStyle="single">
    <Text>Column 2</Text>
  </Box>
  <Box flexGrow={1} borderStyle="single">
    <Text>Column 3</Text>
  </Box>
</Box>

Centered Content

tsx
<Box justifyContent="center" alignItems="center" height={10}>
  <Text>Centered!</Text>
</Box>

Fixed and Flexible Layout

Use flexShrink={0} for fixed elements and flexGrow={1} for flexible areas:

tsx
<Box flexDirection="column" height="100%">
  {/* Fixed header - won't shrink */}
  <Box height={1} flexShrink={0} backgroundColor="blue">
    <Text color="white" bold>
      Header
    </Text>
  </Box>

  {/* Flexible content - fills remaining space */}
  <Box flexGrow={1}>
    <Text>Content area</Text>
  </Box>

  {/* Fixed footer - won't shrink */}
  <Box height={1} flexShrink={0}>
    <Text dimColor>Footer</Text>
  </Box>
</Box>

Scrollable List

tsx
const [selected, setSelected] = useState(0)

;<Box flexDirection="column" height={5} overflow="scroll" scrollTo={selected}>
  {items.map((item, i) => (
    <Text key={i} inverse={i === selected}>
      {item}
    </Text>
  ))}
</Box>

Sticky Header in Scrollable Container

tsx
<Box flexDirection="column" height={10} overflow="scroll" scrollTo={selected}>
  {/* This header stays visible when scrolling */}
  <Box position="sticky" stickyTop={0} backgroundColor="blue">
    <Text color="white" bold>
      Pinned Header
    </Text>
  </Box>

  {items.map((item, i) => (
    <Text key={i} inverse={i === selected}>
      {item}
    </Text>
  ))}
</Box>

Absolute Positioning

tsx
<Box position="relative" width={40} height={10}>
  <Text>Background content</Text>

  {/* Positioned absolutely within the parent */}
  <Box position="absolute">
    <Text color="red">Overlay</Text>
  </Box>
</Box>

Border Styles

tsx
// Single line border
<Box borderStyle="single">
  <Text>Content</Text>
</Box>

// Rounded corners
<Box borderStyle="round">
  <Text>Content</Text>
</Box>

// Double line border
<Box borderStyle="double">
  <Text>Content</Text>
</Box>

// Colored border
<Box borderStyle="single" borderColor="green">
  <Text>Content</Text>
</Box>

// Partial borders
<Box borderStyle="single" borderTop borderBottom borderLeft={false} borderRight={false}>
  <Text>Top and bottom only</Text>
</Box>

Filled Background

tsx
// Header bar with cyan background
<Box backgroundColor="cyan" paddingX={1}>
  <Text color="black" bold>
    Title
  </Text>
</Box>

// Sidebar indicator that fills available height
<Box
  width={1}
  flexGrow={1}
  backgroundColor="gray"
  justifyContent="center"
  alignItems="center"
>
  <Text color="white">›</Text>
</Box>

Using onLayout

tsx
function MeasuredBox() {
  const [size, setSize] = useState({ width: 0, height: 0 })

  return (
    <Box flexGrow={1} onLayout={(layout) => setSize({ width: layout.width, height: layout.height })}>
      <Text>
        Size: {size.width}x{size.height}
      </Text>
    </Box>
  )
}

Conditional Display

tsx
<Box flexDirection="column">
  <Text>Always visible</Text>
  <Box display={showDetails ? "flex" : "none"}>
    <Text>Conditionally shown</Text>
  </Box>
</Box>

Space Distribution

tsx
// Evenly distributed items
<Box flexDirection="row" justifyContent="space-between" width={40}>
  <Text>Left</Text>
  <Text>Center</Text>
  <Text>Right</Text>
</Box>

// Equal spacing around items
<Box flexDirection="row" justifyContent="space-around" width={40}>
  <Text>A</Text>
  <Text>B</Text>
  <Text>C</Text>
</Box>

// Equal spacing between items (including edges)
<Box flexDirection="row" justifyContent="space-evenly" width={40}>
  <Text>A</Text>
  <Text>B</Text>
  <Text>C</Text>
</Box>

Percentage Sizing

tsx
<Box flexDirection="row" width="100%">
  {/* Takes 30% of parent width */}
  <Box width="30%" backgroundColor="blue">
    <Text>Sidebar</Text>
  </Box>

  {/* Takes 70% of parent width */}
  <Box width="70%" backgroundColor="gray">
    <Text>Main content</Text>
  </Box>
</Box>

Released under the MIT License.