Host fonts locally and guard FOUC

This commit is contained in:
Codex
2026-03-07 23:37:31 +00:00
parent c1aebbb5e2
commit 856b752c0b
11 changed files with 153 additions and 22 deletions

View File

@@ -1,4 +1,74 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap'); @font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/inter/Inter-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/fonts/inter/Inter-Medium.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/inter/Inter-SemiBold.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/inter/Inter-Bold.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('/fonts/inter/Inter-ExtraBold.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
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) ─────────────────────────────── */

View File

@@ -1,12 +1,71 @@
<!doctype html> <!doctype html>
<html lang="en" data-theme="dark" data-palette="classic"> <html lang="en" data-theme="dark" data-palette="classic" data-fonts="loading">
<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" />
<link
rel="preload"
href="/fonts/inter/Inter-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/inter/Inter-Medium.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/inter/Inter-SemiBold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/inter/Inter-Bold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/inter/Inter-ExtraBold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<script> <script>
(function () { (function () {
try {
const doc = document.documentElement; const doc = document.documentElement;
if (!doc.dataset.fonts) {
doc.dataset.fonts = 'loading';
}
try {
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
const savedPalette = localStorage.getItem('palette'); const savedPalette = localStorage.getItem('palette');
const prefersDark = const prefersDark =
@@ -19,6 +78,25 @@
} 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,8 +14,6 @@
let val2 = ''; let val2 = '';
let val3 = ''; let val3 = '';
let activeField: 1 | 2 | 3 = 1; let activeField: 1 | 2 | 3 = 1;
let swapSnapshot: { val1: string; val2: string } | null = null;
let isSwapFlipped = false;
$: has3 = ['3col', '3col-mul'].includes(config.type) || !!config.labels.in3; $: has3 = ['3col', '3col-mul'].includes(config.type) || !!config.labels.in3;
$: isTextInput = ['base', 'text-bin', 'bin-text', 'dec-frac', 'dms-dd', 'dd-dms'].includes(config.type); $: isTextInput = ['base', 'text-bin', 'bin-text', 'dec-frac', 'dms-dd', 'dd-dms'].includes(config.type);
@@ -27,13 +25,7 @@
let paramsInitializing = true; let paramsInitializing = true;
function resetSwapState() {
swapSnapshot = null;
isSwapFlipped = false;
}
function handleInput(source: 1 | 2 | 3) { function handleInput(source: 1 | 2 | 3) {
resetSwapState();
activeField = source; activeField = source;
const result = solve(config, source, val1, val2, val3); const result = solve(config, source, val1, val2, val3);
if (source !== 1) val1 = result.val1; if (source !== 1) val1 = result.val1;
@@ -42,23 +34,14 @@
} }
function swap() { function swap() {
if (isSwapFlipped && swapSnapshot) { const nextSource: 1 | 2 = activeField === 1 ? 2 : 1;
val1 = swapSnapshot.val1; handleInput(nextSource);
val2 = swapSnapshot.val2;
resetSwapState();
} else {
swapSnapshot = { val1, val2 };
[val1, val2] = [val2, val1];
isSwapFlipped = true;
}
activeField = activeField === 1 ? 2 : 1;
} }
function clear() { function clear() {
val1 = ''; val1 = '';
val2 = ''; val2 = '';
val3 = ''; val3 = '';
resetSwapState();
} }
onMount(() => { onMount(() => {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.