All loaders

CSS / SVG · 3.6 KB gzip

Storm Sigil

A charged lightning seal with radial bolts, a segmented storm ring, and an electric core.

signatureelementallightningstormsigil
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/storm-sigil';

<orbux-storm-sigil state="thinking"></orbux-storm-sigil>

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

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

<orbux-storm-sigil state="thinking"></orbux-storm-sigil>

Component source storm-sigil.ts

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

const STYLES = /* css */ `
:host { --_tone:var(--orbux-color,#38bdf8);--_tone-2:var(--orbux-color-2,#f8fafc);--_dur:1.4s; }
.storm { 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) 65%,transparent)); }
.ring { position:absolute;inset:9%;border-radius:50%;background:repeating-conic-gradient(from 8deg,var(--_tone) 0 4deg,transparent 4deg 31deg);-webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 calc(100% - 2px));mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 calc(100% - 2px));animation:orbux-storm-turn calc(var(--_dur) * var(--orbux-speed-scale)) linear infinite; }
.bolt { position:absolute;left:45%;top:3%;inline-size:11%;block-size:35%;clip-path:polygon(55% 0,100% 0,66% 41%,96% 41%,19% 100%,39% 53%,4% 53%);background:linear-gradient(var(--_tone-2),var(--_tone));transform-origin:50% 134%;rotate:var(--_angle);opacity:calc(.32 + var(--_i) * .08);animation:orbux-storm-flash calc((.72s + var(--_i) * .05s) * var(--orbux-speed-scale)) steps(2,end) infinite;animation-delay:calc(var(--_i) * -110ms); }
.core { position:absolute;inset:32%;border-radius:50%;background:radial-gradient(circle at 42% 36%,white,var(--_tone) 36%,color-mix(in srgb,var(--_tone) 52%,#020617) 72%);box-shadow:0 0 12px color-mix(in srgb,var(--_tone) 72%,transparent);animation:orbux-storm-core calc(1s * var(--orbux-speed-scale)) cubic-bezier(.77,0,.175,1) infinite; }
@keyframes orbux-storm-turn { to { transform:rotate(360deg); } }
@keyframes orbux-storm-flash { 50% { opacity:1;transform:scaleY(.82); } }
@keyframes orbux-storm-core { 50% { transform:scale(.88); } }
@keyframes orbux-storm-done { 50% { transform:scale(1.055); } }
@keyframes orbux-storm-error { 35% { transform:translateX(-4%); } 70% { transform:translateX(4%); } }
:host([data-state="streaming"]) { --_dur:.7s; }
:host([data-state="tool-calling"]) .ring { animation-direction:reverse; }
:host([data-state="tool-calling"]) .core { border-radius:22%; }
:host([data-state="idle"]) .ring,:host([data-state="waiting"]) .ring,:host([data-state="idle"]) .bolt,:host([data-state="waiting"]) .bolt,:host([data-state="idle"]) .core,:host([data-state="waiting"]) .core { animation:none; }
:host([data-state="idle"]) .storm { opacity:.5; }
:host([data-state="waiting"]) .bolt:nth-child(odd) { opacity:.12; }
:host([data-state="done"]) { --_tone:var(--orbux-color-success,#22c55e);--_tone-2:#dcfce7; }
:host([data-state="error"]) { --_tone:var(--orbux-color-error,#ef4444);--_tone-2:#fff1f2; }
:host([data-state="done"]) .ring,:host([data-state="error"]) .ring,:host([data-state="done"]) .bolt,:host([data-state="error"]) .bolt,:host([data-state="done"]) .core,:host([data-state="error"]) .core { animation:none; }
:host([data-state="done"]) .storm { animation:orbux-storm-done 220ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-state="error"]) .storm { animation:orbux-storm-error 200ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-settled="true"]) .storm { 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"]) .bolt:nth-child(even) { opacity:1; }
`;

export class OrbuxStormSigil extends OrbuxElement {
  protected build(): void {
    const style = document.createElement('style');
    style.textContent = STYLES;
    const storm = document.createElement('div');
    storm.className = 'storm';
    storm.setAttribute('part', 'storm');
    storm.innerHTML = `<span class="ring"></span>${Array.from({ length: 6 }, (_, index) => `<i class="bolt" style="--_i:${index};--_angle:${index * 60}deg"></i>`).join('')}<span class="core"></span>`;
    this.root.append(style, storm);
  }
}
if (typeof customElements !== 'undefined' && !customElements.get('orbux-storm-sigil'))
  customElements.define('orbux-storm-sigil', OrbuxStormSigil);
declare global {
  interface HTMLElementTagNameMap {
    'orbux-storm-sigil': OrbuxStormSigil;
  }
}

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.