loading screen, better HUD, etc.

This commit is contained in:
legop3
2026-08-20 14:38:07 -04:00
parent 5ab62c3633
commit f77a969098
18 changed files with 235 additions and 100 deletions
@@ -0,0 +1,40 @@
// Initial Session Overlay
// Purpose: Hides incomplete driver-page placeholders until the first authoritative session snapshot arrives.
// Scope: Provides startup presentation only; it deliberately does not delay or alter page initialization underneath.
import { useSessionSelector } from '../../context/SessionContext.jsx';
import spinnerImage from '../../assets/spinner.png';
import './styles.css';
export default function InitialSessionOverlay() {
const connected = useSessionSelector((state) => Boolean(state.connected));
const sessionReady = useSessionSelector((state) => state.session !== null);
if (sessionReady) return null;
return (
<div
className="fixed inset-0 z-[1000] flex items-center justify-center bg-black text-slate-200"
role="status"
aria-live="polite"
aria-label={connected ? 'Loading session' : 'Connecting'}
>
<div className="flex flex-col items-center gap-3">
{/* The source image is intentionally constrained to a small fixed box;
its intrinsic pixel dimensions must never determine overlay layout.
Reduced-motion users still see the identifying image without either
the rotation or continuously changing color. */}
<div className="initial-session-spinner-rotation h-20 w-20" aria-hidden="true">
<img
src={spinnerImage}
alt=""
draggable="false"
className="initial-session-spinner-image h-full w-full select-none object-contain"
/>
</div>
<div className="text-sm font-medium text-slate-300">
{connected ? 'Loading session…' : 'Connecting…'}
</div>
</div>
</div>
);
}
@@ -0,0 +1,35 @@
/*
These animations belong only to InitialSessionOverlay. Keeping them beside
the component avoids adding feature-specific behavior to the global styles.
Rotation and hue use separate elements so their independent infinite cycles
cannot overwrite one another's transform or filter properties.
*/
@keyframes initial-session-spinner-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes initial-session-spinner-hue {
from {
filter: hue-rotate(0deg);
}
to {
filter: hue-rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
.initial-session-spinner-rotation {
animation: initial-session-spinner-rotate 1.8s linear infinite;
}
.initial-session-spinner-image {
animation: initial-session-spinner-hue 2.8s linear infinite;
}
}