The Walycash design language

Every colour, radius, blur, animation and icon below is read straight out of wafr_mobile_apps_shared_package — the single package both Walycash apps are built on. If it is on this page, it already exists in production code.

Package v1.79.0 Flutter · Material 3 Light & Dark 19 icons · 13 Lottie animations
01 — Brand

The mark

The WafR “W” is not an image file. It is drawn as three vector strokes in code (SmoothLogoPainter) so it can be recoloured, re-timed and re-used as the app’s loading indicator. The strokes rise one after another, hold, then drop away — a 2.8 second loop.

Static · on brand gradient
Live · the real Flutter loader
Rules of use
One colour only

The mark is always a single flat colour — brand purple on light surfaces, white on the mesh gradient or any dark surface. Never multi-colour, never outlined.

Native aspect 242 × 166

Scale proportionally. The glass loading box renders it at 50 % scale inside a 20 px-radius blurred card.

It doubles as the loader

WalyMainLoader. A rainbow-cycling variant (WalyColoredLoading) shifts hue every 4 s during long waits.

02 — Colour

Brand palette

Purple is the identity; sky blue is the accent that keeps the gradients from going flat. The two deep “space” purples only ever appear inside the animated mesh background, never as a surface colour.

Light mode surfaces
Dark mode surfaces
Status colours
03 — Gradients

Mesh & button gradients

Three gradients carry nearly all the brand expression. They are diagonal (top-left → bottom-right) and they change between light and dark mode.

Button mesh — light
AppColors.buttonMeshLight
Primary → primary-dark → primary-light at stops 0 / 0.45 / 1. The default fill of every CTA button in light mode.
Button mesh — dark
AppColors.buttonMeshDark
Lifts to light purple and resolves into sky blue so the CTA still glows on a near-black background.
Global mesh effect — the signature header
GlobalMeshEffect(radius, percentage, speed: 1.5)
A solid purple→deep-space base, with a live four-colour animated mesh layered at 50 % opacity on top. Bottom corners are rounded and its shadow/intensity is driven by scroll: fully expanded at the top of a page, flattening as the user scrolls. It fades up over 1 s on first paint and is Hero-tagged, so it animates continuously across page transitions.
04 — Typography

Type scale

System typeface on each platform (San Francisco on iOS, Roboto on Android) — no custom font is shipped. Primary text carries the weight; secondary text is reserved for supporting copy and is a lighter grey in both themes.

05 — Spacing

The spacing scale

A loose 8-point grid. m (18) is the workhorse: it is the padding inside every card, every input field and every standard box.

Named spacing rules
Card / box padding — 18 px all round

Compact variant for list chips: 18 px horizontal × 12 px vertical.

Gap between list items — 12 px

Before a section header inside a list it opens up to 24 px.

Page horizontal gutter — 10 px

Deliberately tight: cards carry their own 18 px padding, so the content still breathes.

Bottom CTA clearance — safe area + 40 px

Safe area + 100 px when a bottom navigation bar is present. Keyboard insets are added on top automatically.

Icon → text gap — 5 px

Used inside buttons, chips and rows so icon-and-label reads as one unit.

Minimum card height — 90 px

Keeps summary cards in a row visually even when their content lengths differ.

06 — Radius

Corner radius

Soft, never sharp. 18 px is the default for cards and buttons; full pills are used for anything selectable or countable.

07 — Elevation

Depth & shadow

Light mode gets a soft, two-layer shadow for depth. Dark mode gets no shadow at all — surfaces separate by brightness instead, which keeps dark screens clean rather than muddy. Toggle the theme to see the difference.

Standard card

0 2px 6px @ 6 % black, plus 0 1px 2px @ 3 %. Radius 18.

Flat card

Shadow switched off. Used for nested cards inside an already-elevated surface.

Pressed CTA glow

A tinted purple shadow (light) or lilac (dark) that compresses as the button is pushed down.

08 — Blur

Blur & glass

Blur is how the apps create hierarchy without adding more colour. It appears in three distinct roles: as a modal scrim, as a floating glass surface, and as a transition effect on content appearing.

1 · Modal scrim — bottom sheets & dialogs
Blur 8 · black 38 %

The background blurs and darkens progressively as the sheet animates in over 380 ms — both values are tied to the animation, so it never pops. Dragging the sheet back down lightens the scrim again.

2 · Glass surfaces
Frosted panel

A translucent tint over a blurred backdrop, clipped to a 20 px radius. Used for the loading box, the floating back/title bar over the map picker, the suggestions overlay and the receipt preview.

3 · Blur as a transition
Content reveal
blur 8 → 0 · 500 ms
Staggered list
blur 6 → 0 · 600 ms
Modal scrim
blur 0 → 8 · 380 ms

Content never simply “appears”. It resolves out of a soft blur while fading and scaling up slightly — that combination is the app’s signature feel.

09 — Buttons

Buttons

One primary action per screen. The CTA is 52 px tall, gradient-filled, carries a slow shimmer sweep, springs under the finger and fires a haptic tap. Try pressing them.

Primary CTA

Gradient mesh fill · radius 18 · shimmer sweep · scale-down on press (easeOutCubic) and spring-back (easeOutBack) · haptic on press and on release.

CTA with icon

