Web Accessibility
Accessible engineering: semantics, keyboard and focus, forms, testing and WCAG in practice.
- Semantic HTML
- Keyboard & focus
- Manual testing
- WCAG
I use React for product interfaces where maintainability, state management, accessibility and API-driven workflows matter more than simply rendering pages.
State management · API integration · Forms · Accessibility
How I think about it
I prefer keeping state as close as possible to where it is used. Global state should solve an actual shared-state problem rather than becoming the default place for application data.
Key experience
Scope
Deep dive
When a React application grows, the problem is rarely whether React can render the interface. The challenge becomes defining clear ownership of state, components and API interactions.
I separate design-system components (buttons, fields, dialogs — no business knowledge), feature components that own a piece of product behaviour, and route-level components that compose them. Generic abstractions come after the third real repetition, not before the first; a component with fifteen boolean props is usually two components.
Most state is local: an open menu, a form draft, a selected tab. State shared by a few siblings moves to their closest common parent. Truly global state — the current user, the active organization, feature flags — is small and changes rarely.
Data from the server is different: it's a cache of backend state, with loading, staleness and invalidation. Handling it as server state through a dedicated data-fetching layer removes most of the global store code applications tend to accumulate.
// Local: only this component cares
const [isOpen, setIsOpen] = useState(false);
// Server state: cached, shared and refetched by the data layer
const { data: members, isLoading } = useMembers(organizationId);
// Global: small, app-wide, rarely changes
const { currentUser, organization } = useSession(); Every request has at least three states the interface must handle — loading, success and error — plus empty, which is the one people forget. Authentication headers, error parsing and retries live in one API client; request logic stays out of presentational components; and cached data is invalidated after mutations rather than patched by hand everywhere.
Optimistic updates are worth it for frequent, low-risk actions. For payments, permissions or anything hard to undo, I wait for the server.
Forms are where product rules meet the interface. The server remains the authority on validation; client-side validation exists for faster feedback. Complex forms — conditional fields, repeating groups, multi-step flows — need an explicit form state model, not dozens of independent useState calls.
Errors appear next to the field, are linked with aria-describedby, and on submit focus moves to the first problem.
React doesn't make interfaces inaccessible; custom components do. I start from native elements, build dialogs, menus and comboboxes on established interaction patterns, manage focus when they open and close and when routes change, and announce asynchronous updates through live regions.
Routes map to product areas and own their data loading, so each page has explicit loading and error states instead of spinners scattered through components. Protected routes check the session and permissions — knowing the server enforces them anyway.
In single-page applications, navigation should move focus and update the document title; otherwise screen-reader users aren't told that the page changed.
Most React performance problems are architectural: state stored too high, so every keystroke re-renders the page, or long lists rendered in full. I fix those first, then split code by route so people download only what they use. memo and useMemo are for measured problems; applied everywhere, they add complexity without making anything faster.
Folders organised by feature rather than file type, components small enough to understand in one read, utilities that are genuinely shared, and conventions applied consistently — naming, data fetching, error handling. The goal is that a developer can change a feature without understanding the whole application.
A design system starts as a handful of well-built primitives — button, field, dialog, menu — that are accessible, consistent and documented by usage. It grows when the product needs it, not ahead of it. Over-configurable components are harder to use correctly than a few clear variants.
I test behaviour the way a user experiences it: render, interact through roles and labels, assert what the user sees. Querying by role doubles as an accessibility check. Critical workflows — sign-in, invitations, permissions, payments — get the most coverage; implementation details get none.
In practice
Real products
React has been the interface layer for SaaS products where the frontend carries real product behaviour — and where accessibility is part of the product's credibility.
Accessibility Technology / SaaS
Accessibility SaaS with a React frontend on Node.js APIs — an interface that has to meet the accessibility standard the product helps others reach.
SaaS / Portfolio Builder
Portfolio builder where users create, customize and publish their presence — an interface-heavy React product backed by a Laravel API and a Python microservice.
Accessible engineering: semantics, keyboard and focus, forms, testing and WCAG in practice.
Predictable API contracts: validation, errors, authorization, idempotency and webhooks.
Vue product interfaces: component boundaries, composables, state and Laravel integration.
Tenancy, permissions, plans, billing and the realities of running SaaS in production.
Build reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.
Learn moreDesign and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.
Learn moreWhether it's an architecture decision, a delivery problem or something specific to React, I can help you find a practical path forward.
What happens next
Share your goal
A few lines about the product, problem, or audit need.
Get a quick read
A practical reply on the best technical path forward.
Move forward
A clear scope, estimate, or concrete next step.