Firstly, a massive disclaimer, I'm not a designer. I have a mathematical mind, but I do know a few design axioms, much like I know a few software patterns, I suppose. When building this site, I'm tasked with coming up with a design that looks good all by myself. I thought I'd share my thought process, and some code!
As I've earlier described, I always start with the user's preferred font size, respecting their needs and preferences. I think that changing font sizes based on screen sizes is a bad idea, as users at different screen sizes have already picked their preferred font sizes, or at least accepted the browser defaults as reasonable. This means leaving the base font size unspecified, defaulting to 1rem
or 100%
.
Then using a Modular Scale , and deciding that I only need 3 levels of font sizes, I set my h3
as 1 rem
, the same as my base font; the larger h2
as k rem
and the largest h1
as k² rem
. If I needed a smaller font for footnotes and the like (and I'll rarely use this to respect user preferences), I'll use a font af 1/k rem
. Keeping to this modular scale means that users have a "zoom independent" cognitive load moving from one level of importance to the next.
A cousin of responsive design is fluid typography . With this, elements of typography fluidly adjusts to available screen size. While the font size is fixed to respect user preferences, we may vary the line-height, as well as the modular scale.
Line-height determines the tightness of one line of text to the next in a large body of text. On small screens, the smaller line-height allows more content on the screen, while on larger screens, the larger line-height allows more luxurious use of white-space. This can continuously adjust to screen width, or jump at discrete breakpoints. I chose discrete breakpoints for simplicity.
The modular scale determines, the ratio between the largest and smallest elements. On small screens, this ratio need to small to maximise useful content. On larger screens, the ratio can be increased to make use of the larger space.
The line-height, in absolute dimensions, apply only to the the main body font. In oder to have vertical rhythm in the page, aligning everything according to a given vertical cadence, regardless of its size, I need to ensure that every block element, including my headers are a multiple of [half] the line-height.
For example, my h3
has the same line-height as my main body font, so this will line up perfectly. The h2
header line-height is 1.5× that of the main body font, and the h1
is 2×. This allows, any combination of headers and body text, and still preserve the overall vertical rhythm. Further, by ensuring that any other element introduced obeys this multiple of [half] the line-height, I've ensured that the rhythm is preserved.
However, this can only go so far. Images can have arbitrary heights, and browser chrome like scroll bars can also have indeterminate height, disrupting the vertical rhythm so carefully constructed. Yet, we remind ourselves that in a continuously scrolling medium, it doesn't matter so much, as long as the visible region is mostly self consistent.
Finally, there's breakpoints. Instead of thinking of sizes in terms of physical screen sizes, ie, mobile, tablet, or laptop. I consider instead responsive breakpoints based on user preferences, or one that is based on the user's preferred font size, as that tells us how the users are using that screen size. For example, a physically large screen used with large fonts, can have the same content as a small screen used with small fonts. Once again, it's about being respectful, and not making assumptions about the users.
Putting it together, I have the following CSS. The resulting output look and feel should already be familiar, you're looking at it right now!
html {
--line-height: 1.375;
--scale: 1.4;
--space-y: calc(0.5rem * var(--line-height));
font-size: 1rem; /* default value anyway */
line-height: var(--line-height);
@media (min-width: 40rem) {
--line-height: 1.4375;
--scale: 1.5;
}
@media (min-width: 50rem) {
--line-height: 1.5;
--scale: 1.6;
}
}
h1 {
font-size: calc(var(--scale) * var(--scale) * 1rem);
line-height: calc(4 * var(--space-y) / var(--scale) / var(--scale));
}
h2 {
font-size: calc(var(--scale) * 1rem);
line-height: calc(3 * var(--space-y) / var(--scale));
}
h3 {
font-size: 1rem;
line-height: calc(2 * var(--space-y));
}
You can see that the base font size is the user preferred font; the headers follow a modular scale, and all elements are a multiple of [half] line-heights to preserve vertical rhythm. Fluid typography scales the tightness of the elements and the scale, according to breakpoints that depend on user preferences.
Next, colours. Since I don't have any pre-existing brand recognition, any primary colour was as good as any other. So, why not make the colour time varying? By now, you would've noticed that the colours on the page is changing all the time.
Browsers have long supported the HSL (Hue, Saturation, Lightness) colour model. Despite not being perceptually uniform, it'll have to do. I slowly and subtly rotate the hue for the primary colour, and use the opposite hue (by 180°) as the secondary colour. If I had 3 brand colors, these would've been separated by 120°. This is occuring with a periodicity of 12 minutes — so wait a bit longer if you don't see the colours changing.
In addition to the primary and secondary colours, I also subtly tint everything with the primary colour. Whites, blacks and various greys in between are tinted for cohesive effect.
And there you have it, a design that is built technically. It obeys several good design axioms, and so should be "perfect". But design is not mathematical. Take centering for example, a technically centered element may not be optically centered. And so a well rounded design needs to take that into consideration too. I did say that I wasn't a designer!