Icon is 20 px, separated from the label by the 5 px icon gap.

Loading

The gradient stays vibrant while loading — only the label swaps, softly. Taps are ignored.

Disabled

Fill drops to 50 % opacity, label to 60 %, shadow and shimmer removed.

Secondary — outlined

1.5 px brand border, transparent fill, same 52 px height.

Tertiary — text

14 px semibold, no fill. For low-stakes, reversible actions.

Contact action — WhatsApp

Uses the official WhatsApp green #25D366 — the one place a non-brand colour owns a button. When more than one contact action is available, tapping opens a bottom sheet to choose between chat, WhatsApp call and phone call.

10 — Components

Component language

The recurring building blocks. Everything sits on a card, every card uses the same radius and padding.

Filter chips

Pill-shaped. Unselected: 10 % tint of its status colour with the icon showing. Selected: solid fill, white label, icon animates away and an optional count appears. 200 ms easeOutCubic. Tap one.

Counts & badges
Notifications 3 Pending 12 Done 99+ Offers 5

Always a full pill, minimum 20 px wide so single digits stay circular.

Input field

Filled, radius 16, 18 px padding. Border thickens to 1.8 px and turns brand purple on focus; red on error.

Info card
Order#A-10482
CustomerSara B.
StatusDelivered
Total1 250MAD

Label left in secondary text, value right in semibold primary. Hairline divider between rows, none after the last.

Loading placeholders

Shimmer boxes mirror the shape of the content that is coming — never a spinner in a list. 2 s pulse, radius 8, then each one cross-fades into its real content.

Glass loading box

The blocking loader: animated mark inside a frosted 20 px card, floating over whatever was on screen.

11 — Live components

Sheets, drawer, date picker & switcher

These are not mock-ups. The phone below runs the real Flutter package compiled to the web — the same AppBottomSheet, AppDrawer, WalyDateRangePicker and WalySegmentedSwitcher that ship in the apps. Tap, drag and dismiss them exactly as a user would; the theme toggle in its top bar switches the whole app between light and dark.

The live preview has not been built yet.

Run this once from docs/design-system/ — it compiles example/ into live/:

npm run build:preview
Starting the app…
375 × 720 · real Flutter build
One shell, four overlays

The bottom sheet, the multi-step sheet and both drawers all run through the same drag-to-dismiss shell. They differ only in which edge they are anchored to — so they animate, blur and dismiss identically.

Floating, never edge-to-edge

Sheets keep a small side margin and float above the safe area; the drawer is capped so a slice of the page stays visible behind it. Corners are 24 px, the scrim is blur 8 over black at 38 %.

Drag to dismiss

Drag a sheet downward or a drawer back toward its edge: the panel follows your pointer and the scrim lightens as it goes. Release early and it springs back. Try it in the frame.

Content reveals after the panel lands

The drawer's items don't ride in with the panel — they resolve out of blur one by one once it is almost in place, so the drawer feels filled rather than flown in.

Multi-step sheets resize, they don't jump

Each step pops its own response and the sheet decides where to go next. The card's height animates between steps and a dot indicator stretches the active dot into a pill.

Date range picker

A range picker, not a single-date picker. Swipe left/right to page months — the outgoing grid scales away toward the swipe and the incoming one grows in from the other side. Tapping the month or year opens a month/year grid with the same transition.

Segmented switcher

One pill with a sliding brand-purple thumb; icons and labels recolour to white as it arrives under them. The same widget covers tab switches, filter switches and sort-direction toggles.

12 — Iconography

Icon library

A small, domain-specific SVG set that sits alongside the standard Material icons. All icons are single-path, solid-filled and drawn on a 24 × 24 grid — which means they can be tinted to any colour at runtime. These are the real files from assets/icons/.

Default icon size is 24 px (20 px inside buttons, 14 px inside chips). Default tint is near-black in light mode and secondary grey in dark mode; brand purple signals an interactive or active icon.

13 — Animations

Lottie library

Illustrated animations carry every empty, error, success and permission moment — so the apps never show a bare error string. These are the live files from assets/animations/.

14 — Motion

How things move

Motion is quick, soft and physical. Nothing snaps, nothing bounces excessively, and nothing moves for longer than ~700 ms. Easing is almost always easeOutCubic: fast start, gentle landing.

Content reveal

A block of content resolving out of blur as it fades and scales up from 92 %.

500 ms · easeOutCubic · blur 8→0

Staggered list

List items arrive one after another, each from blur + 94 % scale.

600 ms · 100 ms stagger

Fade & slide up

The alternative entrance: rises 30 % of its height into place.

700 ms fade · 500 ms slide · 300 ms delay

Press feedback

Everything tappable compresses under the finger, its shadow deepens, and a haptic fires.

150 ms down · spring back

Shimmer

Loading placeholders and the CTA both sweep a soft highlight across themselves.

2 s loop · ping-pong

Bottom sheet

Slides up while the backdrop blurs and darkens in step. Drag it down to dismiss — the scrim lightens as you pull.

380 ms in · 350 ms step switch
Haptics
Press down

A light tactile click the moment the finger lands.

Release

A confirmation tap when the action actually fires.

Long press

A light pulse to confirm the gesture was recognised.

Copied