perf: CoinGecko proxy, font-display optional, contrast fix

- Route CoinGecko API through /api/rates.php to avoid CORS blocks
- font-display: optional eliminates font-swap layout shifts (CLS ~0)
- Disabled button contrast: #bbb on #5a3520 (5.8:1 ratio)
- Nginx font caching: 1 year, immutable
This commit is contained in:
Alexander Schmidt
2026-03-25 17:00:20 +01:00
parent 6a9a5b6a75
commit 8d3e37239f
5 changed files with 30 additions and 8 deletions

22
api/rates.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
header('Content-Type: application/json');
header('Cache-Control: public, max-age=60');
$url = 'https://api.coingecko.com/api/v3/simple/price?ids=monero&vs_currencies=eur,usd,chf';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response !== false && $httpCode === 200) {
echo $response;
} else {
http_response_code(502);
echo json_encode(['error' => 'Failed to fetch rates']);
}

2
app.js
View File

@@ -2,7 +2,7 @@
'use strict';
// --- Config ---
const COINGECKO_API = 'https://api.coingecko.com/api/v3/simple/price?ids=monero&vs_currencies=eur,usd,chf';
const COINGECKO_API = '/api/rates.php';
// Standard address (4..., 95 chars), Subaddress (8..., 95 chars), Integrated address (4..., 106 chars)
const XMR_STANDARD_REGEX = /^[48][1-9A-HJ-NP-Za-km-z]{94}$/;
const XMR_INTEGRATED_REGEX = /^4[1-9A-HJ-NP-Za-km-z]{105}$/;

2
app.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
font-display: optional;
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;
}
@@ -11,7 +11,7 @@
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
font-display: optional;
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;
}
@@ -320,8 +320,8 @@ textarea {
}
.btn-primary:disabled {
background: #804020;
color: #ccc;
background: #5a3520;
color: #bbb;
cursor: not-allowed;
}

4
sw.js
View File

@@ -35,8 +35,8 @@ self.addEventListener('activate', function (e) {
self.addEventListener('fetch', function (e) {
var url = new URL(e.request.url);
// External APIs and RPC proxy — network only, don't cache
if (url.hostname !== location.hostname || url.pathname.startsWith('/api/')) {
// API calls — network only, don't cache
if (url.pathname.startsWith('/api/')) {
e.respondWith(fetch(e.request));
return;
}