Markdown & MDX reference
Sample content, not real writing. Every markdown element plus a live React island and a live Vue island — rendered in one place so the whole content pipeline stays visible to itself.
Good design is as little design as possible. Less, but better — because it concentrates on the essential aspects, and the products are not burdened with non-essentials.
— Dieter Rams, 1976
This is a reference post, not real writing. Its job is to render every markdown element the prose styles need to handle, plus prove that MDX can embed interactive islands from any framework. When I tune the theme I can see everything at once and catch anything that looks wrong. The companion piece — Design tokens — does the same for colors and the semantic typography scale.
The text is meaningful rather than lorem ipsum because lorem ipsum reads like static and it’s hard to judge rhythm on noise. Treat the quotes and the commentary as placeholder content that happens to be worth reading — swap either out when real writing arrives.
Headings breathe differently at each level
Headings aren’t just about being big. Each level needs enough room above it to feel like the start of a new idea, and just enough below it to belong to the text that follows. When that rhythm breaks, the page feels stacked instead of flowing.
Section heading, slightly quieter
The H3 does the real work on most articles. It’s where I group sub-ideas without needing to escalate the visual weight.
Fourth-level, for tight subdivisions
Below this the hierarchy usually stops mattering.
Five deep is almost always a sign the structure is wrong
And six is basically never used
Inline formatting you’ll actually see
Paragraph text mixes a bunch of small things: bold for the word you want the eye to catch, italic for emphasis that shouldn’t interrupt, inline code for names of things, and hyperlinks for places you can go next. Occasionally there’s a struck-through correction that someone thought about, then changed their mind.
The real test is whether all of those can live in the same sentence: I wrote const count = items.length while thinking “there must be a better name”, and a colleague linked me to an article about naming conventions that was honestly kind of overengineered thoughtful.
Blockquotes carry weight
A blockquote is the page saying “pause here; someone else said this better.”
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
— Antoine de Saint-Exupéry
Inside a quote, normal styles still apply — you can have bold, italic, code, or a link. What changes is the weight: the text is indented, the left edge is marked, and the whole block asks the reader to slow down.
Sometimes a quote spans multiple paragraphs.
The second paragraph keeps the indentation and the border. It should feel like one continuous passage, not two separate fragments sitting next to each other.
Lists are where rhythm matters most
An unordered list for things that don’t need an order:
- Items can be short.
- Or they can run longer, wrapping onto a second line to test that the bullet alignment stays correct and the leading doesn’t collapse when a single entry becomes a paragraph on its own.
- Nested lists exist for when the outer idea has sub-ideas:
- like this one
- and this one
- which can go as deep as you want, though usually you only want one or two
- And then back to the outer level.
An ordered list, for steps that need to happen in order:
- Understand the problem before touching code.
- Find the smallest change that makes the failing test pass.
- Refactor only after you’re sure what “right” looks like.
Make it work, make it right, make it fast.
— Kent Beck
Task lists track things you can actually finish:
- Write the first post
- Make sure headings work at every level
- Add an illustration when I find one worth using
- Convince myself this is finished
Code, because this is a developer’s site
Inline code like git rebase -i HEAD~3 lives comfortably inside a sentence. For anything more than a few characters, fenced blocks earn their space:
// The semantic typography scale this site uses.
// Every token maps to both a font-size and a matching line-height,
// so there's a single source of truth — no magic numbers anywhere.
export const typography = {
micro: { size: 10, lineHeight: 1.4 }, // tag pills, type badges
meta: { size: 11, lineHeight: 1.5 }, // dates, mono meta labels
caption: { size: 14, lineHeight: 1.55 }, // card descriptions
body: { size: 16, lineHeight: 1.65 }, // main reading register
card: { size: 17, lineHeight: 1.35 }, // card titles
heading: { size: 18, lineHeight: 1.3 }, // section headings
name: { size: 20, lineHeight: 1.15 }, // hero name
lead: { size: 22, lineHeight: 1.5 }, // hero tagline
} as const;
Shell commands in their own language:
pnpm build
pnpm lint
pnpm format:check
CSS, since half of this post is about CSS:
@theme {
--text-body: 16px;
--text-body--line-height: 1.65;
}
Programs are meant to be read by humans and only incidentally for computers to execute.
— Donald Knuth
Tables for structured data
Tables are tricky to get right on the web. A good one stays readable at narrow widths and doesn’t fight with the prose around it.
| Token | Size | Line height | Used for |
|---|---|---|---|
text-micro | 10px | 1.4 | Tag pills, type badges |
text-meta | 11px | 1.5 | Dates, mono meta labels |
text-caption | 14px | 1.55 | Card descriptions |
text-body | 16px | 1.65 | Main reading register |
text-card | 17px | 1.35 | Post and project card titles |
text-heading | 18px | 1.3 | Section headings |
text-name | 20px | 1.15 | Hero name |
text-lead | 22px | 1.5 | Hero tagline |
That’s the semantic scale. Every text-[Npx] in the codebase was replaced with one of these eight tokens, so tuning typography is a one-file change.
Horizontal rules and breathing room
A horizontal rule is the page saying “this idea is finished; something different is coming.”
After a rule, the next section feels like a new beat. Which is the whole point.
Interactive islands
The difference between .md and .mdx is that MDX lets you import components straight from any framework Astro supports, drop them into the middle of prose, and hydrate them on the client with a directive. Below are two identical counters — one built in React, one built in Vue — both rendered inside this post and both fully interactive. They use the same CSS variables as the rest of the site, so they flip with the theme toggle.
Notice the uppercase eyebrow label inside each counter. React shows react · useState; Vue shows vue · ref. Both islands are lazily hydrated via client:visible, so they only boot when they scroll into view.
The MDX source for this section looks like this:
import CounterReact from "@/components/demo/CounterReact.tsx";
import CounterVue from "@/components/demo/CounterVue.vue";
<CounterReact client:visible />
<CounterVue client:visible />
That’s it — no route setup, no hydration boilerplate, no per-post build config. The @astrojs/mdx, @astrojs/react, and @astrojs/vue integrations registered in astro.config.ts handle the rest.
When to reach for an island
Most posts don’t need this. Prose, code blocks, and tables cover 95% of writing. Reach for an island when:
- A concept is genuinely clearer when the reader can poke at it (sliders, toggles, live previews).
- Static text would require three paragraphs to describe what one small interactive component shows in one beat.
- You need a demo of a component you’re also shipping in the app — reusing the real thing is cheaper than mocking screenshots.
And when in doubt, stay with prose. An island that isn’t earning its hydration cost is a worse version of a paragraph.
Admonitions for inline explanations
Sometimes a term or phrase deserves a short explanation without breaking the reader’s flow. The A popover that appears on click, giving extra context without leaving the page. Uses the native Popover API — no JavaScript positioning library needed. component handles this: click the dotted-underline trigger and a popover appears with the explanation.
When a concept has both an explanation and a reusable prompt behind it, the admonition shows tabs. For example, Semantic tokens like text-body or text-heading map to a specific font-size and line-height pair. They replace magic numbers so the entire typography scale can be tuned from one file. List every semantic typography token used in this site, their pixel sizes, and where each one appears in the component tree. in this site uses eight named tokens instead of raw pixel values.
A footnote, for completeness
Sometimes a small point wants a small escape hatch.1 A good footnote is quiet — it doesn’t interrupt the main line of thought, but it’s there for the reader who wants more.
Closing
Simplicity is prerequisite for reliability.
— Edsger Dijkstra
That’s every element I can think of, rendered in context. If something here looks wrong in either light or dark mode, the fix goes into src/styles/global.css — and this post will show the result immediately on reload. That’s the whole job of a validation document: make the system visible to itself.
Footnotes
-
Like this one. Footnotes are rendered at the bottom of the article with a return link back to where they were referenced. ↩