CSS / SVG · 3.2 KB gzip
Comet Track
A bright comet that follows a rounded-square track and changes speed and color with state.
comettrackorbitmechanical
Interactive preview
Usage
Packages are @vikast908/core and
@vikast908/loaders. Prefer 0.1.1+
(0.1.0 loaders is broken on npm).
npm install @vikast908/loaders@0.1.1 @vikast908/core@0.1.1
import '@vikast908/loaders/comet-track'; <orbux-comet-track state="thinking"></orbux-comet-track>
Browser-only alternative: Download HTML (self-contained) or load the hosted ESM bundle:
<script type="module" src="http://localhost:4321/orbux/comet-track.js"></script> <orbux-comet-track state="thinking"></orbux-comet-track>
Component source comet-track.ts
import { OrbuxElement } from '@vikast908/core';
/**
* Comet Track: a bright comet tracing a rounded-square track. Fast while
* calling a tool, medium while streaming, parked on an accent-green track on
* `done`, red on `error`.
*/
const STYLES = /* css */ `
:host { --_dur: 2.4s; }
svg { inline-size: 100%; block-size: 100%; overflow: visible; }
.track {
fill: none;
stroke: color-mix(in srgb, var(--orbux-color, #6366f1) 30%, transparent);
stroke-width: 6;
}
.comet {
fill: none;
stroke: var(--orbux-color-2, #a855f7);
stroke-width: 6;
stroke-linecap: round;
stroke-dasharray: 42 258;
filter: drop-shadow(0 0 5px var(--orbux-color-2, #a855f7));
animation: orbux-comet calc(var(--_dur) * var(--orbux-speed-scale)) linear infinite;
}
@keyframes orbux-comet { to { stroke-dashoffset: -300; } }
/* ---- states ---- */
:host([data-state="thinking"]) { --_dur: 2.4s; }
:host([data-state="streaming"]) { --_dur: 1.5s; }
:host([data-state="streaming"]) .comet { stroke-dasharray: 64 236; }
:host([data-state="tool-calling"]) { --_dur: 0.9s; }
:host([data-state="tool-calling"]) .comet { stroke-dasharray: 18 48; animation-timing-function: steps(12, end); }
:host([data-state="tool-calling"]) .track { stroke-dasharray: 12 10; }
:host([data-state="idle"]),
:host([data-state="waiting"]) { --_dur: 4.5s; }
:host([data-state="idle"]) .comet { animation: none; opacity: .32; stroke-dashoffset: -40; }
:host([data-state="waiting"]) .comet { animation: none; opacity: .72; stroke-dashoffset: -120; }
:host([data-state="waiting"]) .track { opacity: .85; }
:host([data-state="done"]) .comet { opacity: 0; animation:none; }
:host([data-state="done"]) .track { stroke: var(--orbux-color-success, #22c55e); }
:host([data-state="error"]) .comet { stroke: var(--orbux-color-error, #ef4444); filter: drop-shadow(0 0 5px var(--orbux-color-error, #ef4444)); animation:orbux-comet-error 200ms ease-out 1; }
:host([data-state="error"]) .track { stroke: color-mix(in srgb, var(--orbux-color-error, #ef4444) 30%, transparent); }
@keyframes orbux-comet-error { to { stroke-dashoffset:-52; } }
:host([data-settled="true"]) .comet { animation:none; }
:host([data-paused="true"]) .comet { animation-play-state: paused; }
:host([data-reduced-motion="true"]) .comet { animation: none !important; }
:host([data-reduced-motion="true"][data-state="streaming"]) .comet { stroke-dasharray:72 228; }
:host([data-reduced-motion="true"][data-state="tool-calling"]) .track { stroke-dasharray:18 8; }
:host([data-reduced-motion="true"][data-state="waiting"]) .comet { opacity:.35; }
`;
export class OrbuxCometTrack extends OrbuxElement {
protected build(): void {
const style = document.createElement('style');
style.textContent = STYLES;
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 100 100');
svg.innerHTML =
'<rect class="track" part="track" x="16" y="16" width="68" height="68" rx="22" pathLength="300"/>' +
'<rect class="comet" part="comet" x="16" y="16" width="68" height="68" rx="22" pathLength="300"/>';
this.root.append(style, svg);
}
}
if (typeof customElements !== 'undefined' && !customElements.get('orbux-comet-track')) {
customElements.define('orbux-comet-track', OrbuxCometTrack);
}
declare global {
interface HTMLElementTagNameMap {
'orbux-comet-track': OrbuxCometTrack;
}
}
Depends on @vikast908/core. Paste the source, add the base
element, and set state from your agent code.
Attributes & properties
| Name | Type | Description |
|---|---|---|
state | AgentState | idle · thinking · streaming · tool-calling · waiting · done · error |
size | CSS length | Overall square size. |
--orbux-width | CSS length | Optional inline-size override. |
--orbux-height | CSS length | Optional block-size override. |
speed | number | Animation speed multiplier (1 = default). |
label | string | Accessible label (defaults per state). |
paused | boolean | Freeze the animation. |
Respects prefers-reduced-motion, exposes
status/progressbar semantics with a live label, and auto-pauses (sets
data-paused) when offscreen or the tab is hidden.