All loaders

CSS / SVG · 3.7 KB gzip

Solar Flare

A radiant star core surrounded by breathing rays, a rotating corona, and a looping prominence.

signaturecosmicsunfireenergy
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/solar-flare';

<orbux-solar-flare state="thinking"></orbux-solar-flare>

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

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

<orbux-solar-flare state="thinking"></orbux-solar-flare>

Component source solar-flare.ts

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

const STYLES = /* css */ `
:host { --_tone:var(--orbux-color,#f59e0b);--_tone-2:var(--orbux-color-2,#fef08a);--_dur:3.2s; }
.solar { position:relative;inline-size:100%;block-size:100%;display:grid;place-items:center;filter:drop-shadow(0 0 10px color-mix(in srgb,var(--_tone) 62%,transparent)); }
.corona { position:absolute;inset:4%;border-radius:50%;background:repeating-conic-gradient(from 8deg,color-mix(in srgb,var(--_tone) 72%,transparent) 0 5deg,transparent 5deg 28deg);-webkit-mask:radial-gradient(farthest-side,transparent 57%,#000 59% 75%,transparent 77%);mask:radial-gradient(farthest-side,transparent 57%,#000 59% 75%,transparent 77%);animation:orbux-solar-turn calc(var(--_dur) * var(--orbux-speed-scale)) linear infinite; }
.ray { position:absolute;left:46%;top:1%;inline-size:8%;block-size:31%;clip-path:polygon(50% 0,100% 100%,0 100%);background:linear-gradient(to bottom,transparent,var(--_tone));transform-origin:50% 161%;rotate:var(--_angle);animation:orbux-solar-ray calc((1.1s + var(--_i) * .06s) * var(--orbux-speed-scale)) cubic-bezier(.77,0,.175,1) infinite;animation-delay:calc(var(--_i) * -100ms); }
.sun { position:absolute;inset:25%;border-radius:50%;background:radial-gradient(circle at 38% 32%,#fff 0 4%,var(--_tone-2) 9%,var(--_tone) 54%,color-mix(in srgb,var(--_tone) 62%,#7c2d12));box-shadow:inset -5px -6px 10px #7c2d1266,0 0 12px color-mix(in srgb,var(--_tone) 82%,transparent);animation:orbux-solar-pulse calc(1.3s * var(--orbux-speed-scale)) cubic-bezier(.77,0,.175,1) infinite; }
.flare { position:absolute;inset:17%;border-radius:50%;border-block-start:3px solid color-mix(in srgb,var(--_tone-2) 76%,transparent);border-inline-end:2px solid color-mix(in srgb,var(--_tone) 62%,transparent);animation:orbux-solar-turn calc(1.7s * var(--orbux-speed-scale)) linear infinite reverse; }
@keyframes orbux-solar-turn { to { transform:rotate(360deg); } }
@keyframes orbux-solar-ray { 50% { transform:scaleY(.62);opacity:.42; } }
@keyframes orbux-solar-pulse { 50% { transform:scale(.9); } }
@keyframes orbux-solar-done { 50% { transform:scale(1.055); } }
@keyframes orbux-solar-error { 35% { transform:translateX(-4%); } 70% { transform:translateX(4%); } }
:host([data-state="streaming"]) { --_dur:1.6s; }
:host([data-state="tool-calling"]) .corona { animation-timing-function:steps(12,end); }
:host([data-state="tool-calling"]) .sun { border-radius:25%; }
:host([data-state="idle"]) .corona,:host([data-state="waiting"]) .corona,:host([data-state="idle"]) .ray,:host([data-state="waiting"]) .ray,:host([data-state="idle"]) .sun,:host([data-state="waiting"]) .sun,:host([data-state="idle"]) .flare,:host([data-state="waiting"]) .flare { animation:none; }
:host([data-state="idle"]) .solar { opacity:.52; }
:host([data-state="waiting"]) .ray:nth-child(even) { opacity:.22; }
: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:#fef2f2; }
:host([data-state="done"]) .corona,:host([data-state="error"]) .corona,:host([data-state="done"]) .ray,:host([data-state="error"]) .ray,:host([data-state="done"]) .sun,:host([data-state="error"]) .sun,:host([data-state="done"]) .flare,:host([data-state="error"]) .flare { animation:none; }
:host([data-state="done"]) .solar { animation:orbux-solar-done 220ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-state="error"]) .solar { animation:orbux-solar-error 200ms cubic-bezier(.23,1,.32,1) 1; }
:host([data-settled="true"]) .solar { 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"]) .flare { transform:rotate(36deg); }
`;

export class OrbuxSolarFlare extends OrbuxElement {
  protected build(): void {
    const style = document.createElement('style');
    style.textContent = STYLES;
    const solar = document.createElement('div');
    solar.className = 'solar';
    solar.setAttribute('part', 'solar');
    solar.innerHTML = `<span class="corona"></span>${Array.from({ length: 10 }, (_, index) => `<i class="ray" style="--_i:${index};--_angle:${index * 36}deg"></i>`).join('')}<span class="flare"></span><span class="sun"></span>`;
    this.root.append(style, solar);
  }
}
if (typeof customElements !== 'undefined' && !customElements.get('orbux-solar-flare'))
  customElements.define('orbux-solar-flare', OrbuxSolarFlare);
declare global {
  interface HTMLElementTagNameMap {
    'orbux-solar-flare': OrbuxSolarFlare;
  }
}

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.