Theming / Motion

Motion

Six touch points in zits/ui forward plain class strings from Navius.Motion: a shake on ZitsFieldError, a shimmer on ZitsSkeleton, a pulse on ZitsBadge, and a presence class on the three overlay contents (ZitsDialogContent, ZitsAlertDialogContent, ZitsPopoverContent). None of it depends on the Navius.Motion assembly: every opt-in is a raw class name, inert until the app links the stylesheet below. This preview is the real components running in WebAssembly.

Setup

The six opt-ins below already ship with every navius add copy: the params exist whether or not the stylesheet is linked, they just render byte-identical output when it is not. Two steps light them up.

1. Reference the Navius.Motion preview package from your app:

bash
dotnet add package Navius.Motion --prerelease

2. Link the generated stylesheet in your app shell, the same way app.css is linked:

html
<link href="_content/Navius.Motion/navius-motion.css" rel="stylesheet" />

That is the whole install. No component code changes: the params are already there, waiting.

Micro pack opt-ins

Bool params, default off. Each forwards one motion-* class through the component's existing Cn.Class merge. Click "Trigger error" to feel the shake fire on mount. ZitsSkeleton.Shimmer is a separate mechanism from the Shimmer text component: this one swaps the skeleton's animation, that one paints a text-clip sweep.

Live Idle
razor
<ZitsField data-invalid="true">
    <ZitsFieldLabel For="pw">Password</ZitsFieldLabel>
    <ZitsInput id="pw" type="password" aria-invalid="true" />
    <ZitsFieldError Shake="true">Password must be at least 8 characters.</ZitsFieldError>
</ZitsField>

<ZitsBadge Pulse="true">Live</ZitsBadge>

<ZitsSkeleton Shimmer="true" class="h-4 w-[200px]" />
Prop Type Default
ZitsFieldError.Shake bool false
ZitsSkeleton.Shimmer bool false (swaps the default animate-pulse, not additive)
ZitsBadge.Pulse bool false

Presence hooks

A single string? Motion param, default null, on the three overlay contents. It lands on the underlying Base UI popup, the part that carries the data-open, data-closed, data-starting-style, and data-ending-style attributes the presence classes key off. Raw class string, not a mirrored enum: it forwards without helm encoding any Navius class-name knowledge.

razor
<ZitsDialog>
    <ZitsDialogTrigger>Open dialog</ZitsDialogTrigger>
    <ZitsDialogContent Motion="motion-pop">
        <ZitsDialogHeader>
            <ZitsDialogTitle>Edit profile</ZitsDialogTitle>
        </ZitsDialogHeader>
    </ZitsDialogContent>
</ZitsDialog>
Prop Type Default
ZitsDialogContent.Motion string? null
ZitsAlertDialogContent.Motion string? null
ZitsPopoverContent.Motion string? null

Preset classes

Every class the stylesheet generates, usable directly (splat one onto any markup, wired opt-in or not). Durations and easings are baked spring linear() curves, so they run on the compositor with zero JavaScript.

Presence (pair with an element carrying the Navius state attributes; this is what the Motion param above accepts):

Prop Type Default
motion-fade class opacity only
motion-fade-up class fade, enters from 8px below
motion-fade-down class fade, enters from 8px above
motion-zoom class fade plus a 0.95 to 1 scale
motion-pop class bouncy spring, fade plus a 0.9 to 1 scale
motion-slide-up class enters from 24px below
motion-slide-down class enters from 24px above
motion-slide-left class enters from 24px right
motion-slide-right class enters from 24px left

Enter-once (animates on insertion via @starting-style, no state attributes needed, delay with --navius-motion-delay):

Prop Type Default
motion-enter-fade class opacity only
motion-enter-fade-up class fade, enters from 8px below
motion-enter-fade-down class fade, enters from 8px above
motion-enter-zoom class fade plus a 0.95 to 1 scale
motion-enter-pop class bouncy spring, fade plus a 0.9 to 1 scale
motion-enter-slide-up class enters from 24px below
motion-enter-slide-down class enters from 24px above
motion-enter-slide-left class enters from 24px right
motion-enter-slide-right class enters from 24px left

Scroll-reveal (starts hidden, transitions to visible once [data-in-view] is set on the element; pair with Motion.InView to splat the class and CreateInViewAsync to attach the observer that sets the attribute, stagger a group with --navius-motion-delay per child):

Prop Type Default
motion-in-view-fade class opacity only
motion-in-view-fade-up class fade, enters from 8px below
motion-in-view-fade-down class fade, enters from 8px above
motion-in-view-zoom class fade plus a 0.95 to 1 scale
motion-in-view-pop class bouncy spring, fade plus a 0.9 to 1 scale
motion-in-view-slide-up class enters from 24px below
motion-in-view-slide-down class enters from 24px above
motion-in-view-slide-left class enters from 24px right
motion-in-view-slide-right class enters from 24px left

Gesture and micro pack (the micro classes back the bool opt-ins above; motion-focus-glow has no wired param yet, add it directly):

Prop Type Default
motion-press class scale down on :active, keyboard activation included
motion-hover class lift on hover, hover-capable pointers only
motion-shake class one-shot decaying nudge, backs ZitsFieldError.Shake
motion-pulse class looped opacity/scale breathing, backs ZitsBadge.Pulse
motion-shimmer class looped sweep, backs ZitsSkeleton.Shimmer
motion-focus-glow class looped hairline ring pulse, no wired param

New in the motion package

Beyond the class strings above, Navius.Motion ships four package-level components: an auto-animate container (NaviusAutoAnimate, FLIP on child add, remove, or reorder), a fluent multi-element timeline builder (MotionSequence), a spring-driven selection indicator (NaviusSelectionIndicator), and an experimental same-document page transition wrapper (NaviusPageTransition). None of them are wired into a helm component yet, so this site does not carry API tables for them. They are documented on navius-docs, the primitives docs site.

Reduced motion

Handled once, in the stylesheet, under a single @media (prefers-reduced-motion: reduce) block. No per-component code needed on the zits/ui side.

  • Every presence and enter-once preset collapses to an opacity-only transition; the transform is dropped.
  • motion-press and motion-hover drop their transform on interaction, so pressing or hovering no longer moves anything.
  • motion-shake, motion-shimmer, and motion-focus-glow stop outright (all transform, background-position, or box-shadow driven, nothing left to show under reduce).
  • motion-pulse keeps a plain opacity beat: it still reads as "live" without the scale, so it stays non-vestibular rather than vanishing.