Skip to content

Layout — Overview

@andersseen/layout is pure-CSS, attribute-driven layout and typography utilities. No JavaScript, no build step for consumers — import one stylesheet and start composing layouts with HTML attributes instead of utility classes. Usable entirely on its own: an app that only wants attribute-driven layout/typography can install just this package, without @andersseen/web-components, @andersseen/headless-components, or any framework adapter.

Terminal window
pnpm add @andersseen/layout
@import '@andersseen/layout';
/* or the pre-minified build */
@import '@andersseen/layout/dist/layout.min.css';

That’s the whole setup — there’s no JS entry point to call, nothing to initialize, and nothing to register. Because it’s static CSS keyed off attributes, it works identically in plain HTML, Astro, React, Vue, Angular, or inside server-rendered markup (no hydration step, SSR-safe by construction).

Two attributes drive everything: and-layout for flex/grid/spacing, and-text for typography. Their values are space-separated tokens, so many utilities share one attribute:

<div and-layout="horizontal gap:md align:center justify:between"></div>

Under the hood each token is a whitespace-separated-word attribute selector — [and-layout~='gap:md'], [and-layout~='align:center'], and so on. Two consequences worth knowing:

  • Order doesn’t matter. and-layout="grid gap:md cols:3" and and-layout="cols:3 grid gap:md" are identical.
  • Specificity is low and flat. Every utility is a single attribute selector (specificity 0,1,0), so overriding one with your own class or inline style is easy — there are no !importants and no deep selectors to fight.

Title

Description text using the p-sm preset.

Cancel Confirm
<div and-layout="horizontal gap:md align:center justify:between wrap:wrap">
<div and-layout="vertical gap:sm">
<h2 and-text="h2 weight:bold">Title</h2>
<p and-text="p">Description</p>
</div>
</div>
<!-- Responsive: prop@breakpoint:value -->
<div and-layout="grid cols:1 cols@md:2 cols@lg:3 gap:lg">...</div>

horizontal sets display: flex; flex-direction: row; vertical sets flex-direction: column. Both are plain switches — see the note on responsive display about why they (and grid) have no @breakpoint form.

start between end
TokenValuesCSS property
align:*start, end, center, baseline, stretchalign-items
justify:*start, end, center, between, around, evenlyjustify-content
wrap:*nowrap, wrap, wrap-reverseflex-wrap

start/end map to flex-start/flex-end; between/around/evenly map to space-between/space-around/space-evenly.

grid switches display: grid. Gap tokens (below) are shared with flex.

1
2
3
span:2
4
TokenRange / valuesCSS property
cols:*112grid-template-columns: repeat(n, minmax(0, 1fr))
span:*112, fullgrid-column (full = 1 / -1)
col-start:*113, autogrid-column-start
col-end:*113, autogrid-column-end

The same spacing scale drives gap and every padding/margin token:

  • Gapgap, gap-x (column-gap), gap-y (row-gap). Works for both flex and grid.
  • Paddingp, p-t, p-b, p-l, p-r, p-x (left+right), p-y (top+bottom).
  • Marginm, m-t, m-b, m-l, m-r, m-x (left+right), m-y (top+bottom).
TokenValueTokenValue
none0lg1.5rem
xxxs0.125remxl2rem
xxs0.25remxxl3rem
xs0.5remxxxl4rem
sm0.75remautoauto
md1rem

The auto value is what makes the margin utilities double as a centering tool: m-x:auto on a block with a width centers it horizontally (see Recipes → Center a block). Example combining several: and-layout="p:lg m-x:auto gap-x:md".

Each preset bundles a font-size / weight / line-height combination (caption sets a muted color instead of a line-height). Presets are plain switches — no @breakpoint form.

PresetFont sizeWeightLine height
h13.75rem (6xl)700 bold1.1
h23rem (5xl)700 bold1.1
h32.25rem (4xl)600 semibold1.2
h41.875rem (3xl)600 semibold1.2
h51.5rem (2xl)500 medium1.3
h61.25rem (xl)500 medium1.3
p1rem (base)400 normal1.5
p-sm0.875rem (sm)400 normal1.5
p-xs0.75rem (xs)400 normal1.5
caption0.75rem (xs)400 normal— (muted color)
Heading — h3 preset
Body copy uses the p preset: a comfortable 1rem / 1.5 line height for running text.
A smaller p-sm line, tinted with color:muted.
CAPTION PRESET — small and muted by default.
TokenValuesCSS property
align:*left, center, right, justifytext-align
weight:*thin, light, normal, medium, semibold, bold, extrabold, blackfont-weight
color:*primary, secondary, accent, muted, destructive, background, foregroundcolor

Weight numeric values: thin 100, light 300, normal 400, medium 500, semibold 600, bold 700, extrabold 800, black 900.

:::caution[color:* needs --color-* tokens] color:* (and the caption preset’s color) resolve through --color-primary, --color-foreground, etc. This is a real gap between the two packages as shipped: @andersseen/layout’s README says these resolve through “the tokens already provided by @andersseen/web-components’s theme files”, but packages/web-components’s palettes define bare --primary/--foreground (no --color- prefix) — so and-text="color:primary" silently falls back to inherited text color when the two packages are combined as-is. A standalone consumer needs to define the --color-* custom properties themselves. This docs site does exactly that (a one-time :root { --color-primary: hsl(var(--primary)); … } bridge), which is why the color:* examples above actually render. :::

Breakpoints are mobile-first (min-width). Every map-based token accepts a @breakpoint suffix — prop@breakpoint:value — and they stack, so a value applies from its breakpoint up until a larger one overrides it:

<!-- 1 column on mobile, 2 from md, 3 from lg -->
<div and-layout="grid cols:1 cols@md:2 cols@lg:3 gap:lg">...</div>
<!-- padding grows with viewport; text centers only from lg -->
<div and-layout="p:sm p@md:lg" and-text="p-sm align@lg:center">...</div>
BreakpointMin-widthBreakpointMin-width
sm640pxxl1280px
md768px2xl1536px
lg1024px

What has no responsive form: the bare horizontal / vertical / grid display switches and the and-text presets (h1caption). There is no horizontal@md or grid@md, so this package can’t swap display/direction or a whole type preset at a breakpoint — only the map-based tokens (align, justify, wrap, gap*, cols, span, col-*, all padding/margin, and text align/weight/color) are responsive. If you need to flip flex↔grid responsively, do it with your own media query on top.

Recipes — real, live layout patterns built from these tokens: centering, responsive card grids, the media object, a sidebar shell, and a toolbar.