For AI agents and crawlers: a structured index of this site is available at https://danny.is/llms.txt.

Prose & Typography

Site styleguide

The foundations section covers the fundamentals of typography and spacing. This section demonstrates how content is styled and rendered in various contexts. It's a reference for the inline and block-level elements I use on this site. Where an example has a tabbed switcher, it's previewing that example across the site's typographic contexts.

Inline elements

Here's how inline text elements are styled...

Element You write Renders Notes
Links
Link (internal) [Notes](/notes/) Notes Unvisited links are rendered in the accent colour while visited ones are rendered in purple. Hover and active behaviour depend on the context. In long-form prose on wide viewports, coloured underlines are shown instead of coloured text.
Link (external) [GitHub](https://github.com) GitHub Markdown links are automatically converted to SmartLink components. Those pointing to external domains get a small arrow and appropriate attributes.
Raw anchor <a href="/notes/">Notes</a> Notes
Emphasis & weight
Emphasis *emphasis* emphasis Em elements nested inside other em elements are rendered upright.
Strong **strong** strong
Italic <i>italic</i> italic
Bold <b>bold</b> bold
Code & technical
Inline code `code` code
Keyboard <kbd>Ctrl</kbd> Ctrl + S Keyboard input renders as little buttons which depress on hover.
Variable <var>x</var> f(x) = x2
Sample output <samp>Build complete</samp> Build complete
Subscript H<sub>2</sub>O H2O
Superscript E = mc<sup>2</sup> E = mc2
Edits & highlights
Marked / highlighted <mark>highlighted</mark> highlighted
Deleted ~~deleted~~ deleted
Strikethrough <s>struck</s> struck
References & meta
Abbreviation <abbr title="…">W3C</abbr> W3C Abbreviations are rendered in small caps. If a title is present, they're underlined.
Definition <dfn>design token</dfn> design token
Citation <cite>Title</cite> The Elements of Typographic Style
Inline quote <q>quote</q> Typography exists to honor content.
Small print <small>fine print</small> fine print

Manual inline components

The following inline Astro components are available for use in MDX files or other Astro pages.

Component You write Renders Notes
SmallCaps <SmallCaps>Small Caps</SmallCaps> Small Caps
Highlight <Highlight>highlighted</Highlight> highlighted

Block-level elements

And here's how the basic block-level elements are styled...

Headings

Heading level 1

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Heading level 2

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Heading level 3

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Heading level 4

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Heading level 5

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Heading level 6

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Paragraphs

A paragraph of body copy, with emphasis, strong importance, a bit of inline code and a link woven through it. Nostrud in deserunt et laborum id adipisicing laboris sint. Consectetur laborum consequat mollit laborum.

A second paragraph, so the spacing and rhythm between blocks is visible. Nostrud in deserunt et laborum id adipisicing laboris sint. Consectetur laborum consequat mollit laborum. Dolore occaecat ex anim reprehenderit aliqua. Veniam commodo qui quis anim consectetur pariatur. Dolore exercitation esse et. Minim dolor aliquip laboris Lorem nostrud minim incididunt fugiat voluptate ex veniam. Nisi aliqua ut veniam incididunt officia cillum aliqua reprehenderit velit et mollit eu voluptate. Sunt ut elit ipsum aute duis minim incididunt et nulla dolor magna. Officia consequat ex duis.

Blockquote

A plain markdown blockquote. This is what a single > prefix produces — a blockquote wrapping one or more paragraphs.

Horizontal rule

Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.


Elit est aliqua excepteur aliquip pariatur voluptate officia Lorem tempor occaecat consectetur nisi aliquip est.

Composite sample

Here's a more realistic example of these elements working together.

A section heading

An opening paragraph that introduces the section, with an emphasised phrase and a link to set the scene. Dolore occaecat ex anim reprehenderit aliqua. Veniam commodo qui quis anim consectetur pariatur. Dolore exercitation esse et. Minim dolor aliquip laboris Lorem nostrud minim incididunt fugiat voluptate ex veniam. Nisi aliqua ut veniam incididunt officia cillum aliqua reprehenderit velit et mollit eu voluptate. Sunt ut elit ipsum aute duis minim incididunt et nulla dolor magna. Officia consequat ex duis.

A subsection

Body copy under a subsection heading, showing how headings and paragraphs sit together with the right vertical rhythm. Nisi aliqua ut veniam incididunt officia cillum aliqua reprehenderit velit et mollit eu voluptate. Sunt ut elit ipsum aute duis minim incididunt et nulla dolor magna. Officia consequat ex duis.

A quotation that breaks up the flow and carries a little extra weight.

A closing paragraph before a thematic break.


And a final paragraph after the rule.

Lists

Unordered and ordered lists share much of their styling, and handle nesting properly. Ordered lists are rendered with numerals by default and use letters then roman numerals for nested items.

Unordered lists

  • A first item
  • A second item with emphasis and a link
  • A third item with nested children
    • A nested item
    • Another nested item
  • A fourth item

Ordered lists

  1. The first step
  2. The second step
  3. The third step, with sub-steps
    1. A sub-step
      1. A sub-sub-step
      2. Another sub-sub-step
    2. Another sub-step
  4. The fourth step

Checklists

GFM task lists are supported by default and render as checklists.

A task list rendered from MDX to show actual checklist output:

  • Review the pull request thoroughly
  • Run the test suite locally
  • Update documentation if needed
  • Get approval from at least one reviewer
    • Code review complete
    • Design review pending
  • Merge when all checks pass

List density

Lists earn their vertical rhythm based on how long their items are. A list of short bullets gets tight spacing, while a list whose items run long — essentially paragraphs in their own right — gets the more generous spacing that paragraphs receive. A build-time rehype plugin measures the average length of each list's items and adds the .long-list-items class when it crosses a threshold.

A short-item list (should NOT get the class):

  • First item
  • Second item
  • Third item
  • Fourth item

A long-item list where each item is essentially a paragraph (should get .long-list-items):

  • When writing documentation, it’s important to consider the reader’s context and provide enough background information that they can understand the topic without needing to consult external resources.
  • Code examples should be complete and runnable whenever possible, rather than showing isolated snippets that leave readers guessing about imports, configuration, or surrounding context.
  • Error messages and edge cases deserve special attention because these are often the situations where readers most need guidance, yet they’re frequently overlooked in favour of happy-path documentation.

A nested list where the outer items are short (nested content shouldn’t affect the outer list):

  • Setup requirements
    • This nested item is quite long and contains a lot of detail about the specific requirements for setting up the development environment, including all the tools and dependencies.
    • Another detailed nested item explaining configuration options and recommended settings for optimal performance.
  • Installation steps
  • Running tests

.list-reset

The .list-reset class removes default list styling (bullets, padding etc) for navigation-style lists.

Without .list-reset
With .list-reset

Tables

Tables are built from standard markdown or HTML — thead, tbody, th, td and an optional caption. They stretch to the full width of their container with collapsed, hairline borders and just enough padding to breathe, and the first and last cells sit flush with the text edges so a table lines up with the surrounding prose. Captions sit below the table, and numerals are set with tabular figures so digits align vertically column-to-column for easy scanning.

Basic table

Quarterly results
Quarter Revenue Expenses Profit
Q1 $124,500 $98,200 $26,300
Q2 $156,800 $112,400 $44,400
Q3 $189,200 $134,600 $54,600

Mixed-content cells

Property Value Description
font-size var(--font-size-base) Base text size for body copy
line-height var(--leading-normal) Comfortable reading rhythm for most text
letter-spacing var(--tracking-normal) Default tracking for body text

Wide / scrolling tables

A table wider than its container would overflow the page, so wide tables are wrapped in a focusable, horizontally-scrollable region (div.table-scroll). In MDX this happens automatically — markdown tables are remapped to the <WrappedTable> component — so I never have to think about it while writing. Anywhere else (a plain Astro page or raw HTML) I wrap the table in <WrappedTable> myself, or add the div.table-scroll by hand.

Quarter Revenue Expenses Profit Customers Retention NPS Notes
Q1 $124,500 $98,200 $26,300 1,204 92% 54 Steady growth driven by inbound leads.
Q2 $156,800 $112,400 $44,400 1,488 94% 61 Launched the new pricing tiers mid-quarter.
Q3 $189,200 $134,600 $54,600 1,712 93% 58 Slight churn uptick after the pricing change.

Footnotes

Footnotes are generated automatically from markdown: [^label] places a reference and [^label]: … defines its content. Three parts work together — the superscript reference marker (set in small caps and the accent colour), the footnotes section at the bottom with back-reference arrows, and a progressive enhancement that shows a footnote inline just below the current paragraph when you click its marker. The client-side JavaScript for that popup is only served when a page actually has footnotes; without it the marker simply jumps to the footnotes section, so everything still works.

Good writing often benefits from asides and references1. Footnotes let you add depth without interrupting the main flow of your prose.

The footnote reference appears as a superscript number2. Clicking it can either jump to the footnotes section at the bottom, or—with the inline footnotes enhancement—show the content right below the current paragraph.

  • We have a list.
  • This is a list3 with some footnotes in it.
  • And of course another list.

Multiple footnotes work naturally throughout a piece4. They’re particularly useful for citations, clarifications, or tangential5 thoughts that would otherwise break the reader’s concentration.

Footnotes

  1. This is contextual information that supports the main point but isn’t essential to understanding it. Footnotes keep your prose clean while still providing depth for curious readers.

  2. The superscript styling uses small caps and the accent colour to make footnote references visually distinct but not distracting.

  3. Here is a footnote within the list.

  4. You can have as many footnotes as you need. The numbering is handled automatically by the markdown processor.

  5. Multiple footnotes in a single paragraph.

Realistic example

Here's an example of a bunch of these elements working together.

The craft of small decisions

Good design rarely announces itself. It shows up in the accumulation of small choices — the weight of a heading, the rhythm between paragraphs, the moment a link invites you somewhere new. None of it is loud, and that's the point. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

I keep coming back to a single idea: the interface should get out of the way of the words. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. You can read more about how I think about this on the Astro website, which is where a lot of these patterns started for me.

The details are not the details. They make the design. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore.

Where it adds up

A few places those small decisions compound, none of them glamorous on their own. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam:

  • Setting type that's comfortable to read for more than a paragraph
  • Keeping inline code legible without breaking the line
  • Letting lists and tables carry structure the prose can't

That last one matters more than it sounds. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. The CSS does most of the heavy lifting here, and almost none of it needs a single class.

A process, more or less in order

I don't follow this rigidly, but when a piece is fighting me it usually means I've skipped a step. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur:

  1. Start with the content, in plain markdown, before touching a single style. It is far easier to see whether an argument holds together when nothing is dressed up — quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur.
  2. Read the whole thing aloud, slowly, and mark every place you stumble. Each stumble is usually a structural problem wearing a stylistic disguise, and at vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti.
  3. Only then start shaping the type, working outwards from the body text:
    1. Fix the measure and the leading until a long paragraph feels effortless
    2. Tune the headings so the hierarchy is obvious at a glance, even with the text blurred
    3. Last of all, the small touches — links, emphasis, the occasional highlight
  4. Ship it, then read it again the next morning on a real device. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.

A quick comparison

It helps to see the trade-offs side by side. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus.

Approach Effort Payoff
Sensible defaults Low Consistent everywhere, for free
Per-page tweaks Medium Fits the moment, drifts over time
Bespoke layout High Memorable when it's worth it

One more thing

A worked example, because abstractions only go so far. If a layout holds at 320px wide and still holds when you bump the root size up by two three steps, it'll survive most of what readers throw at it. Press Cmd + P to print this and the type still holds together — the same rules that work on screen mostly work on paper too. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat.

And the maths underneath is hardly exotic: a scale where each step is roughly 1.2n the one below it, with the body sitting at the third second rung. That resilience is the result of decisions made long before anyone reads a word — et harum quidem rerum facilis est et expedita distinctio.