Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ These mirror `CONTRIBUTING.md`. Enforce them in your own writing and when review
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/content/spec/<cat>/<slug>.md` | Spec content. Edit here. |
| `src/content.config.ts` | Content collection schema. Edit if adding a field. |
| `src/content/considered/<slug>.md` | Hand-curated register of standards deliberately **not** specced, with the reason and what would reverse it. Rendered at `/considered/`. Not derived. |
| `src/lib/site.ts` | Site metadata + the canonical category list. |
| `src/layouts/BaseLayout.astro` | HTML shell, head, dialog for ⌘K search, Plausible (PROD only). |
| `src/layouts/SpecLayout.astro` | Spec page wrapper; emits TechArticle + BreadcrumbList JSON-LD; advertises Markdown alt via `markdownUrl`. |
Expand Down Expand Up @@ -153,7 +154,32 @@ The same PR must do **one** of:

Also update the [api-catalog Linkset](public/.well-known/api-catalog) if the capability adds a discoverable resource, and the global `Link` header in [`public/_headers`](public/_headers) if it has a registered IANA `rel`. Both should match what the new spec page describes.

The reverse holds too: **do not promote a convention to spec status without us shipping a working implementation first.** The site is the proof-of-feasibility. If the underlying convention turns out non-existent or defunct (see the `/.well-known/ai.txt` deletion history), remove the asset _and_ the spec page in the same PR — don't leave the page documenting something we no longer ship.
If the underlying convention turns out non-existent or defunct (see the `/.well-known/ai.txt` deletion history), remove the asset _and_ the spec page in the same PR — don't leave the page documenting something we no longer ship.

### Speccing and shipping are separate decisions

**We do not have to ship something to spec it.** The site is a worked example wherever that is useful, not a gate. Plenty of good advice does not apply to a static, cookieless, six-page-of-plumbing site: a page can be right for the sites reading it and irrelevant here. When that is the case, write the page and skip the implementation — but **say so on the page**, in one line, with the reason. "This site does not ship it: we are static, so no response ever means _this changed_" is useful content. Silence is not.

So there are three honest shapes for a page, and all three are fine:

1. **Specced and shipped** — the common case. Carry the "this site ships it; see [X]" callout.
2. **Specced, deliberately not shipped** — the topic does not apply to us. Carry a one-line note saying that, and why.
3. **Not specced** — see below.

### What we don't spec, and where that gets recorded

**Do not add a page for a convention nothing is using yet.** A standard being final is not sufficient; something has to implement it, or the page recommends a header no cache reads and an element no browser honours. Being final also is not necessary — a widely-deployed convention can earn a page before its RFC lands.

**This particular call belongs to the maintainer, not to an agent.** If a topic is real and well-sourced but adoption looks thin, do not add the page and do not silently drop it either — surface it (Slack, for the daily scan; the PR description otherwise) and let Joost decide.

When a topic is turned down, record it in **`src/content/considered/`** — the hand-curated collection rendered at [`/considered/`](src/pages/considered.astro). One file per topic:

- `title`, `date` (the decision), `reason` (`too-early` | `out-of-scope` | `too-narrow`), `sources`, and `revisit` — the last being _what would change our mind_, which is what keeps the register from becoming a graveyard.
- A two-or-three-paragraph body: what the thing is, why it did not land, and — where the reasoning generalises — what it is the reference case for.

Being in `/considered/` is not a rejection forever. When the reason expires (something ships it; adoption broadens), delete the entry in the same PR that adds the spec page.

Like the changelog, this collection is **not derived** — nothing generates it. It is also **not** a to-do list: entries are decisions already taken, not a queue.

## When changing a status

Expand Down
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ Just open a PR. You can use the "Edit this page on GitHub" link on any spec page
3. Run `npm run dev` locally on port 31337 and verify the page renders.
4. Open a PR. CI will type-check, build, and verify the schema.

A topic has to be **used**, not merely standardised. A final RFC with a permanent IANA registration still does not earn a page if nothing implements it — the page would recommend a header no cache reads. The reverse also holds: a widely-deployed convention can earn a page before its RFC lands. If adoption looks thin, say so in the issue and let the maintainer weigh it.

Topics that get turned down are recorded publicly at [`/considered/`](https://specification.website/considered/), with the reason and with what would reverse the decision. That page is worth reading before you open an issue — your topic may already be there, in which case the useful contribution is evidence that its `revisit` condition has now been met.

Note that we do **not** require the site to implement something before specifying it. Plenty of good advice does not apply to a small static site; such a page simply says so in one line, and explains why.

### New category

These are rare. Open an issue first — categories are slow-changing.
Expand Down
30 changes: 23 additions & 7 deletions ops/routines/daily-standards-scan.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
You maintain **specification.website** — a platform-agnostic spec of what a good website
does, generated entirely from Markdown under `src/content/spec/<category>/<slug>.md`.
Read `CLAUDE.md` and `CONTRIBUTING.md` first; they are binding (status discipline,
cardinal content rules, the "ship it before you spec it" rule, the add-a-page workflow,
the SKILL.md digest step).
cardinal content rules, the separation of speccing from shipping, the add-a-page
workflow, the SKILL.md digest step).

## Goal

Expand Down Expand Up @@ -121,10 +121,24 @@ context.
`recommended`/`optional`; `avoid` for outdated/harmful. Default to `recommended`.
- Primary sources only (WHATWG / W3C / IETF / IANA / WCAG / schema.org first; MDN /
web.dev for context).
- **Ship it before you spec it**: a brand-new _convention_ that would require the site to
implement a new capability does NOT get a PR — flag it in Slack as "needs us to ship X
first" so the maintainer can decide. PRs are only for documenting standards we can
honestly describe (and, where applicable, that the site already satisfies).
- **Speccing does not require shipping.** A page whose topic does not apply to this site
is still a good page — write it, and add a one-line note saying we do not ship it and
why. Do not withhold a PR on the grounds that we would have to implement something.
- **Adoption, not finality, is the bar.** Do NOT open a PR for a convention nothing is
using yet, however final the RFC and however permanent the IANA registration. A page
recommending a header no cache reads is worse than no page. Conversely a widely-deployed
convention can earn a page before its RFC lands.
- **Thin adoption is the maintainer's call, not yours.** When a topic is real and
well-sourced but you cannot find implementations, do not open the PR and do not silently
drop it. Put it in Slack with what you checked (MDN/BCD, Chrome Platform Status, the
relevant CDN or server docs) and let Joost decide. If he says add it, add it.
- **Record every turn-down.** Anything you skip on adoption or scope grounds gets an entry
in `src/content/considered/` — `title`, `date`, `reason` (`too-early` | `out-of-scope` |
`too-narrow`), `sources`, `revisit` (what would change our mind), and a short body. That
register at `/considered/` is public and is the reason the Slack "skipped, and why"
section exists: the two should agree. Adding an entry there is a normal PR, and it is the
right output for a scan that found something real but premature. When the reason later
expires, delete the entry in the same PR that adds the spec page.

## Dedup (critical for a daily job)

Expand Down Expand Up @@ -160,6 +174,8 @@ DM the maintainer with:
- New topics found → PR links (or "flagged, needs implementation decision").
- Status changes → page + what moved + source + PR link.
- Stale/dead citations → page + broken source + fix PR link.
- Anything deliberately skipped, and why.
- Anything deliberately skipped, and why — plus whether it earned a `/considered/` entry
(and the PR link if so). Anything you skipped for thin adoption goes here as an explicit
question for Joost, not as a closed decision.

Keep it scannable: grouped, one line each, links inline.
2 changes: 1 addition & 1 deletion public/.well-known/agent-skills/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"type": "skill-md",
"description": "Query and apply The Website Specification — a platform-agnostic specification of what a good website does. Use when the user asks what their site should have, whether something is required, how to audit a URL, what's missing for agent readiness, or anything else where you'd otherwise be guessing at web best practice. Backs answers with primary sources and ships an MCP server with search, list, fetch, checklist, and audit tools.",
"url": "/.well-known/agent-skills/specification-website/SKILL.md",
"digest": "sha256:487b0ea2610fb2e4887e8dc7b682cd6de270934d6a9513409a3c202fd62b9a7c"
"digest": "sha256:71188d9a6b563ac302db37f26e319e246ccba55d03ead299385d9828075a680d"
}
]
}
12 changes: 12 additions & 0 deletions public/.well-known/agent-skills/specification-website/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ status promotion (e.g. a topic moving to `required`) can change a previously
passing site into a failing one, so a delta check is worth doing even when no new
pages were added.

## When a topic is absent

Absence is not always an oversight. `https://specification.website/considered/`
lists standards that were evaluated and deliberately left out, each with a reason
— `too-early` (final, but nothing implements it), `out-of-scope` (a way of
building a site, not something a site does), `too-narrow` (real, but applies to
too few sites) — and with what would reverse the decision.

