Skip to main content
On this page

Primitive components for composing consistent spacing and layout structures without writing custom CSS.

Layout components are low-level primitives that handle spacing and structural composition using theme tokens. They exist so that components and pages can use consistent spacing without writing one-off CSS.

When to use​

  • To add vertical spacing between a sequence of elements: use Stack
  • To group short items that wrap across lines, such as buttons or badges: use Cluster
  • To apply theme-token margin to any element without modifying its styles directly: use Margin
  • To pull an element outside its parent's padding, for example a full-width image inside a padded card: use Bleed
  • To create a responsive grid where columns are determined by a minimum item width, not a fixed column count: use ContentGrid
  • To arrange two or more items side by side above a container-width threshold, falling back to stacked below: use Stack with horizontalAbove

When not to use​

  • For complex responsive page layouts - use Row and Col only as a last resort when no other layout component meets the need. Prefer Stack, Cluster, and ContentGrid.
  • For one-off spacing adjustments - do not add custom margin or padding styles directly to components. Use Margin with a theme token instead.
  • To make an element full-width on small screens - use the component's own fullWidth prop, for example on CTA Button, rather than writing custom responsive width styles. See Stretch for how this works internally.

Stack​

Anatomy​

  1. Container (the root div that owns the spacing contract)
  2. Items (direct children; spacing is applied between consecutive items)

Basic usage​

Use spaceBetween with a theme token. The value can be a numeric scale key (1–12) or a semantic space token.

A numeric scale key:

Loading...
Edit live code
<Stack spaceBetween={2}>
  <Block /* Example only */ />
  <Block /* Example only */ />
  <Block /* Example only */ />
</Stack>

A semantic space token, which keeps the spacing tied to its meaning rather than a fixed step:

Loading...
Edit live code
<Stack spaceBetween="betweenCards">
  <Block /* Example only */ />
  <Block /* Example only */ />
  <Block /* Example only */ />
</Stack>

Responsive spacing​

Pass an array of space keys to change the gap at each breakpoint. The array maps to [default, smallAndUp, mediumAndUp, largeAndUp].

Loading...
Edit live code
<Stack spaceBetween={[2, 4, 6, 8]}>
  <Block /* Example only */ />
  <Block /* Example only */ />
  <Block /* Example only */ />
</Stack>

Horizontal above a threshold​

Pass a horizontalAbove container-width string to switch Stack from vertical to horizontal layout when the container is wide enough. This is container-based, not viewport-based, so it works correctly inside sidebars or narrow panels.

Drag the handle to resize the container and cross the 600px threshold:

Loading...
Edit live code
<Resizable>
  <Stack spaceBetween={2} horizontalAbove="600px">
    <Block /* Example only */ />
    <Block /* Example only */ />
    <Block /* Example only */ />
  </Stack>
</Resizable>

When horizontalAbove is set, items grow equally to fill available space. If you need unequal widths, use flexbox or a Grid directly.


ContentStack​

ContentStack is a pre-configured Stack using the betweenCards spacing token. Use it for collections of cards or similar content blocks where you always want that token applied.

Loading...
Edit live code
<ContentStack>
  <Card>Account one</Card>
  <Card>Account two</Card>
  <Card>Account three</Card>
</ContentStack>

Pass horizontal to switch to a side-by-side layout above 750px.

Loading...
Edit live code
<Resizable>
  <ContentStack horizontal>
    <Card>Account one</Card>
    <Card>Account two</Card>
  </ContentStack>
</Resizable>

Cluster​

Anatomy​

  1. Container (a ul element using flexbox with wrapping)
  2. ClusterItem (an li element wrapping each child)

Cluster renders a ul/li structure. Wrap each child in a ClusterItem.

Basic usage​

Loading...
Edit live code
<Resizable>
<Cluster spaceBetween={2}>
  <ClusterItem>
    <PrimaryCTAButton>Submit meter reading</PrimaryCTAButton>
  </ClusterItem>
  <ClusterItem>
    <SecondaryCTAButton>Cancel</SecondaryCTAButton>
  </ClusterItem>
</Cluster>
</Resizable>

Justification​

Use the justify prop to control horizontal alignment. The default is flex-start.

