Motion System · v0.1

Universal Loader

Nine organic, minimal loading states for the Landjourney platform — derived from the brand's green palette and the stacked geometry of the logo. All pure CSS + inline SVG. Drop into any Angular template.

Format
SVG + CSS
Sizing
currentColor + CSS vars
Deps
Zero JS

Design reasoning

All loaders are controlled by two CSS variables — --lj-size and --lj-color — and default to the brand green (#2b9663). They use currentColor where possible so they adapt when placed on buttons, dark surfaces, or status tiles. Motion is kept short (≈1.4s), soft, and strictly eased — no bounces, no springs. All respect prefers-reduced-motion.

Brand-rooted

2 options

Stacked Fill

01

The logo's three-tone "L" fills itself bottom→top. Most on-brand; best for first-load splash screens.

BrandSplash

Tri-Stack Pulse

03

The three L-shapes breathe together with staggered opacity. Quieter than Stacked Fill.

BrandOverlays

Minimal & organic

2 options

Breathing Dot

04

A dot pulses gently with an outward ripple. The calmest option; feels biological, not mechanical.

OrganicBackground tasks

Sweep Arc

02

Soft dashing arc over a faint track. The most universal — works on every surface, every size.

UniversalButtonsInline

Playful but restrained

3 options

Leaf Sway

07

A seed shape gently sways. The most organic option — references the green "growth" in the brand.

OrganicLong tasks

Leaf Trail

08

A leaf sways with a wider arc; blurred copies lag behind it like a soft motion trail — wind through grass.

OrganicTrailingLong tasks

Progress Strand

09

Linear indeterminate bar with two soft pulses. For page-level / section-level loads.

LinearTop of page

On dark surface

pale accents

Stacked Fill

01
BrandDark

Sweep Arc

02
UniversalDark

Tri-Stack Pulse

03
BrandDark

Breathing Dot

04
OrganicDark

Leaf Sway

07
OrganicDark

Leaf Trail

08
OrganicDark

Progress Strand

09
LinearDark

Sizes & color adaptability

Size scale (Sweep Arc)
16 · inline
24 · button
40 · default
72 · section
On dark surface (sidebar)
Sweep Arc
Breathing Dot
Leaf Trail
Color adaptability (Breathing Dot)
Primary
Success
Warning
Error
Neutral
Inline on text
Saving your application a confirmation will arrive shortly.

In context

Primary button · spinner replaces label
Confirm Payment

Sweep Arc at 18px · inherits the button's white color.

Inline data value · pending
Loan Dashboard
Outstanding Balance Calculating
Next Payment Due Jan 15, 2025

Breathing Dot at 14px · quiet enough not to grab attention.

Card overlay · background refresh
Recent Activity
Loading activity…

Tri-Stack Pulse with a soft scrim over skeleton rows.

When to use which

LoaderUse forAvoid for
01Stacked FillApp boot, first paint, splash / route transitions > 1sInline or in-button use
02Sweep ArcThe default. Buttons, inline actions, small inline loadsOnly if animation has to feel distinctly Landjourney
03Tri-Stack PulseCard and section overlays during a re-fetchTiny sizes (< 24px) — detail is lost
04Breathing DotInline status in text; quiet "is calculating" momentsAnything over 2s — feels unresponsive
05Soft OrbitData fetches, tables, list refreshesInside buttons (too visually busy)
06Wave BarAI / computation / messaging ("thinking")Generic page loads — implies active processing
07Leaf SwayLong-running tasks, onboarding steps, document processingQuick actions — the slow cadence feels wrong
08Dotted RingNeutral fallback at any size; legacy screensWhen brand recognition matters (use 01 / 03)
09Progress StrandTop-of-page indeterminate progress, between route changesAnywhere a circular form fits better

Angular integration

Every loader is a single <span> wrapping inline SVG, with one shared stylesheet. Wrap the whole set in a standalone lj-loader component so usage stays one-line.

// lj-loader.component.ts — standalone, zero deps import { Component, Input } from '@angular/core'; @Component({ selector: 'lj-loader', standalone: true, styleUrls: ['./loaders.css'], template: ` <span class="lj-loader lj-{{variant}}" [style.--lj-size]="size + 'px'" [style.--lj-color]="color" [attr.role]="'status'" [attr.aria-label]="label"> <!-- SVG markup swapped per variant (ngSwitch) --> </span> ` }) export class LjLoaderComponent { @Input() variant: 'sweep-arc' | 'stacked-fill' | 'tri-pulse' | 'breathe' | 'orbit' | 'wave' | 'leaf' | 'dotted-ring' | 'strand' = 'sweep-arc'; @Input() size: number = 40; @Input() color?: string; @Input() label: string = 'Loading'; } // Usage in any template <lj-loader variant="sweep-arc" [size]="24"></lj-loader> <lj-loader variant="stacked-fill" [size]="64" label="Loading your loans"></lj-loader> <button lj-button="cta" [disabled]="submitting"> <lj-loader *ngIf="submitting" variant="sweep-arc" [size]="18" color="white"/> {{ submitting ? 'Submitting…' : 'Confirm Payment' }} </button>

Tweaks