A Tailwind CSS v4 plugin by Numbered

Fluid
Layout System

Design each breakpoint once. Your page renders 1:1 at every mockup width, then columns, gutters, margins and type scale fluidly in between.

$ bun add -d @numbered/tailwind-fluid-layout-systemScroll to resize the screen

01 / 10 · desktop · your screen

Your screen

This is your screen, on the desktop grid of this page. Scroll to shrink it and watch the layout follow.

desktop: { columns: 12, gutter: 10, margin: 24, mockupWidth: 1440, screen: 'lg' }

Reference

Everything the plugin exposes.

Fluid layout = fluid grid + fluid type, both anchored to a single reference: the mockup width of each design breakpoint. At that viewport width, 1px in the design renders as 1px on screen. Between breakpoints, columns, gutters, margins and the root font size scale proportionally with the viewport, so the page keeps the exact proportions of the design. Describe each breakpoint once (mockup width, columns, gutter, margin) and the plugin generates the CSS variables and Tailwind utilities that size, space and position anything on that grid, at any nesting depth.

Simulator

viewport width1440px · desktop

Aa text-4xl · 36.0px

viewport at 65% of 2200px

grid
12 cols · desktop
mockupWidth
1440px
fontScalingMaxWidth
1540px
--grid-width
1392.0px
--column
106.8px
--gutter
10.0px
--margin
24.0px
1rem
16.0px
// tailwind.config.js
theme: {
  grid: {
    mobile:  { columns: 6,  gutter: 10, margin: 10, mockupWidth: 375 },
    desktop: { columns: 12, gutter: 10, margin: 24, mockupWidth: 1440, screen: 'lg' }
  }
}

From design to code

Measure the mockup, write the class. At the mockup width every utility renders the exact pixel value from the design, and between breakpoints it scales in proportion, so the layout never drifts from the design. Here with the desktop grid of this page, drawn at 1440px:

class1024px1440px · mockup1920px
span-w-3242.1px340.5px454.0px
span-w-2-wide166.2px233.7px311.6px
gutter-w-17.1px10.0px13.3px
margin-w-117.1px24.0px32.0px
text-4xl25.6px36.0px38.5px
p-2417.1px24.0px25.7px

Rem based Tailwind values scale the same way until fontScalingMaxWidth (1540px here) stops them. To go from a measured width to a class, use the px-to-cols CLI at the end of this page.

How it works

Each breakpoint writes four variables on html: --column, --gutter, --margin and --grid-width. They are expressed in vw, so a 1440px mockup renders pixel perfect at 1440px and keeps its proportions at every other width. The faint lines on this page are the grid guidelines.

Live values · resize the window

Fluid type

The root font size follows the same rule: 1rem equals 16px at the mockup width and scales with it. Every rem based Tailwind value (font sizes, spacing, radii) scales in step with the grid, so type set in the mockup keeps its size relative to the layout. Set fontScalingMaxWidth to stop the growth on large screens; the simulator above marks where it kicks in.

Columns

span-* sizes an element to a number of columns, gutters between them included.

span-w-1
span-w-2
span-w-3

Add -wide for one extra gutter, -wider for two. Handy for offsets that land on the next column.

span-w-2
span-w-2-wide
span-w-2-wider

Gutters and margins

gutter-* and margin-* give multiples of the gutter and of the outer page margin.

span-w-2
gutter-gap-1
span-w-3
span-w-2
gutter-ml-1
span-ml-1-wider

Any property

The three prefixes combine with width, height, padding, margin, inset, gap, scroll spacing, border width and text indent. Counts can be fractional and negative: span-w-1.5, span-w-1.5-wide, -span-ml-1. Beyond the widest grid, use an arbitrary value: span-w-[16].

Playground

count3.0columns

span-w-3

width: calc(3 * var(--column) + 2 * var(--gutter))

= 0.0px at this viewport

Nesting

CSS subgrid can pass the page grid down, but only if every wrapper in between is itself a subgrid. One flex row or positioned box breaks the chain. These utilities resolve to viewport units instead, not percentages of the parent, so span-w-2 has the same width in the body, in a flex row, in a grid cell or in an absolutely positioned box four wrappers deep. Offset each level by whole columns and its content stays on the page grid, whatever the parent is.

span-w-2

From mockup to class

The bundled CLI converts a measured pixel width into the closest class.

bunx px-to-cols 330 --columns 24 --mockup 1440 --gutter 24 --margin 24
# → span-w-6 (330px, exact)