Alpha: Ships on npm at 7.0.0-alpha.07.0.0-alpha.0
post-list
A blog index: a head, then the posts as ruled rows.
{
"kind": "section",
"shell": {
"inset": "listing",
"density": "listing"
},
"block": {
"type": "post-list",
"head": {
"eyebrow": "Latest",
"title": "Recent issues",
"lede": "The newest three, and issue 1. Issue 1 is free to read; the rest come with the paid letter."
},
"read": "Read",
"posts": [
{
"href": "/subscribe",
"tag": "Pricing",
"dateline": {
"date": "2026-10-20",
"read": {
"minutes": 5,
"unit": "min read"
}
},
"title": "Pricing a rush job",
"dek": "A rush costs your evenings twice: the work and what it pushes out. Price both, and say the rush fee first."
},
{
"href": "/subscribe",
"tag": "Work",
"dateline": {
"date": "2026-10-13",
"read": {
"minutes": 7,
"unit": "min read"
}
},
"title": "When to quit the day job",
"dek": "Three numbers decide it: months of costs saved, months of revenue booked, and the hours the side work takes."
},
{
"href": "/subscribe",
"tag": "Tax",
"dateline": {
"date": "2026-10-06",
"read": {
"minutes": 6,
"unit": "min read"
}
},
"title": "The year-end checklist, early",
"dek": "Twelve items to clear in October, so January holds a return and not a search for receipts."
},
{
"href": "/issues/the-first-invoice",
"tag": "Money",
"dateline": {
"date": "2026-05-12",
"read": {
"minutes": 6,
"unit": "min read"
}
},
"title": "The first invoice",
"dek": "What goes on it, when to send it, and the one line that gets it paid on time."
}
]
}
}
{
"title": "post-list",
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "post-list"
},
"head": {
"type": "object",
"properties": {
"eyebrow": {
"type": "string",
"minLength": 1,
"maxLength": 28
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"lede": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"required": [
"eyebrow",
"title",
"lede"
],
"additionalProperties": false
},
"read": {
"type": "string",
"minLength": 1,
"maxLength": 16
},
"posts": {
"minItems": 1,
"maxItems": 24,
"type": "array",
"items": {
"type": "object",
"properties": {
"href": {
"type": "string",
"minLength": 1,
"maxLength": 160
},
"tag": {
"type": "string",
"minLength": 1,
"maxLength": 20
},
"dateline": {
"type": "object",
"properties": {
"date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"read": {
"type": "object",
"properties": {
"minutes": {
"type": "integer",
"minimum": 1,
"maximum": 180
},
"unit": {
"type": "string",
"minLength": 1,
"maxLength": 12
}
},
"required": [
"minutes",
"unit"
],
"additionalProperties": false
}
},
"required": [
"date"
],
"additionalProperties": false
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"dek": {
"type": "string",
"minLength": 1,
"maxLength": 360
}
},
"required": [
"href",
"tag",
"dateline",
"title",
"dek"
],
"additionalProperties": false
}
}
},
"required": [
"type",
"head",
"read",
"posts"
],
"additionalProperties": false
}When to use
Use a post list for a blog index or a changelog. It holds a head and one to 24 posts, each with a tag of 20 characters, a dateline, a title of 80 and a dek of 360.
Write the dek as what the reader learns. For products or listings, use product-list.
Date every post and keep the newest first. Tag by topic, in one word where it fits.
Use instead
product-list: products, not posts
The record
{
"kind": "section",
"shell": { "inset": "listing", "density": "listing" },
"block": {
"type": "post-list",
"head": {
"eyebrow": "Latest",
"title": "Recent issues",
"lede": "The newest three, and issue 1. Issue 1 is free to read; the rest come with the paid letter."
},
"read": "Read",
"posts": [
{
"href": "/subscribe",
"tag": "Pricing",
"dateline": {
"date": "2026-10-20",
"read": { "minutes": 5, "unit": "min read" }
},
"title": "Pricing a rush job",
"dek": "A rush costs your evenings twice: the work and what it pushes out. Price both, and say the rush fee first."
},
{
"href": "/subscribe",
"tag": "Work",
"dateline": {
"date": "2026-10-13",
"read": { "minutes": 7, "unit": "min read" }
},
"title": "When to quit the day job",
"dek": "Three numbers decide it: months of costs saved, months of revenue booked, and the hours the side work takes."
},
{
"href": "/subscribe",
"tag": "Tax",
"dateline": {
"date": "2026-10-06",
"read": { "minutes": 6, "unit": "min read" }
},
"title": "The year-end checklist, early",
"dek": "Twelve items to clear in October, so January holds a return and not a search for receipts."
},
{
"href": "/issues/the-first-invoice",
"tag": "Money",
"dateline": {
"date": "2026-05-12",
"read": { "minutes": 6, "unit": "min read" }
},
"title": "The first invoice",
"dek": "What goes on it, when to send it, and the one line that gets it paid on time."
}
]
}
}Smallest that parses
{
"type": "post-list",
"head": {
"eyebrow": "Latest",
"title": "Recent issues",
"lede": "The newest three, and issue 1. Issue 1 is free to read; the rest come with the paid letter."
},
"read": "Read",
"posts": [
{
"href": "/subscribe",
"tag": "Pricing",
"dateline": { "date": "2026-10-20" },
"title": "Pricing a rush job",
"dek": "A rush costs your evenings twice: the work and what it pushes out. Price both, and say the rush fee first."
}
]
}Fields and bounds
From @mdxui/site@7.0.0-alpha.0. A count reads as min–max × item; a string bound is its maximum length in characters, and links its refusal.
| Prop | Type and bound | Required |
|---|---|---|
head | object | yes |
head.eyebrow | string ≤28 | yes |
head.title | string ≤64 | yes |
head.lede | string ≤200 | yes |
read | string ≤16 | yes |
posts | 1–24 × object | yes |
posts[].href | string ≤160 | yes |
posts[].tag | string ≤20 | yes |
posts[].dateline | object | yes |
posts[].dateline.date | string (pattern) | yes |
posts[].dateline.read | object | no |
posts[].title | string ≤80 | yes |
posts[].dek | string ≤360 | yes |
Variants and counts
One layout, from 1 post to 24.
One record in five Themes
Shells
In three Businesses
Carried by the blog Template. Seen on pages/sidework/home.json, section 4.
Do and do not
A dek that says what the reader learns.
A dek that repeats the title gives the reader no reason to open the post.
A tag of one or two words.
"tag": "Windows and draft planning"SectionSchema.safeParse(record)block.posts.0.tag26 characters
A tag past 20 characters is refused at its path.
Accessibility
| Check, on the exemplar render | Limit | Now | Pass |
|---|---|---|---|
| Skipped heading levels | 0 | 0 | yes |
| Unnamed nav landmarks | 0 | 0 of 0 | yes |
| Scroll regions a keyboard cannot reach | 0 | 0 of 0 | yes |
| Copy controls that announce the result | all | 0 of 0 | yes |
| Images and icons with no text alternative | 0 | 0 | yes |
Text pairs below AA across the 5 Themes and every scheme they emit: 5. See Render checks.
- Landmark The list sits in
main. It adds no landmark. - Heading level The head title renders as the page
h1and each post title as anh2. - Focus order Each post is one link, in record order.