All loaders

CSS / SVG · 3.7 KB gzip

Void Singularity

A dark event horizon wrapped in an elliptical accretion disk, orbital traces, and distant stars.

signaturecosmicblack-holespacegravity
Interactive preview

Usage

Download HTML

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/void-singularity';

<orbux-void-singularity state="thinking"></orbux-void-singularity>

Browser-only alternative: Download HTML (self-contained) or load the hosted ESM bundle:

<script type="module" src="http://localhost:4321/orbux/void-singularity.js"></script>

<orbux-void-singularity state="thinking"></orbux-void-singularity>

Component source void-singularity.ts

import { OrbuxElement } from '@vikast908/core';

const STYLES = /* css */ `
:host { --_tone:var(--orbux-color,#8b5cf6);--_tone-2:var(--orbux-color-2,#f472b6);--_dur:2.2s; }
.void { position:relative;inline-size:100%;block-size:100%;display:grid;place-items:center;filter:drop-shadow(0 0 8px color-mix(in srgb,var(--_tone) 52%,transparent)); }
.disk { position:absolute;inset:16% 3%;border-radius:50%;background:conic-gradient(from 10deg,transparent 0 8%,var(--_tone) 15%,var(--_tone-2) 31%,transparent 42% 58%,color-mix(in srgb,var(--_tone) 62%,white) 71%,transparent 92%);-webkit-mask:radial-gradient(ellipse,transparent 0 37%,#000 43% 68%,transparent 76%);mask:radial-gradient(ellipse,transparent 0 37%,#000 43% 68%,transparent 76%);animation:orbux-void-turn calc(var(--_dur) * var(--orbux-speed-scale)) linear infinite; }
.horizon { position:absolute;inset:29%;border-radius:50%;background:#020207;box-shadow:0 0 0 2px color-mix(in srgb,var(--_tone) 62%,white),0 0 12px 5px color-mix(in srgb,var(--_tone) 52%,transparent);animation:orbux-void-pulse calc(1.4s * var(--orbux-speed-scale)) cubic-bezier(.77,0,.175,1) infinite; }
.orbit { position:absolute;inset:var(--_inset);border-radius:50%;border-block-start:1px solid color-mix(in srgb,var(--_tone-2) 62%,transparent);border-inline-end:1px solid color-mix(in srgb,var(--_tone) 54%,transparent);rotate:var(--_tilt);animation:orbux-void-turn calc((var(--_dur) + var(--_i) * .7s) * var(--orbux-speed-scale)) linear infinite reverse; }
.star { position:absolute;left:calc(8% + var(--_x));top:calc(10% + var(--_y));inline-size:3%;aspect-ratio:1;border-radius:50%;background:color-mix(in srgb,var(--_tone-2) 72%,white);animation:orbux-void-star calc((1.1s + var(--_i) * .1s) * var(--orbux-speed-scale)) ease-in-out infinite; }
@keyframes orbux-void-turn { to { transform:rotate(360deg); } }
@keyframes orbux-void-pulse { 50% { transform:scale(.88); } }
@keyframes orbux-void-star { 50% { transform:scale(.45);opacity:.28; } }
@keyframes orbux-void-done { 50% { transform:scale(1.055); } }
@keyframes orbux-void-error { 35% { transform:translateX(-4%); } 70% { transform:translateX(4%); } }
:host([data-state="streaming"]) { --_dur:1.1s; }
:host([data-state="tool-calling"]) .disk { animation-timing-function:steps(8,end); }
:host([data-state="tool-calling"]) .horizon { border-radius:24%; }
:host([data-state="idle"]) .disk,:host([data-state="waiting"]) .disk,:host([data-state="idle"]) .horizon,:host([data-state="waiting"]) .horizon,:host([data-state="idle"]) .orbit,:host([data-state="waiting"]) .orbit,:host([data-state="idle"]) .star,:host([data-state="waiting"]) .star { animation:none; }
:host([data-state="idle"]) .void { opacity:.52; }
:host([data-state="waiting"]) .horizon { transform:scale(.86); }
:host([data-state="done"]) { --_tone:var(--orbux-color-success,#22c55e);--_tone-2:#86efac; }
:host([data-state="error"]) { --_tone:var(--orbux-color-error,#ef4444);--_tone-2:#fb7185; }
:host([data-state="done"]) .disk,:host([data-state="error"]) .disk,:host([data-state="done"]) .horizon,:host([data-state="error"]) .horizon,:host([data-state="done"]) .orbit,:host([data-state="error"]) .orbit,:host([data-state="done"]) .star,:host([data-state="error"]) .star { animation:none; }
:host([data-state="done"]) .void { animation:orbux-void-done 220ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-state="error"]) .void { animation:orbux-void-error 200ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-settled="true"]) .void { animation:none; }
:host([data-paused="true"]) * { animation-play-state:paused !important; }
:host([data-reduced-motion="true"]) * { animation:none !important; }
:host([data-reduced-motion="true"][data-state="streaming"]) .disk { transform:rotate(22deg); }
`;

export class OrbuxVoidSingularity extends OrbuxElement {
  protected build(): void {
    const style = document.createElement('style');
    style.textContent = STYLES;
    const voidStage = document.createElement('div');
    voidStage.className = 'void';
    voidStage.setAttribute('part', 'void');
    const stars = [
      [4, 9],
      [66, 3],
      [76, 54],
      [10, 63],
      [55, 72],
    ];
    voidStage.innerHTML = `<span class="disk"></span><i class="orbit" style="--_i:0;--_inset:10%;--_tilt:18deg"></i><i class="orbit" style="--_i:1;--_inset:20%;--_tilt:-28deg"></i>${stars.map(([x, y], index) => `<b class="star" style="--_i:${index};--_x:${x}%;--_y:${y}%"></b>`).join('')}<span class="horizon"></span>`;
    this.root.append(style, voidStage);
  }
}
if (typeof customElements !== 'undefined' && !customElements.get('orbux-void-singularity'))
  customElements.define('orbux-void-singularity', OrbuxVoidSingularity);
declare global {
  interface HTMLElementTagNameMap {
    'orbux-void-singularity': OrbuxVoidSingularity;
  }
}

Depends on @vikast908/core. Paste the source, add the base element, and set state from your agent code.

Attributes & properties

NameTypeDescription
stateAgentState idle · thinking · streaming · tool-calling · waiting · done · error
sizeCSS lengthOverall square size.
--orbux-widthCSS lengthOptional inline-size override.
--orbux-heightCSS lengthOptional block-size override.
speednumberAnimation speed multiplier (1 = default).
labelstringAccessible label (defaults per state).
pausedbooleanFreeze 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.