fix(radio): keyboard a11y — skip link, focus-visible, mobile-menu

human-flow P0-P1 fixes for radio.azcomputerguru.com:
- K1: skip-to-content link (first tab stop) + id/tabindex on <main>.
- K2: global :focus-visible ring (accent outline) across links, buttons,
  inputs and player controls; reveal the seek-bar handle on focus.
- K3: mobile menu a11y — aria-expanded/aria-controls, Escape closes and
  restores focus to the toggle, focus moves to first link on open.
All token-based, no emojis. Not built (node_modules absent on this host).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 17:42:46 -07:00
parent 101e4888a4
commit 8ac514c9ea
3 changed files with 84 additions and 6 deletions

View File

@@ -51,12 +51,13 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
</script>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to content</a>
<header class="site-header">
<nav class="container site-nav">
<a href="/" class="site-logo">
<span class="logo-text">The Computer Guru Show</span>
</a>
<div class="nav-links">
<div class="nav-links" id="primary-nav">
<a href="/episodes">Episodes</a>
<a href="/blog">Blog</a>
<a href="/live">Live</a>
@@ -80,7 +81,7 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu">
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle menu" aria-expanded="false" aria-controls="primary-nav">
<span></span>
<span></span>
<span></span>
@@ -88,7 +89,7 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
</nav>
</header>
<main>
<main id="main-content" tabindex="-1">
<slot />
</main>
@@ -134,9 +135,32 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
});
// Mobile menu toggle
document.getElementById('mobile-menu-toggle').addEventListener('click', () => {
document.querySelector('.nav-links').classList.toggle('open');
document.getElementById('mobile-menu-toggle').classList.toggle('open');
const menuToggle = document.getElementById('mobile-menu-toggle');
const navLinks = document.querySelector('.nav-links');
function closeMobileMenu(returnFocus) {
navLinks.classList.remove('open');
menuToggle.classList.remove('open');
menuToggle.setAttribute('aria-expanded', 'false');
if (returnFocus) {
menuToggle.focus();
}
}
menuToggle.addEventListener('click', () => {
const isOpen = navLinks.classList.toggle('open');
menuToggle.classList.toggle('open', isOpen);
menuToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
if (isOpen) {
const firstLink = navLinks.querySelector('a');
if (firstLink) firstLink.focus();
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && navLinks.classList.contains('open')) {
closeMobileMenu(true);
}
});
// Fade-in on scroll