layout-primitives
Skill50 starsChanged 11 months agoMITView on GitHub
Builds composable layout components (Stack, Cluster, Grid, Sidebar, Center, Cover). Use when creating layout systems, spacing compositions, or Every Layout-style intrinsic design patterns that adapt without breakpoints.
In this repository29 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- aria-patternsSkill
- color-contrastSkill
- focus-statesSkill
- component-docsSkill
- token-docsSkill
- angularSkill
- reactSkill
- svelteSkill
- vueSkill
- getting-startedSkill
- animation-principlesSkill
- compound-componentsSkill
- dark-modeSkill
- icon-systemSkill
- breakpointsSkill
- color-scaleSkill
- design-tokens-structureSkill
- motion-scaleSkill
- radius-scaleSkill
- responsive-typographySkill
- shadow-scaleSkill
- spacing-scaleSkill
- type-scaleSkill
- z-index-scaleSkill
- figmaSkill
- framerSkill
- storybookSkill
- style-dictionarySkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 848
Every Layout: https://every-layout.dev
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Layout Primitives
Overview
Build reusable layout primitives (Stack, Cluster, Sidebar, Grid) that compose to create any layout. Based on Every Layout patterns - intrinsic design that adapts without breakpoints.
When to Use
- Setting up layout components for a design system
- Creating composable layout building blocks
- Replacing repetitive flexbox/grid patterns
- Building layouts that adapt without media queries
- Implementing consistent spacing throughout an app
Quick Reference: Core Primitives
| Primitive | Purpose | Primary CSS |
| Stack | Vertical spacing | flex-direction: column + gap |
| Cluster | Horizontal wrapping groups | flex-wrap: wrap + gap |
| Sidebar | Two-column with minmax | flex-wrap: wrap + basis |
| Switcher | Stack/row based on width | flex-wrap + container query |
| Center | Centered max-width | max-width + margin-inline: auto |
| Cover | Vertically centered hero | min-height + flex |
| Grid | Auto-fill responsive grid | grid-template-columns: repeat(auto-fill) |
| Frame | Aspect ratio container | aspect-ratio |
| Reel | Horizontal scroll | overflow-x: auto + flex-wrap: nowrap |
| Box | Spacing/padding container | padding |
Stack
Vertical rhythm with consistent spacing.
CSS
.stack {
display: flex;
flex-direction: column;
}
.stack > * + * {
margin-block-start: var(--stack-space, 1.5rem);
}
/* Or with gap (modern, simpler) */
.stack {
display: flex;
flex-direction: column;
gap: var(--stack-space, 1.5rem);
}
/* Variants */
.stack[data-space="sm"] { --stack-space: 0.5rem; }
.stack[data-space="md"] { --stack-space: 1rem; }
.stack[data-space="lg"] { --stack-space: 2rem; }
.stack[data-space="xl"] { --stack-space: 4rem; }React Component
interface StackProps {
space?: 'sm' | 'md' | 'lg' | 'xl';
as?: React.ElementType;
children: React.ReactNode;
className?: string;
}
export function Stack({
space = 'md',
as: Component = 'div',
children,
className,
}: StackProps) {
return (
<Component
className={clsx('stack', className)}
data-space={space}
>
{children}
</Component>
);
}
// Usage
<Stack space="lg">
<h1>Title</h1>
<p>Paragraph</p>
<p>Another paragraph</p>
</Stack>Tailwind Utility
// Tailwind approach
<div className="flex flex-col gap-4">
{/* children */}
</div>
// With custom component
function Stack({ space = 4, children }) {
return (
<div className={`flex flex-col gap-${space}`}>
{children}
</div>
);
}Cluster
Horizontal grouping that wraps naturally.
CSS
.cluster {
display: flex;
flex-wrap: wrap;
gap: var(--cluster-space, 1rem);
align-items: var(--cluster-align, center);
justify-content: var(--cluster-justify, flex-start);
}
/* Variants */
.cluster[data-justify="center"] { justify-content: center; }
.cluster[data-justify="space-between"] { justify-content: space-between; }
.cluster[data-justify="end"] { justify-content: flex-end; }
.cluster[data-align="start"] { align-items: flex-start; }
.cluster[data-align="end"] { align-items: flex-end; }
.cluster[data-align="stretch"] { align-items: stretch; }React Component
interface ClusterProps {
space?: 'xs' | 'sm' | 'md' | 'lg';
justify?: 'start' | 'center' | 'end' | 'space-between';
align?: 'start' | 'center' | 'end' | 'stretch';
children: React.ReactNode;
className?: string;
}
export function Cluster({
space = 'md',
justify = 'start',
align = 'center',
children,
className,
}: ClusterProps) {
return (
<div
className={clsx('cluster', className)}
data-space={space}
data-justify={justify}
data-align={align}
>
{children}
</div>
);
}
// Usage
<Cluster space="sm" justify="space-between">
<Tag>React</Tag>
<Tag>TypeScript</Tag>
<Tag>CSS</Tag>
</Cluster>Sidebar
Two-column layout with a sidebar that stacks below breakpoint.
CSS
.with-sidebar {
display: flex;
flex-wrap: wrap;
gap: var(--sidebar-gap, 1rem);
}
.with-sidebar > :first-child {
flex-basis: var(--sidebar-width, 20rem);
flex-grow: 1;
}
.with-sidebar > :last-child {
flex-basis: 0;
flex-grow: 999;
min-inline-size: var(--sidebar-content-min, 50%);
}
/* Sidebar on right */
.with-sidebar[data-side="right"] {
flex-direction: row-reverse;
}React Component
interface SidebarProps {
sideWidth?: string;
contentMin?: string;
gap?: string;
side?: 'left' | 'right';
children: React.ReactNode;
}
export function Sidebar({
sideWidth = '20rem',
contentMin = '50%',
gap = '1rem',
side = 'left',
children,
}: SidebarProps) {
return (
<div
className="with-sidebar"
style={{
'--sidebar-width': sideWidth,
'--sidebar-content-min': contentMin,
'--sidebar-gap': gap,
} as React.CSSProperties}
data-side={side}
>
{children}
</div>
);
}
// Usage
<Sidebar sideWidth="250px" contentMin="60%">
<nav>Sidebar content</nav>
<main>Main content</main>
</Sidebar>Switcher
Switches between horizontal and vertical based on available width.
CSS
.switcher {
display: flex;
flex-wrap: wrap;
gap: var(--switcher-gap, 1rem);
}
.switcher > * {
flex-grow: 1;
flex-basis: calc((var(--switcher-threshold, 30rem) - 100%) * 999);
}
/* Limit number of items per row */
.switcher > :nth-last-child(n+4),
.switcher > :nth-last-child(n+4) ~ * {
flex-basis: 100%;
}React Component
interface SwitcherProps {
threshold?: string;
gap?: string;
limit?: number;
children: React.ReactNode;
}
export function Switcher({
threshold = '30rem',
gap = '1rem',
limit = 4,
children,
}: SwitcherProps) {
return (
<div
className="switcher"
style={{
'--switcher-threshold': threshold,
'--switcher-gap': gap,
} as React.CSSProperties}
>
{children}
</div>
);
}
// Usage - items stay horizontal until container < 30rem
<Switcher threshold="500px">
<Card>First</Card>
<Card>Second</Card>
<Card>Third</Card>
</Switcher>Center
Horizontally center with max-width.
CSS
.center {
box-sizing: content-box;
max-inline-size: var(--center-max, 60ch);
margin-inline: auto;
padding-inline: var(--center-gutter, 1rem);
}
/* Text centering variant */
.center[data-text] {
text-align: center;
}
/* Intrinsic centering (shrink-wrap) */
.center[data-intrinsic] {
display: flex;
flex-direction: column;
align-items: center;
}React Component
interface CenterProps {
max?: string;
gutter?: string;
text?: boolean;
intrinsic?: boolean;
children: React.ReactNode;
}
export function Center({
max = '60ch',
gutter = '1rem',
text = false,
intrinsic = false,
children,
}: CenterProps) {
return (
<div
className="center"
style={{
'--center-max': max,
'--center-gutter': gutter,
} as React.CSSProperties}
data-text={text || undefined}
data-intrinsic={intrinsic || undefined}
>
{children}
</div>
);
}
// Usage
<Center max="800px">
<article>Centered content with max width</article>
</Center>Cover
Full-height section with vertically centered content.
CSS
.cover {
display: flex;
flex-direction: column;
min-block-size: var(--cover-min-height, 100vh);
padding: var(--cover-padding, 1rem);
}
.cover > * {
margin-block: var(--cover-space, 1rem);
}
.cover > :first-child:not([data-centered]) {
margin-block-start: 0;
}
.cover > :last-child:not([data-centered]) {
margin-block-end: 0;
}
.cover > [data-centered] {
margin-block: auto;
}React Component
interface CoverProps {
minHeight?: string;
padding?: string;
children: React.ReactNode;
}
export function Cover({ minHeight = '100vh', padding = '1rem', children }: CoverProps) {
return (
<div
className="cover"
style={{
'--cover-min-height': minHeight,
'--cover-padding': padding,
} as React.CSSProperties}
>
{children}
</div>
);
}
export function CoverCenter({ children }: { children: React.ReactNode }) {
return <div data-centered>{children}</div>;
}
// Usage - header at top, content centered, footer at bottom
<Cover>
<header>Logo</header>
<CoverCenter>
<h1>Hero Content</h1>
</CoverCenter>
<footer>Footer</footer>
</Cover>Grid
Responsive grid without media queries.
CSS
.grid {
display: grid;
gap: var(--grid-gap, 1rem);
grid-template-columns: repeat(
auto-fill,
minmax(min(var(--grid-min, 250px), 100%), 1fr)
);
}
/* Fixed columns variant */
.grid[data-columns="2"] {
grid-template-columns: repeat(2, 1fr);
}
.grid[data-columns="3"] {
grid-template-columns: repeat(3, 1fr);
}
.grid[data-columns="4"] {
grid-template-columns: repeat(4, 1fr);
}React Component
interface GridProps {
min?: string;
gap?: string;
children: React.ReactNode;
}
export function Grid({ min = '250px', gap = '1rem', children }: GridProps) {
return (
<div
className="grid"
style={{
'--grid-min': min,
'--grid-gap': gap,
} as React.CSSProperties}
>
{children}
</div>
);
}
// Usage
<Grid min="300px" gap="1.5rem">
<Card>1</Card>
<Card>2</Card>
<Card>3</Card>
<Card>4</Card>
</Grid>Frame
Aspect ratio container for media.
CSS
.frame {
aspect-ratio: var(--frame-ratio, 16 / 9);
overflow: hidden;
}
.frame > img,
.frame > video,
.frame > iframe {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
}
/* Common ratios */
.frame[data-ratio="1:1"] { --frame-ratio: 1; }
.frame[data-ratio="4:3"] { --frame-ratio: 4 / 3; }
.frame[data-ratio="16:9"] { --frame-ratio: 16 / 9; }
.frame[data-ratio="21:9"] { --frame-ratio: 21 / 9; }React Component
type FrameRatio = '1:1' | '4:3' | '16:9' | '21:9' | string;
interface FrameProps {
ratio?: FrameRatio;
children: React.ReactNode;
}
const ratioMap: Record<string, string> = {
'1:1': '1',
'4:3': '4 / 3',
'16:9': '16 / 9',
'21:9': '21 / 9',
};
export function Frame({ ratio = '16:9', children }: FrameProps) {
return (
<div
className="frame"
style={{
'--frame-ratio': ratioMap[ratio] || ratio,
} as React.CSSProperties}
>
{children}
</div>
);
}
// Usage
<Frame ratio="16:9">
<img src="/hero.jpg" alt="Hero" />
</Frame>Reel
Horizontal scrolling container.
CSS
.reel {
display: flex;
gap: var(--reel-gap, 1rem);
overflow-x: auto;
overflow-y: hidden;
scrollbar-color: var(--color-border-primary) transparent;
}
.reel > * {
flex: 0 0 var(--reel-item-width, auto);
}
/* Hide scrollbar (optional) */
.reel[data-hide-scrollbar] {
scrollbar-width: none;
}
.reel[data-hide-scrollbar]::-webkit-scrollbar {
display: none;
}
/* Snap scrolling */
.reel[data-snap] {
scroll-snap-type: x mandatory;
scroll-padding-inline: var(--reel-gap, 1rem);
}
.reel[data-snap] > * {
scroll-snap-align: start;
}React Component
interface ReelProps {
gap?: string;
itemWidth?: string;
snap?: boolean;
hideScrollbar?: boolean;
children: React.ReactNode;
}
export function Reel({
gap = '1rem',
itemWidth,
snap = false,
hideScrollbar = false,
children,
}: ReelProps) {
return (
<div
className="reel"
style={{
'--reel-gap': gap,
'--reel-item-width': itemWidth,
} as React.CSSProperties}
data-snap={snap || undefined}
data-hide-scrollbar={hideScrollbar || undefined}
>
{children}
</div>
);
}
// Usage
<Reel itemWidth="300px" snap>
<Card>1</Card>
<Card>2</Card>
<Card>3</Card>
<Card>4</Card>
</Reel>Box
Padding and background container.
CSS
.box {
padding: var(--box-padding, 1rem);
border: var(--box-border, 0);
background-color: var(--box-bg, transparent);
border-radius: var(--box-radius, 0);
}
/* Invert colors */
.box[data-invert] {
background-color: var(--color-bg-inverse);
color: var(--color-text-inverse);
}React Component
interface BoxProps {
padding?: string;
bg?: string;
invert?: boolean;
children: React.ReactNode;
}
export function Box({ padding = '1rem', bg, invert, children }: BoxProps) {
return (
<div
className="box"
style={{
'--box-padding': padding,
'--box-bg': bg,
} as React.CSSProperties}
data-invert={invert || undefined}
>
{children}
</div>
);
}Composition Examples
Page Layout
<Stack space="xl">
<Center max="1200px">
<header>
<Cluster justify="space-between">
<Logo />
<nav>
<Cluster space="md">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</Cluster>
</nav>
</Cluster>
</header>
</Center>
<Cover minHeight="80vh">
<CoverCenter>
<Center text>
<Stack space="lg">
<h1>Welcome</h1>
<p>Hero content here</p>
</Stack>
</Center>
</CoverCenter>
</Cover>
<Center max="1200px">
<Sidebar sideWidth="300px">
<aside>
<Stack space="md">
<h2>Sidebar</h2>
{/* sidebar content */}
</Stack>
</aside>
<main>
<Stack space="lg">
<Grid min="300px">
<Card>1</Card>
<Card>2</Card>
<Card>3</Card>
</Grid>
</Stack>
</main>
</Sidebar>
</Center>
</Stack>Card Grid
<Grid min="280px" gap="1.5rem">
{items.map(item => (
<Box key={item.id} padding="1.5rem">
<Stack space="md">
<Frame ratio="16:9">
<img src={item.image} alt="" />
</Frame>
<h3>{item.title}</h3>
<p>{item.description}</p>
<Cluster justify="space-between">
<span>{item.date}</span>
<a href={item.url}>Read more</a>
</Cluster>
</Stack>
</Box>
))}
</Grid>Tailwind Implementation
// tailwind.config.js - custom utilities
module.exports = {
theme: {
extend: {
// Custom layout utilities can use @apply or plugins
},
},
plugins: [
function({ addComponents }) {
addComponents({
'.stack': {
display: 'flex',
flexDirection: 'column',
gap: 'var(--stack-space, 1rem)',
},
'.cluster': {
display: 'flex',
flexWrap: 'wrap',
gap: 'var(--cluster-space, 1rem)',
alignItems: 'center',
},
// ... etc
});
},
],
};Or use utility classes directly:
<!-- Stack -->
<div class="flex flex-col gap-4">
<!-- Cluster -->
<div class="flex flex-wrap gap-2 items-center">
<!-- Center -->
<div class="max-w-prose mx-auto px-4">
<!-- Grid -->
<div class="grid gap-4 grid-cols-[repeat(auto-fill,minmax(250px,1fr))]">Resources
- Every Layout: https://every-layout.dev
- Intrinsic Web Design: https://www.youtube.com/watch?v=AMPKmh98XLY
- Layout Land: https://www.youtube.com/c/LayoutLand
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

