Agents (llms.txt)

Grid

A 12-column responsive grid with span helpers

span 4
span 4
span 4
span 6
span 6
span full
import { Grid } from '@/components/grid';

const Cell = ({ children }: { children: React.ReactNode }) => (
  <div className="rounded-lg border border-border bg-background-secondary p-4 text-center text-foreground-secondary text-sm">
    {children}
  </div>
);

export default function GridPreview() {
  return (
    <Grid>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={6}>
        <Cell>span 6</Cell>
      </Grid.Col>
      <Grid.Col span={6}>
        <Cell>span 6</Cell>
      </Grid.Col>
      <Grid.Col span="full">
        <Cell>span full</Cell>
      </Grid.Col>
    </Grid>
  );
}

Dependencies

Source Code

import type { VariantProps } from 'cva';

import { Slot } from '@/components/slot';
import { cn, cva } from '@/lib/utils/classnames';

const gridStyle = cva({
  base: 'grid',
  variants: {
    cols: {
      1: 'grid-cols-1',
      2: 'grid-cols-1 sm:grid-cols-2',
      3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
      4: 'grid-cols-2 sm:grid-cols-2 lg:grid-cols-4',
      6: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
      12: 'grid-cols-4 sm:grid-cols-6 lg:grid-cols-12',
    },
    gap: {
      none: 'gap-0',
      xs: 'gap-2',
      sm: 'gap-3',
      md: 'gap-4 md:gap-6',
      lg: 'gap-6 md:gap-8',
      xl: 'gap-8 md:gap-12',
    },
  },
  defaultVariants: {
    cols: 12,
    gap: 'md',
  },
});

interface GridProps
  extends React.ComponentPropsWithRef<'div'>,
    VariantProps<typeof gridStyle> {
  asChild?: boolean;
}

const Grid = ({ ref, className, cols, gap, asChild, ...rest }: GridProps) => {
  const Comp = asChild ? Slot : 'div';
  return (
    <Comp
      ref={ref}
      className={cn(gridStyle({ cols, gap }), className)}
      {...rest}
    />
  );
};

const colStyle = cva({
  base: '',
  variants: {
    span: {
      1: 'col-span-1',
      2: 'col-span-2',
      3: 'col-span-2 sm:col-span-3',
      4: 'col-span-2 sm:col-span-4',
      5: 'col-span-2 sm:col-span-5',
      6: 'col-span-full sm:col-span-6',
      7: 'col-span-full sm:col-span-7',
      8: 'col-span-full sm:col-span-8',
      9: 'col-span-full sm:col-span-9',
      10: 'col-span-full sm:col-span-10',
      11: 'col-span-full sm:col-span-11',
      12: 'col-span-full',
      full: 'col-span-full',
    },
  },
  defaultVariants: {
    span: 'full',
  },
});

interface GridColProps
  extends React.ComponentPropsWithRef<'div'>,
    VariantProps<typeof colStyle> {
  asChild?: boolean;
}

const GridCol = ({ ref, className, span, asChild, ...rest }: GridColProps) => {
  const Comp = asChild ? Slot : 'div';
  return (
    <Comp ref={ref} className={cn(colStyle({ span }), className)} {...rest} />
  );
};

interface GridSubgridProps
  extends React.ComponentPropsWithRef<'div'>,
    VariantProps<typeof colStyle> {
  asChild?: boolean;
}

/**
 * Spans columns of the parent `Grid` while making its own children align to
 * the parent's column tracks (`grid-template-columns: subgrid`). Use it to
 * line up nested content — e.g. card title / body / footer — across a row
 * regardless of each item's content length. Requires an explicit `span` so
 * the item occupies tracks before subgrid inherits them.
 */
const GridSubgrid = ({
  ref,
  className,
  span,
  asChild,
  ...rest
}: GridSubgridProps) => {
  const Comp = asChild ? Slot : 'div';
  return (
    <Comp
      ref={ref}
      className={cn('grid grid-cols-subgrid', colStyle({ span }), className)}
      {...rest}
    />
  );
};

const CompoundGrid = Object.assign(Grid, {
  Col: GridCol,
  Subgrid: GridSubgrid,
});

export type { GridColProps, GridProps, GridSubgridProps };
export { CompoundGrid as Grid, colStyle, gridStyle };

Server component. No 'use client' — grid layout primitive; no hooks or own handlers.

Grid provides a 12-column responsive grid with sensible breakpoints. Use Grid.Col to control the column span of children.

Use Grid.Subgrid when a grid item should both span parent columns and align its own children to the parent’s column tracks — e.g. lining up card title / body / footer across a row regardless of content length.

Anatomy


          <Grid>
  <Grid.Col span={6}>{...}</Grid.Col>
  <Grid.Col span={6}>{...}</Grid.Col>
</Grid>

<Grid>
  <Grid.Subgrid span={4}>
    <h3>Title</h3>
    <p>Body</p>
  </Grid.Subgrid>
</Grid>
        

API Reference

Grid

Extends the div element.

Prop Default Type
cols 12 1234612
gap "md" "none""xs""sm""md""lg""xl"
asChild - boolean

Grid.Col

Extends the div element.

Prop Default Type
span "full" 123456789101112"full"
asChild - boolean

Grid.Subgrid

Extends the div element. Spans parent columns via span and applies grid-template-columns: subgrid so its children align to the parent grid.

Prop Default Type
span "full" 123456789101112"full"
asChild - boolean

Examples

Default

span 4
span 4
span 4
span 6
span 6
span full
import { Grid } from '@/components/grid';

const Cell = ({ children }: { children: React.ReactNode }) => (
  <div className="rounded-lg border border-border bg-background-secondary p-4 text-center text-foreground-secondary text-sm">
    {children}
  </div>
);

export default function GridPreview() {
  return (
    <Grid>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={4}>
        <Cell>span 4</Cell>
      </Grid.Col>
      <Grid.Col span={6}>
        <Cell>span 6</Cell>
      </Grid.Col>
      <Grid.Col span={6}>
        <Cell>span 6</Cell>
      </Grid.Col>
      <Grid.Col span="full">
        <Cell>span full</Cell>
      </Grid.Col>
    </Grid>
  );
}

Subgrid

Short

One line.

Footer

Medium

A body with a couple more lines of supporting copy than the others.

Footer

Long

The longest body in the row, with enough copy to push its natural height well beyond its siblings — yet every footer still aligns.

Footer
import { Grid } from '@/components/grid';

export const meta = { layout: 'padded' };

const Card = ({ title, body }: { title: string; body: string }) => (
  // Three rows: title / body / footer. grid-rows-subgrid makes all cards in
  // the row share row tracks, so footers align regardless of body length.
  <Grid.Subgrid
    span={4}
    className="row-span-3 grid-rows-subgrid gap-2 rounded-lg border border-border bg-background-secondary p-4"
  >
    <h3 className="font-medium text-foreground text-sm">{title}</h3>
    <p className="text-foreground-secondary text-sm">{body}</p>
    <span className="text-foreground-secondary text-xs">Footer</span>
  </Grid.Subgrid>
);

export default function GridSubgridPreview() {
  return (
    <Grid cols={12} className="grid-rows-[auto_1fr_auto]">
      <Card title="Short" body="One line." />
      <Card
        title="Medium"
        body="A body with a couple more lines of supporting copy than the others."
      />
      <Card
        title="Long"
        body="The longest body in the row, with enough copy to push its natural height well beyond its siblings — yet every footer still aligns."
      />
    </Grid>
  );
}

Previous

Flex

Next

Input