HTML (SVG) and CSS Only Progress Slider
by AntowaKartowa
HTML
<!-- HTML (SVG) & CSS Only! -->
<div class="progress-snippet">
<div class="progress-display">
<svg viewBox="0 0 240 240" fill="none">
<defs>
<circle
id="circle"
r="100"
cx="120"
cy="120"
pathLength="100"
transform="rotate(-90)"
transform-origin="center"
/>
</defs>
<use href="#circle" class="progress-track" />
<use href="#circle" class="progress-indicator" />
</svg>
<output for="slider" class="big-number"></output>
</div>
<div class="progress-controls">
<label for="slider">
Progress:
</label>
<input
id="slider"
type="range"
value="0"
min="0"
max="100"
step="1"
/>
</div>
</div>
CSS
/* base styles */
html {
background: hsl(210deg 15% 6%);
color: white;
}
body {
margin: 0;
padding: 0;
}
.progress-snippet {
display: grid;
height: 100vh;
width: 100vw;
grid-template-rows: 1fr auto;
}
.progress-display {
position: relative;
place-self: center;
}
.progress-controls {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
background: hsl(0deg 0% 100% / 0.1);
padding: 16px;
}
svg {
width: 240px;
stroke: hsl(45deg 100% 50%);
stroke-width: 10px;
stroke-linecap: round;
}
.progress-track {
stroke: hsl(35deg 30% 20%);
}
.big-number {
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
font-size: 3rem;
font-weight: bold;
font-family: monospace;
text-align: center;
}
#slider {
display: block;
width: 300px;
max-width: 100%;
}
/* progress thumb animation styles */
@property --progress {
syntax: '<number>';
inherits: false;
initial-value: 0;
}
@property --progress-int {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
@keyframes progress {
0% {
--progress: 1;
--progress-int: 100;
}
100% {
--progress: 0;
--progress-int: 0;
}
}
.progress-snippet {
--offset-min: 100px;
--offset-max: 0px;
--stroke-dashoffset: calc(var(--offset-max) * var(--progress) + var(--offset-min) * (1 - var(--progress)));
counter-reset: progress-value var(--progress-int);
timeline-scope: --progress-timeline;
animation: linear forwards;
animation-name: progress;
animation-timeline: --progress-timeline;
animation-range: contain 0% contain 100%;
}
#slider {
overflow: hidden;
&::-webkit-slider-thumb {
view-timeline: --progress-timeline inline;
}
&::-moz-range-thumb {
view-timeline: --progress-timeline inline;
}
}
.progress-indicator {
stroke-dasharray: 100 100;
...