{"id":19806535,"url":"https://github.com/monade/circular-progress-bar","last_synced_at":"2025-11-11T19:30:21.537Z","repository":{"id":53148270,"uuid":"521204412","full_name":"monade/circular-progress-bar","owner":"monade","description":null,"archived":false,"fork":false,"pushed_at":"2022-08-04T20:00:06.000Z","size":15610,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-11T06:22:17.968Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/monade.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-08-04T09:30:46.000Z","updated_at":"2022-11-23T21:21:54.000Z","dependencies_parsed_at":"2022-09-13T18:12:37.435Z","dependency_job_id":null,"html_url":"https://github.com/monade/circular-progress-bar","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monade%2Fcircular-progress-bar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monade%2Fcircular-progress-bar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monade%2Fcircular-progress-bar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monade%2Fcircular-progress-bar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/monade","download_url":"https://codeload.github.com/monade/circular-progress-bar/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241150393,"owners_count":19918350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T09:07:49.939Z","updated_at":"2025-11-11T19:30:21.388Z","avatar_url":"https://github.com/monade.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Title: A Circular Progress Bar made only with div\nAuthors: Federico Mainetti Gamebra (federico@monade.io)\nAbstract: how to use only div elements and some clever css to build a cool Circular Progress Bar.\nCover image url: https://github.com/monade/monade-tech-blog/tree/master/2022/react-circular-progress-bar/images/cover.png\nTags: fronted, react, typescript, progress bar, tutorial\nComplete: false\n\n-------\n\n**TLDR** If you aren't interested in understanding the mechanincs of this circular progress bar built without using `svg` or `canvas`, and the only thing you want is to use this component in your React application, just checkout our **[npm package](https://www.npmjs.com/package/@monade/react-circular-progress-bar)**.\n\n# A Circular Progress Bar made only with `\u003cdiv/\u003e`\n\nWorking with curved elements in HTML is not simple, developers usually find themselves having to use elements like `\u003ccanvas\u003e` or `\u003csvg\u003e` to accomplish their goals.\n\nFor example, an horizontal progress bar is very simple to make in HTML + CSS + JS, however, to make it round, the complexity is much greater.\n\nIn this article I'm going to explain how to use only **`\u003cdiv/\u003e`** elements and some clever **CSS** to build a cool **circular progress bar**.\n\n## 1. How Does It Work?\n\n![](./images/react/final220speed.gif)\n\nTo build a circular progress bar we'll use two important CSS properties:\n\n- **[Clip](https://developer.mozilla.org/en-US/docs/Web/CSS/clip)**: this property allows us to identify a **visibility rectangle** for an element whose position is absolute (es. `clip: rect(1px, 10em, 3rem, 2ch);`). Any children that is not positioned inside the visibility rectangle will not be rendered.\n- **[Transform](https://developer.mozilla.org/en-US/docs/Web/CSS/transform)**: this property is very powerful, but we will only use it to **rotate** some elements.\n\nThe technique I will show you breaks the circle into two halves, the one on the right (from 0% to 50%) and the one on the left (from 50% to 100%).\nTo better understand how we can achive this goal, we'll initially analize\nTo understand more easily how it works, initially we'll analyze only the right half, and, for the left half, we'll just need to apply the same reasoning but mirrored.\n\nSo, for the first half of the circle there are two main elements:\n\n- a square **container** `div`, that contains...\n- ...the actual full **circle** `div`.\n\nThe container height and width are the same as the circle's ones, like this:\n\n![](./images/howDoesItWorks/screen00.png)\n\nWith the **clip** property **applied to the circle** we can hide his right half, obtaining only a left half circle.\n\n![](./images/howDoesItWorks/screen01.png)\n\nWith the **clip** property **applied to the container** we can hide anything that is in the left half and only show what is in the right half. Since there is nothing to show in the right half we now have an empty square, that secretly hides half a circle.\n\n![](./images/howDoesItWorks/screen02.png)\n\nNow, if we slightly rotate the hidden half circle with the **tranform** property, we will see it come out on the right side, like this:\n\n![](./images/howDoesItWorks/screen03.png)\n\nSo, if we want to show some progress, we just need to rotare the half colored circle so that it starts to show in the non-clipped right side.\n\nThis process only covers the progress of the right half (from 0% to 50%), but if we apply the same reasoning for the other half and we put some logic behind the rotation, we can achieve the illusion of a circular progress bar.\n\n## 2. Let's build it with HTML, CSS and JavaScript\n\nLet's put everything we said in practice with a simple and plain HTML, CSS and JavaScript application.\n\nFeel free to follow along step by step, or, if you want to skip to the final solution, you can find all the code that will be used in this section **[here](https://github.com/monade/circular-progress-bar/tree/main/circular-progress-bar-html-css-js)**.\n\n### Step 1: Basic Structure\n\nThe first step we'll take is to get all the elements we need in place:\n\n![](./images/step1/screen1.png)\n\nLet's start with a simple `index.html` that contains two main elements:\n\n- a **slider** to control the percentage of progress and\n- a **group of `\u003cdiv/\u003e` elements** that represent the ciruclar progress bar as explained in the above section.\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.html\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003ctitle\u003eCircular progress bar\u003c/title\u003e\n    \u003clink rel=\"stylesheet\" type=\"text/css\" href=\"./index.css\" /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv class=\"container\"\u003e\n      \u003cdiv class=\"circular\"\u003e\n        \u003cdiv class=\"circle\"\u003e\n          \u003cdiv class=\"bar right\"\u003e\n            \u003cdiv class=\"progress\"\u003e\u003c/div\u003e\n          \u003c/div\u003e\n          \u003cdiv class=\"bar left\"\u003e\n            \u003cdiv class=\"progress\"\u003e\u003c/div\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv class=\"container\"\u003e\n      \u003cspan\u003e0\u003c/span\u003e\n      \u003cinput id=\"input-range\" type=\"range\" /\u003e\n      \u003cspan\u003e100\u003c/span\u003e\n    \u003c/div\u003e\n    \u003cscript src=\"./index.js\"\u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nLet's better understand what the purpose of each of these `\u003cdiv/\u003e` elements is:\n\n```html\n\u003cdiv class=\"bar right\"\u003e\n  \u003cdiv class=\"progress\" /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"bar left\"\u003e\n  \u003cdiv class=\"progress\" /\u003e\n\u003c/div\u003e\n```\n\n- The two `\u003cdiv class=\"progress\" /\u003e` are the actual **half-circle elements** that correctly rotated will represent the right-half of the circle (from 0% to 50%) and the left-half of the circle (from 50% to 100%).\n- `\u003cdiv class=\"bar right\"\u003e` and `\u003cdiv class=\"bar left\"\u003e` are the two **containers** we were talking about in the previous section. Their purpose is to use the clip css property to hide and show part of their children elements.\n\nNow let's apply some basic css to get everything in place:\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.css\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```css\n/* this css rule is used only to help us with visual debug */\n* {\n  border: 1px solid red;\n}\n\nbody {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n}\n\n.container {\n  width: 80%;\n\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  padding: 20px;\n}\n\n.container input {\n  width: 100%;\n  margin: 0px 30px;\n}\n\n.circular {\n  position: relative;\n  margin: 0;\n  padding: 0;\n\n  /*\n  * Diameter of the circle: 500px, \n  * we'll fix the fact that this value is hardcoded later \n  */\n  height: 500px;\n  width: 500px;\n}\n\n.circular .bar {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n  border-radius: 50%;\n}\n\n.circle .bar .progress {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n  border-radius: 50%;\n}\n```\n\nAll this css is pretty straight forward, the only things to note are:\n\n- `* { border: 1px solid red; }` is used only for visual debug purpose\n- in the `.circular { /*...*/ }` rule we hardcode the height and the width to match the diamater of the circle of the progress bar we are going to create, but this is only temporary, we'll remove it later.\n\nAll that is left is some basic JavaScript:\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.js\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```js\n/**\n * Waits for the document to be ready before running the init function\n */\nfunction docReady(fn) {\n  if (document.readyState === \"complete\"\n      || document.readyState === \"interactive\") {\n    setTimeout(fn, 1);\n  } else {\n    document.addEventListener(\"DOMContentLoaded\", fn);\n  }\n}\ndocReady(init);\n\nfunction init() {\n  // set up will happen here...\n\n  makeProgressBarInteractive();\n}\n\nfunction makeProgressBarInteractive() {\n  let inputRef = document.getElementsByTagName(\"input\")[0];\n  inputRef.addEventListener(\"input\", updateCiruclarProgressBar);\n}\n\nfunction updateCiruclarProgressBar(event) {\n  console.log(event.target.value);\n\n  // fix the rotation of the circles to match the slider value...\n}\n```\n\n### Step 2: Clip\n\nThe next step is to define a **diameter** in pixels and a **color** for our circles:\n\n```js\nconst DIAMETER = 500;\nconst COLOR = \"#ff0000\";\n```\n\nNow the `init` function will get the references of all the `\u003cdiv/\u003e` elements and set up the **background-color** and the **clip** properties:\n\n```js\nlet inputRef = null;\nlet rightBar = null;\nlet leftBar = null;\nlet rightProgress = null;\nlet leftProgress = null;\n\nfunction init() {\n  rightBar = document.getElementsByClassName(\"bar\")[0];\n  leftBar = document.getElementsByClassName(\"bar\")[1];\n  rightProgress = document.getElementsByClassName(\"progress\")[0];\n  leftProgress = document.getElementsByClassName(\"progress\")[1];\n\n  // clip the right part of the circle\n  rightBar.style.clip = `rect(0px, ${DIAMETER}px, ${DIAMETER}px, ${DIAMETER / 2}px)`;\n  // clip the left part of the circle\n  rightProgress.style.clip = `rect(0px, ${DIAMETER / 2}px, ${DIAMETER}px, 0px)`;\n\n  // clip the left part of the cicle\n  leftBar.style.clip = `rect(0px, ${DIAMETER / 2}px, ${DIAMETER}px, 0px)`;\n  // clip the right part of the circle\n  leftProgress.style.clip = `rect(0px, ${DIAMETER}px, ${DIAMETER}px, ${DIAMETER / 2}px)`;\n\n  // set background color\n  rightProgress.style.backgroundColor = `${COLOR}`;\n  leftProgress.style.backgroundColor = `${COLOR}`;\n\n  makeProgressBarInteractive();\n}\n```\n\n![](./images/step2/screen2.png)\n\n### Step 3: Rotate\n\nNow the fun part! Let's rotate the `rightProgress` and `leftProgress` elements accordingly to the percentage value of the slider.\n\n![](./images/step3/screen4.png)\n\n```js\nfunction makeProgressBarInteractive() {\n  inputRef = document.getElementsByTagName(\"input\")[0];\n\n  inputRef.addEventListener(\"input\", updateCiruclarProgressBar);\n}\n\nfunction updateCiruclarProgressBar(event) {\n  // percentage value is get from the slider\n  const percentage = event.target.value / 100;\n\n  // the right side of the circle handles the progress from 0% up to 50%\n  // if the progress is over 50% then the rotation will max at 180 degrees\n  rightProgress.style.transform = `rotate(${percentage \u003c 0.5 ? percentage * 2 * 180 : 180}deg)`;\n\n  // the left side of the circle handles the progress from 50% up to 100%\n  // if the progress is under 50% then the rotation will be of 0 degrees.\n  leftProgress.style.transform = `rotate(${percentage \u003e 0.5 ? percentage * 2 * 180 + 180 : 0}deg)`;\n}\n```\n\nAn important thing to note is that the `leftProgress` element must start to rotate only when the percentage of progress reaches 50%, on the other side, the `rightProgress` element must stop his rotation when the percentage of progress reaches 50%.\n\n### Step 4: Polishing the details\n\nTo have a beautiful circle all that is left is to get rid of the `* { border: 1px solid red; }` property:\n\n![](./images/step4/screen5.png)\n\nWe also need to handle with JavaScript the question of the height and width that was initially hardcoded in the css:\n\n```css\n.circular {\n  /*...*/\n  /* remove -\u003e height: 500px; */\n  /* remove -\u003e width: 500px; */\n}\n```\n\nand replace it with:\n\n```js\nlet circularRef = null;\n\nfunction init() {\n  circularRef = document.getElementsByClassName(\"circular\")[0];\n  circularRef.style.height = DIAMETER + \"px\";\n  circularRef.style.width = DIAMETER + \"px\";\n\n  //...\n}\n```\n\nAt the end our JavaScript file will look like this:\n\n```js\nconst DIAMETER = 500;\nconst COLOR = \"#ff0000\";\n\nlet inputRef = null;\nlet rightBar = null;\nlet leftBar = null;\nlet rightProgress = null;\nlet leftProgress = null;\nlet circularRef = null;\n\nfunction makeProgressBarInteractive() {\n  inputRef = document.getElementsByTagName(\"input\")[0];\n\n  inputRef.addEventListener(\"input\", updateCiruclarProgressBar);\n  inputRef.value = 0;\n}\n\nfunction init() {\n  circularRef = document.getElementsByClassName(\"circular\")[0];\n  circularRef.style.height = DIAMETER + \"px\";\n  circularRef.style.width = DIAMETER + \"px\";\n\n  rightBar = document.getElementsByClassName(\"bar\")[0];\n  leftBar = document.getElementsByClassName(\"bar\")[1];\n  rightProgress = document.getElementsByClassName(\"progress\")[0];\n  leftProgress = document.getElementsByClassName(\"progress\")[1];\n\n  rightBar.style.clip = `rect(0px, ${DIAMETER}px, ${DIAMETER}px, ${DIAMETER / 2}px)`;\n  rightProgress.style.clip = `rect(0px, ${DIAMETER / 2}px, ${DIAMETER}px, 0px)`;\n\n  leftBar.style.clip = `rect(0px, ${DIAMETER / 2}px, ${DIAMETER}px, 0px)`;\n  leftProgress.style.clip = `rect(0px, ${DIAMETER}px, ${DIAMETER}px, ${DIAMETER / 2}px)`;\n\n  rightProgress.style.backgroundColor = `${COLOR}`;\n  leftProgress.style.backgroundColor = `${COLOR}`;\n\n  makeProgressBarInteractive();\n}\n\nfunction updateCiruclarProgressBar(event) {\n  const percentage = event.target.value / 100;\n  rightProgress.style.transform = `rotate(${percentage \u003c 0.5 ? percentage * 2 * 180 : 180}deg)`;\n  leftProgress.style.transform = `rotate(${percentage \u003e 0.5 ? percentage * 2 * 180 + 180 : 0}deg)`;\n}\n\nfunction docReady(fn) {\n  if (document.readyState === \"complete\" || document.readyState === \"interactive\") {\n    setTimeout(fn, 1);\n  } else {\n    document.addEventListener(\"DOMContentLoaded\", fn);\n  }\n}\ndocReady(init);\n```\n\n### Step 5: Content inside the circle\n\n![](./images/step5/screen7.png)\n\nNow that we have a full circle that fills up based on a percentage, the next step would be to put some content inside the circle. I have decided to simply show a slightly smaller circle colored as the background (white) with the percentage written in the center.\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.html\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```html\n\u003c!-- ... --\u003e\n\u003cdiv class=\"container\"\u003e\n  \u003cdiv class=\"circular\"\u003e\n    \u003c!-- ... --\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"content\"\u003e\n    \u003cspan\u003e0%\u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003c!-- ... --\u003e\n```\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.css\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```css\n/* ... */\n\n.content {\n  position: absolute;\n  z-index: 1000;\n\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  border-radius: 50%;\n}\n```\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003eindex.js\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```js\nconst BORDER_WIDTH = 20;\nconst BACKGROUND_COLOR = \"#ffffff\";\n\nlet content = null;\n\n// ...\n\nfunction init() {\n  // ...\n\n  content = document.getElementsByClassName(\"content\")[0];\n  content.style.height = DIAMETER - BORDER_WIDTH + \"px\";\n  content.style.width = DIAMETER - BORDER_WIDTH + \"px\";\n  content.style.backgroundColor = BACKGROUND_COLOR;\n\n  // ...\n}\n\nfunction updateCiruclarProgressBar(event) {\n  // ...\n\n  content.firstElementChild.innerHTML = `${event.target.value}%`;\n}\n```\n\nThe final result looks like this:\n\n![](./images/step5/final.gif)\n\n## 4. Let's make it in React\n\nNow that we have a good understangind of the mechanics behind this circular progress bar, let's wrap everything we have said so far in a **React component**.\n\nWe want the react component to be **as customizable as possible** throught props like:\n\n- **percentage of progress**\n- **diameter**\n- **color**\n- **border width**\n- **content background color**\n- **content as a children element**\n\n![](./images/react/final220speed.gif)\n\n**Note**: this component is **typescript friendly**.\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003ecircularProgressBar.tsx\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```ts\nimport { MutableRefObject, useEffect, useRef } from \"react\";\nimport \"./circularProgressBar.css\";\n\ninterface circularProgressBarPropsInterface {\n  color: string;\n  diameter: number;\n  percentage: number;\n  borderWidth?: number;\n  contentBackgroundColor?: string;\n  className?: string;\n  contentClassName?: string;\n  children?: JSX.Element;\n}\n\nexport default function CircularProgressBar({\n  color,\n  diameter,\n  percentage,\n  borderWidth,\n  contentBackgroundColor,\n  className,\n  contentClassName,\n  children,\n}: circularProgressBarPropsInterface) {\n  const rightBar = useRef\u003cHTMLDivElement\u003e(null) as MutableRefObject\u003cHTMLDivElement\u003e;\n  const rightProgress = useRef\u003cHTMLDivElement\u003e(null) as MutableRefObject\u003cHTMLDivElement\u003e;\n  const leftBar = useRef\u003cHTMLDivElement\u003e(null) as MutableRefObject\u003cHTMLDivElement\u003e;\n  const leftProgress = useRef\u003cHTMLDivElement\u003e(null) as MutableRefObject\u003cHTMLDivElement\u003e;\n  const content = useRef\u003cHTMLDivElement\u003e(null) as MutableRefObject\u003cHTMLDivElement\u003e;\n\n  // setup dimensions, colors, and clip properties\n  useEffect(() =\u003e {\n    if (borderWidth) {\n      content.current.style.height = diameter - borderWidth + \"px\";\n      content.current.style.width = diameter - borderWidth + \"px\";\n    }\n\n    if (contentBackgroundColor) {\n      content.current.style.backgroundColor = contentBackgroundColor;\n    }\n\n    rightBar.current.style.clip = `rect(0px, ${diameter}px, ${diameter}px, ${diameter / 2}px)`;\n    rightProgress.current.style.clip = `rect(0px, ${diameter / 2}px, ${diameter}px, 0px)`;\n\n    leftBar.current.style.clip = `rect(0px, ${diameter / 2}px, ${diameter}px, 0px)`;\n    leftProgress.current.style.clip = `rect(0px, ${diameter}px, ${diameter}px, ${diameter / 2}px)`;\n\n    rightProgress.current.style.backgroundColor = `${color}`;\n    leftProgress.current.style.backgroundColor = `${color}`;\n  }, [color, diameter, borderWidth, contentBackgroundColor]);\n\n  // handles the rotation\n  useEffect(() =\u003e {\n    if (!percentage) {\n      rightProgress.current.style.transform = `rotate(0deg)`;\n      leftProgress.current.style.transform = `rotate(0deg)`;\n      return;\n    }\n\n    let floatPercentage = percentage / 100;\n    rightProgress.current.style.transform = `rotate(${floatPercentage \u003c 0.5 ? floatPercentage * 2 * 180 : 180}deg)`;\n    leftProgress.current.style.transform = `rotate(${floatPercentage \u003e 0.5 ? floatPercentage * 2 * 180 + 180 : 0}deg)`;\n  }, [percentage]);\n\n  return (\n    \u003cdiv className={`container ${className ?? \"\"}`}\u003e\n      \u003cdiv className='circular' style={{ height: diameter, width: diameter }}\u003e\n        \u003cdiv className='circle'\u003e\n          \u003cdiv className='bar right' ref={rightBar}\u003e\n            \u003cdiv className='progress' ref={rightProgress} /\u003e\n          \u003c/div\u003e\n          \u003cdiv className='bar left' ref={leftBar}\u003e\n            \u003cdiv className='progress' ref={leftProgress} /\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv className={`content ${contentClassName ?? \"\"}`} ref={content}\u003e\n        {children}\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n\u003ch5 a\u003e\u003cstrong\u003e\u003ccode\u003ecircularProgressBar.css\u003c/code\u003e\u003c/strong\u003e\u003c/h5\u003e\n\n```css\n.container {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  padding: 20px;\n}\n\n.circular {\n  position: relative;\n  margin: 0;\n  padding: 0;\n}\n\n.circular .bar {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n  border-radius: 50%;\n}\n\n.circle .bar .progress {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n  border-radius: 50%;\n}\n\n.content {\n  position: absolute;\n  z-index: 1000;\n\n  display: flex;\n  justify-content: center;\n  align-items: center;\n\n  border-radius: 50%;\n}\n```\n\nThat's it. Now we have a fully customized and reusable circular progress bar that we can easily throw in any of our projects.\n\n## 3. npm package\n\nIf you want to use this circular progress bar in your React application we have made an **[npm package](https://link.to.thingy)** for you.\n\nFirst we need to install the package with **npm**:\n\n```\nnpm install @monade/react-circular-progress-bar\n```\n\nor with **yarn**:\n\n```\nyarn add @monade/react-circular-progress-bar\n```\n\nThen we just need to **import** and **use** it like this:\n\n```js\nimport { CircularProgressBar } from \"@monade/react-circular-progress-bar\";\n```\n\n```html\n\u003cCircularProgressBar\n  diameter=\"{...}\"\n  color=\"{...}\"\n  percentage=\"{...}\"\n  borderWidth=\"{...}\"\n  contentBackgroundColor=\"{...}\"\n  className=\"{...}\"\n  contentClassName=\"{...}\"\n\u003e\n  \u003cspan\u003e{...}\u003c/span\u003e\n\u003c/CircularProgressBar\u003e\n```\n\n## 4. Use case: Hippopod's Player\n\nMy journey through the problem of circular progress bars in web development started when I was working on **Hippopod**, an open source project that takes an RSS feed and transform it in a static podcast websites.\n\nDuring the development I found myself having to develop an audio player, whose play / pause button component had to take care of showing the progress of the track being played like this:\n\n\u003cimg src=\"./images//hippopod-player.png\" alt=\"hippopod player\" width=\"400\"/\u003e\n\nIf you are interested in what Hippopod is, check it out at **[hippopod.xyz](https://hippopod.xyz/)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonade%2Fcircular-progress-bar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonade%2Fcircular-progress-bar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonade%2Fcircular-progress-bar/lists"}