The first time I tried to create this design was over a decade ago, back when we had Web 2.0, when we were all struggling with semantic markup and strict XHTML and the web held so much promise. I wasn’t happy with the results then. Three important things had to happen for this design to work:
So now I can finally share a version of Diplograph that I’ve been thinking about since the time when blogs still mattered.
The main serif typeface, used in the site’s masthead, header elements, and body text, is EB Garamond. Garamond has long been one of my favorite fonts, and I was really happy to find this version! It’s elegant, beautiful, and thoughtful.
The body is set in 21 ⁄ 30. On screens that are large enough (basically any device that isn’t a phone), the body is 530 px wide, chosen so that each line contains about 60–70 characters.
In The Elements of Typographic Style,You would be forgiven if you looked at this site and thought, “she read Typographic Style ten years ago and never moved on from that.” You would be forgiven because you would be right. Robert Bringhurst writes:
Choose a typeface or a group of faces that will honor and elucidate the character of the text.
This is the beginning, middle and end of the practice of typography: choose and use the type with sensitivity and intelligence.
What is the character of my text? I have no idea. The content of this site is as much Animal Crossing screenshots as it is anxiety. So maybe EB Garamond is aspirational: it’s the stories I want to share, the art I want to make.
The accent typeface, used for navigation links in the header and footer, is Poppins. I wanted something super modern for contrast, something like Century Gothic or Avant Garde, to pull the blog away from looking like a 16th century manuscript. Poppins filled that space.
cat << EOF
Code is set in FiraCode (https://github.com/tonsky/FiraCode), which is
also what I use in my editor most of the time. It has a lot of really
neat / weird ligatures and contextual alternatives, such as === for
triple equals. I write a lot of TypeScript, and these hints are really
nice once you get used to them.
EOF
There’s enough really cool stuff going on with the code formatting that it needs its own page. I want to write that one soon.
The full fonts would be too large to send to every visitor. EB Garamond Regular alone would be nearly 200KB.
Each font is processed with pyftsubset, part of fontTools. For each font family, I’ve specified a range of codepoints to keep, an optional set of glyph names to drop, and the layout features I want.
For example, for EB Garamond Regular, I’ve kept these codepoints:
And these layout features:
kern
for inter-character kerningliga
for common ligaturesdlig
for discretionary ligatureslocl
for localized variantsc2sc
and smcp
for small capssups
for superscriptssinf
for scientific inferiorsordn
for ordinalsswsh
for the ridiculous and amazing swash on Qonum
for old-style figuresI also drop the c_t
and s_t
ligature glyphs.
After subsetting and compression, the font is a much more reasonable 56 KB. That’s still not free, but it’s smaller than a high quality photo, and I really like how nice the site looks with beautiful type.There are more than one thousand words on this page, so the math works out.
@font-face
font-family: "EBGaramond"
src: url("/fonts/EBGaramond-Regular.woff2") format("woff2"), url("/fonts/EBGaramond-Regular.woff") format("woff")
unicode-range: U+0000-00FF,U+2000-206F,U+FB00-FB04,U+2103,U+2105,U+2109,U+2116,U+2120,U+2122
The usual set of ligatures, such as ff or ffl are enabled. Compare affably baffled vs affably baffled.
I’ve also enabled discretionary ligatures, for example Th, tt, ss, or fj. Compare: Throttled fjord Crossing vs Throttled fjord Crossing.
I’ve removed a few specific glyphs, such as the ct and st ligatures.These examples of removed features are being rendered with an second font file that still has support for those features. Most pages on the site won’t load this second font file. They’re beautiful but distracting; these glyphs are literally known by the technical term quaint. My process for removing them is pretty fragile, but here’s what I’m doing:
glyph
, glyphName
, value
, or name
attributes.LigatureSet
that no longer has any ligatures, or the modified PairValueRecord
in the GPOS
table.These steps probably only work for the specific glyphs I’m dropping in the fonts I’m dropping them from. But now I get to turn on more ligatures, and that’s cool.
html
font-variant-ligatures: discretionary-ligatures
I use old-style proportional figures in prose; compare 5,678 vs 5,678.
Numbers with enough digits are letterspaced. Small numbers like 17 don’t get this extra space. Compare 2,048 and 555-1234 vs 2,048 and 555-1234.
Tables use tabular figures so numbers will line up with each other:
Note | Frequency (Hz) |
---|---|
C3 | 130.813 |
C4 | 261.626 |
C5 | 523.251 |
C6 | 1046.502 |
html
font-variant-numeric: oldstyle-nums
table
font-variant-numeric: tabular-nums
.figure_sequence
letter-spacing: 0.07em
InkInk is what I call the software that generates this site. automatically turns any "straight quotation marks" into their “curly equivalents,” using an algorithm based on David Dunham’s Smart Quotes algorithm.
It skips over text it doesn’t think is prose (e.g., <code>
). It will traverse sibling nodes, so it will correctly curl the marks around “emphasized text”.
“Opening punctuation hangs into the left margin in supported browsers, so the text remains aligned in each paragraph.” (Your browser may or may not support this.)
html
hanging-punctuation: first
Emphasized text and citations are italicized. Strong text is set in small caps instead of bold, which looks weirdly out of place to me.
Superscript uses the sups
font feature. EB Garamond’s subs
glyphs sit on the baseline, so I use the sinf
scientific inferiors for subscript instead, which sit below the baseline.
em
font-style: italic
strong
font-variant-caps: all-small-caps
letter-spacing: 0.05em
sup
font-variant-position: super
sub
font-feature-settings: "sinf"
cite
font-style: italic
I’ve only styled three levels of headers for now, and by this point you’ve seen all of them. <h1>
is used for the page title. On all but the smallest screens, first and second level headers hang into the left margin a bit, which makes it easier to scan for a header when scrolling through the page.
All of the headers are also anchor links that point to themselves, so you can copy the URL of a header to share a link directly to that section. When someone visits one of these links, the header is highlighted. (You can click a header to see the effect.)
EB Garamond has the wonderfully whimsical capital Q as a swash alternative, but it’s a lot in body text, so it’s enabled only on headers. Compare Quickly Quoth vs Quickly Quoth.
@mixin main_column
margin: $line_height auto
max-width: $body_width
padding: 0 $body_padding
@mixin main_column_outdent
@include main_column
@media (min-width: $compact_breakpoint)
max-width: $body_width + $line_height * 2
padding-right: $line_height * 2
hanging-punctuation: none
h1, h2, h3
font-feature-settings: "swsh" on
a
color: inherit
h1
@include main_column
@media (max-width: $column_breakpoint - 1)
@include main_column_outdent
margin-top: $line_height
@media (min-width: $column_breakpoint)
max-width: $col4_width
line-height: $line_height * 2
font-size: 48px
margin-top: $line_height * 3
color: var(--header-color)
h2
@include main_column_outdent
font-size: 30px
margin-top: $line_height * 3
&:first-child
margin-top: 0
h3
@include main_column
@include small_caps
letter-spacing: 0.2em
padding-left: $body_padding + $line_height * 3
@counter-style
to implement lists one day, but at the moment it’s not widely supported.@mixin has_hanging_marker
@media (max-width: $compact_breakpoint)
padding-left: $line_height
&::before
display: block
position: absolute
text-align: right
margin-left: -$line_height
width: $line_height
ol, ul
li
@include has_hanging_marker
ol
counter-reset: contentol
li::before
counter-increment: contentol
content: counter(contentol) "\2002"
ul
li::before
content: "•\2002"
Block quotes are marked with a vertical line and use a subdued color. The text is indented to offset it from the main text.
That was probably written by someone very cool.
blockquote
color: var(--secondary-color)
border-left: 2px solid var(--layout-color)
padding-left: $line_height - 2px
Ink supports sidenotes.They look like this. At larger layout sizes, on laptops or desktops, the sidenotes appear in the right margin. The text is set in 16 ⁄ 22, and there’s a bit of top padding so the baseline of the first line of the sidenote aligns with the body baseline. On smaller screens, the ordinal is tappable and shows the sidenote as an inline parenthetical in a subdued color.
Instead of using superscript, the inline numbers are actually EB Garamond’s ordinals. Ordinals may be intended for series, as in 1st, 2nd, 3rd, but I like how they aren’t raised quite as much. Compare superscript1 vs ordinals1.
The style and implementation of sidenotes takes after the margin notes in Joel Dueck’s Try Pollen.And Matthew Butterick’s Pollen inspired some of Ink’s markup language and architecture.
A sidenote’s structure and styling look like this:
<span class="sidenote">
<label for="9d6a952f-c07c-4416-b594-b6d05f936710"></label>
<input type="checkbox" id="9d6a952f-c07c-4416-b594-b6d05f936710">
<span class="sidenote_contents">Sidenote contents go here.</span>
</span>
span.sidenote
counter-increment: sidenote_counter
label::before
content: "\2009" counter(sidenote_counter)
font-feature-settings: "ordn" on
input[type="checkbox"]
display: none
@media (min-width: $column_breakpoint)
.sidenote_contents
@include right_column
font-size: $aside_font_size
line-height: $aside_line_height
padding-top: 6px
.sidenote_contents::before
content: counter(sidenote_counter)
font-variant-numeric: tabular-nums
display: inline-block
width: $line_height
@media (max-width: $column_breakpoint - 1)
label::before
color: var(--link-color)
.sidenote_contents
display: none
color: var(--secondary-color)
.sidenote_contents::before
content: " ("
.sidenote_contents::after
content: ") "
input:checked + .sidenote_contents
display: inline
Words and acronyms formed entirely of UPPERCASE LETTERS stick out from their surrounding text like shouting, and so Ink instead sets them in SMALL CAPS. A mix of numbers, letters, and some punctuation is also allowed: JPEG, 3DES, C-3PO, A-Z, A/B, 3 PM.
Capitals that are a part of another word are not included: macOS. Some initialisms are skipped over when part of specific phrases: EB by itself is set in small caps, but not in EB Garamond.
Some technical acronyms are set in small caps, with an initial full capital, when they are part of a mixed case name: MozJPEG, OptiPNG.
To be honest, I haven’t really found an exact set of rules I’m happy with, so Ink has a lot of exceptions to make this work.
A small amount of tracking has been added to any sequence of small caps, including acronyms, strong text, and headers. Compare: JPEG vs JPEG.
strong, h3, .small_caps
font-variant-caps: all-small-caps
letter-spacing: 0.05em
Math is typeset with . I don’t write math that often, but I think there must be a law that states as a blogging engine grows older, the probability of it adding support for approaches 1. Or, because Ink follows this law, we can write it as:
The rendering is done during site generation; there’s no client-side JavaScript needed.
I’ve made a couple of tweaks to the default styles. math is normally larger than the surrounding text, “which makes super- and subscripts easier to read,” per the docs. Diplograph’s base font size of 21 px is large enough that I think it’s okay without the size adjustment: .
For accessibility, the equivalent MathML is included in the document before the visually formatted text. Browser support for MathML is really rough, so it’s hidden from visual rendering. This is a default behavior of and not something I wrote, but I think it’s cool and important.
The fonts are specific to . Once I figure out how to generate the right glyph mappings and metrics generation, I’d like to try using Gyre Termes. When I think of math I really think of high school and college textbooks, set in Times or Times-like fonts, so Termes feels right.
isn’t a full environment, and it’s not exactly perfect. I’ve had to add a hack for to look right, but I really can’t blame anyone for not expecting someone ridiculous (me) would try to write P_{\TeX}
. In my limited use so far it’s been great.
Technical prose and captions are set in Inter. EB Garamond is great for reading, unless the words are made up or have weird capitalization rules like HTMLElement and printf and performant, so I went looking for a straight-forward sans-serif instead. I think it looks okay with
inline code
too.It’s generally true that smaller text benefits from positive tracking, and display text looks great with negative tracking. Inter tries to capture this in a formula, which it calls “Dynamic Metrics”, and honestly it seems to work pretty well.