Skip to main content

Stack

πŸŒ“Dark Mode Compatible

A flexible layout component for arranging children in a vertical or horizontal stack with consistent spacing using CSS flexbox and gap.

Overview​

Use Stack when you need…

  • Vertical or horizontal layouts with automatic spacing between children
  • Consistent spacing using semantic design tokens (none, tight, compact, default, comfortable, spacious)
  • Flexbox alignment controls (align, justify) for cross-axis and main-axis positioning
  • Wrapping behavior for responsive horizontal layouts
  • Semantic HTML elements via the polymorphic as prop (nav, ul, section, and more)
  • Performance-optimized layouts (uses CSS gap instead of margins)
  • A foundation component for navigation bars, toolbars, lists, and form groups
Live demoInteractive
Item 1
Item 2
Item 3

Anatomy​

The Stack component is a single flex container with customizable direction, spacing, alignment, and justification.

Vertical Stack (direction="vertical"):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Stack Container (display: flex) β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Child 1 β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ ↕ gap β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Child 2 β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ ↕ gap β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Child 3 β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Horizontal Stack (direction="horizontal"):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Stack Container (display: flex) β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚Child 1β”‚ ↔ β”‚Child 2β”‚ ↔ β”‚Child 3β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜ gap β””β”€β”€β”€β”€β”€β”€β”€β”˜ gap β””β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Component Structure:

  • Container: Single flex element (default div, customizable via as prop)
  • Direction: vertical (column) or horizontal (row)
  • Spacing: CSS gap property with semantic tokens
  • Alignment: Cross-axis alignment via align-items
  • Justification: Main-axis distribution via justify-content
  • Wrapping: Optional flex-wrap for responsive horizontal layouts

Layout Properties:

  • Direction: vertical or horizontal
  • Spacing: none, tight, compact, default, comfortable, spacious
  • Align: start, center, end, stretch, baseline
  • Justify: start, center, end, space-between, space-around, space-evenly
  • Wrap: true or false

Usage​

Import the component:

import { Stack } from '@grasdouble/lufa_design-system';

Basic usage​

src/App.tsx
import { Stack } from '@grasdouble/lufa_design-system';

function App() {
return (
<>
<Stack spacing="default">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Stack>

<Stack direction="horizontal" spacing="comfortable" align="center">
<button>Cancel</button>
<button>Save</button>
<button>Submit</button>
</Stack>

<Stack as="nav" direction="horizontal" spacing="default" aria-label="Main navigation">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</Stack>
</>
);
}

Pattern: Vertical form layout​

src/components/LoginForm.tsx
import { Stack } from '@grasdouble/lufa_design-system';

export function LoginForm() {
return (
<Stack as="form" spacing="comfortable" style={{ maxWidth: '400px' }}>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="email" style={{ width: '100%', padding: '8px' }} />
</div>

<div>
<label htmlFor="password">Password</label>
<input id="password" type="password" style={{ width: '100%', padding: '8px' }} />
</div>

<Stack direction="horizontal" spacing="default" justify="end">
<button type="button">Cancel</button>
<button type="submit">Login</button>
</Stack>
</Stack>
);
}

Pattern: Card grid with wrapping​

src/components/CardGrid.tsx
import { Stack } from '@grasdouble/lufa_design-system';

export function CardGrid() {
const items = [
{ id: 1, title: 'Card 1', description: 'Description 1' },
{ id: 2, title: 'Card 2', description: 'Description 2' },
{ id: 3, title: 'Card 3', description: 'Description 3' },
{ id: 4, title: 'Card 4', description: 'Description 4' },
];

return (
<Stack direction="horizontal" spacing="comfortable" wrap={true}>
{items.map((item) => (
<div
key={item.id}
style={{
minWidth: '250px',
flex: '1 1 250px',
padding: '24px',
background: '#ffffff',
border: '1px solid #e0e0e0',
borderRadius: '8px',
}}
>
<h3 style={{ margin: '0 0 12px' }}>{item.title}</h3>
<p style={{ margin: 0, color: '#666' }}>{item.description}</p>
</div>
))}
</Stack>
);
}

Props​

PropTypeDefaultDescription
as'div' | 'section' | 'article' | 'header' | 'footer' | 'main' | 'nav' | 'aside' | 'ul''div'HTML element to render
direction'vertical' | 'horizontal''vertical'Stack direction (column or row)
spacing'none' | 'tight' | 'compact' | 'default' | 'comfortable' | 'spacious''default'Gap between children using semantic tokens
align'start' | 'center' | 'end' | 'stretch' | 'baseline''stretch'Cross-axis alignment (align-items)
justify'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly''start'Main-axis distribution (justify-content)
wrapbooleanfalseEnable flex wrapping for responsive layouts
classNamestringundefinedAdditional CSS classes
childrenReactNodeundefinedChild elements to render inside the Stack

Also supports all standard HTML attributes for the underlying element (for example id, role, aria-*, data-*, style, and event handlers).

Accessibility​

Stack is a non-interactive container by default. Use semantic elements and ARIA where they add meaning.

import { Stack } from '@grasdouble/lufa_design-system';

export function MainNav() {
return (
<Stack as="nav" direction="horizontal" spacing="default" aria-label="Main navigation">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</Stack>
);
}
import { Stack } from '@grasdouble/lufa_design-system';

export function FeatureList() {
return (
<Stack as="ul" spacing="default" style={{ listStyle: 'none', padding: 0 }}>
<li>Fast setup</li>
<li>Token-based spacing</li>
<li>Accessible by default</li>
</Stack>
);
}
  • Use as="nav" with aria-label for navigation regions.
  • Use as="ul" with <li> children for list semantics.
  • Add role and ARIA attributes when grouping controls (role="group", role="toolbar").
  • Ensure child elements meet contrast and touch target guidance.

Theming & Tokens​

Stack spacing uses semantic spacing tokens mapped to CSS gap:

  • none: 0px
  • tight: 4px
  • compact: 8px
  • default: 16px
  • comfortable: 24px
  • spacious: 32px

Spacing tokens stay consistent across themes; alignment and justification map directly to CSS flex properties.

import { Stack } from '@grasdouble/lufa_design-system';

export function TokenSpacingExample() {
return (
<Stack spacing="comfortable">
<div>Primary action</div>
<div>Secondary action</div>
</Stack>
);
}

Do / Don’t​

Do
  • Use semantic HTML via the as prop for landmarks and lists
  • Use spacing tokens for consistent rhythm across layouts
  • Combine align and justify for precise positioning
  • Use wrap={true} for responsive horizontal layouts
  • Prefer Stack over ad-hoc flex divs for consistency
Don't
  • Use Stack for a single child when Box is enough
  • Add margins to Stack children instead of using spacing
  • Nest Stacks unnecessarily when a single Stack can work
  • Forget aria-label for navigation stacks
  • Mix spacing with child margins in the same layout
  • Box - Flexible container with spacing, background, and border utilities
  • Grid - Two-dimensional layout primitive for rows and columns
  • Flex - Advanced flexbox layout primitive
  • Divider - Visual separator between Stack children
  • Text - Typography component for text-heavy layouts
  • Container - Max-width centered container for responsive layouts