Improve frontend performance and caching behavior

This commit is contained in:
Codex
2026-03-08 00:06:50 +00:00
parent de10c47a8c
commit adb164c8e1
5 changed files with 69 additions and 71 deletions

View File

@@ -62,14 +62,6 @@
src: url('/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2') format('woff2'); src: url('/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2') format('woff2');
} }
body {
visibility: visible;
}
:root[data-fonts='loading'] body {
visibility: hidden;
}
:root { :root {
/* ─── Colors (Dark Theme) ─────────────────────────────── */ /* ─── Colors (Dark Theme) ─────────────────────────────── */
--bg: #0c0f14; --bg: #0c0f14;
@@ -467,11 +459,7 @@ a:focus-visible {
.palette-dots { .palette-dots {
display: flex; display: flex;
gap: 0.25rem; gap: 0.25rem;
max-width: 38px;
overflow-x: hidden;
overflow-y: visible;
flex: 0 0 auto; flex: 0 0 auto;
transition: max-width 0.2s ease, gap 0.2s ease;
} }
.palette-dot { .palette-dot {
width: 30px; width: 30px;
@@ -482,11 +470,10 @@ a:focus-visible {
background-size: 160%; background-size: 160%;
background-position: center; background-position: center;
cursor: pointer; cursor: pointer;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s, opacity 0.2s; transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
overflow: hidden; overflow: hidden;
flex-shrink: 0; flex-shrink: 0;
order: 0;
transform-origin: center; transform-origin: center;
} }
.palette-dot:hover { .palette-dot:hover {
@@ -500,27 +487,6 @@ a:focus-visible {
.palette-dot:focus-visible { .palette-dot:focus-visible {
outline: none; outline: none;
} }
.floating-palette-controls:not(:hover):not(:focus-within) .palette-dots {
gap: 0;
}
.floating-palette-controls:not(:hover):not(:focus-within) .palette-dot:not(.active) {
opacity: 0;
pointer-events: none;
}
.floating-palette-controls:not(:hover):not(:focus-within) .palette-dot.active {
order: -1;
}
.floating-palette-controls:hover .palette-dots,
.floating-palette-controls:focus-within .palette-dots {
max-width: min(280px, calc(100vw - 2rem));
gap: 0.25rem;
}
.floating-palette-controls:hover .palette-dot:not(.active),
.floating-palette-controls:focus-within .palette-dot:not(.active) {
opacity: 1;
pointer-events: auto;
}
@media (max-width: 520px) { @media (max-width: 520px) {
.floating-palette-controls { .floating-palette-controls {
gap: 0.2rem; gap: 0.2rem;

View File

@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en" data-theme="dark" data-palette="classic" data-fonts="loading"> <html lang="en" data-theme="dark" data-palette="classic">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -62,9 +62,6 @@
<script> <script>
(function () { (function () {
const doc = document.documentElement; const doc = document.documentElement;
if (!doc.dataset.fonts) {
doc.dataset.fonts = 'loading';
}
try { try {
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
const savedPalette = localStorage.getItem('palette'); const savedPalette = localStorage.getItem('palette');
@@ -78,25 +75,6 @@
} catch (error) { } catch (error) {
// Ignore errors if storage or matchMedia is unavailable // Ignore errors if storage or matchMedia is unavailable
} }
const markFontsReady = () => {
if (doc.dataset.fonts !== 'ready') {
doc.dataset.fonts = 'ready';
}
};
const fallback = setTimeout(markFontsReady, 3000);
const resolveFonts = () => {
clearTimeout(fallback);
markFontsReady();
};
if (
document.fonts &&
document.fonts.ready &&
typeof document.fonts.ready.then === 'function'
) {
document.fonts.ready.then(resolveFonts, resolveFonts);
} else {
resolveFonts();
}
})(); })();
</script> </script>
%sveltekit.head% %sveltekit.head%

View File

@@ -14,7 +14,25 @@ const MIME_TYPES: Record<string, string> = {
}; };
const HTML_CACHE_CONTROL = 'public, max-age=0, must-revalidate'; const HTML_CACHE_CONTROL = 'public, max-age=0, must-revalidate';
const IMMUTABLE_ASSET_CACHE_CONTROL = 'public, max-age=31536000, immutable';
const ASSET_404_CACHE_CONTROL = 'no-store'; const ASSET_404_CACHE_CONTROL = 'no-store';
const LONG_CACHE_EXTENSIONS = new Set([
'.js',
'.mjs',
'.css',
'.json',
'.svg',
'.png',
'.jpg',
'.jpeg',
'.webp',
'.avif',
'.ico',
'.woff2',
'.woff',
'.ttf',
'.otf'
]);
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event); const response = await resolve(event);
@@ -36,11 +54,18 @@ export const handle: Handle = async ({ event, resolve }) => {
// keep pointing to already-rotated files long after a deployment. // keep pointing to already-rotated files long after a deployment.
if (response.status >= 400) { if (response.status >= 400) {
response.headers.set('cache-control', ASSET_404_CACHE_CONTROL); response.headers.set('cache-control', ASSET_404_CACHE_CONTROL);
} else if (pathname.startsWith('/_app/immutable/')) {
response.headers.set('cache-control', IMMUTABLE_ASSET_CACHE_CONTROL);
} }
return response; return response;
} }
const extension = path.extname(pathname).toLowerCase();
if (LONG_CACHE_EXTENSIONS.has(extension) && !contentType.includes('text/html')) {
response.headers.set('cache-control', IMMUTABLE_ASSET_CACHE_CONTROL);
}
// HTML documents should revalidate so they can reference the latest client // HTML documents should revalidate so they can reference the latest client
// bundle hashes after each deployment. // bundle hashes after each deployment.
if (contentType.includes('text/html')) { if (contentType.includes('text/html')) {

View File

@@ -412,14 +412,16 @@
.sidebar { .sidebar {
position: fixed; position: fixed;
top: 0; top: 0;
left: -300px; left: 0;
z-index: 100; z-index: 100;
height: 100vh; height: 100vh;
transition: left 0.3s ease; transform: translateX(-100%);
transition: transform 0.3s ease;
will-change: transform;
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2); box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2);
} }
.sidebar.open { .sidebar.open {
left: 0; transform: translateX(0);
} }
.close-btn { .close-btn {
display: block; display: block;

View File

@@ -252,6 +252,13 @@
}, },
}, },
]; ];
const matomoContainerSrc = 'https://matomo.howdoyouconvert.com/js/container_B3r877Kn.js';
type WindowWithAnalytics = Window & {
_mtm?: Array<Record<string, unknown>>;
requestIdleCallback?: (callback: () => void, options?: { timeout: number }) => number;
cancelIdleCallback?: (handle: number) => void;
};
let sidebarOpen = false; let sidebarOpen = false;
let headerSearchOpen = false; let headerSearchOpen = false;
@@ -303,6 +310,22 @@
} }
}; };
const loadMatomoContainer = () => {
if (!browser) return;
if (document.querySelector(`script[src="${matomoContainerSrc}"]`)) return;
const appWindow = window as WindowWithAnalytics;
const queue = appWindow._mtm ?? [];
appWindow._mtm = queue;
queue.push({ 'mtm.startTime': Date.now(), event: 'mtm.Start' });
const script = document.createElement('script');
script.async = true;
script.src = matomoContainerSrc;
script.setAttribute('data-cfasync', 'false');
document.head.appendChild(script);
};
afterNavigate(() => { afterNavigate(() => {
sidebarOpen = false; sidebarOpen = false;
headerSearchOpen = false; headerSearchOpen = false;
@@ -310,6 +333,9 @@
onMount(() => { onMount(() => {
if (!browser) return; if (!browser) return;
const appWindow = window as WindowWithAnalytics;
let idleCallbackId: number | null = null;
let fallbackTimeoutId: number | null = null;
const savedTheme = window.localStorage.getItem('theme') as ThemeMode | null; const savedTheme = window.localStorage.getItem('theme') as ThemeMode | null;
const savedPalette = window.localStorage.getItem('palette'); const savedPalette = window.localStorage.getItem('palette');
@@ -356,6 +382,11 @@
sidebarOpen = false; sidebarOpen = false;
} }
updateHeaderBreakpoint(); updateHeaderBreakpoint();
if (typeof appWindow.requestIdleCallback === 'function') {
idleCallbackId = appWindow.requestIdleCallback(loadMatomoContainer, { timeout: 3000 });
} else {
fallbackTimeoutId = window.setTimeout(loadMatomoContainer, 1200);
}
const cleanup = () => { const cleanup = () => {
if ('removeEventListener' in mediaQuery) { if ('removeEventListener' in mediaQuery) {
@@ -373,6 +404,12 @@
} else { } else {
headerBreakpoint.removeListener(handleHeaderBreakpoint); headerBreakpoint.removeListener(handleHeaderBreakpoint);
} }
if (idleCallbackId !== null && typeof appWindow.cancelIdleCallback === 'function') {
appWindow.cancelIdleCallback(idleCallbackId);
}
if (fallbackTimeoutId !== null) {
window.clearTimeout(fallbackTimeoutId);
}
window.removeEventListener('keydown', handleEscape); window.removeEventListener('keydown', handleEscape);
}; };
@@ -400,16 +437,6 @@
<svelte:head> <svelte:head>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg?v=2" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg?v=2" />
<!-- Matomo Tag Manager -->
<script>
var _mtm = window._mtm = window._mtm || [];
_mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'});
(function() {
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src='https://matomo.howdoyouconvert.com/js/container_B3r877Kn.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Tag Manager -->
</svelte:head> </svelte:head>
<a href="#main-content" class="skip-link">Skip to main content</a> <a href="#main-content" class="skip-link">Skip to main content</a>