Sone

Layout System

Understanding flexbox layouts and positioning in Sone

Layout System

Sone's layout system is based on Flexbox (using Yoga Layout) and provides powerful tools for creating responsive, flexible layouts.

Container Components

Column

Creates a vertical flex container (default flex direction is column):

import { Column, Text } from "sone";

Column(
  Text("First item"),
  Text("Second item"), 
  Text("Third item")
)
.gap(10)
.padding(20)

Row

Creates a horizontal flex container:

import { Row, Text } from "sone";

Row(
  Text("Left"),
  Text("Center"),
  Text("Right")
)
.justifyContent("space-between")
.alignItems("center")
.padding(20)

Flexbox Properties

Flex Direction

Change the primary axis of layout:

// Vertical layout (default for Column)
Column(...)
  .direction("column")

// Horizontal layout (default for Row)  
Row(...)
  .direction("row")

// Reversed layouts
Column(...).direction("column-reverse")
Row(...).direction("row-reverse")

Justify Content

Controls alignment along the main axis:

Row(
  Text("Item 1"),
  Text("Item 2"),
  Text("Item 3")
)
.justifyContent("space-between")  // "flex-start", "flex-end", "center", 
                                  // "space-between", "space-around", "space-evenly"

Align Items

Controls alignment along the cross axis:

Row(
  Text("Short"),
  Text("Taller\nText"),
  Text("T")
)
.alignItems("center")  // "flex-start", "flex-end", "center", "stretch", "baseline"
.height(100)

Align Content

Controls alignment of wrapped lines:

Row(
  Text("Item 1"),
  Text("Item 2"),
  Text("Item 3"),
  Text("Item 4")
)
.wrap("wrap")
.alignContent("space-around")  // "flex-start", "flex-end", "center", "stretch",
                               // "space-between", "space-around", "space-evenly"
.width(200)

Flex Items

Flex Grow, Shrink, and Basis

Control how items grow and shrink:

Row(
  Text("Fixed").width(100),
  Text("Flexible").flex(1),      // Takes remaining space
  Text("2x Flexible").flex(2)    // Takes 2x more space than flex(1)
)
.width(400)

More granular control:

Text("Content")
  .grow(1)        // How much to grow (default: 0)
  .shrink(1)      // How much to shrink (default: 1) 
  .basis(100)     // Initial size before growing/shrinking

Align Self

Override alignItems for individual items:

Row(
  Text("Top").alignSelf("flex-start"),
  Text("Center").alignSelf("center"),  
  Text("Bottom").alignSelf("flex-end")
)
.alignItems("stretch")  // Default for all items
.height(100)

Spacing

Gap

Space between flex items:

Row(
  Text("Item 1"),
  Text("Item 2"),
  Text("Item 3")
)
.gap(20)           // Same gap for both directions
.rowGap(15)        // Vertical gap
.columnGap(25)     // Horizontal gap

Padding

Internal spacing within components:

// Single value (all sides)
Text("Padded").padding(20)

// Two values (vertical, horizontal)  
Text("Padded").padding(15, 30)

// Three values (top, horizontal, bottom)
Text("Padded").padding(10, 20, 15)

// Four values (top, right, bottom, left)
Text("Padded").padding(10, 20, 15, 25)

// Individual sides
Text("Padded")
  .paddingTop(10)
  .paddingRight(20)
  .paddingBottom(15)
  .paddingLeft(25)

Margin

External spacing around components:

// Same API as padding
Text("With margin").margin(20)

// Auto margins for centering
Text("Centered").margin("auto")
Text("Right aligned").marginLeft("auto")

Sizing

Width and Height

Text("Fixed size")
  .width(200)
  .height(100)

// Convenient size method (width, height)
Text("Square").size(100)        // 100x100
Text("Rectangle").size(200, 100) // 200x100

// Percentage values
Text("Half width").width("50%")

// Auto sizing
Text("Auto width").width("auto")

Min/Max Constraints

Text("Constrained content")
  .minWidth(100)
  .maxWidth(300)  
  .minHeight(50)
  .maxHeight(200)

Aspect Ratio

Maintain proportions:

Column(
  Text("16:9 aspect ratio")
)
.aspectRatio(16/9)
.width(320)  // Height will be 180

Positioning

Position Types

// Static positioning (default)
Text("Normal flow").position("static")

// Relative positioning  
Text("Offset from normal position")
  .position("relative")
  .top(10)
  .left(20)

