staging #80

Merged
esteban merged 35 commits from staging into production 2026-08-03 05:07:52 +00:00
1 changed files with 23 additions and 51 deletions
Showing only changes of commit b756830559 - Show all commits

View File

@ -45,18 +45,14 @@ const POLL_INTERVAL_MS = 10_000;
</h1> </h1>
<div <div
class="mt-8 flex items-end justify-center gap-1 font-mono text-colorSecondary sm:mt-10" class="mt-8 flex items-center justify-center font-mono text-colorSecondary sm:mt-10"
style="text-shadow: 0 0 40px rgba(203,161,106,0.35)" style="text-shadow: 0 0 40px rgba(203,161,106,0.35)"
> >
<span <span
id="group-thousands" id="counter-number"
class="countdown hidden text-[clamp(3.5rem,16vw,9rem)]" class="counter-number text-[clamp(3.5rem,16vw,9rem)] font-bold tabular-nums"
> aria-hidden="true"
<span id="digit-thousands" style="--value:0;" aria-hidden="true">0</span> >0</span>
</span>
<span class="countdown text-[clamp(3.5rem,16vw,9rem)]">
<span id="digit-rest" style="--value:0;" aria-hidden="true">0</span>
</span>
</div> </div>
<p id="sr-count" class="sr-only" aria-live="polite"> <p id="sr-count" class="sr-only" aria-live="polite">
@ -92,19 +88,11 @@ const POLL_INTERVAL_MS = 10_000;
.counter-card { .counter-card {
animation: counter-fade-in 0.6s ease-out; animation: counter-fade-in 0.6s ease-out;
} }
.countdown { .counter-number {
font-variant-numeric: tabular-nums; display: inline-block;
} }
/* Fix para Safari/iOS: sin esto, las dos capas del dígito (actual y .counter-number.counter-bump {
siguiente) no quedan recortadas ni aisladas y se ven superpuestas. */ animation: counter-bump 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
.countdown > * {
overflow: clip;
isolation: isolate;
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
.pulse-update {
animation: counter-pulse 0.5s ease-out;
} }
@keyframes counter-fade-in { @keyframes counter-fade-in {
from { from {
@ -116,15 +104,18 @@ const POLL_INTERVAL_MS = 10_000;
transform: translateY(0) scale(1); transform: translateY(0) scale(1);
} }
} }
@keyframes counter-pulse { @keyframes counter-bump {
0% { 0% {
transform: scale(1); transform: scale(1);
opacity: 1;
} }
30% { 35% {
transform: scale(1.06); transform: scale(1.12) translateY(-0.05em);
opacity: 0.55;
} }
100% { 100% {
transform: scale(1); transform: scale(1) translateY(0);
opacity: 1;
} }
} }
</style> </style>
@ -132,15 +123,12 @@ const POLL_INTERVAL_MS = 10_000;
<script is:inline define:vars={{ POLL_INTERVAL_MS }}> <script is:inline define:vars={{ POLL_INTERVAL_MS }}>
const ENDPOINT = "/api/voluntarios/count"; const ENDPOINT = "/api/voluntarios/count";
const groupThousands = document.getElementById("group-thousands"); const counterNumber = document.getElementById("counter-number");
const digitThousands = document.getElementById("digit-thousands");
const digitRest = document.getElementById("digit-rest");
const srCount = document.getElementById("sr-count"); const srCount = document.getElementById("sr-count");
const statusDot = document.getElementById("status-dot"); const statusDot = document.getElementById("status-dot");
const statusPing = document.getElementById("status-ping"); const statusPing = document.getElementById("status-ping");
const statusText = document.getElementById("status-text"); const statusText = document.getElementById("status-text");
const lastUpdated = document.getElementById("last-updated"); const lastUpdated = document.getElementById("last-updated");
const numberWrap = digitRest.closest("div");
const STATUS_STYLES = { const STATUS_STYLES = {
live: { color: "bg-emerald-400", text: "En vivo" }, live: { color: "bg-emerald-400", text: "En vivo" },
@ -151,31 +139,15 @@ const POLL_INTERVAL_MS = 10_000;
let currentCount = null; let currentCount = null;
function renderCount(count) { function renderCount(count) {
const safe = Math.max(0, Math.min(99999, Math.round(count))); const safe = Math.max(0, Math.min(999999, Math.round(count)));
const thousands = Math.floor(safe / 1000);
const rest = safe % 1000;
if (thousands > 0) {
groupThousands.classList.remove("hidden");
digitThousands.style.setProperty("--value", String(thousands));
digitThousands.textContent = String(thousands);
digitThousands.setAttribute("aria-label", String(thousands));
digitRest.style.setProperty("--digits", "3");
} else {
groupThousands.classList.add("hidden");
digitRest.style.removeProperty("--digits");
}
digitRest.style.setProperty("--value", String(rest));
digitRest.textContent = String(rest);
digitRest.setAttribute("aria-label", String(rest));
counterNumber.textContent = safe.toLocaleString("es-ES");
srCount.textContent = `${safe} ${safe === 1 ? "voluntario registrado" : "voluntarios registrados"}`; srCount.textContent = `${safe} ${safe === 1 ? "voluntario registrado" : "voluntarios registrados"}`;
if (currentCount !== null && currentCount !== safe && numberWrap) { if (currentCount !== null && currentCount !== safe) {
numberWrap.classList.remove("pulse-update"); counterNumber.classList.remove("counter-bump");
void numberWrap.offsetWidth; void counterNumber.offsetWidth;
numberWrap.classList.add("pulse-update"); counterNumber.classList.add("counter-bump");
} }
currentCount = safe; currentCount = safe;
} }