On this page
This opening paragraph sets the baseline: default font size, line height, color, and measure. It's intentionally long so you can judge reading rhythm and line length on desktop and mobile. A second sentence confirms spacing.
This second paragraph (after a blank line) checks p + p spacing.
1. Headings#
The renderer styles h1–h4; h5/h6 are smaller (h6 is an eyebrow label).
Every heading also gets an id + a hover # anchor, and h2/h3 feed the TOC.
H1 — page-title scale#
H2 — section scale#
H3 — subsection scale#
H4 — minor heading#
H5 — small#
H6 — eyebrow label#
2. Inline formatting#
Bold, italic, bold italic, strikethrough, and inline code.
Combined: bold with code inside, bold link,
code with *no* emphasis (emphasis shouldn't apply inside code).
Escapes: literal asterisks *not italic*, a literal backtick ` and a literal hash # at line start. HTML entities: © — → & <tag>.
Hard line break (trailing backslash) —
this sits directly below the previous line.
Soft wrap: this line
and this line collapse into ONE paragraph (no break — breaks is off).
3. Links#
- Inline: internal link and external link
- With title: hover me
- Autolink (bare URL in angle brackets): https://github.com
- Reference-style: the reference link points the same place.
4. Blockquotes#
A single-line quote — check left border, tint, and italic styling.
A multi-paragraph quote.
Second paragraph inside the quote (
blockquote > * + *spacing).A nested blockquote one level deeper.
— Attribution line
5. Lists#
Unordered + nested#
- First item
- Second item, deliberately long so a wrapped bullet's alignment against the
marker indent is visible across the column width
- Nested level two
- Nested level three
- Nested level two
- Back to level one
Ordered, custom start, nested mixed#
- Starts at three (custom start)
- Fourth
- Sub-step a
- Sub-step b
- mixed unordered child
- Fifth
Loose list (paragraph spacing between items)#
First item with its own paragraph.
A second paragraph inside the same list item.
Second item, also loose.
Task list (GFM checkboxes)#
- Completed task (aria-label "Completed")
- Incomplete task (aria-label "Not completed")
- Nested task lists:
- sub-task done
- sub-task pending
6. Tables (GFM)#
Basic table with the original gap (now styled): header fill, borders, zebra, hover, horizontal scroll on overflow.
| Feature | Supported | Notes |
|---|---|---|
| Headings | Yes | h1–h4 tuned, h5/h6 smaller |
| Tables | Yes | styled |
| Footnotes | Yes | numbered + named |
| Math | Yes | KaTeX, server-rendered |
| Diagrams | Yes | Mermaid, lazy-loaded |
Alignment + rich cells (inline formatting, code, links, math inside cells):
| Left | Center | Right |
|---|---|---|
| bold cell | code cell |
link |
| 🚀 | ||
| long left text | centered | 1,234.56 |
7. Code#
Inline: call renderArticle(source) then hydrate.
Fenced with language — note the lang label + Copy button (top-right):
// TypeScript via Shiki (github-dark)
import { renderArticle } from "@/lib/markdown"
export async function preview(src: string): Promise<string> {
const { html } = await renderArticle(src)
return html.trim()
}Several grammars to sanity-check highlighting breadth:
def fib(n: int) -> int:
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a.prose table { border-collapse: collapse; width: 100%; }
.prose th { background: var(--bg-soft); }SELECT title, published_at
FROM articles
WHERE status = 'PUBLISHED'
ORDER BY published_at DESC;{ "name": "articles-goashins", "private": true }- const theme = "github-light"
+ const theme = "github-dark"No-language block (no lang label, no colors):
plain preformatted text
indentation preservedLong line to test horizontal scroll inside the block:
docker run --rm -it -v "$(pwd)":/app -w /app -e NODE_ENV=production node:22-alpine sh -c "npm ci && npm run build && npm run start"8. Callouts#
Reminder: callout bodies render raw — markdown inside them does NOT format, only inline HTML does. The three styled types:
Unknown type (falls back to the info icon, class callout-note):
9. Footnotes#
Superscript references1 jump to a list at the bottom and back. Named labels work2, and a footnote can be cited twice1.
10. Emoji shortcodes#
:tada: → 🎉, :rocket: → 🚀, :white_check_mark: →
✅, :warning: → ⚠️, :bulb: → 💡. Emoji in a
heading works too (see below). Unknown names stay literal: :not_a_real_emoji:.
Emoji in a heading ✨#
Confirms shortcodes resolve inside headings (and the TOC text).
11. Math (KaTeX)#
Inline: mass–energy $E = mc^2$; golden ratio $\varphi = \frac{1+\sqrt{5}}{2}$; a sum .
Display equations:
A matrix:
Aligned / cases:
Wide expression (tests horizontal scroll):
12. Diagrams (Mermaid)#
These render client-side (lib loads only on pages with a diagram); the source stays visible as a fallback until rendered.
Flowchart:
flowchart LR
A[Write markdown] --> B{Has diagram?}
B -- yes --> C[Lazy-load mermaid]
B -- no --> D[Ship nothing extra]
C --> E[Render SVG]Sequence diagram:
sequenceDiagram Reader->>Server: request article Server->>Server: renderArticle() Server-->>Reader: HTML + KaTeX
Pie chart:
pie title Time spent "Writing" : 45 "Debugging" : 30 "Reading docs" : 25
Class diagram:
classDiagram Article "1" --> "*" Category Article : +String title Article : +publish()
13. Collapsible details#
Native <details>/<summary> (raw HTML) — styled, good for FAQs / "show more":
Click to expand
Hidden content with bold, a list, and code:
- point one
- point two
inline code works here because this is real markdown (unlike callout bodies).
14. Images#
Inline (Cloudinary URLs get f_auto,q_auto,c_limit,w_1200; all lazy-load):
With a caption — a standalone image whose markdown title is set renders as a
<figure> with the title shown as a visible <figcaption> beneath it:
15. YouTube auto-embed#
A bare YouTube URL alone on a line becomes a responsive iframe:
16. X / Twitter auto-embed#
A bare X (or twitter.com) status URL alone on a line becomes an embedded post. An embed URL placed inline in a sentence — like https://x.com/jack/status/20 — stays an ordinary clickable link.
17. CodePen auto-embed#
A bare CodePen pen URL alone on a line becomes an embedded pen (result tab):
18. Horizontal rule#
Above the rule.
Below the rule.
19. Mixed real-world block#
Heterogeneous elements in sequence, to judge vertical rhythm:
Most work happens in renderArticle. The pipeline:
- Preprocess bare YouTube / X / CodePen URLs into embeds.
- Tokenize with
marked(GFM + callout + emoji extensions). - Highlight code via Shiki; render math with KaTeX.
- Post-process headings → ids, anchors, and the TOC.
The code, it turns out, was the easy part.
20. NOT supported (expected to render literally)#
These have no plugin, so they should appear as plain text — a gap here is intentional, not a bug:
- Definition lists:
Termthen: definition(renders as plain text) - Superscript
x^2^/ subscriptH~2~O(no extension) - Highlight
==marked==(no extension) - Abbreviations
*[HTML]: HyperText…(no extension)
That's the full set. This closing paragraph confirms spacing after the final section and before the end of the article body.


