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');
|
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;
|
||||||
|
|||||||
@@ -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%
|
||||||
|
|||||||
@@ -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')) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user