perf: 100% Lighthouse score — contrast, CLS, caching fixes
- Font preload eliminates layout shift (CLS 0) - Dual accent colors: --accent for filled buttons, --accent-text for text on dark bg - All WCAG AA contrast ratios met (buttons, links, badges, countdown) - Language picker: position absolute instead of fixed - CoinGecko rates proxied with 2min server-side cache (no CORS, no rate limit) - English as default inline text (no empty→text shift)
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Server-side cache: fetch from CoinGecko at most once per 2 minutes
|
||||
$cacheFile = __DIR__ . '/../data/rates_cache.json';
|
||||
$cacheTTL = 120; // seconds
|
||||
|
||||
if (file_exists($cacheFile)) {
|
||||
$cached = json_decode(file_get_contents($cacheFile), true);
|
||||
if ($cached && (time() - ($cached['_time'] ?? 0)) < $cacheTTL) {
|
||||
unset($cached['_time']);
|
||||
header('Cache-Control: public, max-age=60');
|
||||
echo json_encode($cached);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$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,
|
||||
@@ -15,8 +27,27 @@ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response !== false && $httpCode === 200) {
|
||||
echo $response;
|
||||
} else {
|
||||
$data = json_decode($response, true);
|
||||
if ($data) {
|
||||
$data['_time'] = time();
|
||||
file_put_contents($cacheFile, json_encode($data));
|
||||
unset($data['_time']);
|
||||
header('Cache-Control: public, max-age=60');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// On error, serve stale cache if available
|
||||
if (file_exists($cacheFile)) {
|
||||
$cached = json_decode(file_get_contents($cacheFile), true);
|
||||
if ($cached) {
|
||||
unset($cached['_time']);
|
||||
header('Cache-Control: public, max-age=30');
|
||||
echo json_encode($cached);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
http_response_code(502);
|
||||
echo json_encode(['error' => 'Failed to fetch rates']);
|
||||
}
|
||||
|
||||
@@ -6,6 +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="preload" href="fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
19
style.css
19
style.css
@@ -23,8 +23,9 @@
|
||||
--border: #333;
|
||||
--text: #e0e0e0;
|
||||
--text-muted: #888;
|
||||
--accent: #ff6600;
|
||||
--accent-hover: #ff8533;
|
||||
--accent: #c74a00;
|
||||
--accent-hover: #a83f00;
|
||||
--accent-text: #e87830;
|
||||
--success: #4caf50;
|
||||
--error: #f44336;
|
||||
--radius: 8px;
|
||||
@@ -54,7 +55,7 @@ header {
|
||||
}
|
||||
|
||||
.lang-picker {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
z-index: 50;
|
||||
@@ -119,7 +120,7 @@ header {
|
||||
}
|
||||
|
||||
.lang-option.active {
|
||||
color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
@@ -138,7 +139,7 @@ header h1 a:hover {
|
||||
}
|
||||
|
||||
header h1 span {
|
||||
color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
header p {
|
||||
@@ -590,8 +591,8 @@ textarea {
|
||||
.btn-new {
|
||||
margin-top: 0.8rem;
|
||||
background: transparent;
|
||||
border: 1px solid var(--accent);
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent-text);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
.btn-new:hover {
|
||||
@@ -628,7 +629,7 @@ footer {
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -644,7 +645,7 @@ footer a {
|
||||
}
|
||||
|
||||
.countdown.active {
|
||||
color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
|
||||
Reference in New Issue
Block a user