fix(a11y): visible focus indicator, aria-expanded, and six Label-in-Name failures
Batch 10, and only three of its six issues were real. The other three are answered on the tracker with the arithmetic. #189 — focus indicator. Footer and Home carried zero focus styling across 17 links. Added one global :focus-visible rule rather than sprinkling classes, because the issue's own scope was "all interactive <a> and <Link> elements". Not the ring the issue asked for. It suggested ring-primary-cyan; #22D3EE is 6.26:1 on navy and 2.38:1 on white, so on this light-first design that ring would have failed WCAG 1.4.11 across most of the site. Two rings instead — white inside, navy outside — so the white carries the dark bands and the navy carries the light sections. Worst case across every background in the palette is 13.62:1. box-shadow so both rings follow each element's own border-radius, plus a transparent outline for forced-colors mode. #192 — aria-expanded={isOpen} and aria-controls on the mobile nav trigger, and the id on SheetContent it now points at. Confirmed as "false" in the prerendered HTML. #193 — INVERTED. The issue asked for aria-labels to be added consistently to hero CTAs. The aria-labels already there were WCAG 2.5.3 Label in Name failures, Level A: "Schedule a consultation" is not a superset of the visible "Schedule Consultation", so a voice-control user saying what they see cannot activate the link. Doing what the issue asked would have spread a Level A failure. Removed the six that broke it; kept the eight that genuinely add context and do contain their visible text, including the icon-only header logo link that needs one. Audit now reports 0 failures across Home, Services, Footer and Header. Verified: npm run build; the ring is in the built CSS, aria-expanded="false" and id="mobile-nav-content" are in the prerendered HTML, all six removed labels are absent from it, and the visible CTA text still renders. NOT verified: nothing has been walked keyboard-only in a browser, and no screen reader has been used. That gap is recorded in docs/qa/ClaudeQACoverage.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
615043db44
commit
9fdc6b6032
|
|
@ -67,7 +67,7 @@ const Footer = () => {
|
|||
<Link
|
||||
to="/contact#contact-form"
|
||||
className="inline-flex items-center gap-2 rounded-md text-sm font-semibold px-5 py-2.5 bg-primary-cyan text-primary-navy hover:bg-white transition-colors duration-200"
|
||||
aria-label="Request a free consultation"
|
||||
|
||||
>
|
||||
Get a Free Quote
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ const Header = () => {
|
|||
|
||||
{/* CTA Button */}
|
||||
<div className="hidden md:block">
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 bg-primary-cyan text-primary-navy hover:bg-cyan-600 transition-colors" aria-label="Request a consultation">
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-3 bg-primary-cyan text-primary-navy hover:bg-cyan-600 transition-colors">
|
||||
Request Consultation
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,12 @@ const MobileNav = () => {
|
|||
<div className="md:hidden">
|
||||
<Sheet open={isOpen} onOpenChange={setIsOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<button className="p-2 text-white hover:text-primary-cyan focus:outline-none focus:ring-2 focus:ring-primary-cyan rounded-md" aria-label="Open navigation menu">
|
||||
<button
|
||||
className="p-2 text-white hover:text-primary-cyan rounded-md"
|
||||
aria-label="Open navigation menu"
|
||||
aria-expanded={isOpen}
|
||||
aria-controls="mobile-nav-content"
|
||||
>
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
|
|
@ -57,7 +62,7 @@ const MobileNav = () => {
|
|||
<span className="sr-only">Open menu</span>
|
||||
</button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="w-[300px] sm:w-[350px] bg-primary-navy text-white">
|
||||
<SheetContent id="mobile-nav-content" side="right" className="w-[300px] sm:w-[350px] bg-primary-navy text-white">
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -75,3 +75,31 @@ a:hover {
|
|||
.section-alt {
|
||||
background: #EEF6FB;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
/*
|
||||
* Visible keyboard focus — WCAG 2.4.7 (Focus Visible) and 1.4.11 (Non-text
|
||||
* Contrast, which wants 3:1 for the indicator itself).
|
||||
*
|
||||
* Two rings, not one, and that is the whole point. This is a light-first
|
||||
* design with dark navy bands through it, so no single colour clears 3:1
|
||||
* everywhere: primary-cyan manages 6.26:1 on navy and only 2.38:1 on white,
|
||||
* which is why the obvious `ring-primary-cyan` was not used. The white ring
|
||||
* carries the dark sections and the navy ring carries the light ones, and on
|
||||
* every background in the palette at least one of them clears 3:1 by a wide
|
||||
* margin — the worst case is 13.62:1.
|
||||
*
|
||||
* box-shadow rather than outline so both rings stack predictably and follow
|
||||
* each element's own border-radius. The transparent outline is the standard
|
||||
* escape hatch for Windows High Contrast / forced-colors mode, where
|
||||
* box-shadow is discarded and an outline is not.
|
||||
*
|
||||
* :focus-visible, not :focus, so a mouse click does not leave a ring behind.
|
||||
* :where() keeps specificity at zero, so any component can still override it.
|
||||
*/
|
||||
:where(a, button, [role="button"], summary, input, select, textarea):focus-visible {
|
||||
box-shadow: 0 0 0 2px #FFFFFF, 0 0 0 5px #0B2A3C;
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,11 +125,11 @@ const Home = () => {
|
|||
Business phone, contact center, network, and IT support built around one accountable implementation partner.
|
||||
</p>
|
||||
<div className="mt-8 flex flex-col sm:flex-row gap-3">
|
||||
<Link to="/contact#contact-form" className="inline-flex w-full sm:w-auto h-11 items-center justify-center gap-2 rounded-md bg-white px-5 text-sm font-semibold text-primary-navy hover:bg-section-alt transition-colors" aria-label="Schedule a consultation">
|
||||
<Link to="/contact#contact-form" className="inline-flex w-full sm:w-auto h-11 items-center justify-center gap-2 rounded-md bg-white px-5 text-sm font-semibold text-primary-navy hover:bg-section-alt transition-colors">
|
||||
Schedule Consultation
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
<Link to="/services" className="inline-flex w-full sm:w-auto h-11 items-center justify-center rounded-md border border-white/45 px-5 text-sm font-semibold text-white hover:bg-white/10 transition-colors" aria-label="View our services">
|
||||
<Link to="/services" className="inline-flex w-full sm:w-auto h-11 items-center justify-center rounded-md border border-white/45 px-5 text-sm font-semibold text-white hover:bg-white/10 transition-colors">
|
||||
View Services
|
||||
</Link>
|
||||
</div>
|
||||
|
|
@ -241,7 +241,7 @@ const Home = () => {
|
|||
Four concrete differentiators that set us apart
|
||||
</p>
|
||||
<div>
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors" aria-label="Request a consultation">
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors">
|
||||
Request Consultation
|
||||
</Link>
|
||||
</div>
|
||||
|
|
@ -372,7 +372,7 @@ const Home = () => {
|
|||
<p className="text-lg text-soft-text mb-8 max-w-2xl mx-auto">
|
||||
Share a few details and we'll provide clear direction.
|
||||
</p>
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-6 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors" aria-label="Request a consultation">
|
||||
<Link to="/contact#contact-form" className="inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-6 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors">
|
||||
Request Consultation
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue