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
asprop (nav,ul,section, and more) - Performance-optimized layouts (uses CSS
gapinstead 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 viaasprop) - Direction:
vertical(column) orhorizontal(row) - Spacing: CSS
gapproperty with semantic tokens - Alignment: Cross-axis alignment via
align-items - Justification: Main-axis distribution via
justify-content - Wrapping: Optional
flex-wrapfor responsive horizontal layouts
Layout Properties:
- Direction:
verticalorhorizontal - Spacing:
none,tight,compact,default,comfortable,spacious - Align:
start,center,end,stretch,baseline - Justify:
start,center,end,space-between,space-around,space-evenly - Wrap:
trueorfalse
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β
| Prop | Type | Default | Description |
|---|---|---|---|
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) |
wrap | boolean | false | Enable flex wrapping for responsive layouts |
className | string | undefined | Additional CSS classes |
children | ReactNode | undefined | Child 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"witharia-labelfor navigation regions. - Use
as="ul"with<li>children for list semantics. - Add
roleand 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: 0pxtight: 4pxcompact: 8pxdefault: 16pxcomfortable: 24pxspacious: 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
asprop for landmarks and lists - Use
spacingtokens for consistent rhythm across layouts - Combine
alignandjustifyfor 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-labelfor navigation stacks - Mix
spacingwith child margins in the same layout
Related Componentsβ
- 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