Skip to content

Instantly share code, notes, and snippets.

@z4nr34l
Created December 30, 2025 18:02
Show Gist options
  • Select an option

  • Save z4nr34l/c7f493c9316d93d0bc0ad3dbefc85e08 to your computer and use it in GitHub Desktop.

Select an option

Save z4nr34l/c7f493c9316d93d0bc0ad3dbefc85e08 to your computer and use it in GitHub Desktop.
React + tailwind responsive grid layout
import { cn } from "@workspace/ui/lib/utils";
import type { ReactNode } from "react";
type GridConfig = {
cols: number;
rows: number;
};
type GridLayoutProps = {
mobile: GridConfig;
desktop: GridConfig;
children: ReactNode;
className?: string;
};
type GridItemProps = {
mobile: {
colStart: number;
colSpan: number;
rowStart: number;
rowSpan: number;
};
desktop: {
colStart: number;
colSpan: number;
rowStart: number;
rowSpan: number;
};
children: ReactNode;
className?: string;
};
export const GridItem = ({
mobile,
desktop,
children,
className = "",
}: GridItemProps) => (
<div
className={cn("z-10 m-px h-[calc(100%-2px)] w-[calc(100%-2px)]", className)}
data-grid-item="responsive"
style={
{
gridColumn: `${mobile.colStart} / span ${mobile.colSpan}`,
gridRow: `${mobile.rowStart} / span ${mobile.rowSpan}`,
["--desktop-grid-column" as string]: `${desktop.colStart} / span ${desktop.colSpan}`,
["--desktop-grid-row" as string]: `${desktop.rowStart} / span ${desktop.rowSpan}`,
} as React.CSSProperties
}
>
{children}
</div>
);
export const GridLayout = ({
mobile,
desktop,
children,
className = "",
}: GridLayoutProps) => {
const desktopCellWidth = 100 / desktop.cols;
const desktopCellHeight = 100 / desktop.rows;
// Generate horizontal lines for mobile (only rows - 1 divs)
const mobileHorizontalLines = Array.from(
{ length: mobile.rows - 1 },
(_, i) => (
<div
aria-hidden="true"
className="pointer-events-none z-0 h-px bg-border md:hidden"
key={`h-line-${i}`}
style={{
gridColumn: "1 / -1",
gridRow: i + 2,
alignSelf: "start",
marginTop: "-1px",
}}
/>
)
);
return (
<div className={cn("-mt-px -ml-px relative w-[calc(100%+1px)]", className)}>
<div
className="grid-with-lines relative grid w-full"
style={
{
gridTemplateColumns: `repeat(${mobile.cols}, 1fr)`,
gridTemplateRows: `repeat(${mobile.rows}, minmax(12.5vw, auto))`,
["--desktop-cols" as string]: desktop.cols,
["--desktop-rows" as string]: desktop.rows,
["--desktop-cell-width" as string]: `${desktopCellWidth}%`,
["--desktop-cell-height" as string]: `${desktopCellHeight}%`,
} as React.CSSProperties
}
>
{mobileHorizontalLines}
{children}
</div>
</div>
);
};
export function Example() {
return (
<GridLayout desktop={{ cols: 12, rows: 8 }} mobile={{ cols: 8, rows: 8 }}>
<GridItem
className="flex flex-col justify-center space-y-8 bg-background p-6 md:items-center md:p-8 md:text-center"
desktop={{ colStart: 2, colSpan: 10, rowStart: 2, rowSpan: 4 }}
mobile={{ colStart: 1, colSpan: 8, rowStart: 1, rowSpan: 4 }}
>
<h1 className="text-balance font-bold text-3xl md:text-5xl">
{t("title")}
</h1>
<p className="text-balance text-muted-foreground">{t("description")}</p>
</GridItem>
</GridLayout>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment