CSS layout is powerful. It is also the single largest source of developer frustration on the web. Flexbox requires understanding main axis vs. cross axis, flex-grow vs. flex-shrink vs. flex-basis, and a dozen properties that interact in non-obvious ways. CSS Grid requires understanding grid-template-columns, grid-template-rows, grid-area, and the difference between fr, auto, and minmax(). Centering a div -- the canonical web development joke -- still requires knowing the right combination of display, align-items, and justify-content.
FlinUI's layout system replaces this complexity with 15 declarative components that handle the most common layout patterns. You do not write CSS. You compose components. for vertical and horizontal arrangements. for grid layouts. for max-width constraints. for centering. Each component maps to CSS flexbox or grid under the hood, but the developer never sees the CSS.
Container: Constrained Width
Every page needs a maximum width. Without it, content stretches to the full viewport width, making text lines impossibly long on wide screens.
<Container>
<h1>Default container (max-width: 1280px)</h1>
<p>Content is centered and constrained</p>
</Container>Container applies max-width, margin: 0 auto (for centering), and horizontal padding. That is three CSS properties that every web page needs and that every developer writes slightly differently. Container standardizes them.
Stack: The Universal Arranger
Stack is the most-used layout component. It arranges children in a line -- vertically or horizontally -- with consistent spacing between them.
// Vertical stack (default)
<Stack gap={4}>
<Text size="xl" weight="bold">Profile</Text>
<Input label="Name" value={name} />
<Input label="Email" value={email} />
<Button variant="primary">Save</Button>
</Stack>// Horizontal stack
The gap prop uses the spacing token scale (gap={4} means space_4 = 16px). The direction prop is either "vertical" (default) or "horizontal". The align prop controls cross-axis alignment: "start", "center", "end", "stretch".
Under the hood, Stack renders a What a developer writes in CSS: .user-info {
display: flex;
flex-direction: row;
gap: 0.5rem;
align-items: center;
}
``` What a developer writes in FLIN: Same result. No CSS file. No class names. No mental model of flex properties. For layouts that need rows and columns, // Responsive grid
The responsive variant uses breakpoint-prefixed props. Grid handles the responsive CSS automatically: Four lines of FLIN instead of twelve lines of CSS. And the FLIN version is readable without knowing CSS Grid syntax. When Stack and Grid are not enough, Flex exposes the full flexbox API through props:
- Most layouts do not need Flex. Stack handles vertical and horizontal arrangements. Grid handles two-dimensional layouts. Flex is for the 10% of cases where you need The The Split handles the responsive collapse automatically: on mobile (below the medium breakpoint), the two panels stack vertically, each taking 100% width. On desktop, they sit side by side at the specified ratio. Box accepts every spacing, color, and visual prop that other components use internally. It is the escape hatch for one-off layouts that do not fit the patterns of Stack, Grid, or Split. Most developers rarely use Box directly. The higher-level components (Card, Alert, Badge) are boxes with specific visual treatment. Box exists for the cases where you need "a div with some padding and a shadow" without creating a new component. // Vertical divider in a horizontal stack
// Fixed-size spacer
// Flexible spacer (pushes content to edges)
The real power of the layout system emerges when components compose:
This is a complete dashboard layout. Container constrains the width. Stack provides vertical spacing. Grid creates responsive columns. No CSS file. No class names. No media queries. The layout is expressed entirely through component composition, and it is responsive by default. Tailwind CSS takes the approach of utility classes: FlinUI takes the approach of semantic components: For FLIN's target audience -- developers who want to build applications, not master CSS -- this is the right abstraction level. The layout system handles the "how" so the developer can focus on the "what." The 15 layout components cover the patterns that appear in nearly every web application: Holy Grail Layout (header, sidebar, content, footer):
`` Centered Form Page (login, registration, password reset):
`` Marketing Landing Page (full-width sections with constrained content):
`` Each pattern is composed from the same 15 layout components. No custom CSS. No memorizing flexbox properties. No debugging why Each layout component generates the appropriate CSS. The Grid component, for example, generates a let mut style = format!(
"display: grid; grid-template-columns: repeat({}, 1fr); gap: {};",
cols, gap
); // Add responsive overrides
if let Some(cols_md) = props.get_opt_int("cols_md") {
// Emitted as a scoped media query
} format!(" The layout components are among the simplest in FlinUI -- most are under 30 lines of FLIN code. Their power comes not from complexity but from composition: fifteen simple components that, combined, express any layout a web application needs. --- This is Part 86 of the "How We Built FLIN" series, documenting how a CEO in Abidjan and an AI CTO built a layout system that replaces CSS with declarative components. Series Navigation:
- [85] Design Tokens and Theming System
- [86] The Layout System (you are here)
- [87] Icons Library Integration
- [88] FlinUI Enterprise Components We built a pure-Rust static analysis engine with 34 rules across 8 categories to catch security issues, misconfigurations, and deployment mistakes before they reach production. One language for frontend, backend, database, and tooling. Built from scratch in Rust with 3,200+ tests. No npm. No Webpack. No framework fatigue. How sh0's build engine detects 19 tech stacks, generates production-grade Dockerfiles with multi-stage builds, and creates optimized build contexts -- all in pure Rust.display: flex, flex-direction, gap, and align-items. But the developer never needs to know this.Stack vs. Flexbox
.profile-form {
display: flex;
flex-direction: column;
gap: 1rem;
}<Stack gap={4}>...</Stack>
<Stack direction="horizontal" gap={2} align="center">...</Stack>Grid: Two-Dimensional Layout
Grid provides a CSS Grid-based layout:// Simple 3-column grid
<Grid cols={3} gap={4}>
<Card>One</Card>
<Card>Two</Card>
<Card>Three</Card>
<Card>Four</Card>
<Card>Five</Card>
<Card>Six</Card>
</Grid>cols={1} is the mobile default (one column). cols_md={2} activates at the medium breakpoint (768px). cols_lg={3} at 1024px. cols_xl={4} at 1280px./* Generated CSS (the developer never sees this) */
.grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 1rem;
}
@media (min-width: 768px) {
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
.grid { grid-template-columns: repeat(4, 1fr); }
}Flex: Fine-Grained Control
Flex provides direct access to flexbox properties:<Flex direction="row" wrap="wrap" justify="space-between" align="center">
<Logo />
<Nav />
<UserMenu />
</Flex>direction: row, column, row-reverse, column-reverse
- wrap: nowrap, wrap, wrap-reverse
- justify: start, center, end, space-between, space-around, space-evenly
- align: start, center, end, stretch, baseline
- gap: spacing token valuejustify-content: space-between or flex-wrap: wrap.Split: Sidebar Layouts
Split component creates a two-panel layout with a fixed ratio:<Split ratio="1:3">
<aside>Sidebar content</aside>
<main>Main content</main>
</Split>ratio prop defines how space is divided between the two children. "1:3" means the first child gets 25% and the second gets 75%. "1:4" means 20% and 80%. "1:1" means 50/50.Box: The Primitive
Box is the base layout component. It is a div with style props:<Box padding={4} margin={2} bg="white" radius="md" shadow="sm">
Content here
</Box>Center: The Solved Problem
<Center>
<Spinner size="lg" />
<Text>Loading...</Text>
</Center>Center centers its children both horizontally and vertically. It applies display: flex; align-items: center; justify-content: center -- the three properties that solve the "centering a div" problem. The optional height prop sets the container height (useful for full-viewport centering).Divider and Spacer
// Horizontal divider
<Divider />
<Divider label="Or continue with" />Divider renders a visual separator line. It can be horizontal (default) or vertical, and it can contain a label (centered text on the line).Spacer creates empty space. With a size prop, it adds fixed space. Without props, it grows to fill available space -- the flexbox flex-grow: 1 pattern that pushes siblings to opposite ends.AspectRatio
<AspectRatio ratio="16:9">
<Image src={video_thumbnail} />
</AspectRatio>AspectRatio constrains its child to a specific aspect ratio. The ratio prop accepts common formats: "16:9", "4:3", "1:1", "21:9". This is essential for responsive images and video embeds that need to maintain their proportions as the container resizes.Wrap
<Wrap gap={2}>
{for tag in tags}
<Tag>{tag}</Tag>
{/for}
</Wrap>Wrap arranges children in a row, wrapping to the next line when they exceed the container width. It is display: flex; flex-wrap: wrap with a gap. Perfect for tag lists, chip groups, and any collection of elements that should flow like text.Composing Layout Components
<Container size="xl">
<Stack gap={8}>
<!-- Page header -->
<Stack direction="horizontal" align="center">
<Stack gap={1}>
<Text size="2xl" weight="bold">Dashboard</Text>
<Text color="muted">Overview of your application</Text>
</Stack>
<Spacer />
<Button variant="primary">Export</Button>
</Stack>Why Components Instead of CSS Classes
flex, flex-col, gap-4, and mx-auto do., . The developer does not need to know CSS at all. Stack arranges things in a line. Grid makes a grid. Container constrains width. Center centers things. The component name is the intent.Common Layout Patterns
flin
``flin
``flin
``margin: 0 auto does not work when you forgot display: block. Just components that do what their names say.Implementation: CSS Generation
fn render_grid(props: &Props) -> String {
let cols = props.get_int("cols", 1);
let gap = props.get_spacing("gap", 4);Responses
Related Articles
34 Rules to Catch Deployment Mistakes Before They Happen
FLIN: The Language That Replaces 47 Technologies
Auto-Detecting 19 Tech Stacks from Source Code