Skip to content

Box ​

The primary layout component. Uses Flexily for CSS-spec-compliant flexbox layout.

Import ​

tsx
import { Box } from "silvery"

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. Works in scroll containers and as a bottom-pinning primitive in any parent with explicit height.

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, Silvery'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 (Silvery 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>