Files
gacha-event-tracker/index.html
T
Lucas WintherandClaude Opus 5.5 50c6e91b00 feat: the redesigned Event Clock
The new reader app replaces the old UI: Checklist with a rail (focus
pills, Next to expire, today's dailies, pinned routines), Timeline in a
new and a classic look, Routines, Archive and a Settings area, in a
Structured (default) and a Relaxed interface. It follows the approved
Round 6 B and Round 7 mockups, corrected screen by screen with the owner.

Reader data moves to a v2 store (gacha-tracker:v2:*). Existing v1 data
is migrated on first load, a recovery copy is written first, and the v1
keys are never removed, so a rollback finds them unchanged. Redeem codes
for Genshin arrive as their own feed.

Also: live's urgency ramp, editable per theme; completion and
section-complete fireworks; a returning-user tour; the design mockups,
specs, plans, reviews and handoff record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 01:55:16 +02:00

107 lines
5.2 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="__BASE__" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Event Clock — gacha events by what expires next</title>
<meta
name="description"
content="Live and upcoming events across your gacha games, sorted by what ends soonest."
/>
<meta name="theme-color" content="#12141c" />
<!--
This page ships dark and offers light; both are drawn deliberately, and
neither wants a third party guessing at one from the other.
color-scheme states the default rendering, so form controls and scrollbars
come out dark before any stylesheet has loaded. It is only the default:
styles.css sets the real `color-scheme` property per theme, and that wins.
darkreader-lock is Dark Reader's documented opt-out, and it still applies
now that there is a light theme — arguably more. The colours in this UI
carry meaning (the urgency ramp, the per-game hues), and an extension
re-mapping them breaks the reading, whichever ground it starts from. A
reader who wants light has a control in settings that was designed.
-->
<meta name="color-scheme" content="dark" />
<meta name="darkreader-lock" />
<!--
The theme, before first paint.
Dark is the default and needs nothing: it is what the stylesheet says with
no attribute set. A reader who chose light is the case this exists for —
without it they get a dark page for as long as the bundle takes to arrive
and mount, on every single load, which is a flash of the thing they
switched off.
It reads the same stored key as the app (`state/storage.ts`) and applies
the same attribute (`state/theme.ts`), and it cannot import either, so a
test pins the three copies together. Wrapped in try/catch and doing
nothing on failure: a reader with storage disabled gets the default theme,
never a blank page.
-->
<script>
(function () {
try {
var prefs = JSON.parse(
localStorage.getItem("gacha-tracker:v1:prefs") || "{}",
);
var reader = JSON.parse(localStorage.getItem("gacha-tracker:v2:reader") || "null");
if (reader && reader.version === 2 && reader.prefs) prefs = reader.prefs;
document.documentElement.dataset.interface = prefs.interface === "relaxed" ? "relaxed" : "structured";
document.documentElement.dataset.density = prefs.density === "compact" ? "compact" : "standard";
var light =
prefs.theme === "light" ||
(prefs.theme === "system" &&
window.matchMedia("(prefers-color-scheme: light)").matches);
if (!light) return;
document.documentElement.dataset.theme = "light";
var meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute("content", "#edf0f7");
} catch (e) {
/* No stored preference we can read: the default stands. */
}
})();
</script>
<!--
The feed, in flight before the bundle that asks for it.
`fetchFeed` cannot run until main.js has downloaded, parsed and mounted
React, so the one request the page exists to make was starting last: on a
4G/4x-CPU profile the bundle finished at ~900ms and the feed only then
began, landing at ~1260ms. Nothing about it depends on the bundle — the URL
is a constant — so the preload scanner can have it on the wire in the first
few milliseconds instead, alongside the JS rather than behind it.
Resolved against `<base>` above, exactly as `fetchFeed` resolves it against
`document.baseURI`, so this is correct at a domain root and under a Pages
subpath alike. `crossorigin` is not decoration: `as="fetch"` has to match
the credentials mode of the `fetch()` that follows, and a mismatch is not a
no-op — the browser discards the preload and downloads the feed a second
time, which is worse than not preloading at all.
-->
<link rel="preload" href="data/events.v1.json" as="fetch" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&amp;family=Barlow+Condensed:wght@500;600;700&amp;family=Bricolage+Grotesque:wght@500;600;700&amp;family=Figtree:wght@400;500;600;700&amp;family=Chakra+Petch:wght@500;600;700&family=Public+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap"
rel="stylesheet"
/>
<link rel="manifest" href="manifest.webmanifest" />
<link rel="icon" href="icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="icon.svg" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script type="module" src="main.js"></script>
<noscript>
<img src="https://analytics.lucaswinther.info/ingress/b85b98e5-4692-411f-b6f6-bbe7e54d8c83/pixel.gif">
</noscript>
<script defer src="https://analytics.lucaswinther.info/ingress/b85b98e5-4692-411f-b6f6-bbe7e54d8c83/script.js"></script>
</body>
</html>