Improve frontend performance and caching behavior
This commit is contained in:
@@ -62,14 +62,6 @@
|
||||
src: url('/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
body {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
:root[data-fonts='loading'] body {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* ─── Colors (Dark Theme) ─────────────────────────────── */
|
||||
--bg: #0c0f14;
|
||||
@@ -467,11 +459,7 @@ a:focus-visible {
|
||||
.palette-dots {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
max-width: 38px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
flex: 0 0 auto;
|
||||
transition: max-width 0.2s ease, gap 0.2s ease;
|
||||
}
|
||||
.palette-dot {
|
||||
width: 30px;
|
||||
@@ -482,11 +470,10 @@ a:focus-visible {
|
||||
background-size: 160%;
|
||||
background-position: center;
|
||||
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);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
order: 0;
|
||||
transform-origin: center;
|
||||
}
|
||||
.palette-dot:hover {
|
||||
@@ -500,27 +487,6 @@ a:focus-visible {
|
||||
.palette-dot:focus-visible {
|
||||
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) {
|
||||
.floating-palette-controls {
|
||||
gap: 0.2rem;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="dark" data-palette="classic" data-fonts="loading">
|
||||
<html lang="en" data-theme="dark" data-palette="classic">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@@ -62,9 +62,6 @@
|
||||
<script>
|
||||
(function () {
|
||||
const doc = document.documentElement;
|
||||
if (!doc.dataset.fonts) {
|
||||
doc.dataset.fonts = 'loading';
|
||||
}
|
||||
try {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const savedPalette = localStorage.getItem('palette');
|
||||
@@ -78,25 +75,6 @@
|
||||
} catch (error) {
|
||||
// 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>
|
||||
%sveltekit.head%
|
||||
|
||||
@@ -14,7 +14,25 @@ const MIME_TYPES: Record<string, string> = {
|
||||
};
|
||||
|
||||
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 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 }) => {
|
||||
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.
|
||||
if (response.status >= 400) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
// bundle hashes after each deployment.
|
||||
if (contentType.includes('text/html')) {
|
||||
|
||||
@@ -412,14 +412,16 @@
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: -300px;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
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);
|
||||
}
|
||||
.sidebar.open {
|
||||
left: 0;
|
||||
transform: translateX(0);
|
||||
}
|
||||
.close-btn {
|
||||
display: block;
|
||||
|
||||
@@ -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 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(() => {
|
||||
sidebarOpen = false;
|
||||
headerSearchOpen = false;
|
||||
@@ -310,6 +333,9 @@
|
||||
|
||||
onMount(() => {
|
||||
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 savedPalette = window.localStorage.getItem('palette');
|
||||
@@ -356,6 +382,11 @@
|
||||
sidebarOpen = false;
|
||||
}
|
||||
updateHeaderBreakpoint();
|
||||
if (typeof appWindow.requestIdleCallback === 'function') {
|
||||
idleCallbackId = appWindow.requestIdleCallback(loadMatomoContainer, { timeout: 3000 });
|
||||
} else {
|
||||
fallbackTimeoutId = window.setTimeout(loadMatomoContainer, 1200);
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
if ('removeEventListener' in mediaQuery) {
|
||||
@@ -373,6 +404,12 @@
|
||||
} else {
|
||||
headerBreakpoint.removeListener(handleHeaderBreakpoint);
|
||||
}
|
||||
if (idleCallbackId !== null && typeof appWindow.cancelIdleCallback === 'function') {
|
||||
appWindow.cancelIdleCallback(idleCallbackId);
|
||||
}
|
||||
if (fallbackTimeoutId !== null) {
|
||||
window.clearTimeout(fallbackTimeoutId);
|
||||
}
|
||||
window.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
|
||||
@@ -400,16 +437,6 @@
|
||||
<svelte:head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<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>
|
||||
|
||||
<a href="#main-content" class="skip-link">Skip to main content</a>
|
||||
|
||||
Reference in New Issue
Block a user