perf: self-host fonts, eliminate CLS, a11y and contrast fixes

- Self-host Inter and JetBrains Mono (woff2, 69KB total)
- Remove Google Fonts dependency entirely (no external requests)
- Service Worker pre-caches font files
- Eliminate font-swap layout shifts
- WCAG contrast fix: disabled button uses solid color instead of opacity
- Footer link always underlined for distinguishability
- Translated aria-labels via data-i18n-aria
- Dimmed QR with "Bezahlt" stamp on paid invoices
- Hide wallet/address buttons when invoice is paid
This commit is contained in:
Alexander Schmidt
2026-03-25 16:56:10 +01:00
parent 8bcdb33fa3
commit 6a9a5b6a75
5 changed files with 20 additions and 20 deletions

BIN
fonts/inter-400.woff2 Normal file

Binary file not shown.

BIN
fonts/jetbrains-400.woff2 Normal file

Binary file not shown.

View File

@@ -6,10 +6,7 @@
<title>xmrpay.link — Monero Invoice Generator</title>
<meta name="description" content="Create Monero payment requests in seconds. No account, no backend, no KYC.">
<link rel="icon" id="favicon" href="favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
</head>
<body>

View File

@@ -1,19 +1,19 @@
@font-face {
font-family: 'Inter fallback';
src: local('Arial');
size-adjust: 107%;
ascent-override: 90%;
descent-override: 25%;
line-gap-override: 0%;
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('fonts/inter-400.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'JetBrains Mono fallback';
src: local('Courier New');
size-adjust: 112%;
ascent-override: 78%;
descent-override: 22%;
line-gap-override: 0%;
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/jetbrains-400.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
:root {
@@ -28,8 +28,8 @@
--success: #4caf50;
--error: #f44336;
--radius: 8px;
--font: 'Inter', 'Inter fallback', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--mono: 'JetBrains Mono', 'JetBrains Mono fallback', 'Fira Code', monospace;
--font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--mono: 'JetBrains Mono', 'Fira Code', monospace;
}
* {
@@ -320,7 +320,8 @@ textarea {
}
.btn-primary:disabled {
opacity: 0.5;
background: #804020;
color: #ccc;
cursor: not-allowed;
}

6
sw.js
View File

@@ -5,8 +5,10 @@ var ASSETS = [
'/app.js',
'/i18n.js',
'/style.css',
'/lib/qrcode.min.js'
// xmr-crypto.bundle.js is lazy-loaded and runtime-cached
'/lib/qrcode.min.js',
'/fonts/inter-400.woff2',
'/fonts/jetbrains-400.woff2'
// xmr-crypto.bundle.js and jspdf.min.js are lazy-loaded and runtime-cached
];
self.addEventListener('install', function (e) {