Files
gacha-event-tracker/index.html
T
Lucas WintherandClaude Opus 5 448c70968d feat: add the light palette, and the module that resolves a theme
The dark tokens re-struck under `[data-theme="light"]`. Nothing sets that
attribute yet, so the app still renders exactly as it did.

Two things could not be tokens. Game hues are data — ours and the
reader's — and every one was picked against a near-black ground, so
`readableHue` darkens them along the same hue until they read on paper,
and returns dark untouched by construction. And the attribute has to be
on the root before React can mount, or a reader who chose light gets a
dark flash on every load, so the shell reads the preference itself.

That leaves the ground colour written in three files that cannot import
each other, which is what the test pins together.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-18 23:30:26 +02:00

85 lines
3.6 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 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>
<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=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>