Skip to content
Shiny.Net.HttpServer v1 - A lightweight feature rich HTTP Server - Tunnels, Websockets, AOT, ASPNET Featureset, & Works EVERYWHERE!Let me see!

Motion Icons

42 hand-drawn icons that animate — a bell that rings from its crown, a hamburger that morphs into a cross, a tick that draws itself on, a spinner whose arc chases its own tail. They run on a timer, while the pointer is over them, on tap, when they scroll into view, or entirely under your control.

MotionIconView on .NET MAUI, <MotionIcon> on Blazor. Both live in the core packages — there is nothing extra to install.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor

MAUI (iOS)

Triggers, code-driven playback, presets Presets & scrubbing The icon set
Trigger, code-driven and preset demos on MAUI Presets applied to any icon, and progress scrubbing The built-in icon set on MAUI

Blazor

Triggers Driven from code Two-tone & colour Presets on any icon
Loop, hover, press and appear triggers on Blazor IsPlaying bound to a busy flag on Blazor Colour and accent colour on Blazor Motion presets applied to any icon on Blazor
MAUI
xmlns:shiny="http://shiny.net/maui/controls"
<shiny:MotionIconView Icon="bell"
Trigger="Loop"
Interval="0:0:1.5"
Color="{AppThemeBinding Light=#2563EB, Dark=#60A5FA}"
WidthRequest="32"
HeightRequest="32" />
Blazor
<MotionIcon Icon="bell"
Trigger="MotionTrigger.Loop"
Interval="TimeSpan.FromSeconds(1.5)"
Size="32" />

No registration, no builder.Use…() call. The icon names are plain lower-kebab-case strings, and a name that does not exist renders nothing rather than throwing — a typo leaves a gap you can see instead of taking the page down.

One definition, two very different engines

Section titled “One definition, two very different engines”

The icon artwork and its motion live in Shiny.Controls.MotionIcons.Shared, a dependency-free package both hosts reference. What each host does with that definition could hardly be more different:

.NET MAUI Blazor
Rendering a KeyframeScene on a GraphicsView inline SVG
Animation a keyframe Timeline, evaluated per frame compiled once to CSS @keyframes
Driven by the Keyframe engine’s Player, on one shared dispatcher timer per window the browser’s compositor
C# per frame evaluate + redraw none

That split is deliberate. On the web, nothing about a motion icon needs a render loop: once the keyframes are declared the browser composites them off the main thread, at the display’s refresh rate, and keeps going while WebAssembly is busy doing something else. A C# ticker driving re-renders would be slower, jankier, and would stop dead the moment the app did some work.

On MAUI there is no such compositor to hand the work to, so an icon is drawn — but by the Keyframe engine rather than by machinery of its own. The scene is a KeyframeScene layer tree and playback is its Player, which means motion icons and hand-written keyframe timelines share one clock per window, one set of easing curves and one implementation of position, rate and baselines.

Because both sides compile the same spec — including the same easing curves, evaluated by the same code — an icon looks and moves the same in a MAUI app and in a browser.

Named identically on both hosts unless noted.

Property Type Notes
Icon string A built-in name.
Source MotionIconDefinition Explicit artwork; beats Icon and PathData.
PathData string Raw SVG path drawn in a 24×24 box.
Motion MotionPreset Defaults to Default — the motion drawn for that icon.
Trigger MotionTrigger [Flags]. Defaults to Hover | Press.
Duration TimeSpan Overrides one cycle. Zero uses the motion’s own.
Interval TimeSpan Resting gap between cycles while looping.
Speed double Rate multiplier. Negative reverses (MAUI only).
RepeatCount int Cycles per triggered play. Zero or less repeats forever.
Color Color / string MAUI: unset follows the app theme. Blazor: currentColor.
AccentColor Color / string Secondary colour for two-tone artwork.
StrokeWidth double In the icon’s own 24-unit space. The set is drawn for 2.
IsPlaying bool Two-way — bind it to a busy flag.
Size double Blazor only; MAUI uses WidthRequest / HeightRequest.
Progress double MAUI only. Two-way — bind a Slider to scrub the animation.
Command ICommand MAUI only. Blazor uses OnClick.
Title string Blazor only. Without it the icon is aria-hidden.

Methods on both hosts: Play(), Stop(), StopAtCycleEnd(). MAUI adds PlayAsync() and Reset(); Blazor adds Loop(). Both raise Completed at the end of a play.

On Blazor, Color defaults to currentColor, so an icon dropped inside a button, link or menu row takes that element’s colour automatically — including the hover and disabled states — with nothing to wire up:

<button class="danger" disabled="@busy">
<MotionIcon Icon="trash" Trigger="MotionTrigger.Hover" Size="20" /> Delete
</button>

MAUI has no equivalent of currentColor, so an unset Color follows the theme pack’s on-surface token through SetDynamicResource instead — which means icons restyle along with a live SetTheme. Assigning Color explicitly clears that, as it should. Set it explicitly wherever the icon sits on a coloured surface.

Motion icons are usually decoration sitting beside a real label, which is how they are treated by default: on Blazor an icon with no Title is marked aria-hidden, so a screen reader announces the button’s text once rather than twice. Give it a Title when the icon is the label.

On the web, prefers-reduced-motion: reduce is honoured automatically — the icon still renders and still responds to clicks, it simply holds its resting pose.