Notifio has 38 public pages, all generated from TypeScript arrays. I have counted their links, their images and their HTML weight before. This time I fetched all 38 and counted the accessibility primitives, because a score out of 100 from a tool tells you how you did on one page and a census tells you which decisions are repeated 38 times.

curl -s https://notifio.app/sitemap.xml | grep -o '<loc>[^<]*' | cut -c6- \ | while read u; do curl -s "$u" -o "pages/$(basename $u).html"; done 

Then regex over the served markup. Scripts and styles stripped first, so these are the elements a reader actually gets.

Everything structural passed, which I did not expect

pages with exactly one <h1> 38/38 pages with <html lang> 38/38 pages with exactly one <main> 38/38 <a> elements 1,091 with no accessible name 0 <button> elements 10 with no accessible name 0 <img> elements 38 with no alt attribute 0 target="_blank" links 48 with rel="noopener" 48 

Not one nameless link in 1,091. Not one missing alt. Every external link safe. This is the part a component-driven site gets for free: there is one Nav, one Footer, one card component per page type, so a thing that is right is right 38 times. The flip side is the rest of this post.

Two details worth pulling out. The 38 img tags all carry alt="", which looks like a gap and is correct: the only image on the site is the bell beside the wordmark inside a link that already says "Notifio", so naming the image would make a screen reader announce the brand twice. And the button count being 10 across 38 pages is because the FAQ accordions are native:

<details> elements 119 <summary> elements 119 aria-expanded attributes 6 aria-controls attributes 0 

One hundred and nineteen native disclosure widgets, which are keyboard operable and announced correctly with no JavaScript. And then six hand-built accordion buttons on /help carrying aria-expanded, with no aria-controls anywhere on the site to say what they expand. Two patterns for one interaction, and the one somebody wrote by hand is the weaker one.

The one form on the site fails four ways

The site has five form controls. Four are the contact form on /help, one is the email field on /pricing. The census splits them cleanly:

<label> elements 5 with a for attribute 1 form controls 5 with an id 1 with an aria-label 0 

The /pricing field is correct: <label for="email"> over <input id="email">. The four fields on /help have labels with no for and inputs with no id, so "Name", "Email", "Subject" and "Message" are text that happens to sit above a box. Clicking the word does not focus the field, and a screen reader reaching the input announces the placeholder or nothing at all, depending on which one.

Then the focus indicator. Both forms share one class string:

... border border-[#262626] bg-[#111] ... outline-none ... focus:border-[#404040] focus:ring-0 

outline-none removes the browser's focus ring. focus:ring-0 removes Tailwind's replacement for it, explicitly. So the entire focus indicator on this site is a one pixel border changing from #262626 to #404040, and those are numbers I can put a ratio on:

focus border #404040 against input background #111111 1.82:1 focus border #404040 against resting border #262626 1.46:1 

WCAG 2.2 wants 3:1 for a focus indicator against what is next to it. 1.82:1 is not a near miss, it is roughly "did the border change or did my screen dim". There are zero focus-visible utilities in 38 pages of HTML, so there is no better-behaved variant hiding somewhere either.

Third: there is no live region anywhere.

aria-live attributes 0 role attributes 0 sr-only utilities 0 tabindex attributes 0 

When the contact form fails it renders an error paragraph above the button. When it succeeds it unmounts the entire form and replaces it with a "Message sent" panel. Both are changes a sighted user cannot miss and a screen reader user is never told about, because nothing is announced and in the success case the element that had focus has just been removed from the document.

The colour finding is the big one, because it is in the footer

Counting the colour utilities across all 38 pages ranks what to fix by how often it appears:

colour uses vs #0a0a0a page background #a3a3a3 1,120 7.85:1 body text, passes #fafafa 1,112 18.97:1 headings, passes #525252 1,035 2.53:1 labels, eyebrows, footer links #737373 508 4.18:1 secondary text and the submit button #404040 272 1.91:1 placeholders and borders 

The honest caveat: not every one of those sits on #0a0a0a. Panels are #111 and #171717, and a few of the dark values are text on a light button, where the ratio inverts. So read it as a ranked list of what to measure next rather than a pass/fail tally.

With that said, #525252 is the third most used text colour on the site at 2.53:1, and this is where it lives:

<a> elements using text-[#525252] 546 

Every link in the footer. Fifteen destinations, on all 38 pages. I counted this site's internal links a few weeks ago and found that 64% of them are the footer, which at the time read as a finding about SEO and crawl paths. Put the two censuses together and it says something worse: the majority of the navigation on this site is rendered at 2.53:1, and it only becomes readable on hover, which is a state that does not exist for a keyboard or a touch user.

The same applies to the submit button on the contact form, which is #737373 at 13px semibold on #0a0a0a:

4.18:1, against a 4.5:1 requirement for text that size 

It goes to #fafafa on hover. So the only submit button on the site is below the threshold in its resting state and comfortably above it in the state you reach with a mouse you may not be using. That pattern, "legible on hover", shows up in both of the colour findings, and it is a consequence of designing on the state your cursor is already in.

Two smaller ones, and the ranking

skip links across 38 pages 0 inline <svg> elements 157 with aria-hidden 6 

No skip link means a keyboard user passes the header's 15 links on every page before reaching the content. And 151 of 157 inline SVGs are decorative chevrons, ticks and graph strokes with no aria-hidden, no role and no name, left to each screen reader's heuristics. None of them is wrapped in a hidden container either; I checked, because 103 aria-hidden spans turned up in the census and every one of them is a breadcrumb separator rather than an icon wrapper.

The homepage also goes h1, then seven h3, then three h2, which is one formal skip and a heading outline that does not describe the page.

Ranked by cost to fix against how many people it affects:

  1. Delete focus:ring-0 and outline-none from one shared class string. One edit, fixes the focus indicator on all five controls.
  2. Lift the footer link colour from #525252 to #a3a3a3. One edit, fixes the majority of the navigation on 38 pages.
  3. Add id and htmlFor to four fields. Four lines.
  4. Add aria-hidden="true" to the icon components. A handful of components, 151 elements.
  5. A skip link in the layout. One element.

Every item on that list is smaller than the census that found it. That is the argument for counting rather than scoring: a Lighthouse run on the homepage would have given this site a respectable number, because the homepage has no form on it, and the four things most worth fixing are all on the two pages where somebody is trying to give me money or ask me a question.

I ran the same census on a sister site with 76 pages and got a different shape of answer, which is the other reason to do this per project rather than trusting a habit.