Fix flash of white theme on page navigation

Add inline script in <head> that applies theme/accent preferences
immediately before body renders, preventing FOUC when navigating
between pages in dark mode.
This commit is contained in:
Daniele Linguaglossa 2026-01-21 20:17:04 +01:00
parent eca412bc28
commit a216576054
3 changed files with 54 additions and 17 deletions

View File

@ -26,7 +26,7 @@ SITE_DIST = dist/site
AWK_UTIL = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {print $$2}' AWK_UTIL = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {print $$2}'
AWK_UTIL_NOCRLF = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {printf $$2" "}' AWK_UTIL_NOCRLF = awk '/^\s*\[[0-9]+\]\s+([^\.].+)/ {printf $$2" "}'
PANDOC_ARGS = --standalone --table-of-contents --section-divs --email-obfuscation=references --css="/main.css" --include-after-body=$(RAW_DIST)/footer.html --include-after-body=$(RAW_DIST)/scripts.html PANDOC_ARGS = --standalone --table-of-contents --section-divs --email-obfuscation=references --css="/main.css" --include-in-header=$(RAW_DIST)/head.html --include-after-body=$(RAW_DIST)/footer.html --include-after-body=$(RAW_DIST)/scripts.html
PYTHON = $(shell which python3) PYTHON = $(shell which python3)
@ -49,7 +49,15 @@ libdzonerzy:
libdzonerzy-dump: libdzonerzy-dump:
$(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL) | xargs dirname {} | sort | uniq | xargs -I{} mkdir -p $(RAW_DIST)/{} $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL) | xargs dirname {} | sort | uniq | xargs -I{} mkdir -p $(RAW_DIST)/{}
@$(foreach fn, $(shell $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL_NOCRLF)), $(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | cat | head -c -1 > $(RAW_DIST)/$(fn);) @$(foreach fn, $(shell $(READELF) -S -W $(DIST)/$(OUTPUT) | $(AWK_UTIL_NOCRLF)), \
if echo "$(fn)" | grep -qE '^(articles|pages|advisory)/'; then \
size=$$($(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | head -c 4 | od -An -tu1 | awk '{print $$1 + $$2*256 + $$3*65536 + $$4*16777216}'); \
$(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | tail -c +5 | head -c $$size > $(RAW_DIST)/$(fn); \
elif echo "$(fn)" | grep -qE '^embed/'; then \
$(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout > $(RAW_DIST)/$(fn); \
else \
$(OBJCOPY) $(DIST)/$(OUTPUT) --dump-section $(fn)=/dev/stdout | tr -d '\000' > $(RAW_DIST)/$(fn); \
fi;)
libdzonerzy-gen: libdzonerzy-dump libdzonerzy-gen: libdzonerzy-dump
@mkdir -p $(SITE_DIST)/assets/ @mkdir -p $(SITE_DIST)/assets/

View File

@ -679,6 +679,18 @@ Copyright:
" .accent-picker { right: 2.75rem; }\\n" \ " .accent-picker { right: 2.75rem; }\\n" \
"}\\n" "}\\n"
#define HEAD_HTML \
HTML_TAG_OPEN("script", "") \
HTML_RAWTEXT("(function(){") \
HTML_RAWTEXT("var t=localStorage.getItem('theme-preference');") \
HTML_RAWTEXT("var a=localStorage.getItem('accent-preference');") \
HTML_RAWTEXT("if(!t)t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';") \
HTML_RAWTEXT("if(t==='dark')document.documentElement.classList.add('dark');") \
HTML_RAWTEXT("if(a&&a!=='blue')document.documentElement.classList.add('accent-'+a);") \
HTML_RAWTEXT("})();") \
HTML_TAG_CLOSE("script") \
HTML_NEWLINE()
#define FOOTER_HTML \ #define FOOTER_HTML \
MD_RAWTAG("footer", "" \ MD_RAWTAG("footer", "" \
"<span>" \ "<span>" \
@ -926,6 +938,8 @@ SECTION("footer.html")
static const char footerhtml[] = FOOTER_HTML; static const char footerhtml[] = FOOTER_HTML;
SECTION("scripts.html") SECTION("scripts.html")
static const char scriptshtml[] = SCRIPTS_HTML; static const char scriptshtml[] = SCRIPTS_HTML;
SECTION("head.html")
static const char headhtml[] = HEAD_HTML;
SECTION("main.js") SECTION("main.js")
static const char mainjs[] = MAIN_JS; static const char mainjs[] = MAIN_JS;
SECTION("sitemap.xml") SECTION("sitemap.xml")

View File

@ -253,13 +253,14 @@ unsigned char *read_file_data(FILE *f, size_t *size)
*size = ftell(f); *size = ftell(f);
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
unsigned char *data = malloc(*size); unsigned char *data = malloc(*size + 1);
if (data == NULL) if (data == NULL)
{ {
return NULL; return NULL;
} }
fread(data, 1, *size, f); fread(data, 1, *size, f);
data[*size] = '\0'; // null-terminate for string operations
return data; return data;
} }
@ -468,15 +469,25 @@ int embed(embed_t *args)
data = data_updated; data = data_updated;
} }
// Prepend 4-byte size (little-endian) for non-embed types
int has_size_prefix = (args->type != TYPE_EMBED);
if (args->autowrap) if (args->autowrap)
{
while (i < size)
{
if (i == 0)
{ {
fprintf(output, " "); fprintf(output, " ");
// Write 4-byte size prefix (little-endian)
if (has_size_prefix)
{
fprintf(output, "0x%02X, 0x%02X, 0x%02X, 0x%02X, ",
(unsigned char)(size & 0xFF),
(unsigned char)((size >> 8) & 0xFF),
(unsigned char)((size >> 16) & 0xFF),
(unsigned char)((size >> 24) & 0xFF));
} }
while (i < size)
{
fprintf(output, "0x%02X", data[i]); fprintf(output, "0x%02X", data[i]);
if (i % 16 == 15) if (i % 16 == 15)
@ -497,13 +508,20 @@ int embed(embed_t *args)
fprintf(output, "\n"); fprintf(output, "\n");
} }
if (args->type == TYPE_EMBED)
fprintf(output, " 0x00,\n");
fprintf(output, "};\n"); fprintf(output, "};\n");
} }
else else
{ {
// Write 4-byte size prefix (little-endian)
if (has_size_prefix)
{
fprintf(output, "0x%02X, 0x%02X, 0x%02X, 0x%02X, ",
(unsigned char)(size & 0xFF),
(unsigned char)((size >> 8) & 0xFF),
(unsigned char)((size >> 16) & 0xFF),
(unsigned char)((size >> 24) & 0xFF));
}
while (i < size) while (i < size)
{ {
// no wrap // no wrap
@ -519,9 +537,6 @@ int embed(embed_t *args)
i++; i++;
} }
if (args->type == TYPE_EMBED)
fprintf(output, ", 0x00");
fprintf(output, "};\n\n"); fprintf(output, "};\n\n");
} }