
Markdown is a lightweight and easy-to-use syntax for styling your writing. This page is both a reference and a test page: everything below should render correctly on this site.
Basic syntax
Headings
Use one to six hashes for headings. The table of contents on the right is generated from them automatically, including nested levels.
A slightly smaller heading
And a smaller one still
Emphasis
Italic text and bold text, together will be bold italic text. You can also use strikethrough and inline code.
Links
A link to somewhere opens in a new tab when it points outside the site. Bare URLs become links too: https://astro-pure.js.org
Blockquotes
A good sentence is a small architecture.
It has a doorway, a window, and just enough room to move.
Lists
Unordered
- The first item
- The second item
- A nested item
- Another nested item
- The third item
Ordered
- Write it down.
- Read it aloud.
- Cut the sentence that only exists to impress.
- Publish.
Task list
- Set up the design tokens
- Write the content pipeline
- Add a proper sitemap for images
- Stop rewriting the introduction
Tables
| Feature | Supported | Notes |
|---|---|---|
| GFM tables | Yes | Aligned columns, header styling |
| Task lists | Yes | Via remark-gfm |
| Footnotes | Yes | See below |
| Math | Yes | KaTeX inline and block |
| Code highlighting | Yes | Shiki, dual themes |
| MDX components | Yes | In .mdx posts |
Footnotes
Markdown footnotes are surprisingly useful for small asides1 that would interrupt the flow of a paragraph.
Code
const posts = await getVisiblePosts()
const latest = posts.slice(0, 5)
return latest.map((post) => ({
title: post.title,
url: `/memory/${post.slug}`,
minutes: post.minutesRead
}))jsexport async function getPostBySlug(slug: string): Promise<Post | undefined> {
await ensureSchema()
const [row] = await getDb().select().from(posts).where(eq(posts.slug, slug)).limit(1)
return row ? rowToPost(row) : undefined
}tspnpm install
pnpm db:setup
pnpm devbash{
"name": "rememoi",
"private": true,
"scripts": {
"dev": "pnpm db:setup && next dev",
"build": "pnpm db:setup && next build"
}
}jsonA longer block collapses automatically once it passes fifteen lines, keeping very long snippets from taking over the page:
type ImageMeta = {
width: number
height: number
color?: string
}
interface PostInput {
title: string
slug: string
description: string
body: string
tags: string[]
heroImage?: ImageMeta
}
function normalizeTags(tags: string[]): string[] {
return Array.from(new Set(tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean)))
}
function readingTime(text: string): string {
const words = text.trim().split(/\s+/).length
return `${Math.max(1, Math.ceil(words / 200))} min`
}
export function summarize(input: PostInput) {
return {
...input,
tags: normalizeTags(input.tags),
minutesRead: readingTime(input.body)
}
}tsImages

Images are lazy-loaded, get rounded corners from the prose styles, and can be opened in a zoomable lightbox.
Math
Inline math works: , and so does a display equation:
A slightly more useful example from information theory:
Horizontal rules
That is the full set. If something looks wrong, it is probably a bug worth fixing.
Footnotes
-
Like this one. It renders at the bottom of the article with a link back to the reference. ↩
