this page uses everything an article on this blog can use, and says how each thing is written. it is also the page to open after changing the blog, to see that nothing broke.

every number on this page was measured from the blog's own files on 16 october 2026

how a page reaches you

the blog is one html page, the frame. it holds the sidebar, the type and the list of articles. an article is a plain fragment of html in a folder of its own, and the folder is its address. the frame sets it on the page, however the reader came to it. whatever an article needs beyond prose and tables, a chart, a block of code, a note in the margin, is a piece: a small script, a stylesheet or both, fetched only by the articles that use it.

the frame the article its pieces its images three files, once when it is opened only what it uses when in view
what is fetched, and when. this drawing is an <svg> written into the article, in the same inks as the charts.

nothing comes from another site: no fonts, no scripts, no counters.1 four words come up again and again below:

the frame index.html, site.css, site.js
what every page pays for: 13.9 kb gzipped, of the 14 it is allowed.
an article its own index.html
a fragment: its text, under a few lines that make the file a page of its own and say what it is to a link preview. 40 kb at most, except for this one: it has to show every piece there is, and the checker names it and allows it 60.
a piece assets/chart.js, assets/chart.css and the like
fetched when an article needs it. the article says which in a line at its own top, written there by a script that reads its text. each has a budget of its own.
the library at the foot of index.html
the list of articles: a title, a line, a date and a few tags for each.
what the heaviest pieces weigh, gzipped
pieceweight
chart5.5 kb
dump5.1 kb
code4.7 kb
graph3.5 kb
notes3.4 kb
bars3.0 kb
listing2.7 kb
knob2.7 kb
the eight heaviest of sixty-nine. this page asks for every one of the sixty-nine, and an article with no chart never fetches the first.

every address is a folder with a page in it, so the blog runs from any static host, in any folder, with nothing to configure: typography/ is a section, typography/everything-this-blog-can-do/ is this page, and tag/blog/ is everything filed under a tag. every heading like the one above has an address of its own, after a #, and a line in the sidebar. the addresses the blog had before, like #/typography, still lead to the right place.

the frame around an article

before any piece, this is what the frame does for every article:

one piece comes with length, not with anything written: an article that takes 20 minutes or more to read, as this one does, keeps the part its reader left off at, in their own browser, and offers it when they come back. read past a heading or two, close the tab, and open this article again from its address: a note at the foot of the screen leads back to the part, and escape or a scroll sends it away. nothing is kept short of the first heading, and the part is let go at the end.

two more things belong to the frame without being part of it. the frame only knows how to fetch them, and does so when a reader first asks. the search is one: the entry under the sections, or /. it answers at once from titles, decks and tags, then reads the words of every article and searches those as well: a file written beside the feed, each article cut at its headings with its markup taken out, a quarter lighter than the feed and nothing to take apart. under each article it found are the parts that say what was asked for, and each is a link to that heading. try it with glider: it finds this sentence, and the clip under images. nothing typed there is sent anywhere.

the other is a handful of keys, for reading without the mouse:

what each key does
keywhat it does
/opens the search. ? does too, and the keys are listed at its foot
j kdown and up one part of an article. in a list of articles, down and up one article
n othe newer article and the older one
uup, from an article to its section and from a section to the front

single keys can get in the way of software that types for its reader, so the foot of the search has a switch that turns them off, remembered on that device. the two together weigh 5.0 kb, and a reader who never presses a key or opens the search never fetches a byte of them.

words

everything visible is lowercase. a capital survives only where it carries meaning, and the markup has to say so:

the bacterium Escherichia coli carries the gene lacZ in its DNA and grows best near pH 7. to check an article, type node scripts/check.mjs. to close a picture, press esc. and a phrase can be picked out like this one.

it reads like an article, never like a dashboard

— the first rule in the readme

<p>the bacterium <i lang="la">Escherichia coli</i> carries the gene <var>lacZ</var>
in its <abbr title="deoxyribonucleic acid">DNA</abbr> and grows best near
<span data-case>pH</span> 7. … press <kbd>esc</kbd>. and <mark>a phrase can be
picked out</mark> like this one.</p>

<blockquote>
  <p>it reads like an article, never like a dashboard</p>
  <p class="meta">— the first rule in <cite>the readme</cite></p>
</blockquote>

seven things keep their capitals:

  1. <code>, <pre> and <kbd>: code, and keys.
  2. <cite> and <blockquote>: a citation, and someone else's words.
  3. <abbr>: an abbreviation, set a little smaller so its capitals do not shout.
  4. lang on anything: a scientific name, or a phrase in another language.
  5. <var> and <math>: a symbol, and a formula.
  6. data-case on anything: a unit, a product, a name that insists.
  7. class="qty": a quantity, whose unit may need them.

a quantity is a number and a unit that belong together. marked up as one, it is never broken across two lines, and the space typed between its parts is set as a thin one:

light crosses 299 792 458 m/s, a mole holds 6.022 × 1023 things, the earth leans 23° 26′ from upright, and nothing gets colder than −273.15 °C, which is 0 K.

a quantity can also be written the way a program prints it, and is then set the way a paper does. an electron carries 1.602176634e-19 C, betelgeuse stands at 5h 55m 10.3s, and the universe is growing by 67.4 +- 0.5 km/s for every megaparsec (planck 2020). in the page's own source those are 1.602176634e-19 C, 5h 55m 10.3s and 67.4 +- 0.5 km/s, and that is how the feed still gives them.

chemistry is typed the same plain way, in a <span class="chem">, where it also keeps its capitals. a number after a letter goes below the line and one in front stays, a caret raises a charge or a mass number, and -> is an arrow. a leaf makes sugar by 6CO2 + 6H2O -> C6H12O6 + 6O2, a sulfate ion is SO4^2-, and the carbon that dates a bone is ^14C.

the checker holds a quantity to the signs made for it: a minus and not a hyphen, a multiplication sign and not the letter beside it, primes and not straight quotes, a raised power and not a caret.

a key is a <kbd>, and so is the button of a controller, with class="pad": round when it holds one sign, a pill when it holds more, and ringed in one of the four inks when the button has a colour of its own. the best known cheat in games is ↑ ↑ ↓ ↓ ← → ← → B A, and then start. buttons held together go inside one more <kbd>, which keeps them on a line: A + B + select + start sends most game boy games back to their title screen.

and what a reader may not want to know yet can wait under a cover, until it is clicked or opened from the keyboard with enter. the first seven castles of Super Mario Bros. all end on the same news: the princess is in another castle.

<span class="qty">6.022 × 10<sup>23</sup></span>
<span class="qty">−273.15 °C</span>
<span class="qty">67.4 +- 0.5 km/s</span>
<span class="chem">6CO2 + 6H2O -&gt; C6H12O6 + 6O2</span>

<kbd class="pad">↑</kbd> <kbd class="pad" data-ink="2">B</kbd> <kbd class="pad">start</kbd>
<kbd><kbd class="pad">A</kbd> + <kbd class="pad">B</kbd></kbd>

<span class="spoiler">the princess is in another castle</span>

the cover is put on by the same script that takes it off. a reader whose browser runs no scripts, or reads the article in a feed, gets the sentence plain.

this is a line of small print, a paragraph with class="meta". the line under it is an <hr>.


words in another language ask for more. a sentence taken apart word by word is a <dl class="gloss">: each word in a <div> with what it means under it, and the pairs wrap the way words do. the labels of grammar go in <abbr>, and are set in small capitals.

vos
2SG
sabés
know.2SG
que
that
no
not
vino
come.PST.3SG
“you know he did not come”, as it is said in buenos aires: vos sabés, where madrid says tú sabes.

a reading set over a word is a <ruby>, which browsers have long known how to set: 日本語にほんご is the japanese word for the japanese language, with the way it is said written over it in the script that spells sounds.

<dl class="gloss">
  <div><dt lang="es">sabés</dt><dd>know.<abbr>2SG</abbr></dd></div>
  <div><dt lang="es">que</dt><dd>that</dd></div>
</dl>

<ruby lang="ja">日本語<rt>にほんご</rt></ruby>

an original and its translation are a <figure class="facing">. the paragraphs are written in turn, one of each, and set in two columns, so every pair starts level however long either half runs. on a narrow page each translation goes under its original.

Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.

all gaul is divided into three parts. the belgae live in one, the aquitani in another, and in the third those who are called celts in their own tongue, and gauls in ours.

Hi omnes lingua, institutis, legibus inter se differunt.

all of them differ from one another in language, in customs and in laws.

Gallos ab Aquitanis Garumna flumen, a Belgis Matrona et Sequana dividit.

the river garonne parts the gauls from the aquitani, and the marne and the seine part them from the belgae.

how caesar's account of his war in gaul begins (caesar), with a translation made for this page.

and a source set down as it was written is a <blockquote class="source"> around a list of its lines. the lines are numbered the way an edition numbers them, the first and every fifth. a quotation keeps its capitals, so these do. what an editor has supplied goes in an <ins> and is set in brackets, and an empty one is a gap nobody could fill.

  1. SENATVS · POPVLVS · QVE · ROMANVS
  2. IMP · CAESARI · DIVI · NERVAE · F · NERVAE
  3. TRAIANO · AVG · GERM · DACICO · PONTIF
  4. MAXIMO · TRIB · POT · XVII · IMP · VI · COS · VI · P · P
  5. AD · DECLARANDVM · QVANTAE · ALTITVDINIS
  6. MONS · ET · LOCVS · TANTIS · OPERIBVS · SIT · EGESTVS
the six lines cut into the base of trajan's column in rome (as transcribed). a small piece at the bottom of the stone has been lost, and the letters in brackets are what was put back. these are the capitals that roman type has been drawn from ever since.
<figure class="facing">
  <p lang="la">Gallia est omnis divisa in partes tres, …</p>
  <p>all gaul is divided into three parts. …</p>
</figure>

<blockquote class="source" lang="la">
  <ol>
    <li>AD · DECLARANDVM · QVANTAE · ALTITVDINIS</li>
    <li>MONS · ET · LOCVS · TANT<ins>IS · OPER</ins>IBVS · SIT · EGESTVS</li>
  </ol>
</blockquote>

some things have to be heard. a link to a sound file with class="say" gets a mark to press: the sound plays in the page, and stops when it is pressed again. nothing is fetched before that, and nothing ever plays by itself. this is one note, twice: the a that orchestras tune to, 440 vibrations a second, first as a square wave and then as a sine. the pitch is the same and the sound is not.

<a class="say" href="a440.wav">one note, twice</a>

and each word can carry a number. in a <p class="tokens">, every word written as a <data value="…"> is tinted by how large its number is, the way a cell is in a table with class="heat". the scale is written under the text with what the numbers count, and pointing at a word gives its own. writing about language models, it is the usual way to show how much weight each word was given. here it is one of this blog's rules, each word tinted by how many times this article had used it when the sentence was added, on , among 15,046 words:

it reads like an article, never like a dashboard

the numbers run from 2 to 698, so the scale is in powers of ten, which is what data-log asks for. data-min and data-max pin its ends, for two paragraphs that should be read against each other, and numbers on both sides of zero take an ink for each side.

<p class="tokens" data-log data-unit="times in this article">
  <data value="303">it</data> <data value="16">reads</data> <data value="17">like</data> …
</p>

lists

a list is bullets, numbers, or terms with their definitions, and this page uses all three. seven more kinds are drawn with more than a marker.

a list inside a list, with class="tree", is drawn with its branches. this is the folder the blog is served from:

a tree can also show a change. a name inside <ins> is new, a name inside <del> is gone, and a <data> is a size, set at the far end of its line so that the sizes make a column. this is what the pull request that added this paragraph did, in bytes as they are stored. nothing has ever been taken out of the blog's folder, so there is no file struck out to show: the only things struck out are the sizes that changed.

<ul class="tree">
  <li><code>assets/</code>
    <ul>
      <li><ins><code>tabs.js</code></ins> <data value="1931">1,931</data></li>
      <li><del><code>old.js</code></del></li>
      <li><code>tree.css</code> <data value="2390"><del>1,985</del> <ins>2,390</ins></data></li>
    </ul>
  </li>
</ul>

with data-across the same list runs across the page, each fork opening to the right of its name. a fork may go without a name, which is all a family tree asks for. these are the great apes, in the order their lines parted (prado-martinez and others 2013):

the order of the forks is the whole of it. the lengths of the branches say nothing about time.
<ul class="tree" data-across>
  <li>great apes
    <ul>
      <li>orangutans <i lang="la">Pongo</i></li>
      <li>
        <ul>
          <li>gorillas <i lang="la">Gorilla</i></li>
          <li>…</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

when the lengths should say something, the list is a class="clade" and every item carries a data-length: how long the branch that leads to it is. each tip gets a line of its own, and each branch runs as far across the page as it is long. with data-ago the lengths are time, and an axis under the tree counts back from the tips. with data-unit they are anything else, and a bar of one round length says how much. these are the african apes, by when their genomes parted (scally and others 2012):

a tree whose lengths are time. the gorilla line parted from ours about ten million years ago, and the chimpanzee line about six. the two gorilla species are younger by far: on average their sequences parted 1.75 million years ago.
<ul class="clade" data-ago="million years">
  <li data-length="8.25">
    <ul>
      <li data-length="1.75">western gorillas</li>
      <li data-length="1.75">eastern gorillas</li>
    </ul>
  </li>
  <li data-length="4">…</li>
</ul>

things done in order are an <ol class="steps">: a number for each, on one line from the first to the last, a <small> for the detail, and a <time> for a step that has one, set beside it. this is how every change reaches the blog, with the times of the one that added this section:

  1. open an issue it says what is wanted and why, and carries a label for its kind and a milestone.
  2. make a branch from it named after the issue: 55-small-things-for-words.
  3. commit one line, lowercase, starting with the area it touches: site: …
  4. open a pull request its description starts with closes #55, which ties the two together.
  5. wait for two checks one reads the steps above, the other runs scripts/check.mjs.
  6. squash and merge the branch lands as one commit on main, which moves in no other way.
