mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 13:03:10 +00:00
ux: improve navigation accessibility
This commit is contained in:
parent
b501c9da75
commit
8ce353262b
3 changed files with 108 additions and 8 deletions
|
|
@ -114,6 +114,30 @@
|
|||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body.is-scroll-locked {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
top: 0.75rem;
|
||||
left: 0.75rem;
|
||||
z-index: 1000;
|
||||
padding: 0.65rem 0.9rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(calc(-100% - 1rem));
|
||||
transition: transform var(--motion-normal) var(--easing-standard);
|
||||
}
|
||||
|
||||
.skip-link:focus,
|
||||
.skip-link:focus-visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-primary-muted);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
|||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main-content">Skip to content</a>
|
||||
<VotingIcons />
|
||||
<div class="app">
|
||||
<header class="header">
|
||||
|
|
@ -109,7 +110,7 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
|||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="main">
|
||||
<main class="main" id="main-content" tabindex="-1">
|
||||
<slot />
|
||||
</main>
|
||||
<footer class="footer">
|
||||
|
|
@ -121,15 +122,29 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
|||
const nav = document.getElementById('site-nav');
|
||||
const navToggle = document.getElementById('nav-toggle');
|
||||
|
||||
function setNavOpen(open) {
|
||||
function openNav() {
|
||||
if (!nav || !navToggle) return;
|
||||
nav.classList.toggle('is-open', open);
|
||||
navToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
nav.classList.add('is-open');
|
||||
navToggle.setAttribute('aria-expanded', 'true');
|
||||
if (window.matchMedia('(max-width: 640px)').matches) {
|
||||
document.body.classList.add('is-scroll-locked');
|
||||
}
|
||||
}
|
||||
|
||||
function closeNav() {
|
||||
if (!nav || !navToggle) return;
|
||||
nav.classList.remove('is-open');
|
||||
navToggle.setAttribute('aria-expanded', 'false');
|
||||
document.body.classList.remove('is-scroll-locked');
|
||||
}
|
||||
|
||||
if (nav && navToggle) {
|
||||
navToggle.addEventListener('click', () => {
|
||||
setNavOpen(!nav.classList.contains('is-open'));
|
||||
if (nav.classList.contains('is-open')) {
|
||||
closeNav();
|
||||
} else {
|
||||
openNav();
|
||||
}
|
||||
});
|
||||
|
||||
nav.addEventListener('click', (event) => {
|
||||
|
|
@ -138,13 +153,27 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
|||
if (target.closest('#nav-toggle')) return;
|
||||
if (!target.closest('a')) return;
|
||||
if (window.matchMedia('(max-width: 640px)').matches) {
|
||||
setNavOpen(false);
|
||||
closeNav();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!nav.classList.contains('is-open')) return;
|
||||
const target = event.target;
|
||||
if (!(target instanceof Node)) return;
|
||||
if (nav.contains(target)) return;
|
||||
closeNav();
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
closeNav();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth > 640) {
|
||||
setNavOpen(false);
|
||||
closeNav();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ const defaultTheme = DEFAULT_THEME;
|
|||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main-content">Skip to content</a>
|
||||
<div class="public-app">
|
||||
<header class="public-header">
|
||||
<nav class="public-nav" id="public-nav">
|
||||
|
|
@ -108,7 +109,7 @@ const defaultTheme = DEFAULT_THEME;
|
|||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="public-main">
|
||||
<main class="public-main" id="main-content" tabindex="-1">
|
||||
<slot />
|
||||
</main>
|
||||
<footer class="public-footer">
|
||||
|
|
@ -156,15 +157,39 @@ const defaultTheme = DEFAULT_THEME;
|
|||
if (!nav || !toggle) return;
|
||||
nav.classList.add('is-open');
|
||||
toggle.setAttribute('aria-expanded', 'true');
|
||||
if (window.matchMedia('(max-width: 768px)').matches) {
|
||||
document.body.classList.add('is-scroll-locked');
|
||||
}
|
||||
}
|
||||
|
||||
function closeNav() {
|
||||
if (!nav || !toggle) return;
|
||||
nav.classList.remove('is-open');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
document.body.classList.remove('is-scroll-locked');
|
||||
}
|
||||
|
||||
function setActiveNav() {
|
||||
const links = document.querySelectorAll('#public-nav-menu a');
|
||||
const path = window.location.pathname.replace(/\/$/, '') || '/';
|
||||
|
||||
links.forEach((a) => {
|
||||
if (!(a instanceof HTMLAnchorElement)) return;
|
||||
const hrefPath = a.pathname.replace(/\/$/, '') || '/';
|
||||
if (hrefPath === '/' || hrefPath === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = path === hrefPath || path.indexOf(hrefPath + '/') === 0;
|
||||
if (isActive) {
|
||||
a.setAttribute('aria-current', 'page');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (nav && toggle) {
|
||||
setActiveNav();
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
if (nav.classList.contains('is-open')) {
|
||||
closeNav();
|
||||
|
|
@ -181,6 +206,20 @@ const defaultTheme = DEFAULT_THEME;
|
|||
});
|
||||
});
|
||||
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!nav.classList.contains('is-open')) return;
|
||||
const target = event.target;
|
||||
if (!(target instanceof Node)) return;
|
||||
if (nav.contains(target)) return;
|
||||
closeNav();
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
closeNav();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth > 768) {
|
||||
closeNav();
|
||||
|
|
@ -331,6 +370,14 @@ const defaultTheme = DEFAULT_THEME;
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-links a[aria-current='page'] {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.nav-links a[aria-current='page']::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
|
|
|||
Loading…
Reference in a new issue