Firefox users saw a flash on the first navigation after an idle spell, and Chrome occasionally flashed white. Nothing in the network path explained it (assets are immutable, the stylesheet is render-blocking, a cold Firefox load paints fully styled), so this removes every remaining way a page could look different between its first paint and its final state: - Both Newsreader faces are embedded in the served stylesheet as data: URIs (CSP gains `font-src 'self' data:`; the preloads go away). A font fetched by URL is applied after first paint whenever the browser has to bring it back from disk, which is exactly the "first click after a while" case. - The cross-document view transition is gone; the operator wants snappy. - A color-scheme meta, kept in step with the saved theme, so the canvas the browser paints before the stylesheet is the right shade. - The theme toggle's icon and label are chosen by CSS from html[data-theme] (set pre-paint by theme.js) instead of being rewritten by app.js. - Dashboard table filters are rendered by the templates (shown under `.has-js`) rather than inserted by app.js, so tables no longer jump. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
40 lines
1.8 KiB
JavaScript
40 lines
1.8 KiB
JavaScript
(() => {
|
|
// Marks the document as scripted before first paint so progressively enhanced
|
|
// widgets (the contents panel) can start collapsed without a flash.
|
|
document.documentElement.classList.add("has-js");
|
|
// The color-scheme meta decides what the browser paints before the stylesheet
|
|
// arrives; keep it in step with an explicit theme so that first paint is not
|
|
// the wrong shade.
|
|
const scheme = document.querySelector('meta[name="color-scheme"]');
|
|
try {
|
|
const theme = localStorage.getItem("theme");
|
|
if (theme === "light" || theme === "dark") {
|
|
document.documentElement.dataset.theme = theme;
|
|
if (scheme) scheme.content = theme;
|
|
} else {
|
|
document.documentElement.removeAttribute("data-theme");
|
|
}
|
|
} catch (_) {
|
|
document.documentElement.removeAttribute("data-theme");
|
|
}
|
|
|
|
// Restore persisted disclosures as the parser creates them. This head script
|
|
// runs before paint, so a cold app.js request cannot expose the server-default
|
|
// open state and then shift the dashboard when the script finally arrives.
|
|
const restoreDetails = (root) => {
|
|
const details = [];
|
|
if (root.nodeType === Node.ELEMENT_NODE && root.matches("details[id]")) details.push(root);
|
|
root.querySelectorAll?.("details[id]").forEach((element) => details.push(element));
|
|
details.forEach((element) => {
|
|
try {
|
|
element.open = localStorage.getItem("details:" + element.id) === "open";
|
|
} catch (_) {}
|
|
});
|
|
};
|
|
const detailsObserver = new MutationObserver((records) => {
|
|
records.forEach((record) => record.addedNodes.forEach(restoreDetails));
|
|
});
|
|
detailsObserver.observe(document.documentElement, { childList: true, subtree: true });
|
|
document.addEventListener("DOMContentLoaded", () => detailsObserver.disconnect(), { once: true });
|
|
})();
|