diff --git a/assets/js/critical.js b/assets/js/critical.js index c72d801..033e819 100644 --- a/assets/js/critical.js +++ b/assets/js/critical.js @@ -1,28 +1,38 @@ /** - * Critical JavaScript that needs to run before page rendering + * Critical JavaScript that needs to run before page rendering. + * SINGLE SOURCE OF TRUTH for theme initialization. + * + * Loaded inline via partials/head/critical-css.html using safeJS so it + * executes before stylesheets paint and avoids a flash of wrong theme. */ (function() { - // Theme initialization + const savedTheme = localStorage.getItem('theme'); const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)'); - const currentTheme = localStorage.getItem('theme') || - (prefersDarkScheme.matches ? 'dark' : 'light'); - - // Apply theme to document + const currentTheme = savedTheme || (prefersDarkScheme.matches ? 'dark' : 'light'); + document.documentElement.setAttribute('data-theme', currentTheme); - - // Apply critical colors directly to prevent flash - document.documentElement.style.backgroundColor = - currentTheme === 'dark' ? '#1a202c' : '#275f85'; - document.documentElement.style.color = - currentTheme === 'dark' ? '#e2e8ff' : '#cad6ff'; - - // Listen for theme preference changes + + const rootStyle = document.documentElement.style; + if (currentTheme === 'dark') { + rootStyle.setProperty('--critical-bg', 'var(--dark-background, #1a202c)'); + rootStyle.setProperty('--critical-text', 'var(--dark-text-primary, #e2e8ff)'); + rootStyle.backgroundColor = '#1a202c'; + rootStyle.color = '#e2e8ff'; + } else { + rootStyle.setProperty('--critical-bg', 'var(--light-background, #275f85)'); + rootStyle.setProperty('--critical-text', 'var(--light-text-secondary, #cad6ff)'); + rootStyle.backgroundColor = '#275f85'; + rootStyle.color = '#cad6ff'; + } + + if (!savedTheme) { + localStorage.setItem('theme', currentTheme); + } + + // Track system preference only when the user hasn't picked a theme. prefersDarkScheme.addEventListener('change', (e) => { if (!localStorage.getItem('theme')) { - document.documentElement.setAttribute( - 'data-theme', - e.matches ? 'dark' : 'light' - ); + document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light'); } }); })(); diff --git a/assets/js/math-init.js b/assets/js/math-init.js new file mode 100644 index 0000000..5653b15 --- /dev/null +++ b/assets/js/math-init.js @@ -0,0 +1,20 @@ +/** + * Math reprocessing helper for page refreshes / visibility changes. + * Relies on window.reprocessMath being defined by the math rendering setup + * (KaTeX/MathJax wiring lives in partials/head/katex.html — Unit 11). + */ +(function() { + document.addEventListener('DOMContentLoaded', function() { + setTimeout(function() { + if (window.reprocessMath) { + window.reprocessMath(); + } + }, 200); + }); + + document.addEventListener('visibilitychange', function() { + if (!document.hidden && window.reprocessMath) { + setTimeout(window.reprocessMath, 100); + } + }); +})(); diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index c905a0c..909ea7a 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -1,81 +1,22 @@ -
- - - - - - - {{/* Main head partial handles all meta tags and SEO */}} - {{ partial "head.html" . }} - - {{ block "head" . }}{{ end }} - - {{ if .Param "math" }} - {{ partialCached "math.html" . }} - {{ end }} - - - {{ $mainCSS := resources.Get "css/main.css" }} - {{ if $mainCSS }} - {{ $css := $mainCSS | minify | fingerprint }} - - - - - {{ end }} - +{{/* + is opened by partials/head.html (Unit 6 ensures that partial + owns the single tag). We intentionally do NOT open another + here so the rendered HTML stays valid. + + Theme init, critical CSS, and the main stylesheet are delegated to + partials in head/* (critical-css.html, styles.html, etc.) and + assets/js/critical.js — that file is the single source of truth for + the inline theme-init script. +*/}} +{{ partial "head.html" . }} +{{ block "head" . }}{{ end }} + +{{ if .Param "math" }} + {{ partialCached "math.html" . }} +{{ end }} +