Loading...
Edit live code
<Resizable>
  <Cluster spaceBetween={2} justify="flex-end">
    <ClusterItem>
      <Badge variant="success">Active</Badge>
    </ClusterItem>
    <ClusterItem>
      <Badge variant="warning">Non-communicating</Badge>
    </ClusterItem>
    <ClusterItem>
      <Badge>Peak</Badge>
    </ClusterItem>
  </Cluster>
</Resizable>

Row gap​

When items wrap to a second line, the vertical gap defaults to the same value as spaceBetween. Pass rowGap to use a different value.

Loading...
Edit live code
<Resizable>
  <Cluster spaceBetween={8} rowGap={2}>
    <ClusterItem>
      <PrimaryCTAButton>Submit meter reading</PrimaryCTAButton>
    </ClusterItem>
    <ClusterItem>
      <SecondaryCTAButton>Cancel</SecondaryCTAButton>
    </ClusterItem>
  </Cluster>
</Resizable>

Margin​

Margin applies theme-token spacing as margin to any child element. Use it when you need to offset a single element without changing its own styles or creating a wrapper with custom CSS.

Anatomy​

  1. Container (a div, or inline wrapper when inline is set, that applies margin)

Use top, bottom, left, or right to apply margin on a single side:

Loading...
Edit live code
<>
  <Block /* Example only */ />
  <Margin top={4}>
    <Block /* Example only */ />
  </Margin>
</>

Use horizontal, vertical, or all for shorthand.

Loading...
Edit live code
<Margin all={4}>
  <Block /* Example only */ />
</Margin>

Bleed​

Bleed applies negative margin to pull an element outside its parent's padding. Use it when you need something to appear full-width inside a padded container, such as a full-bleed image inside a Card or a divider that touches the card edges.

Anatomy​

  1. Container (a div that uses negative margin to extend beyond its parent's padding)

The bled element extends past the Card's horizontal padding to touch its edges:

Loading...
Edit live code
<Card>
  <P>This paragraph sits inside the card's padding.</P>
  <Bleed horizontal={[3, 4]}>
    <Block /* Example only */ />
  </Bleed>
</Card>

Pass inline to make the Bleed container display: inline-block rather than block.


Stretch​

Stretch controls how wide an element is at different screen sizes. Use it when you need a button or other element to be full-width on small screens and auto-width on larger screens.

Note: Stretch is not exported from the Nebula public API. It is documented here because Nebula's own field and button components use it internally to implement their fullWidth prop. In application code, use the fullWidth prop on the component itself — for example CTA Button — rather than reaching for Stretch.

Anatomy​

  1. Container (a div that sets width and max-width based on the fullWidth prop)

This example is not live, because Stretch is internal to Nebula and cannot be imported.

// Internal to Nebula - not importable from '@kaluza-platform/nebula'.
<Stretch fullWidth="small" defaultWidth="auto">
<PrimaryCTAButton>Continue</PrimaryCTAButton>
</Stretch>

fullWidth values:

  • always: always 100% wide
  • never: always defaultWidth
  • small: 100% on small screens, defaultWidth on smallAndUp

ContentGrid​

ContentGrid uses CSS Grid with auto-fit and minmax to fit as many columns as possible at a minimum item width, without fixed column counts or media queries.

Anatomy​

  1. Container (a div using CSS Grid)
  2. Items (direct children placed into auto-sized columns)
Loading...
Edit live code
<ContentGrid min="280px" spaceBetween={4}>
  <Card>Account one</Card>
  <Card>Account two</Card>
  <Card>Account three</Card>
</ContentGrid>

Orphaned items on the last row expand to fill available space rather than leaving gaps. Pass rowGap to use a different vertical spacing value to spaceBetween.


Row and Col​

Row and Col form a 12-column grid system. Use them only as a last resort when Stack, Cluster, and ContentGrid cannot meet the layout need.

Anatomy​

  1. Row (the grid container, centred with a max-width set to breakpoints.large)
  2. Col (a column cell; defaults to full width, span 12)
Loading...
Edit live code
<Row>
  <Col span={6}>Left content</Col>
  <Col span={6}>Right content</Col>
</Row>

Use the small, medium, and large props on Col to change the column span at each breakpoint.

Loading...
Edit live code
<Row>
  <Col span={12} small={6} large={4}>Sidebar</Col>
  <Col span={12} small={6} large={8}>Main content</Col>
</Row>

Nest rows by passing isNested to the inner Row.