Fluid Space Sizing

Fluid typography sizing with modular scales

Introduction

This article assumes you've read the Fluid Typography Sizing and Scales first.

Spacing is the cement that holds together the bricks of components on a UI. The importance of good spacing is hard to overstate: it's the most important aspect of design. And the difference between an exquisitely elegant design and a graceless one. So it's no wonder designers are rightly taught to pay great attention to whitespace.

However, it remains one of the most unnecessarily unsystematized aspects of web design and development. Most designers still space things out by hand: Shift + Left, Shift + Right, Shift + Up, and Shift + Down to move things around by increments of 10px and counting 10, 20, 30 in their heads is the bread and butter of spacing.

As for developers, spacing is handled with hard-coded utility classes 🥴, infinitely growing BEM classes 🤢, or, God forbid, plain old ah hoc CSS 🤮. Out of all of these, utility classes is the least worse: if there's a clear spacing scale agreed upon between developer and designer, it's relatively easy to implement a given design. But if the designer decides to change a spacing rule that runs across the entire ui, you're going to have a hard time identifying all the instances that pertain to that spacing rule and update them to something else.

Why is that? Because, even if you're using utility classes, those spacing rules have been hard-coded in the UI's components.

We can do better.

Let's give spacing the same well-thought-out treatment that we gave typography with a hand-picked scale and fluid space sizing. This enables clear communication between designer and developer, preserves the designer's complete control over spacing, even after the implementation is complete, and greatly speeds up development.

Modular scale

For the same reason we're not going to use a modular scale for typography, we're not going to use a modular scale for spacing. The reason is that modular scales are too limited and you could try different ratios and equations, but at that point you’re just trying to pick a scale that happens to match the sizes you already know you want.

Hand-picked scale

It's more pragmatic to hand-pick the values because this approach grants the designer complete control over the number and variety of space sizes.

Let's take a look at TailwindCSS's spacing scale.

NameSizePixelsVisual representation
00px0px
px1px1px
0.50.125rem2px
10.25rem4px
1.50.375rem6px
20.5rem8px
2.50.625rem10px
30.75rem12px
3.50.875rem14px
41rem16px
51.25rem20px
61.5rem24px
71.75rem28px
82rem32px
92.25rem36px
102.5rem40px
112.75rem44px
123rem48px
143.5rem56px
164rem64px
205rem80px
246rem96px
287rem112px
328rem128px
369rem144px
4010rem160px
4411rem176px
4812rem192px
5213rem208px
5614rem224px
6015rem240px
6416rem256px
7218rem288px
8020rem320px
9624rem384px

This is a comprehensive enough scale, however a few notes.

First, there's one major no-go: semantics are tied with implementation. The names of the steps in the scale (1, 1.5, 2, 2.5, etc) are tied to multiples of 4px, which is an implementation detail. In order for the designer to have complete control over the design after the implementation is done, semantics and implementation have to be decoupled. This is absolutely crucial for development speed.

The other awkward thing is that we have half steps (0.5, 1.5, 2.5, etc) on the lower end of the scale because we need multiples of 2px, and we skip steps (64, 72, 80, etc) on the higher end of the scale because we don't need those multiples of 4px. This inconsistency makes for a harder-to-communicate scale and an awkward developer experience where if the designer asks you to step a component's spacing up, you don't go from 64 to 65, but from 64 to 72 🤦‍♂️.

Let's do better.

NameSizePixelsVisual representation
00px0px
11px1px
20.125rem2px
30.25rem4px
40.375rem6px
50.5rem8px
60.625rem10px
70.75rem12px
80.875rem14px
91rem16px
101.25rem20px
111.5rem24px
121.75rem28px
132rem32px
142.25rem36px
152.5rem40px
162.75rem44px
173rem48px
183.5rem56px
194rem64px
205rem80px
216rem96px
227rem112px
238rem128px
249rem144px
2510rem160px
2611rem176px
2712rem192px
2813rem208px
2914rem224px
3015rem240px
3116rem256px
3218rem288px
3320rem320px
3424rem384px

This simple change is deceptively powerful.

First, the designer doesn't have to think about absolute values (4px, 10px, 16px, etc), but relative ones ("this should be more spaced out than that by 2 steps", "that should be more spaced out than this by 4 steps", etc). Eventually the designer has to converge on absolute values, of course, but as long as the designer starts with a somewhat decent scale, the flexibility to think in relative terms is a huge speed booster, specially in the early phases of design.

Also, if the designer gets the relative values right - which is much easier than to get the absolute values right - the implementation will not change: step 30 will still be step 30, whether the designer decides later that that means 240px or 244px. This frees the developer to develop at full speed and, given an initial mockup, in parallel, without having to wait for the designer to get the values right.

Finally, by giving the designer complete control over the absolute values, we eliminate an entire category of back-and-forth work between designer and developer! The only possible mistakes or changes that require coordination between the two are those that involve a different relative step. But this is a much smaller set of changes and it would most likely involve changing a single class on a single component, for example.

Breakpoint-based space sizing

As is the case with typography, we're not going to use breakpoint-based sizing because it creates a jarring user experience and an unsustainable burden on designers and developers.

Fluid space sizing

As is the case with typography, if we're going to change the sizing at different viewport widths, we don't need as many steps, because a space of 384px would always be too overwhelming (it would overflow, in fact), on a 320px wide viewport.

And we need no variation in the lower steps, some from step 15 onwards, and more variation in the higher steps because of the two opposing forces between the need to increase the spacing on larger screens to keep the design balanced and the fact that users don't resize their browser windows to see the same content bigger, but to see more content - and therefore we don't want to just scale up the entire UI (that'd be a terrible UX).

So let's cap the scale at step 20 and add a min and a max space size.

NamePixels (min)Pixels (max)Visual representation
00px0px
11px1px
22px2px
34px4px
46px6px
58px8px
610px10px
712px12px
814px14px
916px16px
1020px20px
1124px24px
1228px28px
1332px32px
1436px36px
1540px48px
1648px64px
1764px96px
1880px144px
19128px256px
20160px416px

Resize your browser window to see how the bars change size starting from step 15 onwards.

Note on inversion of control

The technique we're using here is generally referred to, in software engineering, as "inversion of control". We create an abstraction (the scale) and implement our UI using it. This allows the designer to retain complete control over the specific values each step in the scale maps to.

Conclusion

In conclusion, as is the case with typography, between modular or hand-picked scales, hand-picked scales offer the right amount of control, while between breakpoint-based and fluid space sizing, fluid space sizing offers a significantly more efficient design and development process. This allows the developer to start working right after mockups are available, while giving the designer complete control over spacing, and offers a smoother (let's call it "fluid" 😉) user experience.

The knowledge I'm sharing in these articles is encoded in my andrecasal/ui component library. All you need from the designer is a mockup and potentially some layout rules (i.e. what to do as the viewport width increases), and you can hit the ground running building your app. Refine the design details later!

If you liked this article, you'll love my newsletter.

Golden nuggets of in-depth code knowledge. Delivered to your inbox every 2 weeks.

Guide to full-stack web development

Once you subscribe you'll get my free guide to modern full-stack web development and solve analysis paralysis from choosing which tools to use.

 

Got it, thanks!

“I thought the website was good. But the newsletter? Even better!”

Keeran Flanegan