WFD 17living

writings for discussion

process
·10 min read·Ryana May Que

Writing things down is important. It forces you to think clearly about what you're building, what you've learned, and what you still don't understand. A half-formed idea in your head stays half-formed until you try to put it into sentences.

Important

This WFD supersedes WFD 12. WFD 12 remains in the archive as a legacy document. WFDs are never deleted, even when superseded, because they're a record of how things were at the time they were written.

WFD (Writing for Discussion) is how I capture that process. It's a document format inspired by Oxide's RFD system, which itself draws from the original spirit of the IETF Request for Comments:

Notes are encouraged to be timely rather than polished. Philosophical positions without examples or other specifics, specific suggestions or implementation techniques without introductory or background explication, and explicit questions without any attempted answers are all acceptable. The minimum length for a note is one sentence.

The bar for writing a WFD is intentionally low. If you can explain it in a sentence, that's enough to start. Polish comes later, or not at all.

when to write a wfd

Note

This document says "you" a lot. The WFD format isn't proprietary or exclusive to this site. If the structure works for you, take it. Adapt it. Make it yours. That's the whole point.

Anything worth remembering is worth writing down. Some examples:

  • debugging a problem that took more than an hour
  • learning something that surprised you
  • building something and wanting to document the decisions
  • an opinion about tools, patterns, or process
  • something you'd want to reference later

There's no approval process. No review board. No minimum length beyond one sentence. If the idea matters to you, write it down.

writing conventions

don't repeat what another WFD already said

if something has been covered in a previous WFD, reference it instead of restating it. a link to the relevant WFD is always better than a paraphrase. this keeps documents focused and avoids content that drifts out of sync when the original gets updated.

drafts should use arguments for/against

when a WFD is in draft and hasn't reached a decision yet, lay out the tension explicitly:

markdown
arguments against:

- reason one
- reason two

arguments for:

- reason one
- reason two

this makes the open question visible. the reader can see exactly what's unresolved and why a decision hasn't been made yet. see WFD 18 and WFD 19 for examples.

wfd metadata and state

Every WFD starts with YAML frontmatter:

yaml
---
number: 17
title: "writings for discussion"
updated: "2026-02-11T00:35:00"
state: "published"
labels: ["process"]
excerpt: "short description of what this document covers."
---

The fields:

field required description
number yes sequential WFD number. WFD 1, WFD 2, WFD 17. no gaps required.
title yes lowercase. short. descriptive.
updated yes ISO 8601 datetime with time. the only timestamp that matters.
state yes current lifecycle state (see below).
labels yes array of tags. keep it to three or fewer. use [] for none.
excerpt yes one or two sentences. shows up in search results and the listing page.

states

A WFD can be in any of these states:

state meaning
draft placeholder. not ready for anyone to read.
discussion actively being written or revised. feedback welcome.
published the idea is formed and the document says what it means to say.
committed i'm committing to this post. it's not going to change significantly.
living actively maintained. will be kept up to date on a best effort basis.
legacy written before the WFD system existed. migrated from the old blog format. may not follow current conventions.
abandoned the idea didn't pan out. kept for the record.
Tip

States are freeform text. These are suggestions. I might not even follow them myself because I felt like a word meant something different that day, or because I wanted a state that doesn't exist yet. Type whatever makes sense.

Unlike Oxide's RFD process, there's no branch-per-document workflow, no pull request for discussion, and no formal review. Documents are markdown files in a content directory. State changes are just a frontmatter edit.

numbering

WFDs are numbered sequentially. WFD 1, WFD 2, WFD 17. No dashes, no leading zeros in the display. The number is permanent. If a WFD is abandoned, the number stays taken.

updated, not created

There is no creation date. Only updated. If you come back six months later and rewrite half the document, the timestamp reflects that. The creation date is noise. What matters is when the document was last touched.

the markdown engine

The rendering pipeline is custom. I didn't want to use an off-the-shelf markdown engine like MDX, Contentlayer, or Markdoc because they all impose opinions about how content should be structured, and I wanted full control over what syntax is available and how it renders.

The engine is built on remark and rehype for the base markdown-to-HTML conversion, with shiki for syntax highlighting. Everything else is custom parsing on top.

