Index
- Text lines
- Heading lines
- Link lines
- List lines
- Quote lines
- Preformatted blocks
- Images
- What makes this AAA
Text lines
Any line that starts with something other than a marker is a text line, and a run of them is a paragraph. That is the whole rule. There is no bold, no italic, no small caps, no inline link, no footnote — the format has no way to change a word in the middle of a sentence, so emphasis has to be carried by word order and by what you choose to say.
Writing under that constraint is closer to writing for a teleprinter than for the web. Sentences carry their own stress. Paragraphs stay short because there is no typographic scaffolding to hold a long one up.
Heading lines
One, two, or three number signs, and no deeper. The markers you see to the left of these headings are drawn by the stylesheet from the element type, not typed into the markup — and each is given an empty alternative text, so it renders as syntax on screen and stays silent in a screen reader. The fragment after each sub-heading is derived from its own words, which is what makes the index above possible without a single hand-written id.
The title at the top of this page carries its single number sign the same way. It was the last line here to get one: a heading that large reads as a title whether or not it is marked, so the omission took a while to notice, and the page was quietly the one thing on the site not dressed as its own source.
Third level, and the floor
A document that wants a fourth level of nesting wants to be two documents. The format simply declines to provide one, and the processor says so on standard error if you try.
Link lines
A link occupies a whole line: an arrow, a URL, and an optional label. The arrow and the scheme tag are drawn by the stylesheet, matched on the href attribute rather than on a class, and both sit outside the underline — the rule runs under the label only, because the label is the part you are being asked to read.
- Back to the top of this page
- How this page works, and why the markers are not really there
- Persistence, a second page inside this capsule
- The gemtext specification, at geminiprotocol.net
- The same project, over the Gemini protocol
- Write to the maintainer
Because a link is never embedded in a sentence, every label has to make sense read aloud on its own — which is exactly what WCAG asks for at AAA, arrived at by accident.
Links that point inside this capsule are rewritten on the way out: a .gmi target becomes .html, a bare directory becomes its index, and an absolute gemini:// URL back to this same capsule becomes a relative path. Everything else is left exactly as it was written.
Two of the six links above go to other pages of this capsule; the rest are a fragment, two other hosts and an address. The head of this document names those two and nothing else, in a speculation rules block. Hover either for a fifth of a second and the browser fetches that page in the background; click and it is already there. Nothing is fetched because this page loaded, and the image link further down is not in the list — the processor knew it was a picture rather than a page, so it never offered it.
List lines
An asterisk, a space, an item. Unordered only; there are no numbered lists and no nesting.
- Bullets are drawn with the marker pseudo-element, so the list keeps its semantics
- Items can wrap to as many lines as they need
- A list inside a list is, again, not offered
Quote lines
A greater-than sign quotes a line. Consecutive quote lines make one block, and an empty quote line starts a new paragraph inside it — which is the only way to write a two-paragraph quotation, since a real blank line would end the block outright.
The amber screen was never a design decision anyone agonised over. P3 phosphor persisted a little longer than green, flickered a little less, and cost about the same.
Forty years on, the thing people remember is the colour.
Preformatted blocks
Three backticks open and close a block, and the opening line may carry alternative text. What that alt text says decides what the block becomes. A phrase reads as a description, so the block is marked as an image and reads out as its caption instead of as four hundred box-drawing characters; it is also the one block set at a line height of exactly 1, so the box glyphs meet. The double rule around it is drawn by the stylesheet; the single rule inside it is typed.
┌──────────────────────────────┐
│ │
│ B E R N S T E I N │
│ P3 AMBER PHOSPHOR │
│ ───────────────── │
│ > gemtext ready █ │
│ │
└──────────────────────────────┘
╱ ╲
╱__________________________╲
A single bare word reads as a language tag instead, and the block becomes a code element carrying it. That is the one class attribute in the document, and the only thing on the page that a gemtext author can add which the stylesheet has to be told about in advance.
echo "a language tag, not a description";
With no alt text at all, the block is left as an ordinary preformatted block: text a reader might actually want, such as source, a table, or a transcript. This is very nearly the entire syntax of the page you are reading:
# Heading ## Sub-heading ### Sub-sub-heading A plain line becomes a paragraph. => gemini://geminiprotocol.net/ A link with a label => /relative/path * a list item * another list item > a quoted line
Very nearly, because one line is missing from that sample and cannot be put back: the fence itself. A block is closed by the first fence inside it, so gemtext is the one format on this page that cannot quote its own syntax.
Images
Gemtext has no inline image syntax — a picture is a link line, and the client decides whether to fetch it. So this is the one place the page steps outside the seven line types, and it does it on the format's own terms. The link points at the full-size original, exactly as it would over Gemini. The plate below it was not written by anyone: the processor generated it from that original, one bit per pixel, two colours of which one is transparent. There is no greyscale to spend, so the tones are ordered 8 × 8 Bayer dither, which both looks like 1983 and compresses about three times smaller than error diffusion.
One line of gemtext, four values in the HTML: the plate is inlined as a data URI, the original is copied alongside and linked, the dimensions come from the generated file so nothing shifts as it loads, and the label becomes the caption.
The image carries a fifth value the gemtext never mentions, and it is the one the picture cannot do without. A plate is wrapped in a link, and a link whose only content is an image with no alternative text has no accessible name at all — a screen reader announces "link" and then spells out the address. The caption does not rescue it, because a caption names the figure and takes no part in naming the anchor inside it. So the alt says where the link goes rather than what the picture shows: two strings, two jobs, and the caption still read exactly once.
The colour is baked into the two-entry palette rather than applied by the stylesheet, so the file arrives already amber, and the palette it is tinted with is the same one the stylesheet is built from. Paper mode burns it down to brown with a filter instead of shipping a second copy.
What makes this AAA
The constraint and the conformance mostly agree with each other. A format with no inline links, no rich text and almost no colour has already made most of the hard choices.
- Text contrast is 10.6:1 in phosphor mode and 13.0:1 in paper mode; the dimmed amber used for markers, captions and quotes holds 8.7:1. AAA asks for 7:1, and the build checks every pair on every run.
- The inverse status bars run the same 10.6:1 the other way round, dark on amber.
- Measure is capped at 66 characters, line spacing is 1.6, and the gap between paragraphs is 1.5 times that. Nothing is justified.
- Every link is its own line, so every target clears 44 by 44 CSS pixels without an inline exception.
- And because every link is its own line, links arrive in runs — so a run of two or more is marked up as a list. The index above is announced as a list of eight rather than as eight unrelated links, which is a count and a boundary a screen reader can act on. The arrows are still drawn by the stylesheet and the page looks exactly as it did before the change.
- Focus is a 3-pixel offset ring at better than 3:1 against everything behind it.
- The first thing in the document, and so the first thing a tab key reaches, is a link that skips past all of this to the main content. It is parked off-screen until something focuses it, and then it arrives as a centred box in inverse video at 10.6:1 — drawn with the same double rule as the framed blocks below, because there was already a way to draw a box on this page.
- The bitmap is a data plot, not an image of text, so 1.4.9 is satisfied outright; its caption describes the trend rather than the pixels, and its ink clears 3:1 against the panel in both modes.
- Motion is one warm-up on load: a second of phosphor overdrive that cools into amber. Ask for reduced motion and it falls back to a half-second cross-fade, which has no motion in it.
- The block after the title breathes three times and then holds still. It moves between the two colours the palette already documents, 10.6:1 and 8.7:1, so both ends of the breath are AAA and nothing new enters the design. It is over at 4.8 seconds, which is not an arbitrary number: five seconds is where WCAG stops allowing something to blink without offering a control to stop it, and a page with nowhere to put such a control has to finish before then. Nothing here blinks in the sense that matters — there is no on and off, only a glow settling.
- The vignette is painted beneath the text, so it can only darken the background and raise contrast. The raster is painted over everything, and lifts the dark rows rather than darkening the lit ones — which is both what a tube actually does and the only direction that keeps the dim amber above the floor. On a raster line the two text colours read 9.8:1 and 8.0:1.
- Phosphor or paper follows the system colour scheme, unless the capsule pins one — a single line of configuration turns the paper rules into a block that always matches, or into one that never can. Both are dropped for forced colours and for increased contrast whichever way that goes.
- All sizing is in rem and ch, so the page reflows past 200 percent zoom with no horizontal scrolling.
The markup underneath is the same seven elements throughout, plus the figure, plus the list that a run of links is gathered into, plus the header, main and footer that hold them all: no div, no span, no id except the anchors the headings name themselves, one class on the one code block that asked for it, and one script whose only job is to swap the plate for its full-colour original in place — remove it and the link still works.
There is a second script element, and it contains no script: the speculation rules block is a JSON data island the browser reads and never executes. A browser that has not heard of it sees an unknown script type and does nothing at all, which is the whole of the fallback story. The stylesheet arrives minified, its comments stripped down to a single banner pointing at the source they came from — the reasoning is still written out in full, just not in every copy of every page.