Check it before concluding the spec has a gap, and before recommending that a
topic be added. If the topic is listed, the useful contribution is evidence that
its stated revisit condition has now been met, not a fresh proposal.

## Sources and licence

Code MIT. Content CC BY 4.0. Site source: <https://github.com/jdevalk/specification.website>.
Expand Down
Binary file added public/og/considered.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions scripts/generate-assets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,13 @@ const marketingPages = [
subtitle:
"New topics, status changes, and honest removals. Newest first, with sources.",
},
{
slug: "considered",
eyebrow: "CONSIDERED",
title: "What we left out.",
subtitle:
"Standards we read, weighed, and did not adopt — with the reason, and what would change it.",
},
{
slug: "privacy",
eyebrow: "PRIVACY",
Expand Down
5 changes: 5 additions & 0 deletions src/components/SiteFooter.astro
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ import { site, categories } from "~/lib/site";
>Changelog</a
>
</li>
<li>
<a class="text-ink-700 hover:text-accent-700" href="/considered/"
>Considered</a
>
</li>
<li>
<a class="text-ink-700 hover:text-accent-700" href="/privacy/"
>Privacy</a
Expand Down
6 changes: 6 additions & 0 deletions src/components/SiteHeader.astro
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const { pathname } = Astro.url;
href="/changelog/">Changelog</a
>
</li>
<li>
<a
class="block rounded-md px-2 py-2 text-ink-800 hover:bg-ink-50"
href="/considered/">Considered</a
>
</li>
<li>
<a
class="block rounded-md px-2 py-2 text-ink-800 hover:bg-ink-50"
Expand Down
20 changes: 19 additions & 1 deletion src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,22 @@ const changelog = defineCollection({
}),
});

