npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deftlycreative/progress-ring

v1.1.0

Published

A circular progress ring as a Web Component with Vue and React wrappers

Readme

progress-ring

npm version CI license

A lightweight, scalable circular progress bar built as a Web Component. Works in plain HTML/PHP, Vue 3, and React with a single shared implementation — no framework dependencies required.


Features

  • SVG-based — scales to any size without blurring
  • Zero dependencies
  • Works in plain HTML, PHP loops, Vue 3, and React
  • Fully customizable via attributes/props
  • CSS animation with configurable duration and delay
  • Gauge/speedometer mode via cut + rotation
  • Linear gradient arc stroke
  • Avatar image inside the circle (replaces the label)
  • Accessible (role="progressbar" + aria-valuenow/min/max + customisable aria-label)
  • Shadow DOM encapsulation prevents style conflicts

Preview


Installation

CDN / Plain HTML / PHP

<!-- UMD (no import, works with a plain <script> tag) -->
<script src="https://unpkg.com/@deftlycreative/progress-ring/dist/progress-ring.umd.js"></script>

<!-- ESM (use with type="module") -->
<script type="module" src="https://unpkg.com/@deftlycreative/progress-ring/dist/progress-ring.js"></script>

You can also use jsDelivr:

<script src="https://cdn.jsdelivr.net/npm/@deftlycreative/progress-ring/dist/progress-ring.umd.js"></script>

Note: ES modules require an HTTP server — they will not load over file://. Use a local dev server or serve the file from your web root.

npm (Vue / React)

npm install @deftlycreative/progress-ring

Vue 3

import ProgressRing from '@deftlycreative/progress-ring/vue';

React

import ProgressRing from '@deftlycreative/progress-ring/react';

Usage

Plain HTML / PHP

<script src="https://unpkg.com/@deftlycreative/progress-ring/dist/progress-ring.umd.js"></script>

<progress-ring value="72"></progress-ring>

<!-- PHP loop -->
<?php foreach ($tasks as $task): ?>
  <progress-ring
    value="<?= $task['completed'] ?>"
    max="<?= $task['total'] ?>"
    primary-color="#4f8ef7"
  ></progress-ring>
<?php endforeach; ?>

Vue 3

<script setup>
import ProgressRing from '@deftlycreative/progress-ring/vue';
</script>

<template>
  <ProgressRing
    :value="task.completed"
    :max="task.total"
    primary-color="#4f8ef7"
    :animated="true"
  />
</template>

React

import ProgressRing from '@deftlycreative/progress-ring/react';

export default function App() {
  return (
    <ProgressRing
      value={task.completed}
      max={task.total}
      primaryColor="#4f8ef7"
      animated
    />
  );
}

Parameters

All parameters are optional. Defaults are shown below.

In HTML/PHP use kebab-case attributes. In React use camelCase props. In Vue use either.

Value

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | value | value | number | 0 | The current progress value | | min | min | number | 0 | The minimum value (maps to 0%) | | max | max | number | 100 | The maximum value (maps to 100%) |

The percentage is computed internally as (value - min) / (max - min) * 100, clamped to 0–100. With the defaults, value behaves exactly like a percent.

Colors

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | primary-color | primaryColor | string | #4f8ef7 | Arc (progress) color | | muted-color | mutedColor | string | #e0e0e0 | Track (background arc) color | | background-color | backgroundColor | string | transparent | Background fill of the host element |

