
Essay
Introducing Englyn: The Ramp Was Load-Bearing
Accessibility work spent twenty-five years losing to ship dates. Software is now used by things that can't see, and the argument changed under everybody's feet.
Listen
Side A — The Read
This recording may be out of date. The text was edited after this audio was recorded.
About this recording
- Nature
- Human-recorded speech, not synthetic
- Recorded by
- Micah Eberman, using GarageBand 10.4.12
- Source text
- Introducing Englyn: The Ramp Was Load-Bearing, written by Micah Eberman
- Recorded on
- Reviewed by
- Micah Eberman,
- Owner
- Micah Eberman
About this recording
- Nature
- Human-recorded speech, not synthetic
- Recorded by
- Micah Eberman, using GarageBand 10.4.12
- Source text
- Introducing Englyn: The Ramp Was Load-Bearing, written by Micah Eberman
- Recorded on
- Reviewed by
- Micah Eberman,
- Owner
- Micah Eberman
The text on this page is the transcript of this reading.
Read
Essay briefing
The claim: the argument for accessible software has been losing to sprint deadlines for twenty-five years, and it just changed, because software is now used, in volume, by agents that can’t see a page any better than a screen reader user can. The floor already existed, the W3C’s own POUR principles. What was missing is a fifth mark: a claim a consumer can check, not merely receive. This essay makes that case in seven moves: what actually changed and who else needs the answer now, the missing fifth mark and the 2002 standard it guards against, a defect that got past four green automated checks on this very site, what Englyn actually is and why it has to be a standard rather than a library, a tour of what is published here and how to run it against your own work, where the tiers and the published limits sit, and an ask aimed specifically at people who use assistive technology every day. Reading time: about twenty minutes. Three thousand, eight hundred and fourteen words. Two links close the essay: the specification itself and its source repository. Both are real addresses; the repository goes live in a second, separate step, so either may not resolve yet.
I have been making the case for accessible software for a long time, and for most of that time I have been losing.
Not losing arguments. Winning arguments and losing sprints. Everybody nods. The ticket gets written, the ticket gets estimated, the estimate gets cut, and the person who couldn’t use the product still can’t use it. Then the next quarter starts.
If you have done this work you know the shape of it. If you haven’t, the short version is that being right has never been the bottleneck.
What changed isn’t the argument. It is who else needs the answer.
Section 1 of 12.Software is now being used, in volume, by things that can’t see.
An agent hitting your interface has the same problem a screen reader user has had since 1999. It can’t see your layout. It can’t read meaning off position. It can’t tell that the red button is dangerous, because red is a color and a color is a pixel. It needs the structure stated, not implied.
That isn’t a metaphor and it isn’t a coincidence. It is the same requirement, arriving with a budget attached.
Which puts a lot of us in a strange position. The thing I couldn’t get funded on the grounds that all people deserve to use software is now getting funded on the grounds that all agents need to parse it. I have complicated feelings about that. I am also going to take the money and build the ramp.
The curb cut, and why I keep coming back to it
Section 2 of 12.Curb cuts were fought for by disabled activists. They were resisted, at the time, as an expensive accommodation for a minority.
Now they carry strollers, delivery trolleys, suitcases, bicycles, and anybody with a bad knee. The people who needed them most were never the only ones who benefited. They were the ones who couldn’t proceed without them.
Semantic structure is the ramp. Headings that mean something. Regions that say what they are. Consequences stated before you act rather than after. It was built for assistive technology, and it is now what an agent needs to act on your page without guessing.
So here is the part I want to be careful about, because it is the part that gets me suspicious of my own argument.
This is the rare case where doing right by people isn’t a cost you absorb. The work that makes your product survive contact with an agent is the same work that makes it usable by somebody who can’t see it. You don’t have to choose. You were probably going to build half of it anyway.
That’s a good deal and I’m not going to pretend it is charity. Take the ride, and take the credit for showing up. Then go and find out who was already here, because they left you notes.
What is actually missing
Section 3 of 12.Here is the specific gap, and it is narrower than “accessibility is hard.”
The floor already exists. POUR is the organizing principle under the Web Content Accessibility Guidelines: content must be Perceivable, Operable, Understandable, and Robust. It has been there since 1999. It was built by people doing this work before any of it was fashionable, and it is a genuine achievement.
Englyn has five marks. Four of them are POUR, restated for a consumer POUR wasn’t written for. Observable is Perceivable. Recoverable is Operable. Explicit is Understandable. Typed is Robust. I’m not claiming those four. If you have done accessibility work, you are further along here than you expect.
The fifth one is the reason I wrote anything down.
Verifiable: a consumer must be able to check a claim, not merely receive it.
POUR has no verification principle, and that isn’t an oversight. POUR was written for documents, and documents make no claims. A page that says “Contact Us” isn’t asserting anything you could be wrong about. It is text. You read it or you don’t.
An interface an agent acts on isn’t a document. It is a transaction. When your markup says an action is safe, or reversible, or complete, that’s a claim with consequences, and the consumer needs a way to check it rather than trust it.
A description is adequate when a human is in the loop to catch the lie. It isn’t adequate when nothing is.
The failure mode I built this against
Section 4 of 12.In 2002 the W3C published P3P, a privacy specification. Internet Explorer checked that a policy string was present. It never checked whether it was true.
So sites shipped a string, the check passed, and behavior never changed. Google’s read, in production, for years:
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
The check passed. The W3C working group had gone quiet years earlier, and Microsoft finally pulled P3P support out of Internet Explorer and Edge around 2018.
Presence isn’t conformance. Any standard that can be satisfied by declaring something will be, and the declaration will be false, and the tooling will report success.
And then it happened to me
Section 5 of 12.Every heading on my site is written in sentence case. CSS renders them as capitals. Correct source, correct rendering, and a defect underneath it.
WebKit hands the style-transformed string to the accessibility tree. So VoiceOver received
“US” and read it as the country.
“What this buys us.” became… “What this buys U-S.”
Six instances across four pages. At the time: axe reported zero violations. html-validate passed. My own checker passed. The DOM was perfect. Four layers of automated testing, all green, on a page a screen reader was mispronouncing.
I found it by running a screen reader on that page for the first time and recording what it actually said.
So I’m not going to tell you my tooling is good. I am going to tell you it is honest about what it can’t see, and that’s a different claim.
So here is what I built, and what I think it is for
Section 6 of 12.Englyn is a conformance standard for interfaces that are read by people and by agents, and it is public as of today.
That sentence is doing a lot of work, so let me take it apart.
It is a standard, not a tool. Fourteen normative requirements about properties a page has to have, written so that a stranger can satisfy them without adopting any of my process, my framework, or my opinions about how to build software. §2.1 says a conforming page must be achievable by hand-authored markup in a text editor, and that requirement is load-bearing: if a future version of this can’t be met that way, the version is wrong. There is nothing to install and nothing to buy.
It is data-englyn-* attributes on HTML you already have. Eleven of them. They say what
a region is, what an action will do, whether it can be undone, whether a claim carries a
receipt, and … the one I care most about … which consumer the annotation is for. No
new elements. No runtime. Nothing in your production bundle.
And it is a set of instruments that are honest about their own blindness. Six test layers, each one able to see things the others structurally can’t, and three verdicts instead of two: CONFORMS, FAILS, and CANNOT-CHECK. That third verdict is the point. Automated tooling catches somewhere between thirty and fifty percent of accessibility problems by its own maintainers’ reckoning, which means a tool reporting “no violations” is telling you about itself as much as about your page. Englyn makes it say which.
What it is actually for
One payload, many renderings, and nobody gets the lesser version.
The same marked-up source serves a person using a screen reader, a person on a forty-cell braille display, a person whose attention is gone at four in the afternoon, an agent fetching the page directly, and an agent asking for the markdown twin instead. Not five sites. Not a main site and an accessible site that goes stale in a couple weeks. One document, read differently by different consumers, all of them entitled to the same meaning.
That’s the whole vision and it isn’t a new idea. It is what semantic markup was always supposed to do, and the reason it kept not happening is that the payoff went to people the budget didn’t prioritize. What is new is that the payoff now also goes to something with a budget attached, and I would rather use that than complain about it.
Why it has to be a standard rather than a library
Because a library serves whoever installs it, and the whole problem is the consumer you did not anticipate.
An agent hitting your page next year isn’t going to import your design system. A braille user isn’t running your framework. The only thing they can both rely on is the document, which means the contract has to live in the markup, be checkable from outside, and cost nothing to adopt. That’s a standard, and a standard’s success condition is that other people use it without talking to me.
And why it is deliberately unfinished
The top tier can’t be reached alone, by design. Bronze and Silver are claims about testing and I can assert those on my own. Gold requires recorded assistive-technology verification with paid community participation, and it can’t be self-certified by anyone, including me.
That tier isn’t written yet. Writing the requirements that govern disabled reviewers, without disabled reviewers in the room, would be the original problem in a new document. So that section is an invitation rather than a specification, and it is the part I most want help with.
What I have is a floor, a set of instruments, a report on my own failures, and an unfinished ceiling. That’s version 0.1.0, and it is enough to be useful and not enough to be trusted, which is exactly what it says on its first screen.
What is in the repository, and what each piece is for
Section 7 of 12.Seventeen published paths. Here is what every one of them does, because a repository you have to reverse-engineer isn’t an invitation.
Read these three, in this order
spec/SPEC.md is the specification. Six sections. §0 is why it exists, §1 points at the
vocabulary, §2 is the fourteen normative requirements, §3 is conformance and tiers, §4 is
what a passing verdict does and doesn’t warrant, §5 is the limitations.
Start at §2 if you are here to implement, and §4 if you are here to decide whether to trust it. §4 is the liability section: what a CONFORMS claims, at which layer, and who is asserting it. Most standards leave that implicit. Leaving it implicit means your sharpest reader writes it for you.
spec/VOCABULARY.md is the normative source for every data-englyn-* attribute: the
value spaces, which mark each one carries, whether it is MUST or SHOULD, and which consumer
it serves. If a value isn’t in here, a conforming page doesn’t emit it. This file is
the one that stops the vocabulary drifting, and it exists because it drifted: three published
documents once defined six attributes while the site emitted eight.
spec/UNVERIFIED.md is the honesty file. Every assumption this project hasn’t verified,
with what would settle each one. If you are looking for a reason to dismiss this, that file
is the shortest route and I wrote it myself. I would rather you found the gap there than in
production.
Then the tiers and the report
spec/TIERS.md defines Bronze and Silver, and declares Gold without defining it. It also
carries the rule that took me longest to get right: the difference between a CANNOT-CHECK
that blocks a tier and one that doesn’t. An instrument that never ran hasn’t passed. An
instrument that ran, reached everything it could, and named what it couldn’t judge has told
you the truth, and that’s a different thing.
reference/CONFORMANCE-REPORT.md is my own site’s report. Bronze, self-asserted, dated, against a named build. The longest section in it is everything the instruments couldn’t determine, and it publishes twenty-four linter failures with my reasoning for disagreeing rather than suppressing them. There is a clone-and-run block at the bottom. Running it against my own repository is what caught a false claim in an earlier draft of that very report.
Then the practical files
implementation/ENGLYN_QUICKSTART.md is the shortest path from nothing to a marked-up
page. Start here if you want to see the shape before reading the specification.
implementation/ENGLYN_IMPLEMENTATION_GUIDE.md is the longer version: patterns, worked
examples, and the mistakes that are easy to make.
testing/ENGLYN_TESTING_PROTOCOL.md describes the six layers and, more usefully, what
each layer is blind to. That blindness is the design, not a gap.
testing/ENGLYN_ACCEPTANCE_CHECKLIST.md is what you tick against real output before you
ship. It is deliberately boring.
testing/axe-integration-example/ is a worked example of wiring the automated audit into
a build.
And the measurement files, which are optional
measurement/ENGLYN_MEASUREMENT_SCHEMA.md, englyn_schema.sql, and
ENGLYN_TRANSPARENCY_REPORT_TEMPLATE.md are for anyone who wants to instrument
conformance over time rather than at a point. Skip them entirely on a first pass. They
matter at organizational scale and they are noise if you are marking up one page.
skills/englyn-accessible-page/scripts/spoken_number.py is a small utility for
ambiguous digit strings. It exists because of a rule I got wrong twice: digits stay digits
in an accessible name. Spelling numbers out reads fine to a speech synthesizer and eats
cells on a braille display, which is forty to eighty characters of window over your whole
page.
LICENSE, CHANGELOG.md, CONTRIBUTING.md
Specification text is CC-BY-4.0. The tooling is Apache-2.0 and lives in its own repository, because a patent grant is what somebody else’s legal review needs before they will run your code in their CI.
CONTRIBUTING.md is short and its first line is the important one: the most useful thing
you can send is a failure.
How to actually do this to a site you own
Section 8 of 12.Four stages. The first one is an afternoon and it is the only one I would insist on.
Stage 0 … Measure before you change anything
Run the checkers against your site as it is. Don’t mark anything up first.
You will get a number and a list of things the instruments couldn’t judge. That list is the useful output, not the number. It tells you where your page is ambiguous to something that can’t see it, which is usually not where you would have guessed.
Fire the controls first. npm run englyn:selftest before anything else. Every check has a
must-fail fixture that has to fire and a must-not-fire fixture that has to stay quiet. A
check whose control doesn’t fire is a permanent cannot-check wearing a pass. Mine once
reported CONFORMS while scanning zero files.
Stage 1 … Regions and reading order
Tag the regions a briefing of your page would name. data-englyn-role and
data-englyn-primitive on each, values from VOCABULARY.md.
Then check your DOM order against your reading order: title, narration, highest-priority content, supporting detail, actions. Visual arrangement is a rendering. Reading order is the payload. If CSS is doing the ordering, a consumer that can’t see the CSS gets a different document than you do.
This is also where you write the briefing region: a short sr-only section that says what is
on the page and in what order. Put it where a reader chooses it, never first. Seventy-one
point six percent of screen reader users navigate by heading, so the heading list has to carry
the story on its own, and a briefing that interrupts before the heading list is available is a
tax rather than a service.
Stage 2 … Consequences and reversibility
For every control that changes state: a data-englyn-consequence that says what will happen,
mirrored in the accessible name or description.
That mirroring is the requirement I would fight for hardest. Every attribute this standard adds is invisible to a screen reader by default. Put your operation semantics in attributes and stop, and you have built a channel for agents and left the person using assistive technology with the worse page. Then the standard has made things worse for the people it was written for, which isn’t a hypothetical failure mode. It is the obvious one.
And declare reversibility rather than letting it be inferred. A consumer parsing English prose to decide whether something is safe can be accidentally wrong: correct wording, wrong pattern, wrong answer. Nobody accidentally declares an action reversible.
Stage 3 … Run the layers, in order, and read the verdict as measured
npm run englyn:selftest # controls. if these do not fire, stop.
npm run build # L1 static markup
npm run englyn:schema # L2 schema, reads SOURCE not output
npm run a11y:html # L1 HTML validity
npm run a11y:axe # L3 axe-core in a real browser
npm run l4:keyboard # L4 real keys, real focus
Serve the production build, not your dev server. Dev tooling injects elements that never ship. I measured a page that didn’t exist for three runs before noticing, and I only noticed because a screen reader read out four buttons that shouldn’t have been there.
Don’t tune a checker until it passes. If the verdict is FAILS, that’s the finding. Report it. Every defect worth having found here was found by an instrument that was allowed to fail.
Stage 4 … Listen. This is the one that matters.
Turn on a screen reader and go through your page. VoiceOver plus Control-Option-A on macOS reads from the cursor. Use the rotor, Control-Option-U, to walk the headings and the landmarks.
Write down what it actually says. Not what you expected. Not what the accessibility tree reports. What comes out of the speakers.
This is the layer that caught the only defect on my site that mattered, and every automated layer above it was green at the time. If your first listen finds nothing, that’s suspicious, not good.
Where the scores are, and why there is no single number
Section 9 of 12.There is no Englyn score, deliberately.
A result is a tier (Bronze, Silver, Gold), bounded by a layer (L1 to L6), carrying a verdict (CONFORMS, FAILS, or CANNOT-CHECK), with everything undetermined published beside it. A single number would hide exactly the part worth reading.
And three verdicts rather than two, because automated tooling catches somewhere between thirty and fifty percent of accessibility issues by its own maintainers’ account. If half the problems are invisible to your tools, a tool that reports no violations has to be able to say “and here is what I couldn’t judge.”
What is wrong with this, on the first screen rather than the last
Section 10 of 12.This is version 0.1.0. The limits ship in the same release as the claims, because a standard about verifiable claims that hid its own would be self-refuting.
No external review. No standards body. No independent audit. One person wrote the specification and the reference implementation, and then asserted the reference implementation conformant. That’s the weakest form of conformance claim there is, and §4 says so in those words.
One assistive technology tested. VoiceOver on WebKit. NVDA, JAWS, TalkBack, Narrator, and every braille display are untested. The one defect this layer found is WebKit-specific, so virtual-buffer readers may not even reproduce it.
Not every disabled reviewer ‘type’/condition has read this. The neurodivergent reading of the problem is first-hand, and I will stand behind that part: I am formally diagnosed, and the argument about cognitive load being a state rather than a trait is written from inside it. Screen reader experience isn’t mine. This document doesn’t speak for it.
The screen reader harness isn’t reproducible. Two runs of the same page returned sixteen headings and seventeen. The protocol requires identical results across two runs, so no run counts as a record yet. Silver is therefore not claimed, and the layer that has caught every defect worth catching is the layer I can’t yet run properly.
Gold is undefined. On purpose.
Which brings me to the ask
Section 11 of 12.Gold requires recorded assistive-technology verification with paid community participation, and it can’t be self-certified by anyone, including me. You can’t reach the top tier of this standard without the community’s paid participation. That’s structural. It is in the test suite, not in a code of conduct where it is easy to ignore.
And it isn’t written yet, because writing the tier that governs disabled reviewers, without disabled reviewers, is just the original problem in a new document.
So:
If you build interfaces: clone it, run it against something you own, and send me the report. Especially if it fails. A FAILS from a stranger’s page is worth more to me than a hundred passes on my own machine, and I mean that operationally, not as a gesture. I have twenty-four published failures on my own site and I found the most useful one by having somebody else look.
If you use assistive technology: tell me where this is wrong. There are two specific
questions in CONTRIBUTING.md that I can’t answer and you can, both about whether a pattern
I chose actually serves you or just satisfies a linter. Your answer outranks my reasoning
and I will change the code.
And on the unpaid part, plainly: there is no rate set and no budget in place, and I’m not going to dress that up. The invitation right now is unpaid and honestly framed. Paid review arrives when Gold is real. If that makes this the wrong ask for you today, that’s a completely reasonable answer, and I would rather you said so than felt obliged.
Two addresses, always cited together
Section 12 of 12.waxandwires.com/englyn is the normative text. github.com/waxandwires/englyn is the source of record, and where to file against it.
Specification text is CC-BY-4.0. The checker is Apache-2.0, in its own repository.
Nothing about us without us is a demand, not a courtesy. This was written by somebody inside one part of that “us” and outside most of it. The right response to that isn’t to claim more. It is to leave the door open, and say which side of it you are standing on.
I built the ramp. I would like to know if it is the wrong shape.
Run-out groove
Four green lights, and a voice saying U-S. Etched by M.E., 2026.
Hidden track: the actual VoiceOver capture, 2026-08-17, trimmed to four seconds. Synthesized speech — a screen reader’s own voice, not this essay’s narrator. Transcript: “Heading level 2, what this buys US.”
Run-out groove
Run-out groove
Four green lights, and a voice saying U-S. Etched by M.E., 2026.
Hidden track: the actual VoiceOver capture, 2026-08-17, trimmed to four seconds. Synthesized speech — a screen reader’s own voice, not this essay’s narrator. Transcript: “Heading level 2, what this buys US.”
Liner Notes
The soundtrack to this essay
instruction → disorientation → presence isn't conformance → a label that lies → parts that fit
