Summary#
A spinner says one thing: wait. An orb says what for. Orb(OrbState.Searching) is a dotted globe with a scan
line sweeping it; Orb(OrbState.Composing) is a sash of dots rolling like a line being written; Orb(OrbState.Weaving)
is three strands plaiting — each a hand-tuned animation from Jakub Antalik's
thinking-orbs (MIT), ported to Osy# so it is an ordinary kit:
one use line, no JavaScript bundle.
app Desk {
use Osysharp.LoadingOrbs@0;
model "model/**/*.osy";
}Signature#
using Osysharp.LoadingOrbs;
Orb(OrbState.Searching) // 64px — beside a name, in a panel
Orb(OrbState.Composing, size: OrbSize.Inline) // 20px — in a line of text, a button, a field
Orb(OrbState.Weaving, label: "Translating from Swedish…") // what a screen reader (and a test) hears
Orb(OrbState.Working, speed: 1.5) // a multiplier on the tuned speed
Orb(OrbState.Connecting, paused: true) // hold still on the current frame
Orb(OrbState.Shaping, accent: true) // drawn in the theme's accent instead of its inkDescription#
The nine states — pick the one that names the wait#
| state | what it draws | label | a wait it names |
|---|---|---|---|
Working | particles on tilted orbits | Working… | an automation or a process running |
Searching | a scan meridian sweeps a dotted globe | Searching… | a search, similar items, a knowledge base |
Solving | bands scramble in quarter turns, then click back | Solving… | a rule or a check being evaluated |
Listening | a waveform rolls through latitude rings | Listening… | voice |
Connecting | a constellation wires itself | Connecting… | a mailbox, a channel, another service |
Weaving | three strands plait around the sphere | Weaving… | translating |
Composing | an undulating multi-band sash | Composing… | writing a draft |
Breathing | a face-on ring slowly morphing | Thinking… | idle, ready |
Shaping | a dotted outline, circle → triangle → square | Shaping… | building or formatting something |
Two sizes, two designs#
OrbSize.Avatar is 64px and OrbSize.Inline is 20px. They are separate tunings, not one drawing scaled: the
small one has fewer, larger dots and runs faster, so it still reads at text size.
Which colour is it?#
The theme's ink — Colors.OnSurface — so a re-themed app's orb is drawn in that app's text colour, light or dark.
accent: true draws it in the theme's accent (Colors.Primary) instead, for an app whose one accent means
"something is working". It takes no colour of its own: an orb in a third colour is a second accent.
When to show one#
For a wait the reader can see the reason of, and that lasts longer than about 400ms. Under that, show nothing — a spinner that flashes for a fifth of a second is noise. For content arriving in a known shape, a skeleton says more (skeleton); an orb is for a PROCESS whose result has no shape yet.
What it does for you#
- It is drawn in your theme's ink —
Colors.OnSurface, at the depth-mapped strength of the original, so it is dark on a light page, light on a dark one, and follows a mode switch on the next frame. - Reduced motion shows the original's representative still frame, redrawn only when the theme changes.
- Out of view it does no work — scrolled away or in a hidden tab, it runs no frames ([[ui-canvas#offscreen]]).
- It is sharp on a high-density screen ([[ui-canvas#density]]).
- It has a name —
role: UiRole.Graphicwith its state's label, so a screen reader announces "Searching…" and a test reads it:Assert.Visible("Searching…").
The same technique, for a drawing of your own#
An orb is an ordinary component: a Canvas, an on frame body, and Draw.* in theme colours. The pieces it needs
are in [[ui-canvas#themed]]; the smallest version of the idea — a lamp that breathes, still when the reader asks for
less motion — compiles on its own:
theme Desk { Colors { Lamp = Modes.Of(light: "#0E9AA9", dark: "#33CAD8"); } }
[Route("/lamp")]
[AllowAnonymous]
component Lamp() {
double t = 0;
on frame (double dt) {
t = Theme.ReducedMotion() ? 0 : t + dt;
var ring = 0.5 + 0.5 * Math.Sin(t * 2.6);
Draw.Clear();
Draw.Alpha(0.3 * (1 - ring));
Draw.Circle(10, 10, 4 + 5 * ring, Colors.Lamp);
Draw.Alpha(1);
Draw.Circle(10, 10, 4, Colors.Lamp);
}
render { Canvas(w: 20, h: 20, density: 2, role: UiRole.Graphic, label: "Live"); }
}Credit#
The geometry, the nine tunings and the golden vectors the kit's tests are generated from are Jakub Antalik's, under
the MIT licence; kits/loading-orbs/LICENSE carries the notice. The port is checked number for number against the
original's own golden vectors (kits/loading-orbs/tests/golden.test.osy).
See also#
- Canvas — the drawing surface an orb is made of, and how to draw on a themed page
- animation — keyframe motion, on entry, on exit, in a loop, or as it scrolls into view — declarative motion for ordinary elements, and reduced motion
- skeleton — for content arriving in a known shape
- Designing an app that looks designed — direction, type, colour, composition and motion — where motion belongs, and where it does not