a11y: bring UI to WCAG 2.2 AAA compliance
Semantic HTML: lang attr, landmarks (header/main/region/complementary), heading hierarchy (h1-h3), dl/dt/dd for info panel. ARIA: labels on all icon buttons, aria-hidden on decorative icons, progressbar role with dynamic aria-valuenow, aria-haspopup/expanded on all menu triggers, role=listbox/option on playlist, aria-selected, computed aria-labels on playlist rows. Contrast: raised --textMuted/--textDim/--icon to AAA 7:1 ratios. Focus: global :focus-visible outline, slider thumb glow, menu item highlight, switch focus-within, row focus styles. Target sizes: 44x44 hit areas on zoom/window/remove buttons via ::before pseudo-elements. Keyboard: playlist arrow nav + Enter/Space activate + Alt+Arrow reorder with live region announcements + move buttons. Speed menu, subtitles menu, and recent menu all keyboard-navigable with Arrow/Enter/Space/Escape. Dividers resizable via Arrow keys. Dynamic document.title updates on video/folder load.
This commit is contained in:
28
src/main.ts
28
src/main.ts
@@ -3,18 +3,15 @@
|
||||
* Orchestrates all modules and holds cross-module callbacks.
|
||||
*/
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||
import '@fontsource/sora/500.css';
|
||||
import '@fontsource/sora/600.css';
|
||||
import '@fontsource/sora/700.css';
|
||||
import '@fontsource/sora/800.css';
|
||||
import '@fontsource/manrope/400.css';
|
||||
import '@fontsource/manrope/500.css';
|
||||
import '@fontsource/manrope/600.css';
|
||||
import '@fontsource/manrope/700.css';
|
||||
import '@fontsource/manrope/800.css';
|
||||
import '@fontsource/ibm-plex-mono/400.css';
|
||||
import '@fontsource/ibm-plex-mono/500.css';
|
||||
import '@fontsource/ibm-plex-mono/600.css';
|
||||
import '@fontsource/fraunces/600.css';
|
||||
import '@fontsource/fraunces/700.css';
|
||||
import '@fontsource/fraunces/800.css';
|
||||
import '@fontsource/inter/400.css';
|
||||
import '@fontsource/inter/500.css';
|
||||
import '@fontsource/inter/600.css';
|
||||
import '@fontsource/inter/700.css';
|
||||
import '@fontsource/space-mono/400.css';
|
||||
import '@fontsource/space-mono/700.css';
|
||||
import './styles/main.css';
|
||||
import './styles/player.css';
|
||||
import './styles/playlist.css';
|
||||
@@ -37,7 +34,7 @@ import {
|
||||
getVideoDuration, getPlayer, isVolDragging,
|
||||
} from './player';
|
||||
|
||||
import { initPlaylist, renderList } from './playlist';
|
||||
import { initPlaylist, renderList, isDragging } from './playlist';
|
||||
import { initSubtitles, refreshSubtitles, clearSubtitles } from './subtitles';
|
||||
import {
|
||||
initUI, applyZoom, applySplit, applyDockSplit,
|
||||
@@ -97,6 +94,7 @@ async function boot(): Promise<void> {
|
||||
updateInfoPanel();
|
||||
buildSpeedMenu(1.0);
|
||||
notify('Open a folder to begin.');
|
||||
document.title = 'TutorialVault - Open a folder';
|
||||
}
|
||||
|
||||
// ---- onLibraryLoaded ----
|
||||
@@ -123,6 +121,7 @@ async function onLibraryLoaded(info: LibraryInfo, startScan: boolean): Promise<v
|
||||
updateOverall();
|
||||
renderList();
|
||||
updateInfoPanel();
|
||||
document.title = info?.folder ? `${info.folder} - TutorialVault` : 'TutorialVault';
|
||||
|
||||
if (startScan) {
|
||||
try { await api.startDurationScan(); } catch (_) {}
|
||||
@@ -144,6 +143,7 @@ async function loadIndex(
|
||||
|
||||
const it = library.items[currentIndex] || null;
|
||||
updateNowHeader(it);
|
||||
document.title = it ? `${it.title || it.name} - TutorialVault` : 'TutorialVault';
|
||||
|
||||
await api.setCurrent(currentIndex, Number(timecode || 0.0));
|
||||
renderList();
|
||||
@@ -197,7 +197,7 @@ async function tick(): Promise<void> {
|
||||
updateInfoPanel();
|
||||
updateNowHeader(currentItem());
|
||||
|
||||
if (oldIndex !== currentIndex || oldCount !== (library?.items?.length || 0)) {
|
||||
if (!isDragging() && (oldIndex !== currentIndex || oldCount !== (library?.items?.length || 0))) {
|
||||
renderList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user