<ol class="steps">
  <li><time datetime="2026-10-12T11:14:41-03:00">11:14:41</time> open an issue
    <small>it says what is wanted and why.</small></li>
  <li>make a branch from it <small>named after the issue.</small></li>
</ol>

when it is the thing handed on that matters, and not the hour, the list is an <ol class="flow">: a box for each step and an arrow to the next, across the page for as long as there is room. a list inside a step is what happens there side by side. this is the same road, as a change travels it:

  1. an issue
  2. a branch
  3. a pull request
  4. one commit on main
<ol class="flow">
  <li>a branch</li>
  <li>a pull request
    <ul>
      <li>flow</li>
      <li>checks</li>
    </ul>
  </li>
</ol>

a conversation is an <ol class="chat">: a turn for each item, and each turn starts with who spoke, in a <b>. it is set like the script of a play, and nobody's words go in a bubble. each speaker gets a mark of their own, in the order they first speak. a turn holds whatever a list item can, so a call to a tool is a block of code. and a turn that runs long is folded until it is asked for, as the third one is here.

  1. user what were the last things added to the blog?
  2. assistant the history will say. one line for each pull request, newest first:
    $ git log --oneline --first-parent -20
  3. tool git
    ff17ef0 site: an article names the pieces it needs, and the frame stops growing with them (#88)
    f9f097b site: glosses, facing text, sources as written, kinds of source, and a sound (#86)
    04cc995 site: bits, traces and alignments (#84)
    a77e419 site: tabs, sessions at a terminal, yes and no, changes in a tree, and downloads with a sum (#82)
    db8741b site: pipelines, graphs and trees with lengths (#80)
    a5a01ca site: distributions, flows and treemaps (#78)
    eec2d02 site: areas, slopes, bars back to back, waterfalls and indexed lines (#76)
    f8ba532 site: tracks, deep time and events on a chart (#74)
    d1d1360 article: the tour's own numbers, measured again (#72)
    4f84a1f fix: a page with many blocks of code is laid out once, not once for every block (#71)
    cf420ff site: a title and a description in every article's own file, for link previews (#68)
    1dcab41 site: a search, and keys to read without the mouse (#66)
    dc83c27 site: notes that open where they are cited, and references that say what kind of evidence they are (#64)
    6c2d653 site: more from a picture: marks with a key, before and after, pixel art, and short clips (#62)
    ab985a9 site: more ways to read bytes: a diff between two dumps, a map to scale, and sequences (#60)
    02eac6d site: code read closely: blocks side by side, callouts, and disassembly listings (#58)
    81c346a site: small things for words: quantities, keys, trees, steps, spoilers, and changes that show (#56)
    4a1bd96 site: tables to read at a glance: heat, sorting, and changes with a sign (#54)
    9145e4f site: tables that draw themselves: ranges, spans, blocks, days, and shares counted out (#52)
    bc0a6f5 site: charts that say how sure they are, and against what (#50)
  4. assistant the newest eight are one piece of work: ways to write about more subjects, from tracks of time to a sentence taken apart word by word. the last of them took the list of pieces out of the frame, so that it stops growing.
an exchange written for this page. what the tool says is what git printed on .
<ol class="chat">
  <li><b>user</b> what were the last things added to the blog?</li>
  <li><b>tool <code>git</code></b> <pre><code>ff17ef0 site: an article names the pieces it needs …</code></pre></li>
</ol>

a few facts about one thing are a <dl class="card">: terms and what each comes to, ruled like a small table and no wider than a card. in an aside with a name it is the box of facts an encyclopedia sets beside its text. the numbers on this one were counted by running the gun, which is further down, and who found it is as gardner told it.

<dl class="card">
  <dt>period</dt><dd>30 generations</dd>
</dl>

the last kind is the list at the top of this page: files that travel with an article are a <ul class="files"> of links. a paragraph under a link says what is known about the file: which version it is, when it was made, and its sha-256, which is shown by its two ends and copied whole. the checker takes the sum of the file again on every run, so a file that changes without its line changing fails.

<ul class="files">
  <li><a href="weights.csv" download>the weights behind the charts <small>csv · 2.7 kb</small></a>
    <p>version 25 · <time datetime="2026-10-16">16 october 2026</time> · sha-256 <code>28326cb7…690499</code></p></li>
</ul>

tables

a table may run wider than the text, and scrolls sideways when it has to. the first column names each row and stays put while the rest moves. numbers are set right, in figures of equal width, so they line up.

seven of the sixty-nine pieces, heaviest first
#piecefetched when an article hasscriptstylesbudget
1charta table with the class of a chart3.9 kb1.6 kb6 kb
2dumpa hex dump, a sequence, a map or the layout of a header3.2 kb1.9 kb5.5 kb
3codea block of code3.2 kb1.4 kb5 kb
4notesa list of notes or of references2.1 kb1.4 kb3.5 kb
5imagean image1.4 kb1.1 kb3 kb
6asidean aside, or a section that folds—1.0 kb1.5 kb
7matha formula—0.7 kb1 kb
the seven13.8 kb9.1 kb25.5 kb
a position hangs in front of each name. the second column is words, so it wraps on a narrow screen. the lightest stylesheet is picked out, and a piece with no script says so with a dash. the last row is a <tfoot>, which is what the columns come to: the checker adds them up, and refuses a foot that says anything else.
<tr>
  <th scope="row"><span class="rank">7</span>math</th>
  <td class="words">a formula</td>
  <td class="none">—</td>
  <td class="best">0.7 kb</td>
  <td>1 kb</td>
</tr>

some tables draw themselves. the numbers stay in their cells, and a last column is added with a picture of each row.

class="range" is for a value and the range it is expected in: a name, a value, a low end and a high end. the range is a band across the middle of the row and the value is a point, so a point outside its band is a number outside its range, and that number is set heavier. every row has a scale of its own, which lets rows in different units share a table. a range with one end has a dash for the other.

this blog, against what is asked of a page
measurehereat leastat most
characters to a line644575
the same, on a phone394575
line height1.51.5—
contrast, text16.67—
contrast, quiet text5.14.5—
the frame, kb13.9—14
this article, kb56.0—60
values against their ranges. the length of a line is from bringhurst 2012, contrast and line height from the accessibility guidelines (w3c 2024), and the last two are this blog's own budgets. one number is out of range: a phone held upright has no room for forty-five characters at a size worth reading.

class="spans" is for things that last: a name, when it starts and when it ends, as numbers, as times on a clock like 1:23.4, or as dates. every row gets a bar on one scale, from the earliest start to the latest end, and the two ends of the scale are written over it. a row with no end is a moment, and gets a point.

a first visit to this page
what is fetchedasked forarrived
this article0 ms4.0 ms
the upright font9.0 ms11.7 ms
the stylesheet9.3 ms11.1 ms
the script9.3 ms12.0 ms
the frame21.6 ms22.8 ms
the italic font32.5 ms33.8 ms
its pieces106.4 ms149.0 ms
spans. one cold load, from a server on the same machine, so the times are small and the order is the point: this article, which is the page, then its type, the stylesheet and the script, then the frame, which the script fetches to set the article in. the italic font is asked for when the first slanted word is about to be drawn, and the hundred and eighteen files of the pieces it calls for come last.

rows that follow each other under one name are a track: the name is said once, and they share its line. a gene is the usual case, its exons on one line and the stretches that are read into protein on the next.

the gene for β-globin, letter by letter
whatfromto
exons1142
exons273495
exons1,3461,608
read into protein51142
read into protein273495
read into protein1,3461,474
the sickle-cell letter70
tracks. HBB as its reference sequence gives it (ncbi), counted from the first letter that is transcribed: three exons, the two stretches between them that are cut out, and inside the exons the 444 letters that are read. the point is the one letter whose change, a T where an A should be, gives sickle-cell disease.

a start or an end can also lie deep in the past, 541 mya or 12 ka, or before year one, 44 bce. when the times are too far apart for one ruler, data-log sets the scale in powers of ten. zero has no place on such a scale, so the present is put one power of ten past the nearest time the table names.

the earth's time, in powers of ten
stretchfromto
hadean4,567 mya4,031 mya
archean4,031 mya2,500 mya
proterozoic2,500 mya538.8 mya
paleozoic538.8 mya251.9 mya
mesozoic251.9 mya66 mya
cenozoic66 mya0 mya
pleistocene2.58 mya11.7 ka
holocene11.7 ka0 ka
deep time, with the ages of the international chart (cohen and others 2013). on a ruler the holocene would be three millionths of the width, and invisible. here every tenfold step back gets the same room, so the nearer to now the more there is, and it is the hadean that shrinks to a sliver. neither picture is wrong, and the caption has to say which one it is.

three more classes are for reading a table at a glance. with class="sort" every column head is a button: one click puts the rows in order by that column, a second turns the order round, and a third puts them back the way they were written. numbers start from the largest, words from a.

every piece, and what calls for it
piecefetched when an article hasscriptstylesbudget
codea block of code3.2 kb1.4 kb5 kb
pairtwo blocks side by side—0.3 kb0.5 kb
calloutsnumbered marks in a block0.4 kb0.8 kb1.5 kb
tabsblocks behind tabs0.8 kb0.5 kb1.5 kb
listinga listing of machine code1.9 kb0.9 kb3 kb
charta table with the class of a chart3.9 kb1.6 kb6 kb
barsa chart drawn as bars1.9 kb1.1 kb3.5 kb
linesa chart drawn as lines2.4 kb—2.5 kb
dotsa chart drawn as dots2.4 kb—2.5 kb
plota chart drawn as lines or as dots—1.6 kb2 kb
areasa chart drawn as areas1.2 kb0.3 kb1.5 kb
slopea chart drawn as slopes1.5 kb0.4 kb2 kb
hista histogram, or boxes1.6 kb0.8 kb2.5 kb
flowsa table of flows1.6 kb0.6 kb2.5 kb
treemapa list drawn as tiles by size1.8 kb0.7 kb2.5 kb
grapha table of edges2.4 kb1.0 kb3.5 kb
flowa pipeline—0.7 kb1 kb
cladea tree with lengths1.4 kb0.9 kb2.5 kb
eventsevents listed under a chart0.8 kb0.6 kb1.5 kb
blocksa table of blocks1.4 kb0.5 kb2 kb
daysa table of days1.3 kb0.7 kb2 kb
sparka sparkline0.6 kb0.4 kb1 kb
rangea table of ranges0.9 kb0.7 kb2 kb
spansa table of spans1.4 kb0.6 kb2 kb
ofa share to count out0.7 kb0.6 kb1.5 kb
heata table tinted by its numbers0.7 kb0.5 kb1.5 kb
sorta table that sorts1.0 kb0.6 kb2 kb
deltaa change with a sign0.3 kb0.4 kb1 kb
qtya quantity0.5 kb0.3 kb1 kb
padthe buttons of a controller—0.5 kb1 kb
treea tree of files—0.9 kb1 kb
stepsa list of steps—0.5 kb1 kb
spoilertext under a cover0.4 kb0.4 kb1 kb
revisea correction, or a list of changes0.5 kb0.8 kb1.5 kb
asidean aside, or a section that folds—1.0 kb1.5 kb
matha formula—0.7 kb1 kb
imagean image1.4 kb1.1 kb3 kb
marksan image with marks on it1.1 kb1.1 kb2.5 kb
comparetwo images to compare0.9 kb0.9 kb2 kb
pixelpixel art, or a palette0.8 kb0.6 kb1.5 kb
clipa short clip1.1 kb0.6 kb2 kb
notesa list of notes or of references2.1 kb1.4 kb3.5 kb
dumpa hex dump, a sequence, a map or the layout of a header3.2 kb1.9 kb5.5 kb
seqa sequence0.8 kb—1 kb
alignsequences set under each other1.1 kb0.5 kb2 kb
mapa map of a file or of memory0.8 kb—1 kb
filesa list of downloads0.5 kb1.0 kb1.5 kb
matrixa table of yes and no0.4 kb0.5 kb1 kb
tracea table of states, step by step0.4 kb0.3 kb1 kb
glossa gloss word under word, or ruby—0.5 kb0.5 kb
facinga text facing its translation—0.4 kb0.5 kb
sourcea source as it was written—0.4 kb0.5 kb
saya sound to hear0.5 kb0.3 kb1 kb
chata conversation0.8 kb1.0 kb2 kb
tokenswords tinted by a number1.0 kb0.7 kb2 kb
boarda grid written in characters0.7 kb0.7 kb1.5 kb
framesframes in one place, or pictures that blink1.4 kb0.8 kb2.5 kb
carda card of facts—0.5 kb0.5 kb
scalea picture with a scale bar0.8 kb0.4 kb1.5 kb
setpanels with letters—0.8 kb1 kb
skya chart of the sky1.7 kb0.8 kb2.5 kb
tilesa map of tiles1.1 kb0.8 kb2 kb
chema chemical formula0.5 kb0.2 kb1 kb
knoba number a reader can change2.2 kb0.5 kb3 kb
curvea curve from a formula2.1 kb—2.5 kb
suma table with a foot—0.2 kb0.5 kb
glossarya glossary0.7 kb0.6 kb1.5 kb
figa numbered figure—0.2 kb0.5 kb
resumean article of 20 minutes or more1.2 kb0.4 kb2 kb
a table that sorts. try the script column: the chart core is the heaviest thing here, and the pieces with a dash in it are a stylesheet and nothing else.

class="heat" tints every cell by how large its number is, in one hue from the paper up. the numbers stay, so the tint is a second way to read them. one scale covers the table, unless data-by="column" or data-by="row" gives each its own. numbers on both sides of zero take two inks, one for each side.

what gzip leaves of a file, at four of its levels
filelevel 1level 3level 6level 9
feed.xml23.8%22.2%20.1%19.6%
this article30.1%28.4%25.9%25.7%
site.css33.4%31.5%29.0%28.9%
index.html34.2%33.4%32.3%32.3%
site.js39.0%37.5%35.2%35.2%
chart.js43.1%42.0%40.3%40.3%
math.css51.5%50.8%49.9%49.9%
stylesheet.png97.8%97.8%97.7%97.7%
newsreader-core.woff2100.1%100.1%100.1%100.1%
a table with its heat. the longer a file of text is, the more of it gzip can fold away. what is already packed, an image or a font, does not pack again, which is why the blog weighs its fonts as they are.

and class="delta" on a cell marks a number as a change: an arrow goes in front of it, up or down by its sign, in an ink for each.

what six pull requests did to the frame
pull requestwhat it broughtthe frame afterchange
#33a feed11,802+78
#35cells of words that wrap11,933+131
#46a shorter list of pieces, and downloads in a piece11,647−286
#48a piece for each form of chart11,665+18
#50charts that say how sure they are11,6650
#52tables that draw themselves11,696+31
changes with a sign, in bytes gzipped. a feature that lives in a piece costs the frame one line in a list, or nothing.

and a table of yes and no is a class="matrix". every cell starts with yes, no or partly, and shows a disc for it: full, empty or half. the word stays inside the mark, for a screen reader and for whoever copies the table, and whatever a cell says after its word stays beside it. this is what a block of code gets a colour for, by language:

languagecommentsstringsnumbersnames
jsyesyesyesno
cssyesyesyesno
jsonno it has nonepartly values, not keysyesno
htmlyesyesyesyes of tags
four of the twenty-two languages a block knows. the rule is comments, strings and numbers, and nothing else. markup is the exception, because there the names are most of what is written.
<table class="matrix">
  <tr><th scope="row">json</th><td>no <small>it has none</small></td><td>partly</td><td>yes</td></tr>
</table>

code

a block of code gets a button that copies it. a label says where it came from. the language, written the way markdown tools already write it, turns on colour: comments step back, strings and numbers step forward, and everything else stays ink. keywords are left alone on purpose, so a page of code stays calm.

export const pieces = [
  ['code', 'js css', 'pre'],
  ['pair', 'css'],
  ['callouts', 'js css'],
  ['tabs', 'js css'],
  ['listing', 'js css'],
  ['chart', 'js css', '.bars .lines .dots .blocks .days .areas .slope .hist .box .flows .sky .tiles .curve'],
  ['bars', 'js css'],
  ['lines', 'js'],
  ['dots', 'js'],
  ['plot', 'css', '.lines .dots .areas .slope .curve'],
  ['areas', 'js css'],
  ['slope', 'js css'],
  ['hist', 'js css', '.hist .box'],
  ['flows', 'js css'],
  ['treemap', 'js css'],
  ['graph', 'js css'],
  ['flow', 'css'],
  ['clade', 'js css'],
  ['events', 'js css'],
  ['blocks', 'js css'],
  ['days', 'js css'],
  ['spark', 'js css'],
  ['range', 'js css'],
  ['spans', 'js css'],
  ['of', 'js css'],
  ['heat', 'js css'],
  ['sort', 'js css'],
  ['delta', 'js css'],
  ['qty', 'js css'],
  ['pad', 'css'],
  ['tree', 'css'],
  ['steps', 'css'],
  ['spoiler', 'js css'],
  ['revise', 'js css', 'ins del .changes'],
  ['aside', 'css', 'aside details'],
  ['math', 'css', 'math'],
  ['image', 'js css', 'img'],
  ['marks', 'js css'],
  ['compare', 'js css'],
  ['pixel', 'js css', '.pixel .palette'],
  ['clip', 'js css'],
  ['notes', 'js css', '.notes .refs'],
  ['dump', 'js css', '.hex .seq .fields .map .align'],
  ['seq', 'js'],
  ['align', 'js css'],
  ['map', 'js'],
  ['files', 'js css'],
  ['matrix', 'js css'],
  ['trace', 'js css'],
  ['gloss', 'css', '.gloss ruby'],
  ['facing', 'css'],
  ['source', 'css'],
  ['say', 'js css'],
  ['chat', 'js css'],
  ['tokens', 'js css'],
  ['board', 'js css'],
  ['frames', 'js css', '.frames .blink'],
  ['card', 'css'],
  ['scale', 'js css', '[data-across]'],
  ['set', 'css'],
  ['sky', 'js css'],
  ['tiles', 'js css'],
  ['chem', 'js css'],
  ['knob', 'js css'],
  ['curve', 'js'],
  ['sum', 'css', 'tfoot'],
  ['glossary', 'js css'],
  ['fig', 'css'],
];

const calls = (html) => ([name, , signs = `.${name}`]) =>
  signs.split(' ').some((sign) =>
    RegExp(sign[0] === '.' ? `<[^>]+class="[^"]*\\b${sign.slice(1)}\\b` : sign[0] === '[' ? `<[^>]+\\s${sign.slice(1, -1)}[=\\s>]` : `<${sign}[\\s>]`).test(html),
  );

that is the whole list of pieces, and the four lines that read it: a name, what to fetch, and the tags, classes or attributes that call for it, the piece's own name when none are given. the frame never sees the list. a script reads it, and writes at the top of each article the pieces that article needs, so the frame weighs the same however long the list grows. the lines are numbered from 7, where the list starts in the file, and line 13 is marked. a long line does not wrap, and the last but one is long: the block grows past the text until it meets the edge of the page, and scrolls after that.

a patch is a block in the language diff. the lines it adds and removes are tinted:

@@ -652,7 +652,13 @@
-.prose :is(th, td).l {
+.prose :is(th, td):is(.l, .words) {
   text-align: left;
 }
+
+/* a cell of words rather than a number: it wraps where it runs long */
+.prose .words {
+  min-width: 10em;
+  white-space: normal;
+}

and a phrase inside a block can be pointed at with <mark>:

python3 -m http.server 4173  # then open http://localhost:4173
<pre data-label="scripts/pieces.mjs" data-lines="7" data-mark="13">
<code class="language-js">export const pieces = [
  ['code', 'js css', 'pre'],
  …
];</code></pre>

twenty-two languages are known: js, ts, c, cpp, rust, zig, go, java, css, json, python, ruby, sh, make, yaml, toml, asm, sql, lua, html, xml and svg. anything else is shown plain.

the same thing said several ways is a <figure class="tabs"> around the blocks. one shows at a time, under a row of the names their data-labels give them. a name picked in one set is picked in every set on the page that has it, so a language is chosen once. without scripts, and on paper, every block shows. this is a function that counts the steps a number takes to reach one, halving when it is even, and tripling and adding one when it is odd:

int steps(unsigned long n) {
    int count = 0;
    while (n != 1) {
        n = n % 2 ? 3 * n + 1 : n / 2;
        count++;
    }
    return count;
}
def steps(n):
    count = 0
    while n != 1:
        n = 3 * n + 1 if n % 2 else n // 2
        count += 1
    return count
function steps(n) {
  let count = 0;
  while (n !== 1) {
    n = n % 2 ? 3 * n + 1 : n / 2;
    count++;
  }
  return count;
}

and this is each of them asked about twenty-seven. all three say 111.

printf("%d\n", steps(27));
print(steps(27))
console.log(steps(27));
<figure class="tabs">
  <pre data-label="c"><code class="language-c">…</code></pre>
  <pre data-label="python"><code class="language-python">…</code></pre>
</figure>

a session at a terminal is a block of language-console. a line that starts with $, # or % and a space was typed, and so is the line after one that ends in a backslash, when it starts with >. everything else is what came back, and it steps behind. the prompt is not picked up with a selection, and the copy button takes only what was typed. these are the thirty-nine commits on main up to pull request #78, counted by the area each one names. the odd one out is the commit the repository was made with:

$ git rev-list --count --first-parent a5a01ca
39
$ git log --first-parent --format=%s a5a01ca \
>   | cut -d: -f1 | sort | uniq -c | sort -rn
     25 site
      4 fix
      4 article
      3 design
      2 repo
      1 Initial commit

two blocks in a <figure class="pair"> sit side by side, to be read line against line, and stack on a narrow page. pointing at a line picks out the one level with it on the other side, so a blank line is part of the text: it is what keeps the two level. on the left is a function that counts the steps of a collatz sequence, written the way the compiler read it. on the right is what gcc 15.2 made of it at -Os, with the labels named by hand.

int steps(unsigned long n) {
    int count = 0;
    while (n != 1) {

        if (n % 2) {

            n = 3 * n + 1;
            count++;

        }
        n = n / 2;
        count++;
    }
    return count;
}
steps:
        xor   eax, eax
.loop:  cmp   rdi, 1
        je    .done
        test  edi, 1
        je    .half
        lea   rdi, [rdi+rdi*2]
        inc   eax
        inc   rdi

.half:  shr   rdi, 1
        inc   eax
        jmp   .loop
.done:  ret
each line of c sits beside the first instruction that carries it out. twelve instructions, 33 bytes.
  1. a register xored with itself is zero. this is count = 0, in two bytes where a plain move takes five.
  2. three times n without a multiplication: the instruction that works out addresses adds n to twice n.
  3. the + 1, and after it no jump back to the test at the top. three times an odd number, plus one, is even, so the compiler knows which way the next turn goes and falls straight into the halving.

the numbers in the right block are callouts: a <mark data-note="1"> around what is meant, and an <ol class="callouts"> after the block with a line for each number. pointing at a mark lights its line, and pointing at a line lights its mark. the copy button leaves the numbers out.

<figure class="pair">
  <pre data-label="steps.c"><code class="language-c">…</code></pre>
  <pre data-label="gcc -Os"><code class="language-asm">steps:
        <mark data-note="1">xor   eax, eax</mark>
        …</code></pre>
  <figcaption>each line of c sits beside the first instruction that carries it out.</figcaption>
</figure>

<ol class="callouts">
  <li>a register xored with itself is zero.</li>
</ol>

a disassembly is a <pre class="listing">, pasted as objdump prints it. it is set in columns: where each instruction is, the bytes it is made of, and what it says. every jump that lands inside the listing gets an arrow in the gutter, short ones on the inside and long ones further out, and pointing at a jump, or at the place one lands, picks its arrow out. on a phone the bytes step aside. this is the same function, read back out of the object file:

0000000000000000 <steps>:
   0:	31 c0                	xor    eax,eax
   2:	48 83 ff 01          	cmp    rdi,0x1
   6:	74 18                	je     20 <steps+0x20>
   8:	f7 c7 01 00 00 00    	test   edi,0x1
   e:	74 09                	je     19 <steps+0x19>
  10:	48 8d 3c 7f          	lea    rdi,[rdi+rdi*2]
  14:	ff c0                	inc    eax
  16:	48 ff c7             	inc    rdi
  19:	48 d1 ef             	shr    rdi,1
  1c:	ff c0                	inc    eax
  1e:	eb e2                	jmp    2 <steps+0x2>
  20:	c3                   	ret
<pre class="listing" data-label="objdump -d -M intel steps.o">
0000000000000000 &lt;steps&gt;:
   0:	31 c0                	xor    eax,eax
   2:	48 83 ff 01          	cmp    rdi,0x1
   6:	74 18                	je     20 &lt;steps+0x20&gt;
   …
</pre>

what a listing does not show at a glance is the shape of the thing: where it forks, and where it comes back round. a <table class="graph"> is a list of edges, one to a row: where it starts, where it ends and, if it has one, a word for it. the things are set in layers down the page, each under what leads to it, and an edge that goes back up is drawn round the side. a class on a row, a to d, gives its edge an ink. this is every way through the function above, by the addresses in the listing:

fromtowhen
0 count = 02 n is 1?
2 n is 1?20 return countyes
2 n is 1?8 n is odd?no
8 n is odd?10 n = 3n + 1yes
8 n is odd?19 n = n / 2no
10 n = 3n + 119 n = n / 2
19 n = n / 22 n is 1?again
six blocks and seven edges. the one in red is the loop: it leaves the bottom of the function and comes back in at the test. the compiler's shortcut is here too, as an edge that is missing: nothing leads from 10 back to the test.
<table class="graph">
  <thead><tr><th>from</th><th>to</th><th>when</th></tr></thead>
  <tbody>
    <tr><td>n is 1?</td><td>return count</td><td>yes</td></tr>
    <tr><td>n is 1?</td><td>n is odd?</td><td>no</td></tr>
    <tr class="b"><td>n = n / 2</td><td>n is 1?</td><td>again</td></tr>
  </tbody>
</table>

and what it does, step by step, is a <table class="trace">: a row for each step, and the state after it. a cell that is not what it was in the row above is picked out, and the rest step back, so the eye goes to what a step did. this is the function asked about the number two, as a debugger stepped through it:

afterrdieaxzero flag
0 xor eax,eax201
2 cmp rdi,1200
6 je 20200
8 test edi,1201
e je 19201
19 shr rdi,1100
1c inc eax110
1e jmp 2110
2 cmp rdi,1111
6 je 20111
20 ret111
eleven instructions to halve a two. n lives in rdi and the count in eax. the zero flag is how a test is remembered until the jump after it: a je jumps only when the flag holds a one.
<table class="trace">
  <thead><tr><th>after</th><th>rdi</th><th>eax</th><th>zero flag</th></tr></thead>
  <tbody>
    <tr><th scope="row">xor eax,eax</th><td>2</td><td>0</td><td>1</td></tr>
    <tr><th scope="row">cmp rdi,1</th><td>2</td><td>0</td><td>0</td></tr>
  </tbody>
</table>

charts

a chart is a table with a class. the frame reads the numbers and draws them, and the table stays in the page: a screen reader gets it instead of the drawing, and so does a feed reader. the first column names each row, every other column is a series, and the caption becomes the title.

<figure>
  <table class="bars" data-max="100">
    <caption>how full a budget is</caption>
    <thead><tr><th>part</th><th>used</th></tr></thead>
    <tbody>
      <tr class="a"><th scope="row">the frame</th><td>99%</td></tr>
      <tr><th scope="row">chart</th><td>92%</td></tr>
      …
    </tbody>
  </table>
  <figcaption>one row is picked out, and the scale is pinned at 100.</figcaption>
</figure>
how full a budget is
partused
the frame99%
notes98%
code93%
chart92%
dump92%
image83%
aside68%
math66%
bars, one series. one row is picked out with a class on its <tr> and the rest step back to grey. the scale is pinned at 100 with data-max, so a full bar means a full budget. these are the frame and the seven pieces of the first table under tables.

with several columns of numbers, each row becomes a group of bars, and the head of the table becomes the legend. there are four inks, and no chart draws more than four series.2

the five largest files, as written and as sent
fileas writtengzipped
site.css20.5 kb5.9 kb
site.js19.0 kb6.7 kb
chart.js9.8 kb3.9 kb
code.js8.1 kb3.2 kb
dump.js7.6 kb3.2 kb
bars, two series. gzip leaves between three and four tenths of each.

numbers below zero put bars on both sides of a line:

the ten pull requests that moved the frame most, in bytes gzipped
pull requestbytes
#10 charts and code+818
#25 images+158
#30 tags, series, reading time+486
#46 room for more pieces−286
#66 the search and the keys+228
#88 the list of pieces moves out−465
#106 the wheel moves the page+168
#129 real addresses+776
#177 high contrast+184
#178 the type in two files+358
bars around zero: the ten changes that moved the frame's weight most, in the order they were made. two made it lighter, and each time something left the frame: at #46 the styles for a list of downloads, and at #88 the list of pieces itself.

when the columns are parts of a whole, class="bars stacked" sets them end to end and writes the total at the tip:

what the frame and the pieces are made of, in kb gzipped
parthtmlstylesscript
the frame1.235.946.71
chart—1.593.93
dump—1.863.22
code—1.423.24
notes—1.382.07
image—1.121.36
aside—1.03—
math—0.66—
stacked bars. two pieces are a stylesheet and nothing else.

two series can also stand back to back. class="bars mirrored" runs the first left from a middle line and the second right, which is how two halves of one thing are compared: two sexes by age, two sides of a trade, two models on the same tests.

one test, three runs of it
runhaiku 5.5deepseek v4.1 flash
the makers' own tables39.2%31.2%
artificial analysis32.8%26.8%
vals ai35.4%19.7%
bars back to back, from the article on haiku 5.5: the share of terminal-bench 4.0 each model finished, as three parties measured it.

class="bars waterfall" is for changes that add up. each bar starts where the one above it ended, a rise in one ink and a fall in another, and a row in a <tfoot> is what they come to, drawn from zero.

what the frame gained between its first pull request and #68
stretchchange
charts and code, inside the frame+0.79 kb
seven small things+0.24 kb
tags, series and the feed+0.76 kb
the pieces move out−0.28 kb
seven more small things+0.17 kb
the search, the keys, the previews+0.26 kb
gained in all+1.94 kb
a waterfall. the same history as the line chart further down, added up by stretch instead of step by step: six changes, one of them a fall, and the 1.94 kb they come to.

a number can say how sure it is. a cell written as 52.8 ± 1.6 ms, as 1.16 (1.15–1.18) or as two ends, 36–43, gives a chart an interval as well as a value, and each form draws it in its own way. on a bar it is a whisker:

four commands, from start to finish
commandtime
node, doing nothing14.6 ± 1.9 ms
writing the link previews62.2 ± 1.7 ms
writing the feed34.3 ± 2.4 ms
checking the blog188.9 ± 2.3 ms
bars with whiskers: the mean of twenty runs on one laptop, give or take a standard deviation. checking every rule on every page costs seventeen hundredths of a second more than starting node does.

a line chart runs across the rows in order. it does not write every value, so the pointer reads them off it, and a link under it swaps the drawing for the table. data-line on the table is a value to read the rest against: a dotted rule, named in the legend in the words it was written with.

the weight of the frame, pull request by pull request
afterthe frame
#69.86 kb
#79.85 kb
#1010.65 kb
#2210.51 kb
#2310.54 kb
#2410.55 kb
#2510.70 kb
#2610.77 kb
#2710.84 kb
#2810.88 kb
#2910.89 kb
#3011.36 kb
#3211.45 kb
#3311.53 kb
#3511.65 kb
#4611.37 kb
#4811.39 kb
#5211.42 kb
#5411.44 kb
#5611.48 kb
#5811.50 kb
#6011.51 kb
#6211.54 kb
#6611.76 kb
#6811.80 kb
#7411.80 kb
#7611.82 kb
#7811.85 kb
#8011.87 kb
#8211.88 kb
#8411.89 kb
#8611.92 kb
#8811.46 kb
#10011.55 kb
#10611.71 kb
#10911.83 kb
#11111.97 kb
#11911.97 kb
#12611.98 kb
#12912.73 kb
#13212.86 kb
#17512.86 kb
#17612.89 kb
#17713.07 kb
#17813.42 kb
#18713.50 kb
#18813.62 kb
#18913.74 kb
#19013.81 kb
#19113.87 kb
#10
charts and blocks of code arrive, inside the frame
#30–#35
tags, series, reading time and the feed
#46
the frame makes room: what an article may ask for moves out of it
#66
the search and the keys
#88
the list of pieces leaves the frame
#129
every address a page of its own
a line and a rule, from data-line="14 kb, the budget". each step is a pull request that changed the frame, named by its number, from the first to the latest. after #191 it stands 131 bytes from its budget.

the numbers over that chart are events: a <dl class="events"> straight after the table, each <dt> saying when in the words of a row, #46, or of two with a dash between them, #30–#35. a moment gets a rule and a stretch gets a band, and the list becomes the key. without the drawing it is still a list of what happened and when.

two measures on different scales never share a chart as they are. set to a common start they can: data-index="low" names a row, and every series becomes a share of what it was there, which is 100.

what more effort buys, and what it costs, with low effort as 100
effortindex scorecost per task
low29$0.02
medium34$0.05
high38$0.08
xhigh41$0.12
max43$0.21
indexed lines, in powers of ten. the table holds a score out of a hundred and a price in dollars, from the article on haiku 5.5, and the chart shows what happened to each: at the most effort the score is half as high again, and the price is ten and a half times what it was.

class="areas" piles the series of a line chart on each other, for the parts of a whole as they change. the top edge is the total, and only the bottom series can be read against a flat line, so the one that matters most goes first.

all the code the blog can send, pull request by pull request
afterthe framethe pieceswhat a reader asks for
#69.9 kb0.0 kb0.0 kb
#1010.6 kb7.5 kb0.0 kb
#2210.5 kb9.2 kb0.0 kb
#2310.5 kb10.1 kb0.0 kb
#2410.5 kb10.7 kb0.0 kb
#2510.7 kb12.7 kb0.0 kb
#2610.8 kb14.8 kb0.0 kb
#2810.9 kb18.2 kb0.0 kb
#2910.9 kb20.2 kb0.0 kb
#3011.4 kb20.2 kb0.0 kb
#3511.7 kb20.2 kb0.0 kb
#4611.4 kb20.9 kb0.0 kb
#4811.4 kb23.6 kb0.0 kb
#5011.4 kb27.6 kb0.0 kb
#5211.4 kb35.3 kb0.0 kb
#5411.4 kb38.5 kb0.0 kb
#5611.5 kb42.2 kb0.0 kb
#5811.5 kb46.8 kb0.0 kb
#6011.5 kb49.6 kb0.0 kb
#6211.5 kb56.1 kb0.0 kb
#6411.5 kb56.8 kb0.0 kb
#6611.8 kb56.8 kb5.3 kb
#7411.8 kb58.8 kb5.3 kb
#7611.8 kb62.7 kb5.3 kb
#7811.9 kb69.7 kb5.3 kb
#8011.9 kb75.4 kb5.3 kb
#8211.9 kb78.8 kb5.3 kb
#8411.9 kb81.0 kb5.3 kb
#8611.9 kb83.1 kb5.3 kb
#8811.5 kb83.1 kb5.3 kb
#9011.5 kb86.5 kb5.3 kb
#9211.5 kb90.5 kb5.3 kb
#9411.5 kb93.2 kb6.8 kb
#9611.5 kb98.7 kb6.8 kb
#9811.5 kb103.7 kb6.8 kb
#10011.5 kb105.2 kb6.8 kb
#10611.7 kb105.3 kb6.8 kb
#10911.8 kb105.3 kb6.8 kb
#11112.0 kb105.3 kb6.8 kb
#12912.7 kb105.2 kb6.9 kb
#13212.9 kb105.2 kb6.9 kb
#13912.9 kb105.7 kb6.9 kb
#17512.9 kb105.9 kb6.9 kb
#17713.1 kb110.2 kb6.9 kb
#17813.4 kb110.1 kb6.9 kb
#18013.4 kb110.4 kb6.9 kb
#18213.4 kb110.7 kb6.9 kb
#18413.4 kb111.0 kb6.9 kb
#18613.4 kb111.0 kb6.6 kb
#18813.6 kb111.0 kb6.6 kb
#18913.7 kb111.0 kb6.6 kb
areas, in kb gzipped. the frame is the band at the bottom, and it barely thickens. everything the blog has learned since #10 went into the pieces, which a page pays for only when it uses them.

class="slope" reads a table the other way: a line for each row, running across the columns. with two columns it says what rose and what fell between two moments. with more, and data-flip when the numbers are places in a ranking, it says who overtook whom.

one test, as it got harder
modelversion 2.1version 3.0version 4.0
deepseek v4.1 flash90.6%30.0%31.2%
opus 5.089.1%43.3%51.8%
haiku 5.5——39.2%
slopes, from the same article. deepseek led on the old exam by a point and a half, and trails by twenty on the current one. haiku 5.5 was only measured on the last, so it is a point and not a line.

charts set side by side in a <figure class="multiples"> share one scale, so they can be read against each other:

index.html
afterkb gzipped
#60.89 kb
#70.89 kb
#100.89 kb
#220.89 kb
#230.89 kb
#240.89 kb
#250.89 kb
#260.89 kb
#270.89 kb
#280.89 kb
#290.89 kb
#300.89 kb
#320.89 kb
#330.92 kb
#350.92 kb
#460.92 kb
#480.92 kb
#520.92 kb
#540.92 kb
#560.92 kb
#580.92 kb
#600.92 kb
#620.92 kb
#660.95 kb
#680.95 kb
#740.95 kb
#760.95 kb
#780.95 kb
#800.95 kb
#820.95 kb
#840.95 kb
#860.95 kb
#880.95 kb
#1000.95 kb
#1060.95 kb
#1091.00 kb
#1111.08 kb
#1191.08 kb
#1261.08 kb
#1291.09 kb
#1321.22 kb
#1751.22 kb
#1761.22 kb
#1771.22 kb
#1781.23 kb
#1871.23 kb
#1881.23 kb
#1891.23 kb
#1901.23 kb
#1911.23 kb
site.css
afterkb gzipped
#64.99 kb
#74.97 kb
#105.51 kb
#225.13 kb
#235.14 kb
#245.14 kb
#255.14 kb
#265.12 kb
#275.19 kb
#285.19 kb
#295.19 kb
#305.26 kb
#325.26 kb
#335.27 kb
#355.40 kb
#465.08 kb
#485.08 kb
#525.08 kb
#545.08 kb
#565.08 kb
#585.08 kb
#605.08 kb
#625.08 kb
#665.11 kb
#685.11 kb
#745.11 kb
#765.11 kb
#785.11 kb
#805.11 kb
#825.11 kb
#845.11 kb
#865.11 kb
#885.11 kb
#1005.11 kb
#1065.09 kb
#1095.15 kb
#1115.22 kb
#1195.22 kb
#1265.22 kb
#1295.31 kb
#1325.31 kb
#1755.30 kb
#1765.33 kb
#1775.51 kb
#1785.86 kb
#1875.94 kb
#1885.94 kb
#1895.94 kb
#1905.94 kb
#1915.94 kb
site.js
afterkb gzipped
#63.99 kb
#73.99 kb
#104.25 kb
#224.49 kb
#234.51 kb
#244.52 kb
#254.67 kb
#264.77 kb
#274.77 kb
#284.80 kb
#294.81 kb
#305.21 kb
#325.30 kb
#335.34 kb
#355.34 kb
#465.38 kb
#485.40 kb
#525.43 kb
#545.44 kb
#565.49 kb
#585.51 kb
#605.52 kb
#625.55 kb
#665.71 kb
#685.75 kb
#745.75 kb
#765.77 kb
#785.80 kb
#805.81 kb
#825.82 kb
#845.84 kb
#865.87 kb
#885.41 kb
#1005.50 kb
#1065.67 kb
#1095.67 kb
#1115.67 kb
#1195.67 kb
#1265.67 kb
#1296.33 kb
#1326.33 kb
#1756.33 kb
#1766.33 kb
#1776.33 kb
#1786.33 kb
#1876.33 kb
#1886.45 kb
#1896.57 kb
#1906.64 kb
#1916.71 kb
small multiples: the same fifty steps, file by file. the page itself is a sliver. the stylesheet ends 1.0 kb heavier than it began, having twice handed styles to a piece, and the script 2.7 kb heavier, having handed its list of pieces to a file the frame never reads.

on a line, how sure a number is becomes a band around it:

what gzip leaves of a file, level by level
levelthe middle file
148.8% (44.3–53.9)
248.5% (44.1–53.5)
348.1% (43.8–53.3)
447.5% (42.9–52.8)
547.3% (42.6–52.6)
647.3% (42.5–52.6)
747.3% (42.5–52.6)
847.3% (42.5–52.6)
947.3% (42.5–52.6)
a line with a band. a hundred and twenty-five of the blog's files, the frame's and the pieces', each gzipped at all nine levels: the line is the middle file, and the band holds the middle half of them. what a level buys is small beside what sets one file apart from the next, and past level six it buys nothing.

some numbers do not slope. a count stays where it is until something changes it, and class="lines stepped" draws it that way:

pieces an article can call for
afterpieces
#60
#70
#102
#222
#233
#244
#255
#266
#276
#287
#297
#307
#327
#337
#357
#468
#4813
#5218
#5421
#5627
#5830
#6032
#6236
#6636
#6836
#7437
#7639
#7842
#8045
#8247
#8449
#8653
#8853
#9055
#9258
#9460
#9663
#9866
#10068
#10668
#10968
#11168
#11968
#12668
#12968
#13268
#17568
#17668
#17768
#17868
#18768
#18868
#18968
#19068
#19168
a stepped line. the count stood at seven for a while. the jump at #48 is one piece becoming six, a core and each form of chart fetched on its own by the articles that draw it, and every step since is new things an article can use, each a piece of its own.

two columns of numbers make a scatter with class="dots": across by the first, up by the second. the rows picked out are named, and the rest go grey.

length against weight, file by file
filelineskb gzipped
index.html671.23
align.css390.47
areas.css260.31
aside.css1071.03
bars.css1211.07
blocks.css420.53
board.css980.73
callouts.css880.81
card.css400.49
chart.css2471.59
chat.css1161.04
chem.css160.19
clade.css1080.88
clip.css740.60
code.css2061.42
compare.css1200.93
days.css870.67
delta.css350.38
dump.css2701.86
events.css630.58
facing.css350.36
fig.css190.22
files.css981.02
flow.css680.67
flows.css680.61
frames.css1000.82
gloss.css430.49
glossary.css490.59
graph.css1201.01
heat.css430.52
hist.css1180.85
image.css1441.12
knob.css250.45
listing.css1110.88
marks.css1431.12
math.css380.66
matrix.css440.48
notes.css1341.38
of.css570.63
pad.css570.55
pair.css290.34
pixel.css560.56
plot.css2501.61
qty.css230.25
range.css750.68
resume.css210.44
revise.css800.80
say.css230.33
scale.css280.39
search.css2111.32
set.css630.79
site.css1,1275.94
sky.css870.77
slope.css480.39
sort.css590.63
source.css480.44
spans.css780.59
spark.css460.40
spoiler.css380.40
steps.css570.50
sum.css100.21
tabs.css590.55
tiles.css650.76
tokens.css680.75
trace.css180.26
tree.css1300.95
treemap.css800.73
align.js401.14
areas.js541.18
bars.js1061.95
blocks.js551.37
board.js200.67
callouts.js180.44
chart.js2143.93
chat.js330.77
chem.js150.49
clade.js561.39
clip.js581.06
code.js2103.24
compare.js420.90
curve.js922.08
days.js561.28
delta.js100.33
dots.js1052.42
dump.js1713.22
events.js270.81
files.js220.48
flows.js651.61
frames.js841.40
glossary.js300.71
graph.js1102.45
heat.js330.74
hist.js631.59
image.js621.36
keys.js570.98
knob.js1242.21
lines.js1102.37
listing.js801.86
look.js851.54
map.js290.81
marks.js471.08
matrix.js150.39
notes.js1092.07
of.js230.72
pixel.js320.80
qty.js150.49
range.js310.85
resume.js681.16
revise.js200.48
say.js310.55
scale.js270.76
search.js1412.72
seq.js240.78
site.js4866.71
sky.js631.72
slope.js561.46
sort.js450.98
spans.js571.39
spark.js140.56
spoiler.js210.44
tabs.js500.84
tiles.js441.15
tokens.js391.05
trace.js130.36
treemap.js901.76
dots: every file of the frame and its pieces, a hundred and twenty-five of them, by how long it is and what it weighs. four are picked out, two stylesheets and two scripts. a line of script weighs about two and a half times a line of styles.

some numbers need another kind of scale. data-log counts in powers of ten, for what spans several of them, and data-flip runs a scale backwards, for measures where less is more. each names the axis it is for, x, y or both. a third column of numbers sizes the dots, and the area of a dot is what carries the number.

the planets at their brightest
planetdistance from the sun, in aumagnitude at its brightestdiameter
mercury0.387−2.480.383
venus0.723−4.920.949
mars1.52−2.940.532
jupiter5.20−2.9411.21
saturn9.57−0.559.45
uranus19.175.384.01
neptune30.187.673.88
dots across powers of ten and up a scale that runs backwards: a magnitude is smaller the brighter a thing is. distance and diameter are in earths (williams 2025), brightness is as seen from here (mallama and hilton 2018). jupiter is over three times as far from the sun as mars and exactly as bright, being twenty-one times as wide. neptune is the one planet below the rule.

class="dots joined" draws a line through the points in the order of the rows. a line chart spaces its rows evenly, whatever they are called. this one puts each where its number says, so it is the form for a line over uneven time, and with the two scales above, for time counted backwards.

the frame's weight against the clock
afterhours before pull request #72the frame
#614.56 h9.86 kb
#714.53 h9.85 kb
#1013.78 h10.65 kb
#2213.33 h10.51 kb
#2313.26 h10.54 kb
#2413.20 h10.55 kb
#2513.10 h10.70 kb
#2612.99 h10.77 kb
#2712.96 h10.84 kb
#2812.88 h10.88 kb
#2912.80 h10.89 kb
#3012.75 h11.36 kb
#3212.72 h11.45 kb
#3312.68 h11.53 kb
#3512.41 h11.65 kb
#462.36 h11.37 kb
#482.30 h11.39 kb
#521.74 h11.42 kb
#541.66 h11.44 kb
#561.39 h11.48 kb
#581.24 h11.50 kb
#601.11 h11.51 kb
#620.70 h11.54 kb
#660.46 h11.76 kb
#680.29 h11.80 kb
joined dots, on a scale that runs backwards in powers of ten. the first twenty-five steps of the line further up, as far as #68, placed by when each was merged instead of one after another: fifteen in the first two hours, a night with nothing, and ten in the last two and a half hours, which this scale gives most of the room.

with one column of numbers, class="dots" gives each row a line of its own: a point where its number falls, its interval through it, and the number as written at the end. unlike a bar, a point does not start from zero, so it can sit on either side of a rule.

time to gzip at level nine, over time at level six
filetimes as long
feed.xml3.20 (3.19–3.21)
this article1.72 (1.71–1.73)
the model league1.54 (1.53–1.56)
site.css1.19 (1.17–1.20)
site.js1.06 (1.06–1.07)
chart.js1.02 (1.00–1.06)
code.js1.12 (1.06–1.16)
lines.js1.06 (0.98–1.11)
notes.css1.01 (0.99–1.06)
bars.css1.02 (0.99–1.07)
estimates and their intervals, the largest file first. each file was gzipped thirty times at both levels, and the number is one middle time over the other, with the interval that 95 in 100 resamplings of those runs fall in. the large files pay for the higher level. for chart.js, lines.js, notes.css and bars.css the interval reaches the rule: no difference can be told from none.

class="blocks" is for things that lie in an order and take up room: the sections of a file, the functions of a binary, what a page sends. each row is a name, a size and a kind. they are drawn as one long strip cut into rows, every block as wide as its size and tinted by its kind, and the legend says what share each kind comes to.

what a first visit to this page weighs, in the order it arrives
filebytespart
this article57,354this article
site.css6,081the frame
newsreader-core.woff241,600fonts
site.js6,869the frame
index.html1,762the frame
newsreader-italic-core.woff246,384fonts
code.js3,318its pieces
code.css1,457its pieces
pair.css346its pieces
callouts.js451its pieces
callouts.css825its pieces
tabs.js864its pieces
tabs.css562its pieces
listing.js1,909its pieces
listing.css902its pieces
chart.js4,028its pieces
chart.css1,632its pieces
bars.js1,996its pieces
bars.css1,100its pieces
lines.js2,422its pieces
dots.js2,481its pieces
plot.css1,644its pieces
areas.js1,213its pieces
areas.css314its pieces
slope.js1,500its pieces
slope.css403its pieces
hist.js1,628its pieces
hist.css867its pieces
flows.js1,653its pieces
flows.css628its pieces
treemap.js1,805its pieces
treemap.css747its pieces
graph.js2,507its pieces
graph.css1,037its pieces
flow.css690its pieces
clade.js1,428its pieces
clade.css901its pieces
events.js828its pieces
events.css598its pieces
blocks.js1,404its pieces
blocks.css541its pieces
days.js1,307its pieces
days.css683its pieces
spark.js572its pieces
spark.css410its pieces
range.js875its pieces
range.css696its pieces
spans.js1,424its pieces
spans.css601its pieces
of.js739its pieces
of.css642its pieces
heat.js761its pieces
heat.css531its pieces
sort.js1,008its pieces
sort.css649its pieces
delta.js334its pieces
delta.css390its pieces
qty.js502its pieces
qty.css257its pieces
pad.css560its pieces
tree.css972its pieces
steps.css515its pieces
spoiler.js451its pieces
spoiler.css411its pieces
revise.js488its pieces
revise.css821its pieces
aside.css1,051its pieces
math.css680its pieces
image.js1,391its pieces
image.css1,146its pieces
marks.js1,107its pieces
marks.css1,142its pieces
compare.js917its pieces
compare.css948its pieces
pixel.js817its pieces
pixel.css570its pieces
clip.js1,090its pieces
clip.css616its pieces
notes.js2,115its pieces
notes.css1,408its pieces
dump.js3,297its pieces
dump.css1,903its pieces
seq.js796its pieces
align.js1,170its pieces
align.css486its pieces
map.js829its pieces
files.js494its pieces
files.css1,040its pieces
matrix.js395its pieces
matrix.css491its pieces
trace.js373its pieces
trace.css267its pieces
gloss.css502its pieces
facing.css372its pieces
source.css452its pieces
say.js563its pieces
say.css334its pieces
chat.js792its pieces
chat.css1,061its pieces
tokens.js1,073its pieces
tokens.css763its pieces
board.js690its pieces
board.css752its pieces
frames.js1,434its pieces
frames.css840its pieces
card.css506its pieces
scale.js782its pieces
scale.css396its pieces
set.css813its pieces
sky.js1,758its pieces
sky.css784its pieces
tiles.js1,176its pieces
tiles.css777its pieces
chem.js505its pieces
chem.css195its pieces
knob.js2,267its pieces
knob.css463its pieces
curve.js2,131its pieces
sum.css211its pieces
glossary.js722its pieces
glossary.css604its pieces
fig.css224its pieces
resume.js1,184its pieces
resume.css453its pieces
blocks. text is weighed gzipped and the fonts as they are stored. the colours are the ones in the table of spans above.

class="days" is for a number a day: a date in the first column, written year first, and a value in the second. it is drawn as a calendar, a column for each week with monday at the top, and each day is tinted by its number in one hue. the pointer reads a day off it.

how much of the moon is lit, night by night
nightlit
2026-10-0178%
2026-10-0268%
2026-10-0357%
2026-10-0445%
2026-10-0534%
2026-10-0624%
2026-10-0715%
2026-10-088%
2026-10-093%
2026-10-100%
2026-10-110%
2026-10-122%
2026-10-136%
2026-10-1411%
2026-10-1518%
2026-10-1626%
2026-10-1734%
2026-10-1844%
2026-10-1953%
2026-10-2063%
2026-10-2172%
2026-10-2280%
2026-10-2388%
2026-10-2494%
2026-10-2598%
2026-10-26100%
2026-10-2799%
2026-10-2896%
2026-10-2989%
2026-10-3081%
2026-10-3171%
2026-11-0160%
2026-11-0249%
2026-11-0338%
2026-11-0427%
2026-11-0518%
2026-11-0611%
2026-11-075%
2026-11-082%
2026-11-090%
2026-11-100%
2026-11-113%
2026-11-127%
2026-11-1312%
2026-11-1419%
2026-11-1527%
2026-11-1636%
2026-11-1745%
2026-11-1855%
2026-11-1965%
2026-11-2074%
2026-11-2183%
2026-11-2291%
2026-11-2396%
2026-11-2499%
2026-11-25100%
2026-11-2697%
2026-11-2792%
2026-11-2884%
2026-11-2975%
2026-11-3064%
2026-12-0153%
2026-12-0242%
2026-12-0332%
2026-12-0423%
2026-12-0515%
2026-12-069%
2026-12-074%
2026-12-081%
2026-12-090%
2026-12-101%
2026-12-113%
2026-12-128%
2026-12-1313%
2026-12-1420%
2026-12-1529%
2026-12-1638%
2026-12-1748%
2026-12-1858%
2026-12-1968%
2026-12-2078%
2026-12-2187%
2026-12-2294%
2026-12-2398%
2026-12-24100%
2026-12-2599%
2026-12-2694%
2026-12-2788%
2026-12-2879%
2026-12-2969%
2026-12-3058%
2026-12-3148%
a calendar: the last three months of 2026, each night at midnight, universal time, worked out the way meeus 1998 sets down. the moon keeps to neither weeks nor months, so its full nights step down the columns from one month to the next.

and a share can be counted out. a paragraph with class="of" that says 8 in 100 gets a hundred dots over it, eight of them filled. several in one figure sit side by side.

8 in 100 men of european descent see red and green less apart than most people do.

4 in 1,000 women do.

shares counted out, from birch 2012. this is who the four chart inks were measured for.2

and a line can sit in a sentence. the frame went from 9.9 kb to 13.9 9.86 9.85 10.65 10.51 10.54 10.55 10.70 10.77 10.84 10.88 10.89 11.36 11.45 11.53 11.65 11.37 11.39 11.42 11.44 11.48 11.50 11.51 11.54 11.76 11.80 11.80 11.82 11.85 11.87 11.88 11.89 11.92 11.46 11.55 11.71 11.83 11.97 11.97 11.98 12.73 12.86 12.86 12.89 13.07 13.42 13.50 13.62 13.74 13.81 13.87 in fifty steps. a sparkline has no axis and no scale: it says which way something went, so the numbers that matter belong in the words around it (tufte 2006).

the frame went from 9.9 kb to 13.9
<span class="spark">9.86 9.85 10.65 10.51 … 13.87</span> in fifty steps.

some charts are drawn from the things themselves instead of from a summary of them. the table has a row for each thing that was measured, and the class says what to make of them. class="hist" counts the values into bins. class="box" gives each name a box: the middle half of its values, a line at their median, and whiskers out to the smallest and the largest. a table can ask for both, and then they share a scale.

how many lines a pull request changes
kind of pull requestlines added and removed
new features1,933
new features940
new features434
new features62
new features275
new features76
new features346
new features344
new features77
new features113
new features140
new features1,059
new features538
new features758
new features284
new features583
new features496
new features399
new features711
new features147
new features504
new features250
articles861
articles537
articles655
articles744
upkeep316
upkeep114
upkeep210
upkeep8
upkeep19
upkeep36
upkeep13
upkeep5
upkeep9
a histogram over three boxes, in powers of ten. the thirty-five pull requests of the blog's first day, counted by git log --numstat with the feed left out, since a script writes it. the small number beside a name is how many it has. half of them changed fewer than three hundred lines, and a new feature is seldom small.

class="flows" is for what goes from one place to another. each row is a source, a destination and an amount, and each becomes a band as thick as its amount, between boxes as tall as everything that passes through them. a destination that is the source of another row gets a column of its own, further right.

a day of work, by what it was for and where its lines landed
kind of pull requestwherelines added and removed
new featuresthe pieces5,977
new featuresthe frame1,987
new featuresthe articles925
new featuresthe scripts919
new featuresthe readme and the flow661
articlesthe articles2,762
articlesthe frame35
upkeepthe readme and the flow346
upkeepthe pieces257
upkeepthe frame46
upkeepthe scripts45
upkeepthe articles36
flows. the same thirty-five pull requests, and the 13,996 lines they changed. the widest band is new features going into the pieces. the surprise is the third from the top: almost a thousand lines of new features went into articles, because each feature is shown on this page as it is built.

a nested list with a number on each leaf, <ul class="treemap">, is drawn as tiles whose areas are the numbers, each group in its own ink. it shows at a glance what a bar chart of fourteen rows makes the eye add up: how much each group comes to. the list stays behind the drawing, one click away.

a treemap: the 44.83% of the human genome that is copies of sequences able to copy themselves, by class and family, as the first draft of the genome counted them (international human genome sequencing consortium 2001, table 11). two families, L1 and Alu, are more than a quarter of everything we carry.

two more forms are maps. a <table class="sky"> has a row for each star: its right ascension and its declination in degrees, and its magnitude. it is drawn the way an atlas prints the sky, ink on paper, east on the left, a brighter star a larger dot. these are the nine stars marked on the picture of orion, figure 2, from the same numbers:

orion, from a table
starright ascensiondeclinationmagnitude
betelgeuse88.797.410.42
bellatrix81.286.351.64
meissa83.789.933.66
mintaka83.00−0.302.41
alnilam84.05−1.201.69
alnitak85.19−1.941.77
saiph86.94−9.672.06
rigel78.63−8.200.13
hatysa83.86−5.912.77
the chart makes room for the stars it is given. data-frame on the table pins its edges instead, the way it does for marks.

and a <table class="tiles"> draws a map without drawing a coastline. each row is a place, where its tile goes, across and then down, and a number. every place gets a square of one size, tinted by its number, so a small place counts for as much to the eye as a large one. a tile may sit a part of a step along, which staggers the rows. it is made for provinces and countries. here the places are the keys of a keyboard, and the number is how much of this article each letter was on , out of 65,487 letters:

the letters of this article, where the fingers find them
letteracrossdown% of all letters
q110.2
w211.9
e3112.4
r415.8
t5110.2
y611.1
u712.0
i816.8
o916.0
p1012.1
a1.2529.1
s2.2528.6
d3.2523.7
f4.2522.4
g5.2522.1
h6.2525.1
j7.2520.3
k8.2521.4
l9.2524.1
z1.7530.1
x2.7530.3
c3.7533.5
v4.7530.7
b5.7532.1
n6.7536.0
m7.7532.2
<table class="sky">
  <tr><th scope="row">rigel</th><td>78.63</td><td>−8.20</td><td>0.13</td></tr>
</table>

<table class="tiles">
  <tr><th scope="row">e</th><td>3</td><td>1</td><td>12.4</td></tr>
  <tr><th scope="row">a</th><td>1.25</td><td>2</td><td>9.1</td></tr>
</table>

bytes

two drawings take a file format apart. the first is a hex dump: bytes written as pairs of hex digits, set sixteen to a row, or eight where sixteen would not fit, with the offset in front and the same bytes as text behind. these are the first 48 bytes of the file this sentence is drawn from, a woff2 file (levantovsky and levien 2018). its first four spell wOF2.

77 4f 46 32 00 01 00 00 00 00 a2 80 00 14 00 00
00 01 31 e8 00 00 a2 0d 00 01 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

a range in a <mark> is tinted in both columns, and named under the dump with where it starts and how long it is. the second drawing is the layout of a header: a table with a row for each field and its size, and above it a strip with every field as wide as it is.

the header of a woff2 file, 48 bytes
fieldbytesin this file
signature4wOF2
flavour4truetype outlines
length441,600 bytes
tables220
reserved20
size unpacked478,312 bytes
size packed441,485 bytes
major21, the version of the format
minor20
metadata offset40, there is none
metadata length40
metadata unpacked40
private offset40, there is none
private length40
the strip is sixteen bytes to a row, like the dump above it. pointing at a field lights its row in the table.

with data-unit="bit" the same table is a register, or a byte that packs several things. it is written from its highest bit down, as a register is, and the number on each field is the bit it starts at. this is the last byte of the instruction that triples n in the listing, 48 8d 3c 7f: the byte that says rdi, plus twice rdi.

the byte 7f, as the processor reads it after lea
fieldbitsholdswhich means
scale201the second register is doubled
index3111the second register is rdi
base3111the first register is rdi
eight bits, three fields. the strip counts down from bit seven, the way the manuals draw it.
<pre class="hex" data-label="assets/fonts/newsreader-core.woff2">
<mark data-label="signature">77 4f 46 32</mark> <mark data-label="flavour: truetype outlines">00 01 00 00</mark> …
</pre>

<table class="fields" data-per="16">
  <thead><tr><th>field</th><th>bytes</th><th>in this file</th></tr></thead>
  <tbody>
    <tr class="a"><th scope="row">signature</th><td>4</td><td><code>wOF2</code></td></tr>
    …
  </tbody>
</table>

two dumps in a <figure class="pair"> are compared, byte against byte, and the ones that are not the same are picked out in both: on the left in the tint a patch gives to what it takes out, on the right in the tint for what it puts in. each face of the blog's type comes in two files, and their headers begin alike. the first of the upright and the first of the italic:

77 4f 46 32 00 01 00 00 00 00 a2 80 00 14 00 00
00 01 31 e8 00 00 a2 0d 00 01 00 00 00 00 00 00
77 4f 46 32 00 01 00 00 00 00 b5 30 00 14 00 00
00 01 52 3c 00 00 b4 bd 00 01 00 00 00 00 00 00
the first 32 bytes of each. six bytes differ, two in each of three sizes: the file, its tables unpacked, and its tables packed. everything else in the header is the same.

where things sit, in a file or in memory, is a <table class="map">: a name, a start and a size in each row. the strip above it is drawn to scale, except that nothing is drawn too thin to point at, and a stretch that no row accounts for is hatched. this is the object file the listing was read from:

steps.o, 872 bytes
partstarts atbyteswhat it holds
elf header0x00064what kind of file this is, and where the section headers are
.text0x04033the function: the twelve instructions of the listing
.comment0x06139the name and version of the compiler
.symtab0x08872three symbols, one of them steps
.strtab0x0d015their names
.shstrtab0x0df69the names of the sections
section headers0x128576nine entries of 64 bytes, one for each section
two thirds of the file describe the rest of it, and the function is 33 bytes. the hatched sliver in front of the section headers is four bytes of padding, there so that the headers start on a multiple of eight. a name is written in the strip where there is room to read it, and the table has them all.

a sequence is not bytes, but it is read the same way. a <pre class="seq"> sets dna, rna or protein in tens under a ruler, six tens to a row where there is room, with the position each row starts at, and names its marked ranges underneath. this is human insulin as the cell first makes it (uniprot P01308):

MALWMRLLPL LALLALWGPD PAAAFVNQHL CGSHLVEALY LVCGERGFFY TPKTRREAED
LQVGQVELGG GPGAGSLQPL ALEGSLQKRG IVEQCCTSIC SLYQLENYCN

the signal is cut off on the way out of the cell's factory, and the c peptide is cut out from between the two chains, which stay joined. the two pairs of letters left untinted are where those cuts are made.

several sequences that are nearly the same are a <pre class="align">: a line for each, its name and then its letters, with a - wherever one of them has nothing. they are set letter under letter. a letter that is not the one the first sequence has there is tinted, a gap is tinted another way, and the last line has a star under every place they all share. these are the first ninety letters of the gene for β-globin that are read into protein (refseq NG_059281.1), and the same letters with three changes that are found in people: the one behind sickle-cell disease, the one behind haemoglobin e (aziz and others 2025), and a loss of two letters that puts every letter after it out of step (tariq and others 2021).

reference    ATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGC
hb s         ATGGTGCATCTGACTCCTGTGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGC
hb e         ATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTAAGGCCCTGGGC
codon 5 −ct  ATGGTGCATCTGACTC--GAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCTGGGC
<pre class="align" data-label="hbb">
reference    ATGGTGCATCTGACTCCTGAGGAGAAG…
hb s         ATGGTGCATCTGACTCCTGTGGAGAAG…
codon 5 −ct  ATGGTGCATCTGACTC--GAGGAGAAG…
</pre>
<figure class="pair">
  <pre class="hex" data-label="newsreader-core.woff2">77 4f 46 32 … 00 00 a2 80 …</pre>
  <pre class="hex" data-label="newsreader-italic-core.woff2">77 4f 46 32 … 00 00 b5 30 …</pre>
</figure>

<table class="map">
  <thead><tr><th>part</th><th>starts at</th><th>bytes</th></tr></thead>
  <tbody>
    <tr><th scope="row">elf header</th><td>0x000</td><td>64</td></tr>
    <tr class="a"><th scope="row">.text</th><td>0x040</td><td>33</td></tr>
    …
  </tbody>
</table>

<pre class="seq" data-label="preproinsulin, homo sapiens">
<mark data-label="signal peptide">MALWMRLLPL LALLALWGPD PAAA</mark><mark data-label="b chain">FVNQHL …</mark>
</pre>

maths

a formula is written in mathml, which browsers set by themselves: no script, no font to download (carlisle and wang). it can sit in a line, like the step of a chart's axis, s∈{1,2,5}×10k, or take a line of its own. this is how the frame works out the reading time above every title, from the words w outside tables and code, and the number b of tables and blocks of code:3

minutes= ⌈w230+b4⌉
the reading time. the haiku report has about 5,800 words and 20 tables and blocks: 25.2 and 5 make 31 minutes.
<math display="block">
  <mi>minutes</mi><mo>=</mo>
  <mrow><mo>⌈</mo>
    <mfrac><mi>w</mi><mn>230</mn></mfrac><mo>+</mo><mfrac><mi>b</mi><mn>4</mn></mfrac>
  <mo>⌉</mo></mrow>
</math>

numbers and words inside a formula are set in the face of the text around them. a lone letter is a variable and comes out italic. brackets and signs come from the reader's own maths face, so they can look a little different from one machine to the next.

a formula can also be worked out, on numbers the reader changes. a <data class="knob"> is a number in a sentence that is dragged sideways, or moved with the arrow keys, and every <output data-f> works its formula out again. a page that grows by 5% with every pull request weighs 1.6 times what it did after 10 of them, and twice as much after 15.

how many times its weight, by pull request
pull requeststimes its weight
01.0
101.6
202.7
304.3
407.0
5011.5
a <table class="curve"> holds a few values of a formula and is drawn as the whole line, and again when a knob moves. a formula is arithmetic, read by the piece itself and never run as code. the checker works each one out where the knobs stand, so what the page says without scripts, and in the feed, is true.
<data class="knob" data-name="r" data-min="1" data-max="20" value="5"
  aria-label="percent it grows by each time">5</data>
<output data-f="(1 + r/100)^n" data-places="1">1.6</output>

<table class="curve" data-f="(1 + r/100)^x">
  <tr><th scope="row">10</th><td>1.6</td></tr>
</table>

images

an image lives in the article's folder. it has to say what it shows and how large it is, so the page holds its place while the file is on its way and nothing jumps when it lands. it is fetched when the reader gets near it, never before, and says so itself, with loading="lazy", for a reader who opens the article at its own address. in a figure it opens large on a click, and closes on another click, on esc, or when the page is scrolled on. opened, it can be looked into: the wheel or two fingers bring it closer about the point they are on, a drag moves it, and a click steps back out, to the whole picture first and then to the page. +, - and the arrows do the same. the part that does this is fetched the first time an image is opened.

five columns of short dark dashes on pale paper, like lines of text seen from far away, with a few blue, red and green marks among them
the blog's stylesheet, all 1,127 lines of it, drawn the way an editor's minimap would: a mark for every character. letters are ink, digits blue, braces red. the file shown is 6 kb, and a click opens one with four times the pixels.
<figure>
  <a href="stylesheet-large.png">
    <img src="stylesheet.png" alt="five columns of short dark dashes …" width="540" height="330" loading="lazy">
  </a>
  <figcaption>the blog's stylesheet, all 1,127 lines of it …</figcaption>
</figure>

the link around it is optional: without one, the image opens as it is. outside a figure an image sits in the line and does not open, like the dot a small blue dot that marks the current section in the sidebar. an image may weigh 250 kb at most, and the checker measures the file to see that the width and height written beside it are its real shape.

some pictures need pointing at. an <ol class="marks"> after an image is its key, and each item says where its number goes: data-at="62% 31%", across and then down, as shares of the image. where there is room beside the image the key stands there, so that neither has to be scrolled away to read the other, and pointing at a number lights its line.

a picture of somewhere can be marked in that place's own coordinates instead. data-frame on the list says what the picture's left, bottom, right and top edges are, west south east north on a map, and each data-at is then a place: across, and then up. such a mark is a ring round the place, with its number off to the upper right, or wherever left and down send it. and a picture that says how much it spans, with data-across, gets a bar of a round length to measure by. this is orion, drawn from the positions and magnitudes that simbad holds for its stars (wenger and others 2000). its marks are right ascensions and declinations in degrees, and on a chart of the sky east is on the left, so the frame's first number is the larger:

a dark sky with the stars of orion as pale dots: two shoulders, three in a slanted row for the belt, two feet, and the upper left shoulder orange
  1. betelgeuse α · 0.42
  2. bellatrix γ · 1.64
  3. meissa λ · 3.66
  4. mintaka δ · 2.41
  5. alnilam ε · 1.69
  6. alnitak ζ · 1.77
  7. saiph κ · 2.06
  8. rigel β · 0.13
  9. hatysa ι · 2.77
every star brighter than magnitude 5.5 in a field of 18 by 23 degrees, fifty-six of them, with north up and east to the left. a brighter star is a larger dot, and its colour leans the way the star's does. each line of the key gives a star's letter in the constellation and its magnitude.

a figure the text points at from far away can carry a number. class="fig" counts it, and the number is said before its caption: the picture above is figure 2, and the same stars drawn from a table, under charts, are figure 1. the stylesheet does the counting. the checker reads every link that says “figure” and a number, and fails when the number is not the one its figure has.

<figure class="fig" id="orion-picture"> … </figure>

<p>… the picture above is <a href="#orion-picture">figure 2</a>.</p>

two pictures of one thing are compared best in one place. a <figure class="compare"> with two images of the same shape lays the second over the first and puts a divider between them. it can be dragged, sent somewhere with a press, or moved with the arrow keys once it has the focus, and a data-label on an image names its side. the two below are simulated: the belt and the sword from the chart above with the noise of a short exposure added, once, and then sixty-four times over and averaged.

a grainy grey frame in which a handful of bright stars stand out of the noise the same frame, smooth, with fainter stars in it that the first did not show
averaging sixty-four frames leaves an eighth of the noise, the square root of sixty-four. measured in an empty corner of these two, it is 7.98 times less. twenty-one stars are in the field, and the faintest of them can only be told from the noise on the right.

when two pictures should be read side by side, and not one through the other, they are panels. a <figure class="set"> holds a figure for each, letters them in their corners, and keeps one caption under them all. they stand in a row where there is room and under each other where there is not, and each still opens large.

a grainy grey frame in which a handful of bright stars stand out of the noise
the same frame, smooth, with fainter stars in it that the first did not show
the two frames of the divider above, as panels: a one exposure, b sixty-four, averaged.

pixel art is the one kind of picture that must never be smoothed. an <img class="pixel"> is shown at a whole multiple of its size, the largest that fits, counted in the pixels of the screen and not of the page, so that each of its pixels is a clean square and all of them are the same size. data-max="4" holds it to four times. the caption is told what the multiple came to.

a night in four greens: a full moon over two ridges with pines along them, a cabin with a lit window, and a dithered sky with orion in it
a night drawn by a short script, in four greens. the file is 599 bytes.

a <ul class="palette"> shows the colours themselves: each item gets a swatch of the first colour written in it. these are the four greens most often used to stand for the screen of the first game boy, with how many of the scene's 5,184 pixels wear each.

and some things only make sense moving. a short silent loop is written as a still that links to its file, in a <figure class="clip">. that is all a feed reader or a browser without scripts will show, and it is enough. here the still stays until the reader is near, and only then is the clip fetched. it plays while it is in view, rests when it is not, and stops at its button or at a press on the picture. a reader who has asked their system for less motion gets the still, and a button that says play.

black cells on a pale grid: a block at each end of the top, two larger shapes between them, gliders on a line running down to the right, and a hook of seven cells waiting at the end of it
gosper's glider gun, the first pattern found in conway's game of life that grows without end (gardner 1971). it fires a glider every thirty generations. here an eater at the lower right swallows each one, so after thirty frames everything is where it began and the loop has no seam. three seconds, 10 kb.
<figure>
  <img src="orion.png" alt="a dark sky with the stars of orion …" width="720" height="900" data-across="18.4°" loading="lazy">
  <ol class="marks" data-frame="92.90 -12.03 74.48 10.82">
    <li data-at="88.79 7.41">betelgeuse <small><i>α</i> · 0.42</small></li>
    <li data-at="85.19 -1.94 left down">alnitak <small><i>ζ</i> · 1.77</small></li>
    …
  </ol>
</figure>

<figure class="compare">
  <img src="one.jpg" alt="a grainy grey frame …" width="720" height="450" data-label="one exposure" loading="lazy">
  <img src="sixty-four.jpg" alt="the same frame, smooth …" width="720" height="450" data-label="sixty-four, averaged" loading="lazy">
</figure>

<figure class="set">
  <figure><img src="one.jpg" alt="a grainy grey frame …" width="720" height="450" loading="lazy"></figure>
  <figure><img src="sixty-four.jpg" alt="the same frame, smooth …" width="720" height="450" loading="lazy"></figure>
  <figcaption><b>a</b> one exposure, <b>b</b> sixty-four, averaged.</figcaption>
</figure>

<figure>
  <img class="pixel" src="scene.png" alt="a night in four greens …" width="96" height="54" loading="lazy">
</figure>

<ul class="palette">
  <li><code>#0f380f</code> 2,039</li>
  …
</ul>

<figure class="clip">
  <a href="gun.mp4"><img src="gun.png" alt="black cells on a pale grid …" width="552" height="408" loading="lazy"></a>
</figure>

some pictures are better written than drawn. a <pre class="board"> is a grid with a character for each cell, and the list after it is its legend: each item starts with the character it explains, and data-ink gives that character one of the four inks. a character the legend leaves out is an empty cell, and a space is no cell at all, which is how four boards stand in one block here. this is a glider, generation by generation, with what the rules are about to do to it:

...... ...... ...... ......
..x... ...... ...... ......
.+.o.. .x.o.. ..+x.. ..x+..
.xoo.. .+xo.. .x.o+. ...xo.
..+... ..o+.. ..oo.. ..oo+.
...... ...... ...... ......
four generations of a glider, left to right. black and red together are the glider as it is, black and green the glider as it will be. worked out by running the rules.

frames that follow each other can share one file. an <img class="frames" data-frames="24"> is a strip of that many frames side by side, shown one at a time, six a second unless data-fps says otherwise. it moves only while it is in view and it can be stopped. stopped, a press on the picture moves it on by one. this glider lives on a board six cells square whose edges are joined, so after twenty-four generations it is back where it began:

five black cells in the shape of a glider on a pale square, crawling down and to the right, and coming back in on the other side
twenty-four frames in one file of 0.8 kb.

and two pictures of one shape can take turns, which is how astronomers used to find what had moved between two plates. in a <figure class="blink"> the pictures lie on each other and swap every 0.8 seconds, or every data-every, and data-label names each one under the picture. this is the gun of the clip above, and the same gun thirty generations later. everything in it is back where it was, and the one new thing is the glider it has let go:

<pre class="board" aria-label="four small boards …">
..x... ......
.+.o.. .x.o..
</pre>
<ul>
  <li data-ink="2"><code>x</code> alive, and about to die</li>
</ul>

<img class="frames" data-frames="24" src="glider.png" alt="…" width="3456" height="144" loading="lazy">

<figure class="blink">
  <img src="gun-0.png" alt="…" width="456" height="168" data-label="generation 0" loading="lazy">
  <img src="gun-30.png" alt="…" width="456" height="168" data-label="generation 30" loading="lazy">
</figure>

the checker holds each of these to its form. a mark needs its place, the two sides of a comparison need one shape, pixel art has to be stored without loss and at the size written beside it, and a clip is an mp4 or a webm of 500 kb at most, behind a still that is checked like any other image.

asides and folds

an <aside> is a note beside the argument. a <strong> at its start gives it a name, and class="warn" turns it into a warning, like the one near the top of this page.

a <details> folds away what most readers will not need. it can hold anything an article can.

what the checker printed for this blog
weight, gzipped

  the shell (html + css + js)                              13.9 kb  of 14
  the library (5 articles)                                  0.6 kb  of 1.2
  code, for the articles that use it                        4.7 kb  of 5
  pair, for the articles that use it                        0.3 kb  of 0.5
  callouts, for the articles that use it                    1.2 kb  of 1.5
  tabs, for the articles that use it                        1.4 kb  of 1.5
  listing, for the articles that use it                     2.7 kb  of 3
  chart, for the articles that use it                       5.5 kb  of 6
  bars, for the articles that use it                        3.0 kb  of 3.5
  lines, for the articles that use it                       2.4 kb  of 2.5
  dots, for the articles that use it                        2.4 kb  of 2.5
  plot, for the articles that use it                        1.6 kb  of 2
  areas, for the articles that use it                       1.5 kb  of 1.5
  slope, for the articles that use it                       1.9 kb  of 2
  hist, for the articles that use it                        2.4 kb  of 2.5
  flows, for the articles that use it                       2.2 kb  of 2.5
  treemap, for the articles that use it                     2.5 kb  of 2.5
  graph, for the articles that use it                       3.5 kb  of 3.5
  flow, for the articles that use it                        0.7 kb  of 1
  clade, for the articles that use it                       2.3 kb  of 2.5
  events, for the articles that use it                      1.4 kb  of 1.5
  blocks, for the articles that use it                      1.9 kb  of 2
  days, for the articles that use it                        1.9 kb  of 2
  spark, for the articles that use it                       1.0 kb  of 1
  range, for the articles that use it                       1.5 kb  of 2
  spans, for the articles that use it                       2.0 kb  of 2
  of, for the articles that use it                          1.3 kb  of 1.5
  heat, for the articles that use it                        1.3 kb  of 1.5
  sort, for the articles that use it                        1.6 kb  of 2
  delta, for the articles that use it                       0.7 kb  of 1
  qty, for the articles that use it                         0.7 kb  of 1
  pad, for the articles that use it                         0.5 kb  of 1
  tree, for the articles that use it                        0.9 kb  of 1
  steps, for the articles that use it                       0.5 kb  of 1
  spoiler, for the articles that use it                     0.8 kb  of 1
  revise, for the articles that use it                      1.3 kb  of 1.5
  aside, for the articles that use it                       1.0 kb  of 1.5
  math, for the articles that use it                        0.7 kb  of 1
  image, for the articles that use it                       2.5 kb  of 3
  marks, for the articles that use it                       2.2 kb  of 2.5
  compare, for the articles that use it                     1.8 kb  of 2
  pixel, for the articles that use it                       1.4 kb  of 1.5
  clip, for the articles that use it                        1.7 kb  of 2
  notes, for the articles that use it                       3.4 kb  of 3.5
  dump, for the articles that use it                        5.1 kb  of 5.5
  seq, for the articles that use it                         0.8 kb  of 1
  align, for the articles that use it                       1.6 kb  of 2
  map, for the articles that use it                         0.8 kb  of 1
  files, for the articles that use it                       1.5 kb  of 1.5
  matrix, for the articles that use it                      0.9 kb  of 1
  trace, for the articles that use it                       0.6 kb  of 1
  gloss, for the articles that use it                       0.5 kb  of 0.5
  facing, for the articles that use it                      0.4 kb  of 0.5
  source, for the articles that use it                      0.4 kb  of 0.5
  say, for the articles that use it                         0.9 kb  of 1
  chat, for the articles that use it                        1.8 kb  of 2
  tokens, for the articles that use it                      1.8 kb  of 2
  board, for the articles that use it                       1.4 kb  of 1.5
  frames, for the articles that use it                      2.2 kb  of 2.5
  card, for the articles that use it                        0.5 kb  of 0.5
  scale, for the articles that use it                       1.2 kb  of 1.5
  set, for the articles that use it                         0.8 kb  of 1
  sky, for the articles that use it                         2.5 kb  of 2.5
  tiles, for the articles that use it                       1.9 kb  of 2
  chem, for the articles that use it                        0.7 kb  of 1
  knob, for the articles that use it                        2.7 kb  of 3
  curve, for the articles that use it                       2.1 kb  of 2.5
  sum, for the articles that use it                         0.2 kb  of 0.5
  glossary, for the articles that use it                    1.3 kb  of 1.5
  fig, for the articles that use it                         0.2 kb  of 0.5
  resume, for the articles that use it                      1.6 kb  of 2
  keys, when a reader asks for it                           1.0 kb  of 1.5
  search, when a reader asks for it                         4.0 kb  of 4.5
  look, when a reader asks for it                           1.5 kb  of 2
  what the search reads, the first time it is opened       79.9 kb  of 150
  the worker, fetched in the background                     1.4 kb  of 1.5
  fonts, the first file of each face (2)                   85.9 kb  of 86
  gaming/inside-kirby-super-star-ultra/index.html          15.4 kb  of 40
    its 10 images, as stored                              139.7 kb  of 250 each
  typography/everything-this-blog-can-do/index.html        56.0 kb  of 60
    its 12 images, as stored                              304.6 kb  of 250 each
    its clip, as stored                                    10.2 kb  of 500 each
    its sound, as stored                                   29.8 kb  of 100 each
  software/la-liga-de-modelos-de-opencode-go/index.html     7.8 kb  of 40
  software/claude-haiku-5-5/index.html                     17.0 kb  of 40
  software/opencode-go-model-league/index.html              7.2 kb  of 40

all good: 12 sections, 5 articles.
<aside>
  <p><strong>the rule behind all of it.</strong> a page pays only for what it uses.</p>
</aside>

<details>
  <summary>what the checker printed for this blog</summary>
  <pre><code>…</code></pre>
</details>

notes and references

a small number in the text leads to a note. on a wide screen the note moves out into the margin, beside the line that cites it. on a narrow one it waits at the foot of the article, with a caret that leads back, until it is asked for: a press on its number opens it right under the line that cites it, and another puts it away. on paper it stays at the foot. wherever it is, it is only ever in one place. a note cited twice, like the one about the inks,2 gets a letter at the foot for each place that cites it. and when every note has gone to the margin, the list and its heading step aside: on a wide screen this page has no section of notes at all.

a reference is the same idea with a different job. it stays in its list on every screen, keeps the capitals a citation is written with, and can be cited by a number or by name, the way this page is set in newsreader (the newsreader project authors 2020) and its feed is an atom feed (nottingham and sayre 2005).

a reference can also say what kind of evidence it is, which matters most when the subject is health. data-kind takes one of six words, review, trial, observational, lab, guideline or preprint, and the reference wears it as a label in its list. four more are for other subjects. primary is the record itself and secondary an account of it, which is what history asks first of a source: on this page caesar is the one, and the encyclopaedia that transcribes trajan's column is the other. vendor is what the maker of a thing says about it, and benchmark is somebody's measurement of it. three papers about vitamin d show why it is worth a word. in a cohort of 13,331 adults, those with the least of it in their blood died at a higher rate over the nine years or so that followed (melamed and others 2008). in a trial, 25,871 people took it or a placebo for about five years, and about as many broke a bone either way (leboff and others 2022). and a review that pooled 81 trials found no effect on fractures or falls (bolland and others 2018). the first kind can say that two things go together, and only the other two can say what a supplement does.

that aside is the other half. an article about health says once, plainly, what it is not, in an <aside class="care">. the checker asks for one in every article tagged health, and the words are the author's.

a glossary is a third list. a <dl class="glossary"> holds the words an article uses in a sense of its own, each a <dfn>, in the order of the alphabet. a word in the text that links to its entry sits on a dotted line, and a press on it opens what it means under the line it is on, the way a note opens. four words on this page do, where each is first explained, and this is where they lead:

feed
one file, feed.xml, with every article in it whole. a feed reader follows the blog through it. the search reads a file written with it: the same words, without their markup.
frame
the page every address is set in: index.html, its stylesheet and its script. it draws the sidebar and sets the article that was asked for.
library
the list of every article, written inside the frame: its section, its title, its date and its tags.
piece
a script, a stylesheet or both, fetched only by the articles that use what it draws.
<p>the blog is one html page, the <a href="#term-frame">frame</a>.</p>

<dl class="glossary">
  <dt id="term-frame"><dfn>frame</dfn></dt>
  <dd>the page every address is set in …</dd>
</dl>
<p>nothing comes from another site.<sup><a href="#note-fonts">1</a></sup></p>
<p>… its feed is an atom feed (<a href="#rfc-4287">nottingham and sayre 2005</a>).</p>

<ol class="notes">
  <li id="note-fonts">the font's files are served from the blog's own folder.</li>
</ol>

<ol class="refs">
  <li id="rfc-4287"><cite>Nottingham, M. &amp; Sayre, R., eds. (2005).
    <i>The Atom Syndication Format</i>. RFC 4287.</cite></li>
  <li id="leboff-2022" data-kind="trial"><cite>LeBoff, M. S., Chou, S. H., Ratliff, K. A. et al. (2022). …</cite></li>
</ol>

<aside class="care"><strong>a word of care.</strong> nothing here is advice about what to take …</aside>

around the article

an article is listed in the library with a title, a line that says what it is, and a date. three more fields are optional:

{
  "section": "software",
  "slug": "everything-this-blog-can-do",
  "title": "everything this blog can do",
  "deck": "every piece an article can use, shown and explained.",
  "date": "2026-10-12",
  "updated": "2026-10-12",
  "tags": ["blog", "writing"]
}

two more fields are for an article that is not in english. "lang" says which language it is in, and the frame marks its title and its text as that language, for the software that reads a page aloud or breaks its words. a translation also names what it translates, as "translates": "software/opencode-go-model-league", and the checker sees that each of the two leads to the other. the league of models is one: it can be read en español.

the same entry is what a link to this article says of it somewhere else. paste a link into a chat, and the software there fetches it, reads the top of the file for a title, a line about it and a picture, and runs no script. so the first lines of this article's own file say both, among the lines that make the file a page: those the front page starts with, the frame's stylesheet and script among them. nobody wrote them: scripts/head.mjs did, from the entry above and the top of index.html, and the checker fails when they disagree. the one before the last is for a search engine: the title, the dates, who wrote it and the picture, as the data schema.org calls an article. an article that shares tags with others has one more line above it, which names the ones its foot leads to. the last is for the frame and not for a preview: it names the pieces this article needs, which is all of them.

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>everything this blog can do · nahuel genchi</title>
<meta name="description" content="every piece an article can use, shown and explained.">
…
<link rel="stylesheet" href="../../assets/site.css">
…
<script type="module" src="../../assets/site.js"></script>
<link rel="canonical" href="https://nahuelgenchi.com/typography/everything-this-blog-can-do/">
<meta property="og:type" content="article">
<meta property="og:site_name" content="nahuel genchi">
<meta property="og:title" content="everything this blog can do">
<meta property="og:description" content="every piece an article can use, shown and explained.">
<meta property="og:url" content="https://nahuelgenchi.com/typography/everything-this-blog-can-do/">
<meta property="og:image" content="https://nahuelgenchi.com/typography/everything-this-blog-can-do/preview.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="everything this blog can do. every piece an article can use, shown and explained.">
<meta property="article:published_time" content="2026-10-12">
<meta property="article:modified_time" content="2026-10-14">
<meta name="twitter:card" content="summary_large_image">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","headline":"everything this blog can do",…}</script>
<meta name="pieces" content="code pair.css callouts tabs listing chart bars …">

the frame leaves those lines out when it sets the article, and so do the feed and the search. they add about 850 bytes to each article, gzipped. the address in the bar is always the page's own, the one the feed gives out too, so whatever a reader copies, a preview of it is a preview of the article.

the picture is a card in the blog's own type, on its own paper: the article's section and date, its title over its deck, and the blog's name at the foot. scripts/cards.mjs draws it in a browser with no window and writes it beside the article, as preview.png. the front page, the sections and the tags all show the blog's own card. a card carries, inside the file, a fingerprint of what it was drawn from, so the checker can tell without a browser when a title, a deck, a colour or the type has moved on without it, and only a card that has fallen behind is drawn again. this page's card is what a link to it shows.

the third field, "updated", is the day an article last changed, and it comes with a duty: the article has to say what changed. the smallest change is made where it stands, with <del> for what was taken out and <ins> for what was put in. the article on haiku 5.5 first said of four scores that they were inside half a point of each other inside two points of each other, three of them inside 0.6, with the four scores written in the same sentence.

anything larger goes in a list with class="changes": one dated entry for each day the article was touched, newest first. the date at the top of the page then gains an “updated”, linked to the list, and the checker will not let the list and the library disagree about the day. this page has one, and so does the article that needed it most.

<p>… they were <del>inside half a point</del> <ins>inside two points</ins> of each other.</p>

<h2>changes</h2>

<ul class="changes">
  <li><time datetime="2026-10-12">12 october 2026</time> twelve sentences and labels
    disagreed with the numbers beside them, and were made to agree.</li>
</ul>

what keeps it honest

one script, scripts/check.mjs, reads the whole blog and fails on anything that breaks a rule. it runs on every pull request, and nothing reaches the blog without passing it. it checks that:

a second, scripts/browse.mjs, opens every page in chrome, as a small phone, a phone, a laptop and a screen wide enough for notes in the margin, and fails on what only a browser sees: a script that throws, a request that fails or leaves the blog, an article asked for again when its link is pressed after the pointer rested on it, a link that does not fade one page into the next, or does for a reader who asked for less motion, a change to the blog that does not reach a reader who comes back, a page they opened that does not open again with no connection, a long article that does not offer the part its reader left off at, a picture that does not load, a word the search reads otherwise than a browser would, a chart left as a table, a page wider than its screen, and anything drawn past its left edge, where no reader can scroll to it. it also measures four things a look used to be the only check for: no words are drawn over other words, a formula on a line of its own sits in the middle of the text, the pictures of a set sit together, and nothing is drawn over a note in the margin, or waits below one with no need to. it holds the type back until a page has drawn without it, as a slow first visit does, and fails when the page then moves more than a line that gains a word would move it. it reads every letter the page sets in newsreader too, and fails on one that no file of it has, or on a file the page asks for and draws nothing from. and four that a reader who hears the page, moves through it by keys or sees it faintly depends on: every link, control and picture has a name a screen reader can say, every table has a caption, whatever takes the focus shows it, and every letter stands far enough from what is behind it, 4.5 to 1, or 3 to 1 when it is large, in the ink of every section. then it opens every page once more for a reader who asks their system for more contrast, and fails on small print that stands under 7 to 1, and once more as windows draws it for a reader who has asked for more contrast still, in a few colours of its own, and fails on whatever those would paint out, uncover or run together: a mark drawn in a colour alone, words under a cover, two series with one key.

neither can check that a page reads well. that part is done by looking, on a phone and on a wide screen.

changes

notes

  1. the font's files are served from the blog's own folder, with their licence beside them.
  2. each pair of the four inks was measured, on this paper colour, to hold apart for readers with the common forms of colour blindness. the hex dump uses the same four.
  3. 230 words a minute is a comfortable pace for prose on a screen, and a guess. a table or a block of code is looked at, not read, so each counts as a quarter of a minute.

references

  1. Tufte, E. R. (2006). Beautiful Evidence. Graphics Press.
  2. Levantovsky, V. & Levien, R., eds. (2018). WOFF File Format 2.0. W3C Recommendation. w3.org/TR/WOFF2
  3. Carlisle, D. & Wang, F., eds. MathML Core. W3C. w3.org/TR/mathml-core
  4. Nottingham, M. & Sayre, R., eds. (2005). The Atom Syndication Format. RFC 4287. rfc-editor.org/rfc/rfc4287
  5. Mallama, A. & Hilton, J. L. (2018). Computing apparent planetary magnitudes for The Astronomical Almanac. Astronomy and Computing 25, 10–24.
  6. Williams, D. R. (2025). Planetary Fact Sheet: Ratio to Earth Values. NASA Space Science Data Coordinated Archive. nssdc.gsfc.nasa.gov/planetary/factsheet
  7. Bringhurst, R. (2012). The Elements of Typographic Style, 4th ed. Hartley & Marks.
  8. Campbell, A., Adams, C., Bradley Montgomery, R., Cooper, M. & Kirkpatrick, A., eds. (2024). Web Content Accessibility Guidelines (WCAG) 2.2. W3C Recommendation. w3.org/TR/WCAG22
  9. Meeus, J. (1998). Astronomical Algorithms, 2nd ed. Willmann-Bell.
  10. Wenger, M., Ochsenbein, F., Egret, D. et al. (2000). The SIMBAD astronomical database: the CDS reference database for astronomical objects. Astronomy and Astrophysics Supplement Series 143, 9–22. doi.org/10.1051/aas:2000332
  11. Planck Collaboration (2020). Planck 2018 results. VI. Cosmological parameters. Astronomy & Astrophysics 641, A6. doi.org/10.1051/0004-6361/201833910
  12. Gardner, M. (1971). Mathematical games: on cellular automata, self-reproduction, the Garden of Eden and the game “life”. Scientific American 224(2), 112–117.
  13. The UniProt Consortium. Insulin, Homo sapiens. UniProtKB, entry P01308 (INS_HUMAN). uniprot.org/uniprotkb/P01308
  14. Prado-Martinez, J., Sudmant, P. H., Kidd, J. M. et al. (2013). Great ape genetic diversity and population history. Nature 499(7459), 471–475. doi.org/10.1038/nature12228
  15. Scally, A., Dutheil, J. Y., Hillier, L. W. et al. (2012). Insights into hominid evolution from the gorilla genome sequence. Nature 483(7388), 169–175. doi.org/10.1038/nature10842
  16. Aziz, N. A., Ali, E. Z., Mohd Khalid, M. K. N. et al. (2025). Novel HBB:c.375_376delAC mutation in a Malay patient with HbE beta-thalassemia intermedia: a case report. Medicine 104(39), e44817. doi.org/10.1097/MD.0000000000044817
  17. Tariq, A., Khurshid, S. & Sajjad, M. (2021). Evaluation of the high resolution melting approach for detection of β-thalassemia gene mutations. Hemoglobin 45(1), 20–24. doi.org/10.1080/03630269.2020.1867566
  18. Caesar, C. Iulius. Commentarii de Bello Gallico, book 1, chapter 1.
  19. Wikipedia contributors. Trajan's Column. Wikipedia, read 12 October 2026. en.wikipedia.org/wiki/Trajan's_Column
  20. Birch, J. (2012). Worldwide prevalence of red-green color deficiency. Journal of the Optical Society of America A 29(3), 313–320. doi.org/10.1364/JOSAA.29.000313
  21. Bolland, M. J., Grey, A. & Avenell, A. (2018). Effects of vitamin D supplementation on musculoskeletal health: a systematic review, meta-analysis, and trial sequential analysis. The Lancet Diabetes & Endocrinology 6(11), 847–858. doi.org/10.1016/S2213-8587(18)30265-1
  22. LeBoff, M. S., Chou, S. H., Ratliff, K. A. et al. (2022). Supplemental vitamin D and incident fractures in midlife and older adults. The New England Journal of Medicine 387(4), 299–309. doi.org/10.1056/NEJMoa2202106
  23. Melamed, M. L., Michos, E. D., Post, W. & Astor, B. (2008). 25-hydroxyvitamin D levels and the risk of mortality in the general population. Archives of Internal Medicine 168(15), 1629–1637. doi.org/10.1001/archinte.168.15.1629
  24. National Center for Biotechnology Information. Homo sapiens hemoglobin subunit beta (HBB), RefSeqGene on chromosome 11. NCBI Reference Sequence NG_059281.1. ncbi.nlm.nih.gov/nuccore/NG_059281.1
  25. Cohen, K. M., Finney, S. C., Gibbard, P. L. & Fan, J.-X. (2013, updated). The ICS International Chronostratigraphic Chart. Episodes 36(3), 199–204. stratigraphy.org/chart
  26. International Human Genome Sequencing Consortium (2001). Initial sequencing and analysis of the human genome. Nature 409(6822), 860–921. doi.org/10.1038/35057062
  27. The Newsreader Project Authors (2020). Newsreader. SIL Open Font License 1.1. github.com/productiontype/Newsreader