The parser works in a single pass over the raw markdown lines. It maintains state machines for code blocks, admonitions, HTML blocks, and media groups. Each line is checked against a series of regex patterns in order:

  1. If we're inside a fenced code block, accumulate lines until the closing fence.
  2. If we're inside an HTML block (<details>, <div>, etc.), pass lines through to remark as-is.
  3. If we're inside an admonition (> [!NOTE]), accumulate continuation lines that start with >.
  4. Check for code block openings, heading patterns (for TOC extraction), admonition starts, component directives, and media lines.
  5. If a line matches ![alt](url), @gif[alt](url), @video[caption](url), or @embed[title](url), it's collected into a media group.
  6. Consecutive media lines without a non-empty separator become a single carousel.
  7. Everything else is accumulated as text and flushed through remark when a non-text block is encountered.

The output is an array of typed content blocks (text, code, media, mermaid, admonition, component) that React components consume directly. There's no intermediate AST transformation or plugin chain beyond what remark and rehype provide for the text blocks.

This means adding new syntax is straightforward: add a regex to parseMediaLine or a new block type to the line scanner, add a corresponding React component, and it's done. The @gif, @video, @embed, and @component syntaxes were all added this way.

The tradeoff is that the parser is imperative and stateful rather than declarative. It's not as elegant as a proper AST visitor pattern, but it's simple to debug and easy to extend. Every feature in this document was added in under an hour.

writing format

WFDs are written in markdown with some extensions. Everything below is available in any WFD.

text

Standard markdown: **bold**, *italic*, ~~strikethrough~~, `inline code`, [links](url).

bold text, italic text, bold and italic, strikethrough, inline code, a link

headings

Use ## through ####. These are extracted for the table of contents sidebar on desktop and the bottom sheet on mobile. # is reserved for the document title.

code blocks

Fenced with triple backticks. Specify the language for syntax highlighting.

markdown
```typescript
const x: number = 42;
```
typescript
const x: number = 42;

tables

Standard GFM tables. They render with rounded borders and a semi-transparent background.

markdown
| header | header |
|--------|--------|
| cell   | cell   |
feature syntax example
bold **text** bold
italic *text* italic
code `code` code

blockquotes

markdown
> quoted text goes here.

this is a blockquote. it can span multiple lines.

admonitions

GitHub-style alerts with five types:

markdown
> [!NOTE]
> useful information.

> [!TIP]
> helpful advice.

> [!IMPORTANT]
> key information.

> [!WARNING]
> urgent information.

> [!CAUTION]
> risk of negative outcome.

Custom titles:

markdown
> [!NOTE/Custom Title]
> content with a custom title.
Note

useful information that readers should know, even when skimming.

Tip

helpful advice for doing things better or more easily.

Important

key information that readers need to achieve their goal.

Warning

urgent information that needs immediate attention to avoid problems.

Caution

advises about risks or negative outcomes of certain actions.

Custom Title Here

admonitions can have custom titles instead of the default.

images

markdown
![alt text](url)

Consecutive images on adjacent lines automatically group into a scrollable carousel. Images display at their natural aspect ratio with a max-height constraint.

gifs

markdown
@gif[description](url)

videos

markdown
@video[caption](url)
markdown
@embed[title](url)

mermaid diagrams

markdown
```mermaid
graph TD
    A[Start] --> B{Decision}
    B -->|Yes| C[Done]
    B -->|No| D[Retry]
```

Supports flowcharts, sequence diagrams, entity relationship diagrams, and anything else mermaid supports.

Loading diagram...
Loading diagram...

collapsible sections

markdown
<details>
<summary>click to expand</summary>

hidden content here. supports any markdown.

</details>

hidden content goes here. supports any markdown:

  • lists work fine
  • bold text renders correctly
  • even code: const x = 1

react components

markdown
@component[ComponentName]
@component[ComponentName]({"prop": "value"})

Embeds interactive React components directly in the document.

lists

Unordered (-), ordered (1.), and task lists (- [ ] / - [x]).

  • first item
  • second item
    • nested item
  • third item
  1. first step
  2. second step
  3. third step
  • uncompleted task
  • completed task
  • another pending item

quick reference

element syntax
bold **text**
italic *text*
strikethrough ~~text~~
code `code`
link [text](url)
image ![alt](url)
heading ## heading
quote > quote
list - item
ordered list 1. item
task list - [ ] task
table | col |
code block ```
mermaid ```mermaid
note > [!NOTE]
video @video[caption](url)
gif @gif[caption](url)
embed @embed[title](url)
component @component[Name]
collapsible <details>

want to talk about this? hey@ryanaque.com