Skip to main content

Command Palette

Search for a command to run...

Flexible density for all the UI

Updated
3 min read
Flexible density for all the UI
D
Frontend & UI engineer, focused on design systems and native-first UI. I care about the interaction details most people skip.

In our UI system we dont use pixel values to define spacing, we use semantic variables, (t-shirt sizes: gap-sm, p-lg, my-2xl) - so thats like you change one CSS variable, and the whole UI adapts.

We limit user customization to just 5 presets - 0.8 / 0.9 / 1 / 1.1 / 1.2:

  • Symmetric around 1.0 - two steps down, two up;

  • ±20% is deliberately restrained - enough to change the feel (data-dense ↔ spacious) without breaking layouts or distorting proportions;

  • 5 discrete stops = perceptible but decision-light. Most users never open the control, so it's a coarse preference, not a fine instrument

The biggest reason why do we have it - to support bigger themes differentiation. Brand-overlay density has slightly bigger range: 0.7–1.35; so we can differentiate brands providing customizable UI: >1 reads editorial/airy, <1 terminal/dense. Same components, different feel — no fork. The user can always override.

/* Density cascade: user override → brand default → system default (1) */
--density: var(--user-density, var(--brand-density, 1));

Everything is built on a 4px base unit and a single --density variable, all declared in :root:

--unit: 0.25rem; 
/* Every spacing token is base-step × unit × density */
--space-xs:  calc(2  * var(--unit) * var(--density, 1));  /*  8px @ d=1 */
--space-sm:  calc(4  * var(--unit) * var(--density, 1));  /* 16px */
--space-md:  calc(6  * var(--unit) * var(--density, 1));  /* 24px */
--space-lg:  calc(8  * var(--unit) * var(--density, 1));  /* 32px */
--space-xl:  calc(12 * var(--unit) * var(--density, 1));  /* 48px */
--space-2xl: calc(16 * var(--unit) * var(--density, 1));  /* 64px */
--space-3xl: calc(24 * var(--unit) * var(--density, 1));  /* 96px */
--space-4xl: calc(32 * var(--unit) * var(--density, 1));  /* 128px */
--space-5xl: calc(40 * var(--unit) * var(--density, 1));  /* 160px */

But those are details, the main point was to have flexible spacing underneath so all the UI is adaptable to any vibe.


Also if we talk about accessibility - minimum control height is preserved, different for touch screens:

--size-touch-min: 2.75rem; /* 44px — coarse/touch base (scales with density) */
--control-height-min: 2.25rem; /* 36px — coarse floor (hard min) */

@media (pointer: fine) {
  --size-touch-min: 2.25rem; /* 36px — fine/mouse base */
  --control-height-min: 2rem; /* 32px — fine floor */
}

--control-height: max(var(--control-height-min), calc(var(--size-touch-min) * var(--density)));
  • 44px touch base = the WCAG 2.5.5 / Apple HIG / Material touch-target minimum — a finger needs ~44 CSS px.

  • 36px desktop base = the 2026 SaaS convention (Linear / Vercel / shadcn / Notion); a mouse is precise, so desktop controls can be denser than touch.

  • The floors exist because controls, unlike spacing, cant shrink freely. Spacing tokens have no floor (Compact just scales them down). Controls are clamped by max() so Compact density (0.8×) can't shrink a button into an un-hittable size: 44×0.8 = 35.2 → 36 on touch, 36×0.8 = 28.8 → 32 on desktop. That asymmetry (whitespace compresses without limit, hit-targets don't) is the whole point of the floor.

  • The floor is a separate variable from the base so Compact can legally drop below the standard (desktop 36 → 32) while still being bounded. If the floor equalled the base, density could never push a control under its standard size at all.

More from this blog

F

Frontend Notes

11 posts

I've spent the last while building a fairly large design system, and most of what I write here comes out of that: staying close to the browser, runtime theming, accessibility, and the small interaction details that make an interface feel right. This blog is where I write that down - practical frontend craft, honestly, including the parts I'm still figuring out. If you care about building UI that's fast, accessible, and doesn't fight the platform, you'll probably feel at home here.