Docs
Stack

Stack

Display elements vertically or horizontally on the page.

Gap

Gap is a unit on a 4px grid scale.

Installation

Copy and paste the following code into your project.

import * as React from "react"
 
import { cn } from "@/lib/utils"
 
// Define breakpoints and their corresponding min-widths for media queries
const breakpoints = {
  sm: "640px", // Example breakpoint min-widths
  md: "768px",
  lg: "1024px",
  xl: "1280px",
}
 
type BreakPoint = keyof typeof breakpoints
type Gap = number | { [key in BreakPoint]?: number }
type Direction = "column" | "row" | { [key in BreakPoint]?: "column" | "row" }
 
export interface StackProps extends React.HTMLAttributes<HTMLDivElement> {
  gap?: Gap
  direction?: Direction
  align?: React.CSSProperties["alignItems"]
  justify?: React.CSSProperties["justifyContent"]
}
 
function convertNumberIntoPx(n: number) {
  return `${n * 4}px`
}
 
function convertGapToProps(gap?: Gap) {
  if (typeof gap === "number") {
    return { gap: convertNumberIntoPx(gap) } // Applies universally if gap is a number
  } else if (gap && typeof gap === "object") {
    return Object.entries(gap).reduce(
      (acc, [breakpoint, value]) => {
        // Use CSS variables for gap values at different breakpoints
        acc[`--gap-${breakpoint}`] = convertNumberIntoPx(value)
        return acc
      },
      {} as Record<string, Object>
    )
  }
  return {}
}
 
const Stack = React.forwardRef<HTMLDivElement, StackProps>(
  ({ className, gap, direction, align, justify, ...props }, ref) => {
    const styles = convertGapToProps(gap)
 
    // Append styles for direction changes at specified breakpoints
    if (direction && typeof direction === "object") {
      Object.entries(direction).forEach(([breakpoint, dirValue]) => {
        const minWidth = breakpoints[breakpoint as BreakPoint]
        if (!styles[`@media (min-width: ${minWidth})`]) {
          styles[`@media (min-width: ${minWidth})`] = {}
        }
        ;(
          styles[`@media (min-width: ${minWidth})`] as React.CSSProperties
        ).flexDirection = dirValue
      })
    }
 
    return (
      <div
        ref={ref}
        className={cn(
          "flex [align-items:var(--align-items)] [justify-content:var(--justify-content)]",
          !direction && "flex-col",
          typeof gap === "number"
            ? convertGapToProps(gap)
            : [
                gap?.sm && "sm:gap-[var(--gap-sm)]",
                gap?.md && "md:gap-[var(--gap-md)]",
                gap?.lg && "lg:gap-[var(--gap-lg)]",
                gap?.xl && "xl:gap-[var(--gap-xl)]",
              ],
          className
        )}
        style={
          {
            ...styles,
            "--align-items": align,
            "--justify-content": justify,
          } as React.CSSProperties
        }
        {...props}
      >
        {props.children}
      </div>
    )
  }
)
Stack.displayName = "Stack"
 
export { Stack }

Update the import paths to match your project setup.

Usage

import { Stack } from "@/components/ui/stack"
<Stack gap={3}>
  <div className="bg-gray-1000 h-12 w-12 rounded-md" />
  <div className="bg-gray-1000 h-12 w-12 rounded-md" />
  <div className="bg-gray-1000 h-12 w-12 rounded-md" />
</Stack>

Padding

Padding uses the same scale as gap on a 4px grid scale.

Responsive

Resize the window to observe changes to the layout.