What web accessibility is
Web accessibility means building a page that also works with a keyboard, with a screen reader, and for an eye that sees colour differently. Most of it is not hard: correct HTML, enough contrast, a focus state you can see, and images that carry alt text.
- Lesson 10 of 12
- Intermediate
- Free, no signup
The accessibility review sheet, what actually gets checked
A page that works
- Everything clickable is reachable with Tab
- The focus ring is visible at every step
- Ordinary text contrast is at least 4.5:1
- Every image has alt text or an empty alt
- Buttons are button tags and labels are label tags
- In prose links carry a signal other than colour
Where it usually breaks
- outline: none in the reset file
- A div turned into a button with onclick
- A placeholder standing in for a label
- Text over an image with no dark layer
- A modal that Escape does not close
- An icon only button with no name
This sheet catches the mechanical failures. Whether a real person can finish their task is only settled by testing with people.
Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.
What does accessibility mean, and who is it for?
Web accessibility means the page you built stays usable for somebody who does not work with a mouse, who sees colour differently, or who does not see the page at all. Not simpler, not duller; just usable.
The list of people it helps is longer than most teams assume. Somebody using a screen reader. Somebody whose hand shakes and cannot land a mouse precisely. Somebody with colour blindness for whom red and green are one colour. Somebody watching your video on a bus with the sound off. And then the group who never put themselves on that list: you, looking at a screen in noon sunlight, or holding a phone in one busy hand and working the thumb of it.
The standard is WCAG, a set of testable criteria published by the web consortium in three levels, A, AA and AAA. The practical target is almost always AA; for some criteria AAA is not achievable on an ordinary site and nobody is asking you for it.
One plain thing to say as well: in the Iranian market the client usually does not ask for this and competitors usually do not do it, and most Persian sites have never once been tried with a keyboard. Which means doing it is still a differentiator rather than a floor. And most of what follows takes an afternoon, not a project.
Colour contrast: which number is enough?
Contrast is the ratio between the luminance of the text and of its background, expressed as one number: from 1:1, which is invisible, to 21:1, which is black on white. WCAG asks for at least 4.5:1 for ordinary text, 3:1 for large text, and 3:1 for interface parts such as the border of an input or an icon that carries meaning.
The most important thing in this section is one sentence: a colour has no contrast on its own, a pair of colours does. The same blue that reads cleanly on a dark background can fail once it becomes the background of a button with white text on it.
The numbers of the site you are reading make a good example. We computed these on 5 September 2026 from the real palette values: body text on the background is 17.6:1, the secondary text 9.25:1, blue as a text colour 7.32:1 and the dimmer text 6.21:1. Now the same blue as a background: white on it is only 2.72:1, which fails. That is why our palette carries two blues rather than one; the second is darker, white on it reaches 5.17:1, and the first stays untouched for text and icons.
Checking it takes ten seconds. In the browser developer tools, click the text, open the colour value, and the contrast ratio is printed right there next to the colour picker.
Two places produce most contrast failures, and neither of them is body text: text over an image, which has no single number because the photo has a different luminance at every point and whose fix is a dark layer under the text; and the placeholder text inside a form field, which is almost always the palest text on the page and fails in most designs.
Five minutes with a keyboard: the fastest test you can run
Put the mouse aside and press Tab. This test needs no tooling and finds most of the real problems, because anything that does not work with a keyboard does not work with a screen reader either.
Watch four things:
- Can you reach with Tab everything you can click with a mouse? The dropdown, the modal close, the tabs, the filters.
- Do you know at every moment where you are? If you cannot see the focus ring, the test has already failed.
- Does the order of movement match the order of the page? If Tab jumps from the heading to the footer and then back to the middle, the elements sit in the HTML in a different order from where they appear.
- Do you get stuck anywhere? The classic case is a modal that opens, does not close on Escape, and will not let Tab out of it.
The most frequent failure this test finds is one line of CSS: outline: none, written into resets because the browser default ring is ugly. Removing it means somebody working with a keyboard no longer knows where they are. The move is to replace it, not delete it. On this site there is one global rule: :focus-visible with a two pixel blue ring and three pixels of offset from the element. The word focus-visible matters, because a mouse user does not get a ring from clicking and a keyboard user does.
There is one more element that almost no Persian site has and that takes three lines: the skip to content link. It is the first thing Tab reaches, it is invisible until it takes focus, and it saves anybody arriving by keyboard from tabbing through the whole menu. In this theme it is the first element inside the body.
The keyboard test, four steps and no tooling
Put the mouse aside and start at the top of the page.
-
1
Press Tab and watch where you are
If you cannot see the focus ring, you have already found a failure.
-
2
Go to the end of the page and come back with Shift and Tab
The order out and back should match the order you see.
-
3
Try everything with Enter and Space
Anything that works with a mouse but not with these two keys is not a real button.
-
4
Open the modal and the menu and see whether you get out
Escape should close it and focus should return where it came from.
This test tells you whether the page is usable with a keyboard. It does not tell you what it sounds like in a screen reader; only the screen reader says that.
How do you write alt text for an image?
Alt text does not describe how the image looks, it describes what the image is doing on that page. The same photo takes two different alt texts on two different pages.
A photo of a chair on a product page: "black office chair with a mesh back, three quarter view". The same photo in an article about back pain: "correct sitting posture in an office chair". An image that is purely decorative and adds nothing to the meaning takes empty alt text, alt="", so the screen reader passes over it. An empty alt attribute is not the same as a missing one: when it is missing, some screen readers read the file name, and IMG-20260415-WA0007.jpg is something nobody needs to hear.
Three things not to write: the word "image" or "photo" at the front, since the screen reader already says it is an image; a stuffed keyword, which spoils the experience of a real person for a ranking you will not get; and a three line paragraph, because alt text is one sentence and if more explanation is needed its place is the page text.
Two cases everybody forgets: a button that is only an icon, such as a search magnifier or a close cross, has no text to be read and needs its name given with aria-label; and a chart or infographic, whose summary does not fit in alt and whose numbers belong in the page text as well.
On this site the question took a different shape. The figures at the top of this page and between the sections are not images: they are HTML and CSS. Their text is real text, a screen reader reads it, it can be selected and translated, and the decorative pieces such as rails and arrowheads are hidden from the screen reader with aria-hidden. That decision was made for speed first and accessibility was the side benefit. The on page SEO lesson looks at the same question from the search side.
Why a div is never a button
Because <button> gives you four things for free that <div onclick> has none of: Tab reaches it, Enter and Space activate it, the screen reader announces "button", and the browser already understands its disabled and focus states. Getting a div to the same place means writing tabindex, a role and a keyboard listener, and at the end you have something that merely looks like a button.
The same logic runs through the rest of HTML, and that is the whole idea of semantic HTML. Tie a form label to its field with <label for> so that clicking the text moves focus into the field and the screen reader knows what the field wants; the placeholder inside the field does not replace the label, because it disappears the moment somebody types. Keep headings in order, h1 then h2 then h3, because somebody using a screen reader often listens to the heading list first and decides from it where to go. Put the menu inside <nav> and the body inside <main> so that same person can jump straight to the content. And write a list as a <ul>, because the screen reader then says "list, six items", which is real information that a stack of divs throws away.
We hold to this in the figure engine of this site too: the columns of a comparison figure are a real list rather than a run of paragraphs, precisely so the reader hears how many points it has. The HTML lesson walks through these elements one by one.
One ARIA trap belongs here, because it shows up so often in generated output: the first rule of the ARIA specification itself is not to write ARIA when HTML has an element for the job. role="button" on a div is worse than <button>, not better.
In prose links: why colour alone is not enough
One of the WCAG criteria says colour must not be the only way a piece of information is conveyed. The most common place it breaks is a link inside a paragraph: blue on black, with no other signal. Somebody with colour blindness, or reading in sunlight, or on a cheap monitor that pushes colours together, does not see that link. It is text, in a blue that is not blue to them.
The obvious answer is to underline every link, and that answer creates a different problem: the menu, the buttons, the cards, the logo and the page numbers are links too, and they all get a line under them. The page turns noisy, and usually whoever sees that goes back and removes the underline altogether.
The route we took on this site, which you can read in the source, is four decisions. First, a carries text-decoration: none globally and only in prose links get a dedicated class. Second, the underline of that class is a gradient background 1.5 pixels thick that grows to 2 pixels on hover and on :focus-visible, so a keyboard user gets exactly the feedback a mouse user gets. Third, that class carries box-decoration-break: clone, because without it a link that wraps onto a second line gets the underline under only one fragment and the other sits bare. Fourth, there is a @media (forced-colors: active) block that brings a real underline back in Windows forced colours mode, because that mode throws background images away, and if you have not thought about it your gradient underline disappears for precisely the person who needs it most.
Let us state the boundary too: these four lines only fix links inside paragraphs. A link that is an image, a link that is only an icon, and a card that is clickable as a whole gain nothing from this and each needs a readable name of its own.
How do you know it is actually fixed?
You have three tools and their order matters. First the keyboard, which needs nothing and catches the most. Second the browser developer tools: they show you the contrast ratio and the accessibility tree, which is what the screen reader sees; if your button appears in that tree with no name, you already have your answer. Third an actual screen reader: NVDA is free on Windows, VoiceOver is built into macOS and iOS, TalkBack into Android. Ten minutes with one of them teaches more than any article.
Automated tools such as the Lighthouse accessibility section are useful too, as long as you know their place: they check the mechanical rules. Alt text that reads "image" passes. Focus order is not measured. A button labelled "more" whose more of what nobody can tell slips straight through. Scoring a hundred means you have no mechanical failures, not that the page is usable.
A word on accessibility overlay plugins, the floating boxes that promise to make a site accessible with one line of code: what is wrong in your HTML is not fixed by a layer on top of it. A div that is not a button is still not a button after that plugin is installed. We have tested no specific product and we name none; we only say that the route that works is changing the markup itself.
The fast path, with AI
Accessibility work has two halves, and a language model is good at only one of them, but at that one it is genuinely good. The mechanical half, changing the markup, giving elements names, putting headings in order, is exactly what a fast model does in seconds. The other half, whether a real person can finish their task, is beyond it because it cannot see your page. So the fast path is this: run the test yourself, hand over the rewrite. For an ordinary component a fast cheap model is enough; for one with interaction of its own, a date picker or a combobox, move to a stronger model. Our current pick is in <a class="text-link" href="/en/ai/">the AI section</a>.
- Run the keyboard test yourself first and write down where you got stuck. This is the one part the model cannot do for you.
- Isolate one component, not the whole page, and take its real HTML. Describing it instead of pasting the markup is the biggest single cause of an irrelevant answer.
- Run the recipe below. Do not drop the sentence about the appearance not changing, or the answer comes back as a new design rather than a fix.
- Put the output back in the page and run the same keyboard test again. Until you have done this step nothing is fixed, only changed.
Copy-ready recipe
Role: web accessibility reviewer.
The markup of this component:
{paste the real HTML of this one component here}
The colours used, as hex codes:
{for example text #8690a6 on background #07090f}
What this component is supposed to do:
{in one sentence}
Page language: {English}
Rules:
- The appearance does not change. Structure, names and order only.
- Wherever HTML has the right element, use it. ARIA only where HTML has no element for the job.
- For every change write one line: what changed and which WCAG criterion it satisfies.
- Compute the contrast ratios from these hex codes and show the steps so I can check them.
- Any text you add, from an aria-label to alt text, is in the page language.
- At the end, list separately what you cannot verify without seeing the rendered page.
Before you trust the output: That last list, the "things I cannot verify without seeing the page", is the most valuable output here, and your work starts from it. Check two things yourself as well: read the contrast numbers off the colour picker in the developer tools, because decimal arithmetic is exactly where models slip; and delete any alt text written for an image the model has not seen, then write it yourself. That one is not a description, it is a guess.
AI in this kind of work
Our position here is simple: hand over the mechanical half and keep the judgment half. A language model is genuinely good at three things on this topic: rewriting a piece of markup into its semantic form, finding elements that have no readable name, and explaining a WCAG criterion against your own code, which is the fastest way to learn this standard. What we do not hand over: deciding whether the page is usable.
Tools that actually help
- Claude Good for a semantic rewrite of a component and for explaining which criterion each change satisfies. Iran is not on Anthropic supported-countries list; we read that on Anthropic own page.
- Gemini Enough for "why is this not accessible" questions and for alt text in Persian, and it handles Persian well. Google own page says the Gemini web app runs in over 230 countries and territories, and Iran is not on that list.
- Claude Code Because it sees the files, it suits sweeping a whole theme: finding every outline: none, every field without a label and every button without a name. It installs free but needs an Anthropic subscription, and Iran is not on the supported-countries list.
Where it backfires
Three specific risks on this topic. First: the model does not see your rendered page, so anything it says about focus order, whether the ring is visible, or text sitting on an image is a guess rather than a check. Second, and more common than you would expect: when you say "make this accessible", the answer is usually a role, a tabindex and a few aria attributes added to the same div, when the right answer was one plain HTML element. The first rule of the ARIA specification itself says not to write ARIA when HTML has an element for the job; the link is at the end of this block. Third: any text the model adds comes back in English unless you say otherwise, and an English aria-label inside a Persian page means the screen reader changes accent mid sentence. And alt text written for an image the model has not seen is not a description; delete it. For how each tool can be paid for from Iran, see the buying guide.
Sources: W3C: Using ARIA, first rule W3C: Understanding contrast (minimum) W3C: Understanding use of colour Anthropic: supported countries Google: where Gemini Apps are available
Where this advice stops
Meeting level AA is a floor, not a certificate. A page that passes every mechanical criterion can still be unusable for a real person, and the reverse is true as well. Two other boundaries: this lesson is about the web page only, and PDFs, video with captions and audio description, and mobile apps each have rules of their own. And more important than any of it, accessibility does not fix bad text; a screen reader reads a confusing sentence flawlessly and the reader stays confused. There is one more place to be honest about: some components, a date picker or a table with two header rows, are genuinely hard, and the right answer is usually a native browser element or a well tested library rather than hand written ARIA.
From our own work
Three decisions that sit in the CSS file of this site and can be read in the page source. First, the a tag carries text-decoration: none globally and in prose links take a dedicated class, because underlining every link also puts a line under the menu, the cards and the logo. The underline of that class is a gradient background 1.5 pixels thick that reaches 2 pixels on hover and on focus-visible, it carries box-decoration-break: clone because without it a link breaking across two lines gets the underline under only one fragment, and it has a forced-colors block that brings a real underline back in forced contrast mode, because that mode throws background images away. Second, the palette carries two blues rather than one: blue as text on our background is 7.32:1, but white on that same blue is only 2.72:1, so a darker blue at 5.17:1 exists only for when blue is itself the background. The comment explaining that decision sits above the variable in the file. Third, one global focus-visible rule with a two pixel ring and three pixels of offset, and a skip to content link that is the first element inside the body.
Real follow-up questions
Does accessibility affect SEO?
Google has not announced accessibility as a ranking factor, and we are not claiming one. The overlap is real though: ordered headings, correct alt text, semantic HTML and text that is actually text rather than an image help the reader and the crawler alike. The fuller version of this discussion is in the on page SEO lesson.
Does a Persian right to left site change anything?
Two things start to matter. First the lang attribute on the html tag, because a screen reader takes its voice and pronunciation from it and a wrong lang means Persian read in an English accent; passages in another language need their own lang too. Second logical properties in CSS instead of left and right, or the layout comes out correct in one language and mirrored in the other; the CSS lesson shows that with examples.
I have an old site, where do I start?
With the three that are cheap and fix the whole site at once: bring the focus ring back, make sure everything clickable is reachable with Tab, and raise the contrast of the pale text. Those three are usually a few lines of CSS. Alt text and heading structure come next and are done page by page.