Component

Cluster

See on GitHub

A transparent flex container that distributes its design-system gap to children. Replaces cards, justify-between, and other opaque wrappers.

Row with Filler
Project Setup
Draft
Column layout
Project Setup

Configure your project settings and preferences.

Input variant (sidebar pattern)
Footer
'use client'

import { PresentationIcon } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Cluster, Filler } from '@/components/ui/cluster'
import { Kbd } from '@/components/ui/kbd'
import { SearchIcon } from 'lucide-react'

export default function ClusterDemo() {
  return (
    <div className="flex flex-col gap-10 p-6">
      <div className="flex flex-col gap-2">
        <span className="text-muted-foreground font-mono text-xs tracking-wide uppercase">
          Row with Filler
        </span>
        <Cluster className="max-w-sm items-stretch">
          <div className="flex items-center gap-2 px-3 py-2">
            <span className="text-sm font-medium">Project Setup</span>
          </div>
          <div className="bg-fd-background flex flex-1 items-center justify-end px-2">
            <Badge variant="accent">Draft</Badge>
          </div>
        </Cluster>
      </div>

      <div className="flex flex-col gap-2">
        <span className="text-muted-foreground font-mono text-xs tracking-wide uppercase">
          Column layout
        </span>
        <Cluster
          direction="col"
          align="stretch"
          className="max-w-sm"
          bg="muted"
        >
          <div className="p-3 pt-2">
            <span className="text-sm font-medium">Project Setup</span>
            <p className="text-muted-foreground text-sm">
              Configure your project settings and preferences.
            </p>
          </div>
          <label className="focus-within:bg-accent/70 flex items-center gap-3 p-3">
            <PresentationIcon className="text-muted-foreground size-4 shrink-0" />
            <input
              placeholder="Project name"
              className="min-w-0 text-sm outline-none"
            />
          </label>
          <Cluster bg="muted">
            <Filler />
            <Button variant="secondary" className="rounded-none">
              Cancel
            </Button>
            <Button className="rounded-none">Create</Button>
          </Cluster>
        </Cluster>
      </div>

      <div className="flex flex-col gap-2">
        <span className="text-muted-foreground font-mono text-xs tracking-wide uppercase">
          Input variant (sidebar pattern)
        </span>
        <Cluster direction="col" align="stretch" className="h-48 max-w-sm">
          <label className="focus-within:bg-accent/70 flex h-10 items-center gap-3 px-3">
            <SearchIcon className="text-muted-foreground size-4 shrink-0" />
            <input
              type="text"
              placeholder="Search"
              className="text-foreground placeholder:text-muted-foreground min-w-0 flex-1 bg-transparent font-mono text-sm tracking-wide uppercase outline-none"
            />
            <Kbd className="h-[2em] rounded-none px-2">⌘K</Kbd>
          </label>
          <Filler />
          <div className="flex items-center gap-2 px-3 py-2">
            <span className="text-muted-foreground text-sm">Footer</span>
          </div>
        </Cluster>
      </div>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add @joyco/cluster

Core Concept

A Cluster is an invisible layout scaffold. It has no background — it only provides gap and flex behavior. Its direct children automatically receive the Cluster's bg color via a zero-specificity CSS rule, which any utility class on the child can override.

This replaces the traditional "Card" pattern (wrapper with bg + padding). In JOYCO's design system, containers are transparent — children own their own backgrounds.

Usage

import { Cluster, Filler } from '@/components/ui/cluster'

Basic row

<Cluster>
  <div className="px-3 py-2">
    <span className="text-sm font-medium">Label</span>
  </div>
  <Filler />
  <Badge variant="accent">Status</Badge>
</Cluster>

Children inherit bg-muted by default. The <Filler /> pushes the badge to the end — never use justify-between, always use Filler for space distribution.

Column layout

<Cluster direction="col" align="stretch" bg="muted">
  <div className="p-3">
    <span className="text-sm font-medium">Header</span>
    <p className="text-muted-foreground text-sm">Description</p>
  </div>
  <div className="p-3">
    <Input placeholder="Field" />
  </div>
  <Cluster bg="muted">
    <Filler />
    <Button variant="secondary">Cancel</Button>
    <Button>Create</Button>
  </Cluster>
</Cluster>

Override child background

Children can override the inherited bg with any utility class:

<Cluster>
  <div className="px-3 py-2">Uses inherited bg-muted</div>
  <div className="bg-fd-background px-3 py-2">Overrides to bg-fd-background</div>
</Cluster>

Inline display

Use asChild with a <span> for inline contexts:

<Cluster display="inline-flex" asChild>
  <span>
    <Badge>Active</Badge>
    <Badge variant="muted">3 tasks</Badge>
  </span>
</Cluster>

API

Cluster

PropTypeDefaultDescription
display'flex' | 'inline-flex''flex'Display mode
direction'row' | 'col''row'Flex direction
align'start' | 'center' | 'end' | 'stretch' | 'baseline''center'Cross-axis alignment
wrapbooleanfalseEnable flex wrap
bg'muted' | 'accent''muted'Background color inherited by direct children
asChildbooleanfalseRender as child element via Radix Slot

Filler

A presentational spacer that fills available space along the main axis. Renders as flex-1 with aria-hidden="true".

Use <Filler /> between children to distribute space. Never use justify-between or justify-end.

Design Principles

  1. Clusters are transparent — they never have a visible background
  2. Children are solid — every direct child should have its own visual identity (background color, etc.) but never borders or rounded corners. Borders are almost never used in this design system — the only exception is the outline button variant. The theme radius is 0rem. Clusters, Fillers, inputs, and all layout elements rely on background color contrast and spacing to create visual separation, not border lines or rounded shapes.
  3. Filler over justify — use <Filler /> for space distribution, never justify-between
  4. bg cascades down — the bg prop sets a CSS variable that children inherit at zero specificity, overridable with any bg-* class

Related Components

Maintainers
Downloads
8Total
1 downloads today