PageLoader

The wait for a whole page, where the screen would otherwise stand blank white: the mark in glass and colour, in the middle of white — the logo, as the iPhone splash shows it, morphing into the loading pose and looping it.

Preview

It fills the screen (min-h-dvh); here it sits in a box. It's the first paint, with no delay or fade. On a first load the page behind it waits for loaderHold(), so it's up until the logo has morphed (about a second), never a flash; a tap, a refresh or a prefetch doesn't wait.

Loading the studio

Where it goes

  • The booking page's first load, while the studio and today's free times arrive: its own Suspense, in the studio's mix, looked up alone before the rest.
  • The app's start: the dashboard layout waits on the studio behind it, in the mix the studio last wore here (a cookie, RememberMix). Join waits in its own Suspense, in the studio's mix from the invite; onboarding and welcome each have a loading.tsx with it, in Ink. Opened from an iPhone's home screen, the splash comes first, the logo where this starts.
  • Not where a page has a skeleton that copies it (Home's day, Clients, Services, Inbox): a skeleton tells more than a mark.

API reference

PropTypeDefaultDescription
labelstring"Loading"What's loading, announced as a status (visually hidden). Pass it translated.
classNamestring—Layout: a smaller min height where it fills a frame, not the screen.

Guidelines

  • A page behind it starts const hold = loaderHold() first and await hold before it returns (a guard test checks every loading.tsx with it, and the dashboard layout).
  • One per screen, for the whole page. A wait inside a page is a skeleton or a Spinner.
  • Always on white: the booking stage and the app both open on white, so nothing changes colour when the page lands.
  • It's a recording, not the live mark: one take per mix of DotMark in glass — the logo at rest, the morph, one loop of the loading pose (/brand/loading/<mix>.webp), with a still frame for reduced motion — mix picking it; Ink where the studio isn't known. The take's frames are one image in a grid (LOADER_TAKE, 9 a row, 192px each) behind a 64px window, stepped by CSS frame by frame: the morph once, taken at twice the loop's rate since it moves fast, then the loop forever — from the HTML alone, no script. The live mark draws with WebGL only once the page's JavaScript lands, which is after the page, so here it never moved. Re-record when the pose, the morph or the glass changes.