Taming vertical white space with leading-trim and Stitches
Rude Ayelo
/
March 2021
October 2021 update: Updated the code examples to match the Stitches v1 API.
I've been designing and developing websites for a while now and one of the trickiest issues I always come accross is handling vertical white space between text nodes. When working on a layout in a visual design app, my typical workflow is:
- 1.
- 2.Place text right near the previous/next one
- 3.
- 4.Nudge up/down on a 4px basis until it looks right
- 5.
- 6.Repeat
- 7.
That way I'm able to ship solid and crisp designs quite quickly.
Translating those designs to HTML/CSS is a whole different story, text bounding
boxes are defined by the line-height
in use and it's not so easy to separate
text blocks using a consistent spacing system.
That's why a while ago I created leading-trim, a little CSS-in-JS helper to trim spacing above and below the first and last lines of a text block. You can check what it does in this demo:
leading-trim demo
By default text elements include vertical space based on its line-height value. The effect of that extra space may be overlooked or worked around, but when working with precise scales and layout components, there's probably no room for random spacing going around your text.
leading-trim
is a JavaScript port of EightShapes's Text Crop mixin (source). It returns a CSS styles object ready to be used with any CSS-in-JS library that let's you inject styles with nested pseudo-elements.
Since I'm using Stitches to write the styles for this site, I
thought I could make a little wrapper around leading-trim using its utils
feature
import { createStitches } from "@stitches/react";
import { leadingTrim } from "leading-trim";
const fontSizes = {
1: "16px",
2: "20px",
}
const lineHeights = {
1: "1.5",
2: "1.4",
}
const { styled } = createStitches({
theme: {
fonts: {
sans:
"Source Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif",
},
fontSizes,
lineHeights,
}
utils: {
textSize: (value: `$${keyof typeof fontSizes}`) => {
const size = value.substr(1);
return {
textSize: value,
margin: 0,
...leadingTrim({
lineHeight: Number(lineHeights[size]),
reference: {
textSize: 40,
lineHeight: 1,
topCrop: 4,
bottomCrop: 8,
},
}),
};
},
},
});
const Text = styled("p", {
textSize: "$1"
});
I'm keeping a direct relation between fontSizes
and lineHeights
so font size
$1
goes well with line height $1
, that eases a bit the definition of the
util. In this site the definition is a bit more complex to allow
setting line-height
on the fly and also adjusting leading-trim's correction
:
textSize: "$1 / $1 / 2 / -1";
The value
taken by the util is typed to match the fontSizes
provided in the
theme
, but in this site I'm losely typing it to allow other
values like clamp($6, 5vw, $7)
and have responsive font sizes (leadingTrim
returns sizes in em
so it works perfectly fine). In that case parsing the size
requires looking up for a specific token:
const sizes = value.match(/\$(\d+)/g);
const largestSize = sizes[sizes.length - 1].substring(1);
leadingTrim
returns an object of CSS styles so we can destructure it right
away
Since I started using this technique I feel way more confident about the outcome of my code and I don't go into visual design tools that much anymore.
Drop me a line on Twitter if you'd be interested in an npm package wrapping this util to easily include leading-trim in your Stitches based sites.
If your'e interested go check leading-trim on Github, which has some more information and links about the topic.