just-a-moment
v0.1.1
Published
Drop-in spinners, progress bars and progress rings as one tiny custom element. CSS, unicode and terminal-style loaders — determinate or indeterminate — isolated in Shadow DOM. Zero config, no dependencies.
Maintainers
Readme
<just-a-moment>
Drop-in spinners, progress bars & progress rings as one tiny custom element. CSS, unicode and terminal-style loaders — determinate or indeterminate — isolated in its own Shadow DOM. ~5.6 KB min+gzip, zero config, no runtime dependencies.
- 🌀 Eight engines:
ring · dots · pulse · ripple · text · bar · ring-meter · unibar - 🔤 Unicode/emoji spinners (braille, line, arc, clock 🕐, moon 🌑, earth, arrows…)
- 📟 Terminal-style unicode progress bars —
block,pip,wget,curl,arrow - 📊 Determinate bars & rings (
value) or indeterminate loading state - 🌈 Gradient &
rainbowcolors, rounded ends, striped bars, round-cap rings - 🛡️ Self-isolated in Shadow DOM; one shared stylesheet across every instance
- ♿
role="progressbar"+ ARIA built in; respectsprefers-reduced-motion; pauses off-screen
Live docs & examples: https://i-rocky.github.io/just-a-moment/
Install
<script src="https://unpkg.com/just-a-moment"></script>npm install just-a-moment # or: bun add just-a-momentimport 'just-a-moment'; // side-effect import registers the <just-a-moment> elementThe tag must contain a hyphen, so it's
<just-a-moment>. Register another withJustAMoment.register('my-loader').
Quick start
<just-a-moment></just-a-moment> <!-- a sensible ring -->
<just-a-moment preset="dots-pulse" color="#f7768e"></just-a-moment>
<just-a-moment type="text" frames="moon"></just-a-moment>
<just-a-moment type="bar" value="60" rounded label></just-a-moment> <!-- determinate -->
<just-a-moment type="bar" indeterminate rounded></just-a-moment>
<just-a-moment type="unibar" variant="pip" value="45"></just-a-moment> <!-- terminal-style -->
<just-a-moment type="ring-meter" value="72" caps="round" label></just-a-moment>Drive determinate progress from JS:
const el = document.querySelector('just-a-moment');
el.value = 73; // reflects to the attribute and animatesEngines (type)
| type | What it is | Determinate? | Variants |
|---|---|---|---|
| ring (default) | Rotating ring | — | dual; gradient/rainbow color |
| dots | A row of animated dots | — | bounce, pulse, fade; count |
| pulse | A breathing disc | — | — |
| ripple | Expanding "ping" rings | — | — |
| text | A cycled unicode/emoji glyph | — | any frames; interval |
| bar | Linear progress bar | ✓ | striped; rounded |
| ring-meter | Circular progress ring / gauge | ✓ | caps="round" |
| unibar | Terminal-style text progress bar | ✓ | block, pip, wget, curl, arrow |
bar, ring-meter and unibar are determinate when given a value (0…max,
default max=100) and indeterminate otherwise (or with indeterminate).
Frame sets (type="text")
dots (braille), line, arc, circle, square, triangle, arrow, bounce,
bar, star, toggle, moon, earth, clock. Each has a tuned default speed.
Pass your own: frames="◐◓◑◒" or frames="🟥,🟧,🟨,🟩" (comma-separated for
multi-glyph frames). Override speed with interval (ms/frame).
Presets
Drop-in named looks with preset="…": ring, dual, rainbow, gradient, dots,
dots-pulse, dots-fade, pulse, ripple, braille, line, arc, clock, moon, bar,
bar-striped, meter, unibar, unibar-pip, unibar-wget.
Attributes
All optional. Visual knobs map onto CSS custom properties (so you can set them in CSS
too). Bare numbers get sensible units (size="60" → 60px, speed="800" → 800ms).
| Attribute | Maps to | Notes |
|---|---|---|
| type preset variant | — | Engine, named look, engine variant. |
| color | --color | A color, any CSS gradient(…), or rainbow. |
| size | --size | Diameter / glyph size (engine default otherwise). |
| speed | --speed | Animation duration. |
| thickness | --thick | Ring / track thickness. |
| track | --track | Unfilled track color. |
| width height | --w --h | Bar dimensions. |
| radius / rounded | --radius | rounded = fully round ends. |
| ease gap | --ease --gap | Timing function / dot spacing. |
| count | — | Number of dots. |
| frames interval | — | Text engine: frame set + ms/frame. |
| value max | — | Determinate progress. |
| indeterminate | — | Force the animated loading state. |
| caps | — | round → SVG ring-meter with rounded ends. |
| label | — | Show a % label (bar / ring-meter). |
Theming
just-a-moment { --color: #89b4fa; --track: #313244; --size: 56px; --speed: .6s; }
just-a-moment::part(track) {} just-a-moment::part(fill) {} just-a-moment::part(label) {}Extend at runtime
import 'just-a-moment';
JustAMoment.preset('brand', { type: 'ring', color: 'rainbow', size: '56px' });
JustAMoment.frames('pingpong', '▖▘▝▗', 120); // glyphs, ms/frame
JustAMoment.register('my-loader');Develop
TypeScript source, one engine per file under src/engines/, bundled with
Vite (library mode + vite-plugin-dts) and end-to-end tested with
Playwright (including a screenshot of every spinner under shots/).
bun install
bun run dev # demo at http://localhost:8000 (Vite)
bun run build # dist/just-a-moment.js (ESM) + .min.js (IIFE) + just-a-moment.d.ts
bun run test:install # one-time: fetch the Chromium build
bun run test # builds, then e2e assertions + capture shots/Browser support
Custom Elements v1 + Shadow DOM v1 (all current evergreen browsers). One shared
constructable stylesheet where available, with a per-element <style> fallback.