// Absolute positioning
Text("Positioned relative to container")
  .position("absolute")
  .top(50)
  .right(30)

Position Values

Text("Positioned")
  .position("absolute")
  .top(20)        // Distance from top
  .right(30)      // Distance from right  
  .bottom(40)     // Distance from bottom
  .left(50)       // Distance from left
  
// Percentage values
Text("Centered")
  .position("absolute")
  .top("50%")
  .left("50%")

Convenient Position Shortcuts

Text("Shortcuts")
  .start(10)      // Left in LTR, right in RTL
  .end(20)        // Right in LTR, left in RTL
  .inset(15)      // All sides

Wrapping

Flex Wrap

Control line wrapping:

Row(
  Text("Item 1").width(150),
  Text("Item 2").width(150), 
  Text("Item 3").width(150),
  Text("Item 4").width(150)
)
.wrap("wrap")          // "nowrap", "wrap", "wrap-reverse"
.width(300)            // Forces wrapping

Text Wrapping

Text("Long text that should wrap properly...")
  .width(200)
  .wrap(true)      // Enable wrapping (default)

Text("No wrapping text")
  .nowrap()        // Disable wrapping

Box Model

Box Sizing

Control how width/height are calculated:

Text("Content box")
  .boxSizing("content-box")  // Width excludes padding/border (default)

Text("Border box") 
  .boxSizing("border-box")   // Width includes padding/border
  .padding(20)
  .borderWidth(2)
  .width(200)                // Total width is 200px

Overflow

Control content overflow:

Text("Long content that might overflow the container...")
  .width(100)
  .height(50)
  .overflow("hidden")    // "visible", "hidden", "scroll"

Practical Layout Examples

Card Grid

function CardGrid(cards) {
  return Row(
    ...cards.map(card => 
      Column(
        Text(card.title).weight("bold").size(16),
        Text(card.content).size(14).color("gray")
      )
      .flex(1)
      .padding(20)
      .bg("white")
      .rounded(8)
      .margin(10)
    )
  )
  .wrap("wrap")
  .justifyContent("center");
}
function SidebarLayout(sidebar, main) {
  return Row(
    Column(sidebar)
      .width(250)
      .bg("#f5f5f5")
      .padding(20),
      
    Column(main)
      .flex(1)
      .padding(30)
  )
  .height("100%");
}

Centered Content

function CenteredCard(content) {
  return Column(
    Column(content)
      .maxWidth(400)
      .padding(40)
      .bg("white")
      .rounded(12)
      .shadow("0 4px 12px rgba(0,0,0,0.1)")
  )
  .justifyContent("center")
  .alignItems("center")
  .minHeight(400);
}

Header with Navigation

function Header(logo, navItems) {
  return Row(
    // Logo section
    Row(logo).flex(0),
    
    // Navigation section  
    Row(
      ...navItems.map(item => 
        Text(item.label)
          .size(16)
          .color("black")
          .padding(10, 15)
      )
    )
    .gap(10)
    .flex(1)
    .justifyContent("center"),
    
    // Actions section (empty but reserves space)
    Row().flex(0).width(100)
  )
  .alignItems("center")
  .padding(20)
  .bg("white")
  .shadow("0 2px 4px rgba(0,0,0,0.1)");
}

Responsive Container

function ResponsiveContainer(content) {
  return Column(content)
    .width("100%")
    .maxWidth(1200)      // Max width on large screens
    .margin("auto")      // Center horizontally
    .padding(20, 40)     // Responsive padding
}

Form Layout

function FormField(label, input) {
  return Column(
    Text(label).size(14).weight("500").marginBottom(8),
    input
  )
  .marginBottom(20)
  .width("100%");
}

function Form(fields, submitButton) {
  return Column(
    ...fields,
    Row(submitButton)
      .justifyContent("flex-end")
      .marginTop(20)
  )
  .maxWidth(400)
  .margin("auto")
  .padding(30);
}

Best Practices

Layout Strategy

  • Start with the overall structure (rows/columns)
  • Use flex properties for responsive behavior
  • Apply spacing consistently with gap/padding/margin

Performance

  • Avoid deeply nested layouts when possible
  • Use flex properties instead of fixed sizes where appropriate
  • Consider the layout reflow impact of dynamic content

Responsive Design

  • Use percentage widths and max-widths
  • Leverage flex grow/shrink for adaptable components
  • Test layouts with different content sizes

Debugging

  • Use the tag() method to identify components during development
  • Start simple and build complexity gradually
  • Test edge cases (very long text, small containers, etc.)