Skip to main content
CSS/HTML

Why 95.9% of Home Pages Fail WCAG and How I Fix It

WebAIM's Million study found 95.9% of home pages have WCAG failures. I walk through my CSS/HTML process to avoid the most common errors.

I read the WebAIM Million study every year, and the February 2026 result stopped me cold: 95.9% of the top million home pages had detected WCAG 2 failures, up from 94.8% the year before (WebAIM Million). That is not a niche problem. It means if you build web pages for a living, the odds are overwhelming that your last project shipped with at least one detectable accessibility failure. I have shipped those failures too. What changed my practice was not a new tool or a certification. It was accepting that CSS and HTML decisions I make in the first hour of a build determine whether the page can pass at all.

This walkthrough is for front-end developers and designers who write their own markup and styles and want a repeatable process that catches the six error types responsible for 96% of all detected issues (WebAIM Million). I am not going to pretend this is a complete audit method. It is the checklist I run before I show a page to anyone else.

1. Start with semantic HTML, not div soup

My first pass on any new page is structural. I open a blank file and type the regions before I type a single style rule. A section begins with a heading, an article wraps content that would make sense on its own, and an aside holds material only indirectly related to the main content (MDN Web Docs). This is not decoration. Screen readers use those elements to announce page regions, so a keyboard or screen-reader user can jump straight to the main content or the navigation instead of tabbing through everything.

I see developers wrap every block in a div and then add ARIA roles to recreate what a header or nav element would have given them for free. That is backwards. The div carries no semantic value, and stacking dozens of them clutters the markup until nobody can tell what the page is supposed to be (MDN Web Docs). If you are reaching for a role attribute, ask first whether the right element exists. It usually does.

Quick tip: before you write any CSS, run your HTML through a validator and read the outline. If the heading levels jump or the main region is missing, fix that before you style anything.

2. Treat contrast as a build requirement, not a polish pass

Low-contrast text was present on 83.9% of home pages in the WebAIM sample, making it the single most common detected issue (WebAIM Million). I used to treat contrast as something to check at the end, after the brand colors were locked. That is exactly why it fails. By the time the palette is approved, changing it is a political fight. So I check contrast when I define the tokens.

The rule I apply is simple. Normal text needs a contrast ratio of at least 4.5:1, and large text needs at least 3:1 (WCAG 2.1 (AA)). Buttons, icons, and focus states need at least 3:1 against adjacent colors (WCAG 2.1 (AA)). I keep a contrast checker open while I pick grays. A light gray that looks elegant on my monitor is often sitting at 2.8:1, which is a failure that will show up in real audits.

There is a second layer here that teams forget. About one in twelve men have some form of color vision deficiency (NEI (National Eye Institute)). If the only thing distinguishing an error state from a success state is red versus green, a meaningful slice of your audience cannot read it. I pair color with an icon or a text label every time.

3. Build responsive layouts around content, not devices

I do not design to specific device widths anymore. Breakpoints should be added when the content demands them, not at fixed device sizes (MDN Web Docs). I start with a fluid grid using relative units and let the layout tell me where it breaks. When a line of text gets too long or a card gets too cramped, that is where I add a media query.

For type, I use clamp() so font sizes scale with the viewport between safe bounds, like clamp(1rem, 2.5vw, 2rem) (MDN Web Docs). But I never size text with viewport units alone, because that prevents zooming. I combine them with fixed units, such as calc(1.5rem + 4vw) (MDN Web Docs). This matters more than most teams realize. WCAG requires text to be resizable up to 200 percent without loss of content or functionality (W3C WCAG 2.1 Understanding Resize Text).

I also test at the reflow width. Content must not require scrolling in two dimensions at a width equivalent to 320 CSS pixels for vertical content (W3C WCAG 2.1 Understanding Reflow). If I have to scroll sideways to read a paragraph on a narrow screen, I have failed that criterion, and I have probably failed it because I set a fixed pixel width somewhere.

What can go wrong: container queries are widely supported now, but I have watched developers nest them without checking the fallback. If a component relies entirely on @container and the browser does not support it, the component can collapse or overflow. I always verify the base layout works before I layer container queries on top.

4. Make focus and interaction states visible and operable

This is the step most teams skip, and it is where I have seen the most embarrassing bugs. All functionality must be operable through a keyboard interface (W3C WCAG 2.1 Understanding Keyboard), and there must be a visible focus indicator so keyboard users know where they are (W3C WCAG 2.1 Understanding Focus Visible). If you set outline: none without a replacement, you have broken the page for anyone who does not use a mouse.

I style focus states as deliberately as I style buttons. Then I check that when a component receives focus, it is not entirely hidden by author-created content like a sticky header (W3C WCAG 2.2 Understanding Focus Not Obscured (Minimum)). This happens constantly with fixed headers. I scroll the page, tab through it, and watch whether the focused element disappears behind the bar.

Touch targets get the same treatment. Apple's guidelines set a minimum of about 44 by 44 points (Apple Human Interface Guidelines), and Android recommends 48 by 48 dp with spacing between targets (Android Developers (accessibility)). WCAG 2.2 sets the floor at 24 by 24 CSS pixels for pointer targets, with exceptions for spacing and inline text (W3C WCAG 2.2 Understanding Target Size (Minimum)). I design to the larger of those numbers because the smaller floor is a minimum, not a goal.

Forms deserve their own pass. Input errors must be identified in text and described to the user (W3C WCAG 2.1 Understanding Error Identification). I use the constraint validation API and setCustomValidity to write specific messages, and I add novalidate when I want full control over how errors appear (MDN Web Docs (Form validation)). Client-side validation is not security. I still validate everything on the server.

One more habit: I wrap motion in a prefers-reduced-motion check. The media query detects a system-wide reduce-motion preference, and I tone down or remove animation when it is set (MDN Web Docs).

None of this requires a framework or a budget. It requires deciding, in the first hour of a build, that the markup and the styles will carry the accessibility work instead of patching it later. The 95.9% failure rate is not inevitable. It is the accumulated result of skipping steps like these. Pick one step today, run it on your current project, and fix what you find.

Sources

  • WebAIM Million - https://webaim.org/projects/million/
  • WCAG 2.1 (AA) - https://www.w3.org/WAI/WCAG21/quickref/
  • MDN Web Docs - https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Responsive_Design
  • W3C WCAG 2.1 Understanding Resize Text - https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html
  • W3C WCAG 2.1 Understanding Reflow - https://www.w3.org/WAI/WCAG21/Understanding/reflow.html
  • W3C WCAG 2.2 Understanding Target Size (Minimum) - https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html

Share this article:

Comments (0)

No comments yet. Be the first to comment!