on making this site light [archived]

I’ve recently been working on making this site lighter. Most of that time was spent obsessing over streamlining CSS. This post details various optimisations I’ve used


* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-style: normal

This is a simple reset. There’s the usual margin and padding resets. Box sizing is set to border-box so that borders and paddings are taken into account when defining elements’ widths and heights

The font style is reset because I use the dfn and cite tags, which are italicised by default. Italics more apropros for serifs


body {
margin: 0 auto;
padding: 3.3em 1.6em 3.3em 1.6em;
max-width: 64ch;
background-color: #e3dbc3;
font-size: 20px;
line-height: 1.6;
font-family: sans-serif

The site features a simple central column for content. The body is set to a maximum width of 64 ch. The ch unit is defined by the width of the numeral “0”

The font size is set to 20 px and a line height of 1.6. The font is set to the browser’s default sans-serif. These can be reduced to a single property

font: 20px/1.6 sans-serif

The body is divided into four sections: header, aside, main, and footer.

aside, header, main { margin: 0 0 2.6rem 0 }


The values used in margins, paddings, and the type scale are derived from the golden ratio (φ). I round these values to the nearest tenths to trim off a few bytes

0.78, 1.00, 1.27, 1.62, 2.06, 2.59, 2.62, 3.33, …


The aside section appears in project pages and contains links and time-tracker summaries. It’s a simple 3-column grid

aside {
display: grid;
grid-template-columns: 3fr 3fr 3fr;
grid-gap: 1.3em

Using the grid shorthand property and the repeat function, this can be shortened

grid: auto / repeat(3, 3fr)

The links appear in a list with disabled bullets

nav ul { list-style: none }


The main section contains various elements like paragraphs, images, and blockquotes, which are evenly spaced apart

main p, main blockquote,
main img, main code:not(.inline), main h2 {
margin: 0 0 1.3rem 0

However, this takes up way too much space. But since only direct children of the element are being targeted, the child combinator selector can be used:

main > 1 { margin: 0 0 1.3rem 0 }

Hyperlinks are set to match to the body text colour. To avoid duplicated declarations, I combined the two into a separate block:

a, body { color: #222 }

I set the links’ focus and hover states to the inverse of the background and foreground colours. This was also applied to text selection and inline code and kbd elements

::selection, a:focus, a:hover, code.inline, kbd {
background-color: #222;
color: #e3dbc3

I didn’t style links any further. I used to dislike how underlined text was rendered because lines went through descenders but this is no longer the case in modern browsers


As demonstrated on this page, code blocks are a big part of the site. Code blocks are set to horizontally scroll when text overflows. Wrapping of the blocks’ contents is disabled

code:not(.inline) {
display: block;
padding: 0 1.6em 0 1.6em 0;
overflow-x: auto;
white-space: nowrap

Like the code blocks, blockquotes are padded to visually separate them from the rest of the content. The .q class targets the paragraph element housing the quote itself; its only function is to provide a bit of space between the quote and the attribution

blockquote { padding: 0.8em 1.3em 0.8em 1.3em }
.q { margin: 0 0 .8rem 0 }

“Trifles make perfection and perfection is no trifle”

— Michelangelo

Images are housed in figure blocks as they may have captions. The figure blocks aren’t modified, but the captions are center-aligned

figcaption { text-align: center }

Images are set to a width of 100% to make them responsive. The .p class uses the image-rendering property to scale pixel-art images. This enables me to host smaller images, effectively improving page load times

img { width: 100% }
.p { image-rendering: crisp-edges }
Six squares on a black rectangle
This is a 93-byte 16×10 image.

Note: If you’re using Chrome, the image above may be blurry. For Chrome, image-rendering must be set to pixelated. I purposefully omitted this as I don’t care about Chrome


Here are some additional tricks I used for further reductions. I don’t necessarily recommend these practices as the savings they provide are somewhat trivial. That and you may be sacrificing some degree of stylesheet readability. However, for this endeavour, those savings are quite substantial

Remove units for zero values. If a value is a decimal below one, the zero can be omitted

kbd { padding: 0px 5px 0px 0px }
kbd { padding: 0 5px 0 0 }

Use IDs or class names in lieu of tag names, where appropriate

figcaption { text-align: center }
.f { text-align: center }

aside, header, main { margin: 0 0 2.6rem 0 }
.m { margin: 0 0 2.6rem 0; }

Make use of shorthand properties. Margins, for example, can be shortened like so:

body {
margin-top: 3.3em;
margin-right: 1.6em;
margin-bottom: 3.3em;
margin-left: 1.6em

body { margin: 3.3em 1.6em 3.3em 1.6em }
body { margin: 3.3em 1.6em }

Remove spaces around curly brackets and after semicolons and the last semicolon of each block

body { margin: 0 auto; padding: 3.3em 1.6em; }
body{margin:0 auto;padding:3.3em 1.6em}

Optional: Forget vendor prefixes. Forget IE. Forget Chrome.


I ended up with 609 bytes. According to CSS Stats, there are a total of 18 rules, 23 selectors, 30 declarations (all unique, no repetitions), and 19 properties

That’s all. Thanks for reading and I hope you learned something. If you have any suggestions for further optimisations, send me a message on the Fediverse or Twitter

Update: As of , the stylesheet has been reduced to 489 bytes

Update: 461 bytes as of