A curated collection of articles exploring this topic in depth.
Uncovering the Invisible Scaffolding
Building accessibility literacy, focusing on the discovery of the Accessibility Tree and the foundations of structural inclusivity.
Accessibility is sometimes considered at the end of a project to meet compliance requirements. However, it is more effective when integrated early as a key requirement and part of the design process.
For this site, I decided to treat accessibility as a key requirement and also a deliberate learning exercise. While I've always aimed for a good user experience, I wanted to move beyond "sensible defaults" and actively improve my accessibility literacy. This series documents that process: building a mental model for how the web is actually navigated and interpreted, starting with the realisation that every website has a "ghost version" known as the Accessibility Tree(opens in a new tab).
The Technical Constraints
Before diving into the implementation, consider the constraints this site operates under. As I've discussed in my series on Building a Production-Grade Blog, this site is a static export.
This means:
- No server-side runtime: Everything must be determined at build time.
- Minimal dependencies: I avoid heavy client-side libraries for focus management or modals.
- Minimal runtime behaviour: I prefer standard HTML and CSS over complex JavaScript state where possible.
The "Aha!" Moment: The Accessibility Tree
The biggest shift in my thinking occurred when I stopped thinking about "tags" and started thinking about the Accessibility Tree. This is the data structure that the browser generates from our HTML, which assistive technologies, like screen readers, actually use to understand the page.
If our HTML is the skeleton, the Accessibility Tree is the map. When that map is poorly drawn, navigation becomes guesswork.
1. Bypassing the Noise: Skip Links
My first realisation was how disorienting it is for a keyboard user to navigate a site. Every time a new page loads, they are forced to "tab through the noise" (every link in the header and navigation) before they reach the actual content.
The "Skip to Content" link is a simple but powerful solution. Initially implemented as an inline anchor, I later refactored it into a dedicated <SkipLink /> component to ensure consistent behaviour and focus management across the site.
// web/app/layout.tsx
import { SkipLink } from '@/components/skip-link';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<SkipLink />
<main id="main-content" tabIndex={-1}>
{children}
</main>
</body>
</html>
);
}By linking to a <main id="main-content"> tag, we give the user an express lane to the content they actually came for. Explicitly setting tabIndex={-1} on the main element ensures that focus is correctly moved even in browsers that don't naturally focus non-interactive elements via fragment identifiers.
2. Landmark Disambiguation
Assistive technologies allow users to jump between "landmarks" like <nav>, <header>, and <footer>. A key insight for me was that document structure is just as critical as visual layout.
However, if a page contains multiple landmarks of the same type, such as pagination controls at both the top and bottom of a post list, the Accessibility Tree becomes ambiguous. To improve literacy for these tools, I implemented unique aria-label attributes:
{/* Top Pagination */}
<Pagination ariaLabel="Top pagination" />
{/* ... post list ... */}
{/* Bottom Pagination */}
<Pagination ariaLabel="Bottom pagination" />Now, instead of hearing "Pagination, navigation" twice, a user hears "Top pagination" or "Bottom pagination," providing immediate clarity.
3. Pruning the Tree: aria-hidden
I also learned that pruning the Accessibility Tree is just as important as building it. Decorative elements, like the arrows (→) or icons in my "Read more" links, add visual flair but can clutter the experience for screen reader users.
To increase the "signal-to-noise" ratio, I wrapped these decorative elements in aria-hidden="true":
<Link href="/posts/my-post" aria-label="Read more about My Post">
Read more <span aria-hidden="true">→</span>
</Link>This ensures the screen reader focuses on the meaningful text without the interruption of redundant character descriptions.
Key Takeaways
These changes were my first practical application of accessibility in this site as a structural concern rather than a visual one. What started as a goal to "think about accessibility" has turned into a lesson in structural integrity. By understanding the Accessibility Tree, I've moved from guessing what's "correct" to intentionally designing for inclusivity.
In the next part, we'll tackle the logic of interaction: specifically, how to manage focus and state in a mobile menu without over-engineering the solution.