Beyond the UI: Understanding Modern Frontend Engineering · Part 13
New to this series? Start with Part 1
Web Accessibility Explained: Building Interfaces Everyone Can Use
Imagine opening a banking application, navigating to the transfer page, entering the recipient’s details, and preparing to send money. The interface looks polished: the forms are neatly arranged, the colors match the brand, and the buttons and animations respond smoothly. But when you try to complete the transfer using only a keyboard, the experience falls apart. Focus disappears as you press Tab, a custom dropdown ignores the arrow keys, and pressing Enter on Submit gives no clear indication of whether the transaction succeeded.
For someone navigating without being able to see the screen, the same application may present even more barriers. Visual polish cannot compensate for controls that cannot be identified or operated. This makes accessibility an engineering concern that affects how we structure HTML, manage application state, handle navigation, build components, and communicate errors and outcomes.
Accessibility is often addressed after an interface has been built, through changes such as adding alternative text or adjusting contrast. Those changes matter, but they are part of a wider responsibility. The interface users see is not the only interface the browser creates. Behind the visual page, the browser exposes another representation that assistive technologies use to understand and interact with the application.
Understanding that representation changes how we approach frontend development. In this article, we’ll explore how browsers expose accessibility information and how decisions about semantics, keyboard behavior, focus, and feedback determine whether people can complete the application’s core tasks.
1. What Web Accessibility Actually Means
Web accessibility means designing and building websites and applications so that people with different abilities, devices, and interaction methods can perceive, understand, navigate, and use them. That includes people who:
- Navigate using keyboards rather than mice.
- Use screen readers to interpret content.
- Have limited vision or difficulty distinguishing certain colors.
- Use screen magnification or large text.
- Have limited mobility or motor control.
- Experience hearing impairments.
- Have cognitive or learning differences.
- Experience temporary or situational limitations.
Accessibility also matters in temporary and situational circumstances. Someone recovering from a broken arm may depend on keyboard or voice navigation, while a person using a phone in bright sunlight may struggle with low-contrast text. Captions can help someone watching a tutorial in a noisy environment, and a simpler, more predictable interface can benefit a user with a slow connection. These situations differ, and not every accessibility technique addresses all of them. But they illustrate why accessible design often improves usability beyond a narrowly defined group. The objective is not to make every experience identical.
It is to ensure that people can accomplish the same meaningful tasks through different ways of interacting with the application. For a banking application, that might mean being able to review balances, complete transfers, and understand transaction outcomes without relying on a mouse or visual cues. For an e-commerce application, it means being able to search for products, choose variations, manage a cart, and complete checkout. Accessibility is therefore closely connected to functionality. If someone cannot complete the application’s core tasks using their available interaction method, the interface is not fully functional for them.
2. The Four Principles of Accessibility
The Web Content Accessibility Guidelines, commonly known as WCAG, organize accessibility around four foundational principles. These are often abbreviated as POUR.
| Principle | Meaning | Example |
|---|---|---|
| Perceivable | Information must be available through accessible forms of presentation | Providing text alternatives for meaningful images |
| Operable | Users must be able to navigate and interact with the interface | Supporting keyboard navigation |
| Understandable | Content and interactions must be predictable and comprehensible | Clear instructions and useful validation messages |
| Robust | Content must work reliably with browsers and assistive technologies | Using valid semantics and appropriate accessibility APIs |
These principles help us examine an interaction from several perspectives. Consider a form that asks for an email address. If the input has no accessible label, some users may not understand what information is required. That is a perceivability problem. If the input cannot receive keyboard focus, it is an operability problem. If an error appears but does not explain how to correct the value, it is an understandability problem. If the form uses invalid accessibility semantics that assistive technologies cannot interpret consistently, it is a robustness problem. A single component can fail in several of these ways at once, so assessing its accessibility requires considering the entire interaction.
3. The Browser Creates More Than a Visual Page
In the first article of this series, we explored how browsers transform HTML, CSS, and JavaScript into pixels. A simplified rendering process looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HTML
|
v
DOM
|
+----------------+
| |
v v
CSSOM Accessibility
| Tree
v |
Render Tree v
| Assistive
v Technologies
Layout
|
v
Paint
|
v
Pixels
This diagram is conceptual rather than a strict browser implementation sequence. The important point is that browsers expose more than the visual rendering of a document. They also derive accessibility information from the DOM, HTML semantics, relevant CSS properties, and accessibility attributes. This information is exposed through platform accessibility APIs. Screen readers and other assistive technologies use those APIs to interpret the page. For example:
1
<button>Save Changes</button>
Visually, the browser renders a button. Semantically, the browser exposes information such as:
1
2
3
Role: button
Name: Save Changes
State: enabled
A screen reader can use that information to announce the control and communicate how the user can interact with it. Now consider:
1
2
3
<div class="button">
Save Changes
</div>
Styling this div to look like a button does not give it button semantics or native keyboard behavior. The browser sees a generic container rather than a native interactive control. This difference is central to accessible frontend engineering. Visual appearance and semantic meaning are separate concerns. CSS controls how something looks, while HTML semantics help describe what it is.
4. Understanding the Accessibility Tree
The accessibility tree is a browser-maintained representation of the information that assistive technologies need to understand an interface. It is related to the DOM, but it is not simply a duplicate. The browser may omit elements that do not contribute meaningful accessibility information, combine information from multiple DOM nodes, and expose properties such as roles, names, descriptions, and states. Consider:
1
2
3
4
5
6
7
8
9
10
11
12
13
<main>
<h1>Account Overview</h1>
<section aria-labelledby="balance-heading">
<h2 id="balance-heading">
Current Balance
</h2>
<p>KES 24,500</p>
<button>View Transactions</button>
</section>
</main>
A simplified accessibility representation might be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Main
|
+-- Heading, Level 1
| "Account Overview"
|
+-- Region
Name: "Current Balance"
|
+-- Heading, Level 2
| "Current Balance"
|
+-- Text
| "KES 24,500"
|
+-- Button
"View Transactions"
This representation allows assistive technologies to expose the structure of the page. A screen reader user may navigate between headings, landmarks, buttons, form fields, and other meaningful elements. That is a fundamentally different navigation model from visually scanning the screen. A sighted user might locate the Transactions button by recognizing its position, color, or surrounding content. A screen reader user may locate it through its accessible name and role. If those semantics are missing, the application becomes much harder to understand.
The accessibility tree is not the DOM
A DOM element does not always correspond to an independently exposed accessibility node. For example, an element hidden with display: none is generally excluded from the accessibility tree. An element with aria-hidden="true" is also hidden from accessibility APIs, although this attribute does not automatically remove it from keyboard navigation. That can create a dangerous mismatch: an element may remain focusable while being hidden from a screen reader. Understanding these differences becomes especially important when building dialogs, menus, tabs, and dynamically updated interfaces.
5. Semantic HTML Is the Foundation
One of the most effective accessibility techniques is to use the HTML element that represents the intended meaning and behavior. A navigation area illustrates the difference.
1
2
3
4
5
6
7
8
9
<div class="navigation">
<div class="navigation-item">
Home
</div>
<div class="navigation-item">
Reports
</div>
</div>
This structure provides little information about its purpose. A more semantic implementation is:
1
2
3
4
<nav aria-label="Primary navigation">
<a href="/">Home</a>
<a href="/reports">Reports</a>
</nav>
Now the browser can expose a navigation landmark and native links. The links support expected browser behaviors, including keyboard activation, opening in a new tab, and copying link destinations. We gain these capabilities without implementing them ourselves. The same principle applies throughout the document.
| Purpose | Appropriate HTML |
|---|---|
| Main page content | <main> |
| Site navigation | <nav> |
| Independent article | <article> |
| Page or section heading | <h1> through <h6> |
| Perform an action | <button> |
| Navigate to another location | <a href="..."> |
| Collect user input | <input>, <select>, <textarea> |
| Group related form controls | <fieldset> and <legend> |
| Present tabular data | <table> with appropriate headers |
Semantic HTML delegates behavior to the browser, giving us built-in interactions that are usually more reliable than a custom imitation.
6. Why Buttons and Links Are Not Interchangeable
One of the most common frontend accessibility mistakes is using buttons and links interchangeably. They may look similar, but they communicate different intentions. A link navigates to a destination.
1
2
3
<a href="/transactions">
View Transactions
</a>
A button performs an action.
1
2
3
<button type="button">
Refresh Balance
</button>
This distinction affects both semantics and interaction. A native button can generally be activated using Enter or Space when focused. A native link is generally activated using Enter. Links also provide navigation-specific browser functionality. Consider:
1
2
3
<div onclick="openTransactions()">
View Transactions
</div>
This might work with a mouse. But the developer now needs to implement keyboard focusability, activation behavior, accessible semantics, and other expected interactions. A native link already provides those capabilities. The engineering lesson is simple: Before building a custom interactive element, ask whether HTML already provides the behavior you need.
7. Keyboard Accessibility Is More Than Supporting Tab
Many developers test keyboard accessibility by pressing Tab a few times. That is a useful starting point, but it is not sufficient. A fully keyboard-operable interface needs to support the expected interactions for its controls. For example:
- Tab and Shift+Tab move through sequentially focusable elements.
- Enter activates links and many controls.
- Space activates native buttons and certain other controls.
- Arrow keys are commonly used within composite widgets such as tab lists, radio groups, and menus.
- Escape may close dialogs or dismiss certain overlays.
The expected keyboard behavior depends on the component: a native text input behaves differently from a radio group, just as a dialog behaves differently from a menu. A custom tab component may require a different navigation model from a collection of ordinary buttons. Following predictable interaction patterns helps users apply what they already know about those controls.
Focus order matters
Consider:
1
2
3
<button>Open Filters</button>
<input aria-label="Search transactions" />
<button>Export</button>
The browser generally follows the document’s sequential focus order. If the visual layout is significantly different from the DOM order, keyboard navigation may feel confusing. For example, the user might see controls arranged from left to right, but pressing Tab moves unpredictably around the page. CSS can change visual order without changing DOM order. That flexibility is useful, but it can also create accessibility problems. A good rule is to keep the DOM order aligned with the logical reading and interaction order.
8. Focus Management in Dynamic Applications
Traditional websites often rely on full-page navigation, where loading a new document changes the navigation context. Single-page applications must handle that transition within the current document. A route transition may replace most of the visible content without causing a full document navigation. From a visual perspective, the page has changed. But keyboard focus may remain on the navigation link that triggered the transition, or even on an element that no longer exists. This creates a disconnect between what the user sees and where their interaction context is located. Consider:
1
2
3
4
5
6
7
8
9
10
11
User activates "Reports"
|
v
Frontend changes route
|
v
Reports page renders
|
v
Keyboard focus remains
on previous navigation control
A better navigation strategy may involve updating the document title, announcing the route change appropriately, and moving focus to the new page heading or main content when that matches the interaction model. For example:
1
2
3
4
5
6
7
function focusPageHeading() {
const heading = document.querySelector(
"#page-heading"
);
heading?.focus();
}
1
2
3
<h1 id="page-heading" tabindex="-1">
Reports
</h1>
The tabindex="-1" attribute allows programmatic focus without inserting the heading into the normal Tab sequence. However, focus should not be moved indiscriminately after every render. Unexpected focus changes can be disorienting, especially while someone is typing or interacting with a control. Focus management must be tied to meaningful transitions.
9. Building Accessible Dialogs
Dialogs are a particularly useful example because they combine several accessibility concerns. Imagine a button that opens a confirmation dialog.
1
2
3
<button id="delete-button">
Delete Account
</button>
When the dialog opens, the application should consider:
- How the dialog is identified to assistive technologies.
- Where keyboard focus moves.
- Whether users can interact with the page behind it.
- How the dialog closes.
- Where focus returns afterward.
Modern browsers provide a native <dialog> element.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dialog
id="confirmation-dialog"
aria-labelledby="dialog-title"
>
<h2 id="dialog-title">
Delete Account?
</h2>
<p>
This action cannot be undone.
</p>
<button id="cancel-button">
Cancel
</button>
<button id="confirm-button">
Confirm Delete
</button>
</dialog>
1
2
3
4
5
6
7
8
9
10
11
const dialog = document.querySelector(
"#confirmation-dialog"
);
const trigger = document.querySelector(
"#delete-button"
);
trigger.addEventListener("click", () => {
dialog.showModal();
});
Calling showModal() provides native modal behavior, including placing the dialog in the top layer and making the rest of the document inert while it is open. The application still needs to ensure that the dialog has a useful accessible name, that its controls are understandable, and that focus behavior is appropriate. When the dialog closes, focus should normally return to the element that opened it, unless the workflow makes another destination more logical. If a destructive action removes the trigger, focus may need to move to a nearby meaningful element instead. Choosing the right focus destination requires understanding the user’s workflow as well as the dialog’s attributes.
10. Accessible Names: How Controls Explain Themselves
A screen reader needs a meaningful way to identify interactive elements. This is where accessible names become important. Consider:
1
2
3
<button>
<svg><!-- search icon --></svg>
</button>
Visually, the icon may clearly communicate Search. But the button may not have a useful accessible name. A better implementation is:
1
2
3
4
5
6
7
8
<button
type="button"
aria-label="Search"
>
<svg aria-hidden="true">
<!-- search icon -->
</svg>
</button>
The button now has an accessible name. For visible text controls, however, the visible text is usually preferable.
1
2
3
<button>
Search Transactions
</button>
Accessible names can come from several sources, including visible text, associated labels, and ARIA attributes. The browser follows an accessible name computation algorithm to determine which information takes precedence. Developers should avoid adding redundant labels unnecessarily. For example:
1
2
3
<button aria-label="Submit">
Send Payment
</button>
Here, the ARIA label may override the visible text as the accessible name. A screen reader user may hear Submit while a sighted user sees Send Payment. That inconsistency can cause problems, especially for people using speech recognition software. In general, accessible names should align with visible labels.
11. ARIA: Powerful but Easy to Misuse
ARIA stands for Accessible Rich Internet Applications. It provides attributes that help describe roles, states, properties, and relationships when native HTML semantics are insufficient. For example:
1
2
3
4
5
6
<button
aria-expanded="false"
aria-controls="filter-panel"
>
Filters
</button>
The aria-expanded attribute communicates whether the controlled content is expanded. The aria-controls attribute identifies the related element. When the panel opens:
1
2
3
4
button.setAttribute(
"aria-expanded",
"true"
);
This communicates a state change to assistive technologies. But ARIA has an important limitation. ARIA changes accessibility semantics. It does not automatically implement behavior. For example:
1
2
3
<div role="button">
Save
</div>
Adding role="button" does not automatically make the element keyboard-focusable or implement native button activation behavior. You would still need to build those interactions. This is why native HTML should be the first choice. ARIA is most useful when it fills a semantic gap that HTML cannot adequately express. Using more ARIA does not necessarily make an application more accessible. Incorrect ARIA can make it less accessible.
12. Accessible Forms: Labels, Instructions, and Errors
Forms are among the most important parts of many applications. They are also where accessibility failures can prevent users from completing critical tasks. Consider:
1
2
3
4
<input
type="email"
placeholder="Email Address"
/>
The placeholder provides a visual hint, but it should not replace a proper label. Placeholder text may disappear when the user starts typing, and it is not a reliable substitute for an accessible name. A better implementation is:
1
2
3
4
5
6
7
8
9
10
<label for="email">
Email Address
</label>
<input
id="email"
name="email"
type="email"
autocomplete="email"
/>
The label is programmatically associated with the input, and clicking it can also focus the field.
Providing instructions
Some fields need additional explanation.
1
2
3
4
5
6
7
8
9
10
11
12
13
<label for="password">
Password
</label>
<p id="password-help">
Use at least 12 characters.
</p>
<input
id="password"
type="password"
aria-describedby="password-help"
/>
The aria-describedby attribute associates the help text with the input. Assistive technologies can expose that description alongside the field.
Communicating validation errors
Now imagine the user submits an invalid email address. A visual red border alone is not enough. The application should communicate what went wrong.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<label for="email">
Email Address
</label>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<p id="email-error">
Enter a valid email address.
</p>
The error message is connected to the field, and aria-invalid indicates that the current value is invalid. For a form containing several errors, a summary near the top can help users understand what needs attention. The application may also move focus to that summary after an unsuccessful submission, provided the behavior is predictable and appropriate. Useful error feedback explains the problem and helps the user recover.
13. Dynamic Content and Live Regions
Modern frontend applications update content without reloading the page, whether a user adds a product to a cart, a file finishes uploading, or a background request fails. Visually, these changes may be obvious. But a screen reader does not necessarily announce every DOM update automatically. ARIA live regions provide a mechanism for communicating relevant changes. Consider:
1
2
3
4
<div
id="status-message"
role="status"
></div>
Then:
1
2
3
4
5
6
const statusMessage = document.querySelector(
"#status-message"
);
statusMessage.textContent =
"Your changes have been saved.";
A status region can communicate nonurgent updates without requiring the user to move focus. For urgent information, role="alert" may be appropriate. However, not every update should be announced. Imagine an analytics dashboard that refreshes several values every second. If every update triggers a screen reader announcement, the interface becomes overwhelming. Live regions should communicate meaningful events, not every internal state change. A useful distinction is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Internal state changes
|
v
Does the user need to know?
|
+-- No --> Do not announce
|
+-- Yes
|
v
Is the information urgent?
|
+-- No --> Status message
|
+-- Yes --> Alert
This is an engineering decision about information priority. It resembles the broader challenge of designing notifications in distributed systems: not every event deserves immediate attention.
14. Color Contrast and Visual Accessibility
Accessibility also affects visual design. Color contrast helps determine whether text and interface elements can be distinguished from their backgrounds. WCAG defines contrast requirements for different categories of content. For example, under WCAG 2.2 Level AA, normal-sized text generally requires a contrast ratio of at least 4.5:1, while qualifying large text generally requires at least 3:1. Certain non-text interface components and graphical objects also have contrast requirements. These thresholds have exceptions and specific applicability rules, so they should be evaluated against the relevant success criterion rather than treated as universal rules for every pixel. Consider:
1
2
3
4
.muted-text {
color: #c5c5c5;
background: #ffffff;
}
This may look subtle and elegant on a high-quality monitor. But it may be difficult to read for users with low vision or under challenging lighting conditions. Contrast should be checked rather than judged purely by appearance.
Color should not be the only signal
Imagine a form where invalid fields are indicated only by red borders.
1
2
Email Address [red border]
Phone Number [normal border]
Someone who cannot reliably distinguish those colors may miss the error. A better design combines color with text, icons, or other meaningful indicators.
1
2
3
4
5
Email Address
[ Invalid email address ]
Phone Number
[ Valid input ]
The same principle applies to charts. A chart should not rely exclusively on red and green lines to distinguish two data series. Labels, patterns, line styles, or other indicators can make the information easier to interpret.
15. Zoom, Reflow, and Responsive Accessibility
Responsive design is often discussed in terms of screen sizes. But accessibility introduces another important dimension: zoom and text enlargement. A user with low vision may increase browser zoom significantly. An interface that works at 100% zoom may become unusable at 200% or 400%. For example, fixed-width layouts can cause horizontal scrolling that makes reading difficult. Buttons may overlap. Navigation items may disappear behind other elements. Text may become clipped inside containers with fixed heights. Consider:
1
2
3
4
.card {
height: 120px;
overflow: hidden;
}
If the user increases text size, the content may no longer fit. A more flexible approach might use:
1
2
3
.card {
min-height: 120px;
}
This allows the container to grow when needed. Of course, not every fixed dimension is inherently inaccessible. The important question is whether content and functionality remain available when users enlarge text or change the viewport. Responsive accessibility means designing layouts that adapt to users, not merely to devices.
16. Motion, Animation, and Reduced Motion Preferences
Animations can improve perceived responsiveness and communicate changes. But they can also create accessibility problems. Large movements, parallax effects, flashing content, and constant motion may cause discomfort or interfere with concentration. Operating systems commonly provide a reduced-motion preference that websites can detect through CSS.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.card {
transition:
transform 200ms ease,
opacity 200ms ease;
}
.card:hover {
transform: translateY(-4px);
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
.card:hover {
transform: none;
}
}
This example removes an optional decorative movement for users who prefer reduced motion. Not every animation must necessarily disappear. Some animations communicate important state transitions or spatial relationships. The objective is to avoid unnecessary motion and provide alternatives where appropriate. A progress indicator, for example, can communicate loading through text or a static progress bar rather than relying entirely on continuous animation. This connects accessibility directly to interaction design. The question is not simply whether an animation looks attractive. It is whether the animation helps users understand the interface without creating unnecessary barriers.
17. Images, Icons, and Meaningful Alternatives
Alternative text should reflect an image’s purpose: some images communicate essential information, while others are purely decorative. For a meaningful image:
1
2
3
4
<img
src="/charts/revenue-growth.png"
alt="Revenue increased steadily from January to June."
/>
The alternative text communicates the essential message. For a purely decorative image:
1
2
3
4
<img
src="/decorative-pattern.svg"
alt=""
/>
An empty alt attribute allows the image to be treated as decorative by assistive technologies. But complex images need more careful treatment. Imagine a financial dashboard containing a chart with twelve months of revenue data. A short alternative such as “Revenue chart” is not sufficient if the chart communicates important business information. The application may need a textual summary, a data table, or another accessible representation of the underlying information. The principle is that users should be able to access the meaning of the content, not merely learn that an image exists.
18. Accessible Tables and Data-Heavy Interfaces
Tables are common in financial applications, administrative dashboards, and reporting systems. A visual table might be easy to scan because columns and rows align spatially. But assistive technologies need structural information to associate values with their headers. Consider:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<table>
<caption>
Monthly Revenue
</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Revenue</th>
<th scope="col">Expenses</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">January</th>
<td>KES 250,000</td>
<td>KES 180,000</td>
</tr>
<tr>
<th scope="row">February</th>
<td>KES 310,000</td>
<td>KES 205,000</td>
</tr>
</tbody>
</table>
This structure describes the table through its caption and associates values with the column headers that identify categories and the row headers that identify records. A screen reader can use these relationships to provide context when navigating cells. For more complex tables, additional header associations may be necessary.
Interactive data tables
Adding sorting, filtering, row selection, and pagination introduces further accessibility requirements. A sortable column should communicate its current sorting state. For example:
1
2
3
4
5
6
7
8
<th
scope="col"
aria-sort="ascending"
>
<button type="button">
Revenue
</button>
</th>
The button provides an interactive control, while aria-sort communicates the current sort direction. When the sort order changes, the application should update the attribute accordingly. The interface should also ensure that the data change is understandable and that focus is not unexpectedly lost. Complex data grids may require specialized keyboard navigation patterns. The decision to build a custom grid should therefore account for accessibility implementation and testing costs, not only visual flexibility.
19. Accessibility in Single-Page Applications
Single-page applications introduce accessibility challenges that traditional document navigation often handles automatically. When navigating between routes, the application may need to manage:
- Document titles.
- Main content landmarks.
- Focus transitions.
- Route change announcements.
- Loading states.
- Error states.
- Scroll position.
Consider a user navigating from the dashboard to a reports page. A visual user sees the new heading and content. But a screen reader user may not automatically receive a clear indication that navigation has occurred. Updating the document title helps communicate context.
1
2
document.title =
"Reports | Financial Dashboard";
The application should also provide a logical way to reach the new content. This does not mean every route change requires the same focus behavior. A navigation initiated through a menu may require different handling from a background update that changes part of the current page. The key is to make navigation understandable and predictable.
Skip links
A skip link allows keyboard users to bypass repeated navigation and move directly to the main content.
1
2
3
4
5
6
7
8
9
10
11
<a class="skip-link" href="#main-content">
Skip to main content
</a>
<nav>
<!-- navigation links -->
</nav>
<main id="main-content">
<!-- page content -->
</main>
The link can remain visually hidden until it receives focus.
1
2
3
4
5
6
7
8
9
.skip-link {
position: absolute;
top: -100px;
left: 16px;
}
.skip-link:focus {
top: 16px;
}
This small feature can make a significant difference on applications with large navigation structures.
20. Accessibility and Custom Components
Modern frontend applications frequently use component libraries. That is useful for consistency, but it introduces an architectural question. Who owns the accessibility behavior of a component? Imagine a team building a custom dropdown. The component may need to handle:
- Accessible naming.
- Keyboard focus.
- Arrow-key navigation.
- Selection state.
- Disabled options.
- Escape behavior.
- Type-ahead navigation.
- Appropriate roles and relationships.
A visually simple dropdown can contain substantial interaction complexity. When it is reused across fifty screens, an accessibility bug spreads with it, while a fix to the shared component can improve the entire application. This is why accessibility should be part of design-system engineering. A well-designed component library should provide accessible defaults and make it difficult for developers to accidentally remove essential behavior.
For example, a reusable form input component should support visible labels, descriptions, errors, disabled states, and focus indicators. A dialog component should implement predictable focus management. A tab component should follow an established keyboard interaction pattern. The best accessibility improvements are often architectural rather than repetitive. Instead of fixing the same issue on twenty pages, fix the abstraction that created it.
21. Testing Accessibility Properly
Accessibility testing requires more than checking whether a page passes an automated scanner. Automated tools are valuable, but they cannot fully determine whether an interface is understandable and usable. For example, a tool may verify that an image has an alt attribute. It cannot always determine whether the alternative text accurately communicates the image’s meaning. Similarly, a tool may detect whether a button has an accessible name. It may not know whether that name makes sense in the context of the workflow. A practical accessibility testing strategy combines several approaches.
Automated checks
Tools such as axe-core and browser accessibility audits can identify many common problems. Examples include missing labels, certain contrast failures, invalid ARIA usage, and structural issues.
Keyboard testing
Navigate the application without a mouse. Can you reach every important control? Can you see where focus is? Can you activate buttons, navigate menus, and close dialogs? Does focus move predictably?
Screen reader testing
Test important workflows using screen readers. Examples include NVDA with a supported browser on Windows, VoiceOver on macOS or iOS, and TalkBack on Android. Different combinations may behave differently, so testing should reflect the environments your users rely on.
Zoom and responsive testing
Increase browser zoom and text size. Check whether content remains available and usable.
Real-user testing
Where possible, involve people who use assistive technologies or have relevant accessibility needs. This can reveal usability problems that automated tests and developer assumptions miss. The objective is not simply to produce a passing audit score. It is to ensure that people can complete meaningful tasks.
22. Accessibility as a Continuous Engineering Practice
A one-time accessibility audit before launch cannot account for the continuous changes in a frontend application. Teams introduce components, evolve design systems, add routes, and update third-party dependencies, any of which can affect established behavior. A component that was accessible last month may become inaccessible after a seemingly unrelated refactor. This means accessibility should be integrated into normal development workflows. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Design
|
v
Define Accessible Interaction
|
v
Implement Semantic Structure
|
v
Build Keyboard Behavior
|
v
Test with Automated Tools
|
v
Test Real Interactions
|
v
Review and Release
|
v
Monitor and Improve
Accessibility requirements should be considered during design and implementation, not only during testing. A pull request that introduces a new dialog should include the expected focus behavior. A new form should include labels, instructions, and error handling. A chart should have an accessible way to communicate its important information. These expectations become easier to maintain when they are part of the team’s definition of done.
23. A Practical Example: Building an Accessible Financial Dashboard
Let’s bring these ideas together. Imagine a financial dashboard containing:
1
2
3
4
5
6
7
8
9
10
11
12
13
Financial Dashboard
|
+-- Navigation
|
+-- Account Summary
|
+-- Recent Transactions
|
+-- Spending Chart
|
+-- Transfer Form
|
+-- Notifications
A visually polished implementation is only the beginning.
Navigation
Use semantic navigation landmarks and meaningful links. Provide a skip link so keyboard users can reach the main content efficiently.
Account summary
Use clear headings and readable text. Avoid relying exclusively on color to indicate positive or negative balances.
Recent transactions
Use a semantic table with appropriate headers. Ensure sorting controls are keyboard-operable and communicate their state.
Spending chart
Provide a meaningful summary or accessible data representation. Do not rely only on colored segments to distinguish categories.
Transfer form
Associate labels with inputs. Provide clear instructions and validation errors. Ensure the form can be completed using a keyboard.
Confirmation dialog
Use an appropriate dialog implementation. Manage focus predictably. Ensure users understand whether they are confirming a transfer or merely reviewing details.
Notifications
Announce meaningful status changes. For example, after a successful transfer, provide a clear confirmation message. Avoid announcing every background balance refresh. The architecture might look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Application State
|
v
UI Components
|
+-- Visual Presentation
|
+-- Semantic HTML
|
+-- Accessibility Attributes
|
+-- Keyboard Interactions
|
+-- Focus Management
|
v
Browser
|
+-- Visual Rendering
|
+-- Accessibility APIs
|
v
Different Interaction Methods
|
+-- Mouse / Touch
|
+-- Keyboard
|
+-- Screen Reader
|
+-- Voice Control
|
+-- Other Assistive Technologies
Accessibility is part of how the application communicates its structure, state, and behavior across different interaction methods.
24. Common Accessibility Mistakes
Several mistakes appear repeatedly in frontend projects.
Using generic elements for everything
A page built entirely from div and span elements may look correct while providing very little semantic information.
Removing focus indicators
Developers sometimes remove browser focus outlines for visual reasons.
1
2
3
button:focus {
outline: none;
}
Without an appropriate replacement, keyboard users may lose the ability to see where they are.
Using placeholders instead of labels
Placeholders are not reliable replacements for persistent, programmatically associated labels.
Relying only on color
Color alone should not communicate errors, status, or important distinctions.
Overusing ARIA
Adding ARIA attributes without understanding their behavior can create misleading semantics.
Moving focus unexpectedly
Automatic focus changes can interrupt users and make navigation confusing.
Announcing every state change
Excessive live-region announcements can overwhelm screen reader users.
Ignoring accessibility in shared components
A poorly implemented design-system component can spread the same problem across an entire application.
Treating automated tests as complete proof
Automated checks are useful, but they cannot replace interaction testing and human evaluation.
25. How to Prioritize Accessibility Improvements
When working with an existing application, fixing every issue at once may not be realistic. A practical approach is to prioritize barriers based on their impact. Start with the tasks users must be able to complete. For an e-commerce application, that might mean searching for products, adding items to a cart, and checking out. For a financial platform, it might mean logging in, viewing account information, and completing transactions. Then identify issues that prevent or seriously disrupt those workflows.
| Issue | Typical priority |
|---|---|
| Critical action cannot be completed with a keyboard | Very high |
| Form field has no accessible name | High |
| Dialog traps or loses focus incorrectly | High |
| Important status message is not communicated | High |
| Text contrast makes content difficult to read | High |
| Decorative image has unnecessary alternative text | Lower |
| Minor inconsistency in noncritical announcement wording | Lower |
This is not a substitute for meeting applicable accessibility requirements. It is a way to organize remediation work when dealing with an existing backlog. The most important objective is to remove barriers that prevent people from using the product.
Bringing It All Together
Accessible frontend engineering requires us to think beyond pixels. The browser does not simply render a visual interface. It also exposes semantic information, interaction states, and relationships that assistive technologies use to interpret the application. That means our engineering decisions affect multiple experiences simultaneously. Choosing a native button instead of a clickable div improves keyboard behavior and semantic clarity. Using a proper label improves form usability. Managing focus during route transitions helps users maintain context. Providing meaningful status announcements makes asynchronous operations understandable. Building accessible shared components improves consistency across the entire application. The overall relationship looks like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Frontend Architecture
|
v
Semantic HTML
|
v
Accessible Components
|
+-- Names and Roles
|
+-- States and Relationships
|
+-- Keyboard Behavior
|
+-- Focus Management
|
+-- Meaningful Feedback
|
v
Browser Accessibility APIs
|
v
Assistive Technologies
|
v
Users Can Complete Their Tasks
Accessibility is therefore not a separate layer that can always be added after development. It is an outcome of the architecture, semantics, and interaction patterns we choose.
Final Thoughts
Accessibility belongs alongside performance, visual design, maintainability, and functionality when we evaluate frontend engineering. A fast application still needs controls that people can operate, a polished interface needs meaningful labels, and a reusable component library needs interaction patterns that support the people using it.
People experience the web in different ways. Some scan a page visually, while others move through headings and landmarks or use keyboards, screen readers, magnification, voice control, and alternative input devices. Supporting those methods requires semantic structure, predictable focus, clear feedback, and testing that follows real workflows.
The measure of that work is whether people can complete the tasks the application promises to support. Treating accessibility as part of everyday engineering helps keep those tasks available as the product grows and changes.
What’s Next?
The next article in Beyond the UI: Understanding Modern Frontend Engineering is Part 14: Web Performance Explained: Understanding Core Web Vitals. We’ll examine how browsers measure user experiences, what Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS) tell us, and how to diagnose the engineering problems behind poor performance scores.
We’ll also explore the difference between laboratory measurements and real-user monitoring, including why improving a Lighthouse score does not always improve the experience of real users. These measurements are most useful when they help us understand what people actually encounter while using an application.
