Skip to content

thekeydev/gridpack

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gridpack

CSS Grid layouts in one string. See demo.

npm bundle size license


A compact DSL that compiles layout strings into CSS Grid. One React component, optional extensions, zero wrapper divs.

<Grid layout="hsCf hhh scc sff 8">
  <Header />
  <Sidebar />
  <Content />
  <Footer />
</Grid>

That's a full page layout. No CSS files, no class names, no nesting.

Install

npm install gridpack
import { Grid } from "gridpack";

The Layout String

Everything fits in one prop. The string has a simple grammar:

[|] [legend] [rows...] [gap] [?flags] [| col-sizes [| row-sizes]]

Quick examples

String Result
ab Two equal columns
|ab Two equal rows (transpose)
ab abb Two columns, 1:2 ratio
hsCf hhh scc sff 8 Holy grail, 8px gap, content grows
* 8 Auto h-stack with gap (needs children)
| 12 Auto v-stack with 12px gap
*7 ?wh 7-column auto-flow grid
a(e)B ab* 8 | .# Form: labels right-aligned, inputs grow, repeat rows
sa ss Sa* 8 | 120# Pinned sidebar + repeating list
abc | 100~# 100~# 100~# 3 columns, each min 100px

Token vocabulary

Token Meaning
a-z Named area
A-Z Grow area (tracks become 1fr)
. Empty cell (in map) / auto (in sizes)
# 1fr (in sizes)
| Transpose prefix / pipe separator
~ minmax(a, b) — e.g. 200~#
* Auto-legend / repeat row
? Flags (?w width, ?h height, ?cC center)
( ) Per-area alignment — a(cC) centers area a
{ } Template variable — {sidebar}

Flags — SECBAG

Lowercase = justify-content, uppercase = align-content:

  • S start · E end · C center · B space-between · A space-around · G space-evenly
  • ?w / ?h — force full width / height

Repeat rows

Append * to a row to repeat it based on children count:

<Grid layout="habf hh ab* ff 8 | .#">
  <Header />
  <Footer />
  {fields.map(f => <><Label /><Input /></>)}
</Grid>

Uppercase letters in repeat rows are pinned — they span all repetitions:

// Sidebar spans all rows, items repeat next to it
<Grid layout="sa ss Sa* 8 | 120#">
  <Sidebar />
  {items.map(i => <Card />)}
</Grid>

Component Props

Prop Type Description
layout string Layout string
col boolean Shorthand for transpose (| prefix)
gap number | string Override gap
vars object Values for {placeholder} substitution
onVarsChange function Callback when extensions mutate vars
extensions array Extension objects
xs sm md lg xl string Layout strings per container breakpoint
breaks object Custom breakpoint thresholds

Minimal usage

// Horizontal stack — no props needed
<Grid>
  <A /> <B /> <C />
</Grid>

// Vertical stack
<Grid col>
  <Header /> <Content /> <Footer />
</Grid>

// Responsive
<Grid layout="|abc ?w 8" sm="ab aab ?w 8" lg="abc ?w 8">
  <A /> <B /> <C />
</Grid>

Extensions

Behavioral plugins. Composable. Stack them in an array.

import { Grid, splitPane, scrollable, debug } from "gridpack";

let [v, setV] = useState({ w: 200 });

<Grid
  layout="sC | {w}"
  vars={v}
  onVarsChange={setV}
  extensions={[
    splitPane({ var: "w", edge: "s:e", min: 80, max: 400 }),
    scrollable({ area: ["s", "c"] }),
    debug(),
  ]}
>
  <Sidebar />
  <Content />
</Grid>

Available extensions

Extension Description
debug({ color? }) Grid cell overlay
splitPane({ var, edge, min?, max? }) Draggable resize handle
collapsible({ var, area, expanded?, collapsed? }) Toggle area size on click
accordion({ var, items, collapsed? }) Mutual exclusion — expand one, collapse others
scrollable({ area, axis? }) Independent scrolling per area
overlay({ area, over }) Layer one area over another's grid cells
animate({ properties?, duration?, easing? }) CSS transitions on track changes
tabs({ var, items, position? }) Tab bar with content switching
multiColumn({ area, fill? }) CSS columns aligned to grid tracks
fisheye({ axis?, intensity?, min? }) Tracks expand near cursor, compress away

Writing custom extensions

Extensions are plain objects with lifecycle hooks:

let myExtension = (opts) => ({
  name: "myExtension",
  render: ({ parsed, vars, setVar, containerRef }) => [],   // inject elements
  containerStyle: ({ parsed, vars }) => ({}),                // modify container
  areaStyle: (area, vars) => null,                           // modify area wrappers
  transformVars: (vars) => vars,                             // derive vars from vars
  transformAreas: (parsed) => parsed,                        // modify parsed result
});

Grammar (BNF)

layout    = ["|"] [legend] [map-rows] [gap] [?flags] ["|" cols ["|" rows]]
legend    = "*" | "*"digit+ | area-def+
area-def  = letter | LETTER | letter"("mods")" | LETTER"("mods")"
mods      = ("s"|"e"|"c"|"S"|"E"|"C")+
map-row   = (letter|LETTER|".")+ ["*"]
gap       = number [number]
?flags    = "?" ("w"|"h"|"s"|"e"|"c"|"b"|"a"|"g"|"S"|"E"|"C"|"B"|"A"|"G")+
size      = "." | "#" | number | atom"~"atom | css-literal

Implicit rules:
  legend-only           → single-row map
  empty + children      → "*" (auto-legend)
  "|" + empty           → transposed "*"
  *N + children         → N columns, auto rows
  repeated chars        → 1fr (proportional)
  row ending "*"        → repeat (varargs)
  UPPER in repeat       → pinned (shared across repetitions)
  "{var}"               → replaced from vars prop

Before & After

Traditional CSS GridGridpack
.layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar content content"
    "sidebar footer footer";
  grid-template-columns: 200px 1fr 1fr;
  grid-template-rows: auto 1fr auto;
  gap: 8px;
}
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer  { grid-area: footer; }
<Grid layout="hscf hhh scc sff 8 | 200##">
  <Header />
  <Sidebar />
  <Content />
  <Footer />
</Grid>

Links

Support

If gridpack saves you time, consider supporting development: Donate

License

MIT