From 57bb59e7685be6cd6ea3b7fc00dc7d8ae60f6691 Mon Sep 17 00:00:00 2001 From: interlope Date: Mon, 1 Jun 2026 23:48:36 +0200 Subject: [PATCH] feat: enhance performance and accessibility with non-blocking font loading, lazy-loading images, and mobile navbar toggle --- .htaccess | 38 ++++++++++++++++++++++ css/style.css | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ index.php | 11 ++++--- js/blowdit.js | 72 +++++++++++++++++++++++++++++++++++++++++ php/head.php | 6 ++-- php/page.php | 13 ++++++-- 6 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 .htaccess diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..cfff0b3 --- /dev/null +++ b/.htaccess @@ -0,0 +1,38 @@ +# ============================================================ +# BLOWDIT — static asset delivery (Apache) +# Scoped to the theme directory: only affects files served from +# this folder (css/js/img/fonts). Bludit's routing lives in the +# site-root .htaccess and is untouched. +# No-op on nginx / PHP's built-in server. +# ============================================================ + +# ---- Compression ---- + + AddOutputFilterByType BROTLI_COMPRESS text/css application/javascript application/json image/svg+xml application/xml + + + AddOutputFilterByType DEFLATE text/css application/javascript application/json image/svg+xml application/xml text/plain + + +# ---- Far-future caching ---- +# Safe because every asset is requested with a ?v= query string +# (see head.php / index.php), so a changed file gets a fresh URL. + + ExpiresActive On + ExpiresByType text/css "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/webp "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + ExpiresByType image/x-icon "access plus 1 year" + ExpiresByType font/woff "access plus 1 year" + ExpiresByType font/woff2 "access plus 1 year" + + + + + Header set Cache-Control "public, max-age=31536000, immutable" + + diff --git a/css/style.css b/css/style.css index 3dbaeab..bea9b8e 100644 --- a/css/style.css +++ b/css/style.css @@ -1726,6 +1726,95 @@ footer.footer-modern a:hover { .back-to-top.is-visible { transform: none; } } +/* ======================================== + SYNTAX HIGHLIGHTING (highlight.js) + Use only the theme's token colours; keep the code block's own + background/padding (set on pre / --code-bg) rather than hljs's. + ======================================== */ +pre code.hljs { + padding: 0 !important; + background: transparent !important; +} + +/* ======================================== + KEYBOARD FOCUS — visible ring for keyboard users only + ======================================== */ +a:focus-visible, +button:focus-visible, +.swatch:focus-visible, +.tab-btn:focus-visible, +.carousel-arrow:focus-visible, +.carousel-dot:focus-visible, +.lightbox-arrow:focus-visible, +.code-copy:focus-visible, +.back-to-top:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 2px; +} + +/* Don't show the ring for mouse clicks */ +a:focus:not(:focus-visible), +button:focus:not(:focus-visible) { + outline: none; +} + +/* ======================================== + PRINT — clean, ink-friendly article output + ======================================== */ +@media print { + .navbar-modern, + .footer, + .sidebar-container, + .toc-sidebar, + .skip-link, + .reading-progress, + .back-to-top, + .code-copy, + .code-copy-toast, + .theme-picker-wrap { + display: none !important; + } + + html, body { + background: #ffffff !important; + color: #000000 !important; + } + + /* Use the full page width once the chrome is gone */ + .container, + .container-wide { + max-width: 100% !important; + width: 100% !important; + } + + .card-modern { + box-shadow: none !important; + border: none !important; + } + + a { + color: #000000 !important; + text-decoration: underline; + } + + /* Keep code blocks readable on paper */ + pre, code { + background: #f5f5f5 !important; + color: #000000 !important; + border-color: #cccccc !important; + } + + pre { + white-space: pre-wrap; + word-wrap: break-word; + } + + /* Avoid awkward page breaks */ + h1, h2, h3, h4 { page-break-after: avoid; } + pre, blockquote, table, figure, img { page-break-inside: avoid; } +} + /* ======================================== RESPONSIVE ======================================== */ diff --git a/index.php b/index.php index 894a723..6b4032d 100644 --- a/index.php +++ b/index.php @@ -153,11 +153,12 @@ - + - + + diff --git a/php/page.php b/php/page.php index 6affc7f..f668efb 100644 --- a/php/page.php +++ b/php/page.php @@ -7,7 +7,7 @@ coverImage()): ?>
- <?php echo htmlspecialchars($page->title()); ?> + <?php echo htmlspecialchars($page->title(), ENT_QUOTES, 'UTF-8'); ?>
@@ -25,7 +25,16 @@
- content(); ?> + ) are untouched. + echo preg_replace( + '/]*\bloading=)/i', + 'content() + ); + ?>