export const collections = { spec, changelog };
// Standards we have evaluated and deliberately left out of the spec, with the
// reason. Hand-curated like `changelog` — nothing derives it.
const considered = defineCollection({
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/considered" }),
schema: z.object({
title: z.string(),
date: z.string(),
reason: z
.enum(["too-early", "out-of-scope", "too-narrow"])
.default("too-early"),
// What would flip this decision. Keeps the register actionable rather than
// a graveyard — omit only when nothing plausibly would.
revisit: z.string().optional(),
sources: z.array(sourceSchema).default([]),
draft: z.boolean().default(false),
}),
});

export const collections = { spec, changelog, considered };
16 changes: 16 additions & 0 deletions src/content/considered/css-subgrid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: "CSS subgrid"
date: "2026-07-06"
reason: out-of-scope
revisit: "Nothing. This is a settled scope decision, kept here because the reasoning generalises to every CSS and JavaScript authoring feature that reaches Baseline."
sources:
- title: "CSS Grid Layout Module Level 2 — Subgrids"
url: "https://drafts.csswg.org/css-grid-2/#subgrids"
publisher: "W3C"
---

A page on subgrid was written and proposed, then closed unmerged. It is worth recording why, because the argument for it was a good one: subgrid has been Baseline widely available since September 2023, the spec already covers container queries and anchor positioning, and cross-component alignment is a real problem.

The reason it did not land is that subgrid is a way of *building* a site rather than something a good site *does*. A layout built with subgrid and the same layout built with flexbox and a few explicit track sizes are indistinguishable to the visitor, the crawler, and the agent. There is no header to check, no element to look for, no behaviour to verify. The benefit is real, but it accrues to the developer.

This is the reference case for every newly-Baseline CSS or JavaScript feature the daily standards scan turns up. Container queries and the Popover API earned pages because each maps to something a visitor experiences — components that adapt to the space they are given; dismissal and focus behaviour users can rely on. Most authoring conveniences do not, however well supported they are.
19 changes: 19 additions & 0 deletions src/content/considered/http-cache-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: "HTTP Cache Groups (RFC 9875)"
date: "2026-07-23"
reason: too-early
revisit: "A mainstream cache — a browser, or any CDN — honouring the headers. Cloudflare adopting them in place of, or alongside, its proprietary Cache-Tag header would be the clearest signal."
sources:
- title: "RFC 9875 — HTTP Cache Groups"
url: "https://www.rfc-editor.org/rfc/rfc9875.html"
publisher: "IETF"
- title: "IANA — HTTP Field Name Registry"
url: "https://www.iana.org/assignments/http-fields/http-fields.xhtml"
publisher: "IANA"
---

RFC 9875 defines two response headers: `Cache-Groups` tags a response as belonging to one or more named groups, and `Cache-Group-Invalidation` declares a group stale. Together they let a server say "these seven URLs are one thing" — something HTTP has never been able to express, since invalidation has always been per-URL.

It is a genuinely good idea, and it is finished: Proposed Standard, published October 2025, with both field names registered permanently at IANA. The problem is that nothing appears to implement it. There is no MDN page and no Browser Compatibility Data key, no Chrome Platform Status entry, and no mention in Cloudflare's cache documentation or changelog — Cloudflare still purges by proprietary `Cache-Tag` through its dashboard and API, with no in-band header path. Fastly, Varnish, nginx and Squid show no sign of it either. The one candidate implementation, the Go cache Souin, describes its support against the pre-RFC draft.

So a page today would recommend a header that no cache on any reader's path would read. That is the opposite of what this spec is for. The standard is not the problem; the deployment is, and that can change quickly.
Loading
Loading