a11y: skip-to-content link (0.72.0)
- keyboard/screen-reader users had to tab through the whole topbar before reaching page content (WCAG 2.4.1 Bypass Blocks gap) - add a skip link as the first focusable element: hidden until focused, slides in as a cyan pill, and on activation focuses the visible content container (works with the persistent-home structure, and avoids the hash-route conflict by focusing programmatically instead of via href) - verified live: Tab focuses it, it becomes visible, activation focuses content
This commit is contained in:
parent
696d727e44
commit
0f9d553deb
@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#0e0a14" />
|
<meta name="theme-color" content="#0e0a14" />
|
||||||
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
||||||
<meta name="version" content="0.71.0-titles" />
|
<meta name="version" content="0.72.0-skiplink" />
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "amerc-site",
|
"name": "amerc-site",
|
||||||
"version": "0.71.0-titles",
|
"version": "0.72.0-skiplink",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import Account from './Account.jsx';
|
|||||||
import TavernGame from './TavernGame.jsx';
|
import TavernGame from './TavernGame.jsx';
|
||||||
import { api } from './api.js';
|
import { api } from './api.js';
|
||||||
|
|
||||||
const RELEASE = '0.71.0-titles';
|
const RELEASE = '0.72.0-skiplink';
|
||||||
|
|
||||||
const NAV = [
|
const NAV = [
|
||||||
{ id: 'home', label: 'Tavern' },
|
{ id: 'home', label: 'Tavern' },
|
||||||
@ -457,8 +457,14 @@ export default function Scene2DApp() {
|
|||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
const isHome = route === 'home';
|
const isHome = route === 'home';
|
||||||
useEffect(() => { document.title = ROUTE_TITLES[route] || 'amerc'; }, [route]);
|
useEffect(() => { document.title = ROUTE_TITLES[route] || 'amerc'; }, [route]);
|
||||||
|
const skipToContent = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = document.querySelector('.route-enter') || document.querySelector('.route-host');
|
||||||
|
if (target) { target.setAttribute('tabindex', '-1'); target.focus(); }
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<main className="px-app td-app">
|
<main className="px-app td-app">
|
||||||
|
<a className="skip-link" href="#content" onClick={skipToContent}>Skip to content</a>
|
||||||
<h1 className="sr-only">amerc agent mercenary tavern</h1>
|
<h1 className="sr-only">amerc agent mercenary tavern</h1>
|
||||||
<PixelTopbar route={route} setRoute={setRoute} auth={auth} />
|
<PixelTopbar route={route} setRoute={setRoute} auth={auth} />
|
||||||
{/* Home stays mounted (hidden off-route) so returning to the tavern is instant —
|
{/* Home stays mounted (hidden off-route) so returning to the tavern is instant —
|
||||||
|
|||||||
@ -1468,3 +1468,9 @@ button {
|
|||||||
.ap-gate-rich h2 { font-family: Georgia, "Times New Roman", serif; font-size: 24px; color: #fff; margin: 0 0 10px; }
|
.ap-gate-rich h2 { font-family: Georgia, "Times New Roman", serif; font-size: 24px; color: #fff; margin: 0 0 10px; }
|
||||||
.ap-gate-rich p { color: #b6a7d4; font-size: 14px; line-height: 1.65; margin: 0 0 22px; }
|
.ap-gate-rich p { color: #b6a7d4; font-size: 14px; line-height: 1.65; margin: 0 0 22px; }
|
||||||
.ap-gate-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
|
.ap-gate-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
|
||||||
|
|
||||||
|
/* skip-to-content link — visible only on keyboard focus (a11y, 0.72) */
|
||||||
|
.skip-link { position: fixed; top: 10px; left: 10px; z-index: 100; background: var(--cyan); color: #06222f;
|
||||||
|
font-family: Georgia, "Times New Roman", serif; font-weight: 600; font-size: 14px; padding: 9px 16px; border-radius: 9px;
|
||||||
|
box-shadow: 0 6px 18px rgba(0,0,0,0.4); text-decoration: none; transform: translateY(-160%); transition: transform .16s ease-out; }
|
||||||
|
.skip-link:focus, .skip-link:focus-visible { transform: none; outline: 2px solid #fff; outline-offset: 2px; }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user