Axioms
As the mathematician Euclid was aware, even the most complex geometries are founded on simple, irreducible axioms (or postulates). Unless your design is founded on axioms, your output will be inconsistent and malformed. The subject of this section is how to honor a design axiom system-wide, using typographic measure as an exemplar.
Measure
The width of a line of text, in characters, is known as its measure. Choosing a reasonable measure is critical for the comfortable scanning of successive lines. The Elements Of Typographic Style considers any value between 45 and 75 reasonable.
Setting a measure for print media is relatively straightforward. It is simply the width of the paper artefact divided by the number of text columns placed within it — minus margins and gutters, of course.
The web is not static or predictable like print. Each word is separated by a breaking space (unicode point U+0020), freeing runs of text to wrap dynamically according to available space. The amount of available space is determined by a number of interrelated factors including device size and orientation, text size, and zoom level.
As designers, we seek to control the users’ experience. But, as John Allsopp wrote in 2000’s The Dao Of Web Design, attempting direct control over the way users consume content on the web is foolhardy. Enforcing a specific measure would mean setting a fixed width. Many users would experience horizontal scrolling and broken zoom functionality.
To design “adaptable pages” (Allsopp’s term), we must relinquish control to the algorithms (like text wrapping) browsers use to lay out web pages automatically. But that’s not to say there’s no place for influencing layout. Think of yourself as the browser’s mentor, rather than its micro-manager.
The measure axiom
It’s good practice to try and set out a design axiom in a short phrase or sentence. In this case that statement might be, “the measure should never exceed 60ch”.
The measure of what? And where? There’s no reason why any line of text should become too lengthy. This axiom, like all axioms, should pervade the design without qualifications or exceptions. The real question is: how? In Global and local styling we set out three main tiers of styling:
- Universal (including inherited) styles
- Layout primitives
- Utility classes
The measure axiom should be seeded as pervasively as possible in the universal styles, but also made available to layout primitives (see Composition) and utility classes. But first, which property and which value should inscribe the rule?
The declaration
Fixed widths (and heights!) are anathema to responsive design, as we established in Boxes and again here. Instead, we should deal in tolerances. The max-inline-size
property, for example, tolerates any length of text, in any writing mode, up to a certain value.
p {
max-inline-size: 700px;
}
That’s the property covered. However, the px
unit is problematic. We may be able to judge, by eye, that 700px
creates a reasonable measure for the given font-size
. But the given font-size
is really just the font-size
our screen happens to be displaying at the time — it’s our parochial view of our own design.
Changing font-size
for paragraphs, or adjusting the default system font size, will create a different (maximum) measure. Because there is no relationship between character length and pixel width, we do not have an algorithm that can guarantee the correct maximum measure value.
Fortunately, CSS includes the ch
unit. The value of 1ch
is based on the width of the font’s 0
character. Importantly, this means changing the font-size
changes the value of 1ch
, thereby adapting the measure. Using ch
units is an innately algorithmic approach to measure, because the outcome is predicated on a calculation you permit the browser to make for you.
Using ch
enables us to enforce the axiom independent of font-size
, allowing it to be highly pervasive and in no danger of “going wrong”. Where "the measure should never exceed 60ch" might have been a note in some documentation, it can instead be a quality directly coded into the design’s character.
Designing without seeing
Designing by axiom requires something of a mental shift. Axioms do not directly create visual artefacts, only the characteristics of artefacts that might emerge.
Sometimes the resulting artefacts look and behave in ways that you might not have foreseen. For example, in a container which is wider than the agreed measure as applied to the base font size, elements with different font sizes will take up different proportions of that container's width. This is because 1ch
is wider for a larger font size.
At the time of conceiving the axiom, you may not have pictured this specific visual effect. But that’s not to say it isn’t sound or desirable. In fact, it’s your CSS doing exactly as you intended: maintaining a reasonable measure irrespective of the context.
Fundamentally, designing for the web is designing without seeing. You simply can’t anticipate all of the visual combinations produced by
- The modular placement of your layout components
- The circumstances and settings of each end user’s setup
Instead of thinking of designing for the web as creating visual artefacts, think of it as writing programs for generating visual artefacts. Axioms are the rules that influence how those artefacts are created by the browser, and the better thought out they are the better the browser can accommodate the user.
Global defaults
To realize the axiom, we need to ensure all applicable elements are subject to it. This is a question of selectors. We could create a class selector…
.measure-cap {
max-inline-size: 60ch;
}
…but it’s a mistake to think in terms of (utility) classes too early. It would mean applying the style manually, to individual elements in the HTML, wherever we felt it was applicable. Manual intervention is laborious, prone to error (missing elements out), and will lead to bloated markup.
Instead, we should ask ourselves which types of elements the rule might apply to. Certainly flow elements designed for text. Inline elements like <em>
and <small>
would not need to be included, since they would take up only a part of their parent flow elements’ total measure.
p,
h1,
h2,
h3,
h4,
h5,
h6,
li,
figcaption {
max-inline-size: 60ch;
}
Exception-based styling
It’s difficult to know if we’ve remembered everything here. An exception based approach is smarter, since we only have to remember which elements should not be subject to the rule. Note that inline elements would be included in the following example but, since they would take up an equal or lesser horizontal space than their parents, no ill effects would emerge.
* {
max-inline-size: 60ch;
}
html,
body,
div,
header,
nav,
main,
footer {
max-inline-size: none;
}
The <div>
element particularly tends to be used as a generic container/wrapper. It’s likely some of these elements will contain multiple adjacent boxes, with one or more of each wishing to take up the full 60ch
. This makes their parents logical exceptions.
An exception-based approach to CSS lets us do most of our styling with the least of our code. If you are not taking an exception-based approach, it may be because making exceptions feels like correcting mistakes. But this is far from the case. CSS, with its cascade and other features, is designed for this. In Harry Roberts’ ITCSS (Inverted Triangle CSS) thesis, specificity (how specific selectors are) is inversely proportional to reach (how many elements they should affect).
A universal value
Before we start using the measure value everywhere, we’d best define it as a custom property. That way, any change to the value will be propagated throughout the design.
Note that not all custom properties have to be global, but in this case we want our elements, props, and utility classes to agree. Therefore, we place the custom property on the :root
element.
:root {
--measure: 60ch;
}
This is passed into our universal block…
* {
max-inline-size: var(--measure);
}
html,
body,
div,
header,
nav,
main,
footer {
max-inline-size: none;
}
…and to any utility classes we may find we need.
.max-inline-size\:measure {
max-inline-size: var(--measure);
}
.max-inline-size\:measure\/2 {
max-inline-size: calc(var(--measure) / 2);
}
Escaping
The backslashes are required in the previous example to escape the special forward slash and colon characters.
Measure in composite layouts
Certain layout primitives inevitably accept measure-related props, and some set default values for those props using var(--measure)
. The Switcher has a threshold
prop that defines the container width at which the layout switches between a horizontal and vertical configuration:
get threshold() {
return this.getAttribute('threshold') || 'var(--measure)';
}
set threshold(val) {
return this.setAttribute('threshold', val);
}
This is a sensible default, but can easily be overridden with any string value:
<switcher-l threshold="20rem">...</switcher-l>
If we pass an illegitimate value to threshold
, the declaration will be dropped, and the Switcher’s fallback stylesheet will apply the default value anyway. Here’s what that stylesheet looks like:
switcher-l {
display: flex;
flex-wrap: wrap;
}
switcher-l > * {
flex-basis: calc((var(--measure) - 100%) * 999);
flex-grow: 1;
}
Our approach to measure is one where we assume control, but a tempered kind of control that's deferential towards the way browsers work and users operate them. Many of the 'axioms' that govern your design, like "the body font will be Font X" or "headings will be dark blue" will not have an impact on layout as such, making them much simpler to apply just with global styles. When layout comes into the equation, be wary of differing configurations and orientations. Choose properties, values, and units that enable the browser to calculate the most suitable layout on your behalf.