CSS Only Circular Reading Progress Indicator

Category: Javascript | April 10, 2024
Views Total:23 views
Official Page:Go to website
Last Update:April 10, 2024


CSS Only Circular Reading Progress Indicator


A pure CSS circular reading progress indicator that provides a visual representation of the user’s current reading progress.

The circular progress bar automatically fills up, and the percentage of the page that has been scrolled is displayed in the middle of the indicator as your users scroll down the page.

How to use it:

1. Create the HTML for the reading progress indicator.

<div class="progress">
  <span class="progress__txt"></span>

2. The Circular reading progress indicator works by utilizing a combination of CSS variables and animations. First, we define a custom CSS property called --percentage that will be used to track the current scroll position. As the user scrolls, the --percentage value is updated from 0 to 100, animating the circular progress bar.

@property --percentage {
  initial-value: 0;
  inherits: false;
  syntax: '<number>';
@keyframes updatePerc {
  to {
    --percentage: 100;
:root {
  animation: updatePerc linear; 
  animation-timeline: scroll();
 --primary-color: hsl(calc(var(--percentage, 0) * 3.6), 45%, 56%);
 --angle: calc(var(--percentage, 0) * 3.6deg);
 --txt: calc(var(--percentage));

3. To create the circular progress bar, we use a conic-gradient background on the `.progress` element. The gradient is generated using the --primary-color variable, which changes based on the --percentage value. This gives the progress bar a visually appealing color transition as it fills up. Additionally, the --angle variable is used to control the angle of the gradient, ensuring that the progress bar fills up correctly. Finally, we use the counter-reset and counter functions to display the current percentage in the middle of the indicator.

.progress:after {
  background: conic-gradient(
    var(--primary-color) var(--angle),
    transparent var(--angle)
.progress__txt:after {
  counter-reset: myCounter var(--txt);
  content: counter(myCounter) "%";
  display: block;
.progress {
  --borderWidth: 10px;
  --size: 8rem;
  width: var(--size);
  height: var(--size);
  margin: 60px auto;
  border: 1px solid white;
  position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  top: 2rem;
  right: 2rem;
  z-index: 2;
  font-size: 2em;
.progress:before {
  border-radius: 100%;
  content: '';
  width: calc(var(--size) - var(--borderWidth) );
  height: calc(var(--size) - var(--borderWidth));
  position: absolute;
  background: #222;
  z-index: 1;
.progress:after {
  border-radius: 100%;
  content: '';
  position: absolute;
  top: calc(var(--borderWidth)  / -2);
  left: calc(var(--borderWidth) / -2);
  width: calc(var(--size) + var(--borderWidth));
  height: calc(var(--size) + var(--borderWidth))
.progress__txt {
  z-index: 2;

You Might Be Interested In:

Leave a Reply