mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 15:02:04 +00:00
fix(ui): announce failures, keep focus honest and stop the last layout shifts
Hidden states of the floating coach button now leave the tab order and accessibility tree, its focus ring carries an offset, its icon is dark on the gradient, an unread nudge is announced once via a live region, and it sits correctly above the fold on tablet widths and yields to the onboarding tour. Chart error cards announce themselves, share the exact height variables of the skeleton band, carry per-chart retry labels, and a failed chunk renders inside the regular card shell. The header icon cluster spaces its extended hit areas apart, the tab strip keeps horizontal paint room for focus rings, the streak row reserves its slot in every card state, the selection bar clears the bottom nav on landscape phones, and the version poller keeps an in-memory reload guard for browsers that block session storage. Mood notes render as plain paragraphs with a toggle that appears only when text is actually clamped, the remember control reveals on hover and hands focus to its confirmation, and the hypnogram names its source and paints only the lanes the night contains.
This commit is contained in:
1 parent
a2a995e429
commit
95f21bfa4d
29 files changed
+703
-137
No files matched your search
@@ -77,13 +77,21 @@ async function fetchLiveVersion(signal: AbortSignal): Promise<string | null> {
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory mirror of the session guard: under strict-privacy modes
|
||||
// sessionStorage throws, and without a fallback a persistent
|
||||
// shell/live mismatch (misconfigured build) would reload every poll.
|
||||
// Module scope survives re-renders but not the reload itself — after
|
||||
// a reload the storage read is retried first, so the mirror only has
|
||||
// to break the loop within one document lifetime.
|
||||
let inMemoryReloadGuard: string | null = null;
|
||||
|
||||
async function evictAndReload(targetVersion: string): Promise<void> {
|
||||
inMemoryReloadGuard = targetVersion;
|
||||
try {
|
||||
sessionStorage.setItem(SESSION_GUARD_KEY, targetVersion);
|
||||
} catch {
|
||||
// sessionStorage can throw under strict-privacy modes; fall
|
||||
// through to the reload anyway — worst case the next deploy
|
||||
// re-triggers the same heal flow.
|
||||
// sessionStorage can throw under strict-privacy modes; the
|
||||
// in-memory mirror above still breaks same-document loops.
|
||||
}
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
@@ -120,8 +128,9 @@ export function VersionPoller(): null {
|
||||
try {
|
||||
lastReloadedFor = sessionStorage.getItem(SESSION_GUARD_KEY);
|
||||
} catch {
|
||||
/* fall through — treat as "no attempt recorded" */
|
||||
/* storage unavailable — the in-memory mirror still applies */
|
||||
}
|
||||
lastReloadedFor ??= inMemoryReloadGuard;
|
||||
const decision = resolveVersionPollDecision(
|
||||
live,
|
||||
SHELL_VERSION,
|
||||
|
||||
Reference in new issue
Block a user