Accessibility
Accessibility isn't something we bolt on before launch. We build it in from the first commit, because a site that doesn't work for everyone isn't really finished.
Part of our published engineering standards: Performance, Accessibility, Security, Best Practices, and SEO.
In Robles v. Domino's Pizza, a blind customer sued because the company's website and app didn't work with his screen reader. In 2019 the Ninth Circuit held that the ADAAmericans with Disabilities Act applies to that website and app, and the U.S. Supreme Court refused to hear Domino's appeal, so the ruling stands.
So courts now treat the ADA as covering business websites, not just physical storefronts, and an inaccessible site is a real legal liability. Businesses get demand letters and lawsuits over it every year. No single federal regulation spells out the web standards for private companies, but the benchmark courts and the U.S. Department of Justice keep pointing to is WCAG 2.1 AA. That is what we build every site to meet. (General information, not legal advice.)
Source: Robles v. Domino's Pizza, LLC (opens in new tab) — U.S. Court of Appeals, Ninth Circuit (2019); U.S. Supreme Court certiorari denied, October 2019.
Semantic HTML first
Every page starts with meaningful markup.
We use native HTML elements for what they're actually for: <nav>
for navigation, <main> for the primary content,
<article> for self-contained pieces, and
<details>/<summary> for things like our FAQ
accordions, which run without a line of JavaScript.
Why semantic HTML matters Show less
Screen readers, search engines, and assistive tech all lean on semantic
structure to make sense of your content. A <div> with a
click handler isn't a button: it never lands in the keyboard tab order,
never announces its role, and ignores Enter and Space. So we use actual
<button> elements, a proper heading hierarchy, and real
landmark regions, and every user finds their
way around the same way.
Headings run in order, h1 through h6, and every
section heading carries a deep-linkable anchor ID. Screen reader users can
jump around by heading, and everyone else gets a shareable link straight to
a section.
Keyboard navigation
Every interactive element works with the keyboard alone. No mouse required.
How we handle keyboard access Show less
Our skip link lets keyboard users jump straight to the main content instead of tabbing through the whole nav first. It stays hidden until it's focused, then shows up with a high-contrast outline you can't miss.
Navigation dropdowns take full arrow-key control: Up and Down move between
links, Home and End jump to the first and last, Escape closes the menu, and
Tab moves focus out the way you'd expect. The
focus ringThe visible ring that appears around interactive elements when navigating by keyboard
uses :focus-visible everywhere, a 2px outline with a 2px
offset, so keyboard focus is always visible but never clutters things for
mouse users.
Respecting motion preferences
Animation adds warmth and polish, but it can make people with vestibular disorders feel ill. So we honour the prefers-reduced-motion media query on every animation we ship.
What we disable and how Show less
When someone's OS is set to reduce motion, we switch off the scroll-reveal animations (fade-up, scale-in, the staggered cascades), the hover micro-interactions, and the CTA rainbow. Everything stays fully visible. Nothing hides behind an animation that never plays.
We do it in two layers. A global kill-switch in our CSS reset drops
animation-duration and transition-duration to
near zero for everything, and individual animation files add their own
prefers-reduced-motion overrides where we want finer control. The
Sparkles component goes further, with its own
IntersectionObserver motion check that stops spawning particles altogether
when reduced motion is on.
Windows high contrast & forced colors
When forced-colors modeA Windows accessibility feature that overrides all colours to improve visibility for users with low vision is on, the browser overrides every colour on the page. If you don't handle that deliberately, custom borders, focus rings, and decorative bits can vanish and take the site's usability with them.
Our forced-colors strategy Show less
We ship a dedicated @media (forced-colors: active) block that
puts visible borders back on buttons (so they don't disappear into the
background), keeps focus indicators distinct, and holds card edges in place.
That covers 90%+ of browsers, meaning every
Chromium browser plus Firefox on Windows.
Instead of fighting the user's chosen colour scheme, we work with it. We use transparent borders that show up when the browser swaps in system colours, and we make sure controls stay recognisable by shape and border, not by colour alone.
Touch target sizing
Every interactive element meets the WCAG 2.5.5 (AAA) enhanced touch-target size of 44 × 44 pixels, well past the 24×24px WCAG 2.5.8 (AA) minimum, even when the element looks smaller than that.
How we achieve this without visual bloat Show less
Take our heading anchor links. The "#" icon looks small, but a
::after pseudo-element stretches the clickable area to a full
44px. Someone with a motor impairment, or anyone on a touchscreen, can tap
it without aiming carefully, and the design still looks clean.
Same goes for navigation links, buttons, toggle switches, and the FAQ accordions. If it's interactive, you can tap it, on any device, at any size.
Accessible view transitions
Our sites use Astro's view transitionsClient-side page transitions that swap content without full reloads, providing app-like navigation for smooth page-to-page navigation. Client-side routing can quietly break accessibility, though, if you don't manage focus carefully.
How we preserve focus after navigation Show less
After every swap we move focus to #main with
preventScroll: true, so keyboard users land in the new page's
content instead of being stranded at the top of the DOM. It behaves like a
normal full page load, but keeps the fluid feel of client-side navigation.
Accessibility is the baseline
Every TechTailors site ships this way by default.
Start a Conversation