Sizing & Layout

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | size | size | number | "auto" | 100 | Width and height in px. "auto" fills the parent container (equivalent to Bootstrap's img-fluid) | | thickness | thickness | number | 8 | Stroke width of the arc in px (viewBox units) | | padding | padding | number | 0 | Inner padding between the SVG and the host element edge in px | | corner-radius | cornerRadius | number | 0 | Border radius of the host element in px. Most visible with a background color set |

Animation

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | animated | animated | boolean | true | Whether to animate the arc drawing in on mount | | animation-duration | animationDuration | number | 600 | Maximum duration of the draw-in animation in milliseconds. Actual duration depends on animation-mode | | animation-delay | animationDelay | number | 0 | Delay before the animation starts in milliseconds. Useful for staggering multiple circles | | animation-mode | animationMode | "speed" | "duration" | "speed" | "speed" scales the duration proportionally to arc length so all arcs animate at the same visual speed. "duration" uses a fixed duration regardless of arc length |

Label

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | label-format | labelFormat | "percent" | "fraction" | "value" | "integer" | "none" | template string | "percent" | Controls what is displayed in the center. Named modes or a format-token template like "{value} of {max} tasks" | | text-override | textOverride | string | "" | Override the label text entirely. When non-empty, replaces whatever label-format would show | | label-color | labelColor | string | (primary color) | Color of the center label text. Defaults to primary-color | | font-family | fontFamily | string | inherit | Font family for the label | | font-size | fontSize | number | 20 | Font size for the label in SVG units (scales with the component) | | font-weight | fontWeight | number | string | 400 | Font weight for the label. Accepts any valid value: 400, 700, bold, etc. |

Label format examples (value=5, max=10):

| label-format | Output | |---|---| | percent | 50% | | integer | 50 | | fraction | 5/10 | | value | 5 | | none | (hidden) | | {value} of {max} tasks | 5 of 10 tasks | | {percent}% done | 50% done | | {value}/{max} ({percent}%) | 5/10 (50%) |

Label-format templates

Any label-format value that contains { is treated as a template. The following tokens are replaced at render time:

| Token | Replaced with | |---|---| | {value} | Current value (value attribute) | | {max} | Maximum value (max attribute) | | {min} | Minimum value (min attribute) | | {percent} | Rounded percentage (0–100) |

<progress-ring value="3" max="10" label-format="{value} of {max} tasks"></progress-ring>
<!-- renders: "3 of 10 tasks" -->

<progress-ring value="1" min="1" max="5" label-format="Step {value} of {max}"></progress-ring>
<!-- renders: "Step 1 of 5" -->

In React/Vue use the labelFormat prop:

<ProgressRing value={3} max={10} labelFormat="{value} of {max} tasks" />

Arc Style

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | stroke-linecap | strokeLinecap | "round" | "butt" | "square" | "round" | Shape of the arc endpoints | | direction | direction | "clockwise" | "counter-clockwise" | "clockwise" | Direction the arc fills | | cut | cut | number | 0 | Percentage of the circumference to leave open as a gap (0–99). Pair with rotation to create gauge/speedometer shapes — see the Gauge example below | | rotation | rotation | number | -90 | Start angle in degrees. -90 = top, 0 = 3 o'clock, 90 = bottom, 180 = left | | track-thickness | trackThickness | number | (same as thickness) | Stroke width for the background track ring. Set thinner than thickness for a modern floating-arc look | | linear-gradient | linearGradient | string | — | 2+ comma-separated CSS colours for a horizontal gradient arc stroke, e.g. "#f72585,#7209b7,#4361ee". Falls back to primary-color if fewer than 2 colours are provided |

Avatar

| HTML Attribute | React Prop | Type | Default | Description | |---|---|---|---|---| | avatar | avatar | string | — | URL of an image to display inside the circle. Replaces the text label. The image is clipped to a circle that fits inside the arc | | img-padding | imgPadding | number | 0 | Gap in SVG units between the avatar image edge and the inner wall of the arc |

Accessibility

The component is a role="progressbar" element and automatically maintains the following ARIA attributes on the host:

| Attribute | Value | |---|---| | role | progressbar | | aria-valuenow | Current value (rounded to the nearest integer) | | aria-valuemin | Current min | | aria-valuemax | Current max | | aria-label | "N% complete" (auto-generated; see below) |

By default aria-label is auto-generated as "N% complete". Pass aria-label (HTML/Vue) or ariaLabel (React/Vue) to provide a more meaningful description:

<!-- HTML -->
<progress-ring value="72" aria-label="Download progress"></progress-ring>
{/* React */}
<ProgressRing value={72} ariaLabel="Download progress" />
<!-- Vue -->
<ProgressRing :value="72" aria-label="Download progress" />

Once set, your custom label is never overwritten by the component — even as value changes. Remove the attribute to revert to the auto-generated label.

| HTML Attribute | React/Vue Prop | Type | Default | Description | |---|---|---|---|---| | aria-label | ariaLabel | string | (auto-generated) | Accessible label for the ring. Defaults to "N% complete" |


Staggered Animation Example

<?php foreach ($tasks as $i => $task): ?>
  <progress-ring
    value="<?= $task['percent'] ?>"
    animation-delay="<?= $i * 150 ?>"
  ></progress-ring>
<?php endforeach; ?>

Gauge / Speedometer Example

Use cut to leave a gap at the bottom and rotation to position the start of the arc:

<!-- 40% gap at the bottom, starting from the lower-left -->
<progress-ring
  value="68"
  cut="40"
  rotation="126"
  stroke-linecap="butt"
  thickness="10"
  primary-color="#e63946"
  muted-color="#f4a1a7"
></progress-ring>

With a gradient and a thin track:

<progress-ring
  value="75"
  cut="35"
  rotation="112"
  thickness="12"
  track-thickness="3"
  linear-gradient="#f72585,#4361ee"
></progress-ring>

Avatar Example

<progress-ring
  value="78"
  thickness="6"
  img-padding="3"
  avatar="https://example.com/photo.jpg"
></progress-ring>

Project Structure

progress-ring/
├── src/
│   ├── progress-ring.js      # Web Component — all rendering logic lives here
│   ├── ProgressRing.vue      # Vue 3 wrapper (thin prop-forwarding layer)
│   └── ProgressRing.tsx      # React wrapper (thin prop-forwarding layer)
├── examples/
│   ├── index.html               # Standalone HTML/PHP demo — open via HTTP server
│   ├── vue-demo.vue            # Vue 3 demo component
│   └── react-demo.jsx          # React demo component
├── package.json
└── README.md

The Vue and React wrappers contain no rendering logic — they forward props to the Web Component and let it handle everything. Bug fixes and new features go in progress-ring.js only.


Running the Examples

Clone the repo and install dependencies:

git clone https://github.com/deftlycreative/progress-ring.git
cd progress-ring
npm install

Then start whichever demo you want:

npm run dev          # Plain HTML demo (examples/index.html)
npm run dev:vue      # Vue 3 wrapper demo
npm run dev:react    # React wrapper demo

Each command opens the browser automatically via Vite.


Browser Support

All modern browsers (Chrome, Firefox, Safari, Edge). Web Components with Shadow DOM are supported in all evergreen browsers. No polyfills required.


Additional Resources


License

MIT © 2026 — see LICENSE