69 lines
3.2 KiB
Plaintext
69 lines
3.2 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta name="csrf-token" content={get_csrf_token()} />
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
<link rel="icon" href="/favicon.ico" sizes="32x32" />
|
|
<.live_title default="Firehose" suffix=" — Willem van den Ende">
|
|
{assigns[:page_title]}
|
|
</.live_title>
|
|
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
|
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}>
|
|
</script>
|
|
<script>
|
|
(() => {
|
|
// Theme selection is always explicit: the document always carries a
|
|
// data-theme attribute, resolved here before first paint. An explicit
|
|
// choice therefore always wins in CSS, whatever the OS preference is;
|
|
// no stylesheet rule follows the OS on its own.
|
|
//
|
|
// data-theme-choice records what the visitor actually chose (unset,
|
|
// system, light, dark) and is what the toggle's knob follows: in
|
|
// "system" mode the palette may resolve to light or dark, but the
|
|
// system icon must stay selected.
|
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
const stored = () => localStorage.getItem("phx:theme");
|
|
const resolve = (theme) =>
|
|
theme === "system" ? (prefersDark.matches ? "dark" : "light") : theme;
|
|
const apply = (theme) => {
|
|
document.documentElement.setAttribute("data-theme", resolve(theme));
|
|
document.documentElement.setAttribute("data-theme-choice", theme);
|
|
};
|
|
const setTheme = (theme) => {
|
|
localStorage.setItem("phx:theme", theme);
|
|
apply(theme);
|
|
};
|
|
// Light is the default: no stored preference means light, not "follow
|
|
// the OS". "System" is an explicit choice and is stored as such.
|
|
if (!document.documentElement.hasAttribute("data-theme")) {
|
|
apply(stored() || "light");
|
|
}
|
|
// Follow the OS only while the visitor's recorded choice is "system".
|
|
prefersDark.addEventListener("change", () => {
|
|
if (stored() === "system") apply("system");
|
|
});
|
|
window.addEventListener("storage", (e) => e.key === "phx:theme" && setTheme(e.newValue || "light"));
|
|
window.addEventListener("phx:set-theme", (e) => setTheme(e.target.dataset.phxTheme));
|
|
})();
|
|
</script>
|
|
<%= for meta <- get_content_for(assigns, :meta_tags) do %>
|
|
<meta property="og:title" content={meta.og_title} />
|
|
<meta property="og:description" content={meta.og_description} />
|
|
<meta property="og:type" content={meta.og_type} />
|
|
<meta property="og:url" content={meta.og_url} />
|
|
<%= if meta.og_image do %>
|
|
<meta property="og:image" content={meta.og_image} />
|
|
<% end %>
|
|
<meta name="twitter:card" content={meta.twitter_card} />
|
|
<% end %>
|
|
<%= for feed <- get_content_for(assigns, :feed_links) do %>
|
|
<link rel="alternate" type="application/rss+xml" title={feed.title} href={feed.rss_url} />
|
|
<% end %>
|
|
</head>
|
|
<body>
|
|
{@inner_content}
|
|
</body>
|
|
</html>
|