Visual countdown/elapsed SLA indicator.
First response
2h 15m remaining
on track<div class="sla-timer" data-state="on track">
<div class="sla-timer__ring-wrap">
<svg class="sla-timer__ring" viewBox="0 0 36 36" aria-hidden="true">
<circle class="sla-timer__track" cx="18" cy="18" r="15.9155" fill="none" stroke-width="3"/>
<circle class="sla-timer__fill" cx="18" cy="18" r="15.9155" fill="none" stroke-width="3" stroke-dasharray="65 100" stroke-dashoffset="25" style="stroke:#BFD9F2;" stroke-linecap="round"/>
</svg>
<span class="sla-timer__pct">65%</span>
</div>
<div class="sla-timer__body">
<p class="sla-timer__label">First response</p>
<p class="sla-timer__remaining">2h 15m remaining</p>
<span class="sla-timer__state">
<svg class="sla-timer__icon sla-timer__icon--on-track" viewBox="0 0 20 20" aria-hidden="true"><path d="M4 10.5l3.5 3.5L16 5.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<svg class="sla-timer__icon sla-timer__icon--at-risk" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 2L18.5 17H1.5L10 2z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M10 8v4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="10" cy="14.6" r="1" fill="currentColor"/></svg>
<svg class="sla-timer__icon sla-timer__icon--breached" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M7 7l6 6M13 7l-6 6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
on track
</span>
</div>
</div>
<style>
.sla-timer { display: flex; align-items: center; gap: 16px; width: 320px; padding: 16px; background: #FAF7F2; color: #241430; font: 14px/1.4 system-ui, sans-serif; border-radius: 14px; box-shadow: 0 8px 24px rgba(36,20,48,.12); box-sizing: border-box; }
.sla-timer__ring-wrap { position: relative; width: 64px; height: 64px; flex-shrink: 0; }
.sla-timer__ring { width: 64px; height: 64px; transform: rotate(-90deg); }
.sla-timer__track { stroke: #24143018; }
.sla-timer__pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 12px; }
.sla-timer__label { margin: 0; font-weight: 700; }
.sla-timer__remaining { margin: 2px 0 8px; color: #241430b3; font-size: 13px; }
.sla-timer__state { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; font-weight: 700; font-size: 12px; text-transform: capitalize; }
.sla-timer__icon { width: 12px; height: 12px; display: none; }
.sla-timer[data-state="on track"] .sla-timer__icon--on-track { display: block; }
.sla-timer[data-state="at risk"] .sla-timer__icon--at-risk { display: block; }
.sla-timer[data-state="breached"] .sla-timer__icon--breached { display: block; }
.sla-timer[data-state="on track"] .sla-timer__state { background: #A8E6C1; color: #17532F; }
.sla-timer[data-state="at risk"] .sla-timer__state { background: #E8A020; color: #4A2E00; }
.sla-timer[data-state="breached"] .sla-timer__state { background: #F5C2E0; color: #7A1440; }
</style><div class="sla-timer-tw flex items-center gap-4 w-80 p-4 bg-[#FAF7F2] text-[#241430] font-sans text-sm rounded-2xl shadow-lg box-border" data-state="on track">
<div class="relative w-16 h-16 flex-shrink-0">
<svg class="w-16 h-16 -rotate-90" viewBox="0 0 36 36" aria-hidden="true">
<circle cx="18" cy="18" r="15.9155" fill="none" stroke="#24143018" stroke-width="3"/>
<circle cx="18" cy="18" r="15.9155" fill="none" stroke-width="3" stroke-dasharray="65 100" stroke-dashoffset="25" stroke-linecap="round" style="stroke:#BFD9F2;"/>
</svg>
<span class="absolute inset-0 flex items-center justify-center font-bold text-xs">65%</span>
</div>
<div>
<p class="m-0 font-bold">First response</p>
<p class="mt-0.5 mb-2 text-[13px] text-[#241430]/70">2h 15m remaining</p>
<span class="sla-timer-tw__state inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full font-bold text-xs capitalize">
<svg class="sla-timer-tw__icon sla-timer-tw__icon--on-track w-3 h-3" viewBox="0 0 20 20" aria-hidden="true"><path d="M4 10.5l3.5 3.5L16 5.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<svg class="sla-timer-tw__icon sla-timer-tw__icon--at-risk w-3 h-3" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 2L18.5 17H1.5L10 2z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M10 8v4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="10" cy="14.6" r="1" fill="currentColor"/></svg>
<svg class="sla-timer-tw__icon sla-timer-tw__icon--breached w-3 h-3" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M7 7l6 6M13 7l-6 6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
on track
</span>
</div>
</div>
<style>
.sla-timer-tw__icon { display: none; }
.sla-timer-tw[data-state="on track"] .sla-timer-tw__icon--on-track { display: block; }
.sla-timer-tw[data-state="at risk"] .sla-timer-tw__icon--at-risk { display: block; }
.sla-timer-tw[data-state="breached"] .sla-timer-tw__icon--breached { display: block; }
.sla-timer-tw[data-state="on track"] .sla-timer-tw__state { background: #A8E6C1; color: #17532F; }
.sla-timer-tw[data-state="at risk"] .sla-timer-tw__state { background: #E8A020; color: #4A2E00; }
.sla-timer-tw[data-state="breached"] .sla-timer-tw__state { background: #F5C2E0; color: #7A1440; }
</style>export function SlaTimer({
label = "First response",
remaining = "2h 15m remaining",
percentElapsed = 65,
state = "on track",
accent = "#BFD9F2",
}) {
const palette = {
"on track": { background: "#A8E6C1", color: "#17532F" },
"at risk": { background: "#E8A020", color: "#4A2E00" },
breached: { background: "#F5C2E0", color: "#7A1440" },
}[state] ?? {};
return (
<div style={{ display: "flex", alignItems: "center", gap: 16, width: 320, padding: 16, background: "#FAF7F2",
color: "#241430", fontFamily: "system-ui, sans-serif", fontSize: 14, borderRadius: 14,
boxShadow: "0 8px 24px rgba(36,20,48,.12)", boxSizing: "border-box" }}>
<div style={{ position: "relative", width: 64, height: 64, flexShrink: 0 }}>
<svg width="64" height="64" viewBox="0 0 36 36" style={{ transform: "rotate(-90deg)" }} aria-hidden="true">
<circle cx="18" cy="18" r="15.9155" fill="none" stroke="#24143018" strokeWidth="3" />
<circle cx="18" cy="18" r="15.9155" fill="none" strokeWidth="3" strokeDasharray={`${percentElapsed} 100`}
strokeDashoffset="25" strokeLinecap="round" stroke={accent} />
</svg>
<span style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center",
fontWeight: 700, fontSize: 12 }}>{percentElapsed}%</span>
</div>
<div>
<p style={{ margin: 0, fontWeight: 700 }}>{label}</p>
<p style={{ margin: "2px 0 8px", color: "#241430b3", fontSize: 13 }}>{remaining}</p>
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 10px", borderRadius: 999,
fontWeight: 700, fontSize: 12, textTransform: "capitalize", ...palette }}>
{state === "on track" && (
<svg width="12" height="12" viewBox="0 0 20 20" aria-hidden="true"><path d="M4 10.5l3.5 3.5L16 5.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>
)}
{state === "at risk" && (
<svg width="12" height="12" viewBox="0 0 20 20" aria-hidden="true"><path d="M10 2L18.5 17H1.5L10 2z" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" /><path d="M10 8v4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /><circle cx="10" cy="14.6" r="1" fill="currentColor" /></svg>
)}
{state === "breached" && (
<svg width="12" height="12" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" strokeWidth="1.6" /><path d="M7 7l6 6M13 7l-6 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>
)}
{state}
</span>
</div>
</div>
);
}