System
Splash Screen
The screen an app shows before it has anything else to show — six looks, seven indicators, eight ways to leave, and no wiring for the common case.
import { SplashScreen } from 'omaris' Examples
Basic
The whole of it: a splash over the app, and the app behind it once it goes. In a real layout you would leave open alone — the splash waits for the page to load and takes itself off. Here it is bound to a timer so you can watch it again.
Dashboard
The app behind the splash.
Acme
Getting your workspace ready.
Loading your workspace
<script lang="ts"> import { Button, SplashScreen, Text } from 'omaris'; let booting = $state(true); $effect(() => { if (!booting) return; const timer = setTimeout(() => (booting = false), 1800); return () => clearTimeout(timer); });</script><div class="relative h-full w-full overflow-hidden rounded-shape-lg border border-border"> <div class="flex h-full flex-col items-start gap-3 p-6"> <Text variant="headline-sm" as="h2">Dashboard</Text> <Text tone="muted">The app behind the splash.</Text> <Button size="xs" onclick={() => (booting = true)}>Boot again</Button> </div> <SplashScreen bind:open={booting} inline title="Acme" description="Getting your workspace ready." pattern="gradient" message="Loading your workspace" > {#snippet logo()} ... {/snippet} </SplashScreen></div> Looks
Two decisions, taken separately: variant is the colour the screen is, pattern is the decorative layer over it. Six by seven, and every pairing is one word each.
grid
dots
aurora
spotlight
mesh
gradient
<script lang="ts"> import { SplashScreen, type SplashScreenPattern, type SplashScreenVariant } from 'omaris'; const looks: { variant: SplashScreenVariant; pattern: SplashScreenPattern }[] = [ { variant: 'background', pattern: 'grid' }, { variant: 'surface', pattern: 'dots' }, { variant: 'primary', pattern: 'aurora' }, { variant: 'inverse', pattern: 'spotlight' }, { variant: 'background', pattern: 'mesh' }, { variant: 'surface', pattern: 'gradient' } ];</script><div class="grid w-full gap-4 sm:grid-cols-2 lg:grid-cols-3"> {#each looks as look (look.variant + look.pattern)} <div class="relative h-48 overflow-hidden rounded-shape-lg border border-border"> <SplashScreen open inline size="sm" variant={look.variant} pattern={look.pattern} title={look.pattern} indicator="dots" /> </div> {/each}</div> Indicators
Seven ways to say "working on it". bar is the only one that can also say how far along it is; the rest are honest about knowing nothing.
spinner
ring
dots
bar
wave
orbit
pulse
<script lang="ts"> import { SplashScreen, type SplashScreenIndicator } from 'omaris'; const indicators: SplashScreenIndicator[] = [ 'spinner', 'ring', 'dots', 'bar', 'wave', 'orbit', 'pulse' ];</script><div class="grid w-full gap-4 sm:grid-cols-2 lg:grid-cols-4"> {#each indicators as indicator (indicator)} <div class="relative h-40 overflow-hidden rounded-shape-lg border border-border"> <SplashScreen open inline size="sm" {indicator} message={indicator} /> </div> {/each}</div> Exits
The part anyone remembers. Press one and watch it go: iris opens a hole in the middle and the app is behind it, rise takes the whole screen up like a curtain.
fade
scale
zoom
rise
fall
swipe
blur
iris
<script lang="ts"> import { Button, SplashScreen, Text, type SplashScreenExit } from 'omaris'; const exits: SplashScreenExit[] = [ 'fade', 'scale', 'zoom', 'rise', 'fall', 'swipe', 'blur', 'iris' ]; /** Which are currently up. Replaying one drops it and puts it back a frame later. */ let up = $state<Record<string, boolean>>(Object.fromEntries(exits.map((e) => [e, true]))); function replay(exit: string) { up[exit] = false; setTimeout(() => (up[exit] = true), 700); }</script><div class="grid w-full gap-4 sm:grid-cols-2 lg:grid-cols-4"> {#each exits as exit (exit)} <div class="flex flex-col gap-2"> <div class="relative h-40 overflow-hidden rounded-shape-lg border border-border"> <div class="grid h-full place-items-center"> <Text variant="label-sm" tone="muted">the app</Text> </div> <SplashScreen open={up[exit]} inline {exit} size="sm" pattern="gradient" title={exit} indicator="none" minDuration={0} duration={700} /> </div> <Button size="xs" variant="outlined" onclick={() => replay(exit)}>Play {exit}</Button> </div> {/each}</div> Boot sequence
createSplash carries open, progress and message together, so the screen says what the app is actually doing. run walks the steps, weights the bar by how slow each one is, and takes the splash down at the end — including when a step throws.
Workspace
Loaded.
Acme
<script lang="ts"> import { Button, createSplash, SplashScreen, Text } from 'omaris'; // `open: false` because the app is already up here; a real one starts up. const splash = createSplash({ open: false }); const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const boot = () => splash.run([ { label: 'Signing you in', run: () => wait(700) }, { label: 'Loading your workspace', weight: 2, run: () => wait(1200) }, { label: 'Almost there', run: () => wait(500) } ]);</script><div class="relative h-full w-full overflow-hidden rounded-shape-lg border border-border"> <div class="flex h-full flex-col items-start gap-3 p-6"> <Text variant="headline-sm" as="h2">Workspace</Text> <Text tone="muted">Loaded.</Text> <Button size="xs" onclick={boot}>Run the boot sequence</Button> </div> <SplashScreen {splash} inline title="Acme" pattern="aurora" indicator="bar" percent halo exit="iris" > {#snippet logo()} ... {/snippet} {#snippet footer()} <Text variant="label-sm">v2.4.0</Text> {/snippet} </SplashScreen></div> Overridden
Every part is reachable. This one is left-aligned, bottom-anchored, has a display-sized title, a tertiary indicator and a footer of its own — none of which is a prop the component had to invent.
Ledger
Reconciling accounts
<script lang="ts"> import { Button, SplashScreen, Text } from 'omaris'; let open = $state(true);</script><div class="flex h-full w-full flex-col gap-3"> <div class="relative min-h-0 flex-1 overflow-hidden rounded-shape-lg border border-border"> <SplashScreen {open} inline variant="inverse" pattern="grid" align="bottom" enter="reveal" indicator="wave" exit="rise" title="Ledger" message="Reconciling accounts" classes={{ content: 'w-full max-w-none items-start px-10 text-start', title: 'text-display-sm', indicator: 'text-tertiary', message: 'opacity-60', footer: 'items-start px-10 text-start' }} > {#snippet footer()} <Text variant="label-sm">Built by Acme · v2.4.0</Text> {/snippet} </SplashScreen> </div> <Button size="xs" variant="outlined" class="self-start" onclick={() => (open = !open)}> {open ? 'Hide' : 'Show'} </Button></div> When to use it
Use it for
- The first screen of an app with work to do before it can draw: a session to restore, a config to fetch. Drop
<SplashScreen title="Acme" />in+layout.svelte; it renders on the server, waits for load and removes itself. - A boot sequence with steps.
createSplash()plussplash.run([{ label: 'Signing you in', run: … }, …])shows each step and moves the bar as they finish. - A first-run screen.
createSplash({ id: 'boot', once: true })shows it once per tab, so a reload goes straight to the app. - A brand moment on a phone-sized web app:
variant="primary", a logo,exit="iris". - A pane that covers its own content while it reloads.
inlinekeeps the screen inside the container instead of over the viewport.
Not for
- A section of a page still filling in → Skeleton in the shape of what is coming.
- A known fraction of a job, like an upload or export → Progress, next to the thing.
- A route change inside a running app → PageTransition. A splash between screens looks like a restart.
- A short wait after pressing something → the
loadingstate on Button, or Empty for a panel that came back with nothing. - An app that failed to boot. The splash comes down either way; say what went wrong with Alert or an Empty with a Retry.
Do
- Leave
openalone unless you know better than the page-load event. Bind it when you do; auto-hide switches off once the splash is controlled. - Set
appearAfteron a fast app. A boot that finishes inside the delay never shows a splash at all. - Keep
minDurationas it is. It stops a 40ms boot from flashing. - Say what is happening in
message: "Loading your workspace". It changes with a fade. - Use
simulatefor an indeterminate wait, and a realprogressthe moment you have one. Never both. - Match
exitto the app:irisandrisefor a phone-shaped app,fadefor a dashboard. - Reach the parts through
classes, likeclasses={{ title: '…' }}, rather than wrapping the component.
Don't
- Hold it up for work the person did not ask for, like analytics or a prefetch.
- Show one on every navigation. It belongs to the boot, not the router.
- Expect contrast from
variant="primary"withpattern="gradient". Branded variants tint their patterns with the text colour; neutral ones tint withprimary. - Leave a
simulated bar running forever. It stops at 90 on purpose; only the splash coming down puts it on 100. - Put a button on it that is not a way out of a failed boot.
- Give it a
z-index. The fixed splash is above the app already; aninlineone only needs its container to berelative.
Quick reference
variant background(default)surfaceprimaryinverseglassimage
The colour the screen is. primary and inverse are the branded ones; glass is for a splash that goes over an app that is already on screen.
pattern none(default)gradientaurorameshspotlightgriddots
The decorative layer over the colour.
size smmd(default)lg
How big the mark and the type are.
align center(default)topbottom
Where the block sits. top leaves room for a wordmark below it.
API
SplashScreen
The screen an app shows before it has anything else to show.
That is the whole of the common case. With no props it renders on the server — so it is in the HTML, not painted after hydration — waits for the page to finish loading, holds for minDuration so a fast boot doesn't blink, and leaves. Nothing to wire up, nothing to remember to turn off.
Everything past that is a decision you can take back:
- `bind:open` when you know what "ready" means. Auto-hide switches itself off the moment the splash is controlled, so it can never take the screen away from under you. - `splash` — a {@link SplashController} — when booting is a sequence: it carries open, progress and message together, and splash.run([…]) walks the steps and puts each one on screen. - `variant` and `pattern` are the look: a colour scheme and a decorative layer, chosen separately, so a branded splash and a plain one differ by one word rather than by a rewrite. - `exit` is how it leaves, which is the part anyone remembers. iris opens a hole in the middle and the app is behind it. - `inline` puts the whole thing inside its container instead of over the viewport — the same screen for a pane, a dashboard tab or a preview that is still loading.
Two flashes are designed out rather than left to the caller. minDuration keeps a splash that only had 40ms of work to cover on screen long enough to be read instead of blinking, and appearAfter stops it appearing at all when the app was ready before the delay ran out — the two together mean a splash is either absent or deliberate.
import { SplashScreen } from 'omaris' <!-- +layout.svelte --><SplashScreen title="Acme" /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- The full-bleed surface. Everything else sits on it.
scrim- The tint over a photograph, so text stays legible on any image.
backdrop- The decorative layer — the pattern, when there is one.
content- Logo, words and indicator, stacked and centred. Full width on purpose: a bar that is wider than the screen it is on has to be clamped by something, and this is the something.
logo- The mark, at the size
sizeasks for. halo- The ring that leaves the logo when
halois set. title- No description in the source yet.
description- No description in the source yet.
indicator- Wrapper for whichever indicator is running; sets its colour.
track- The bar's groove. Never wider than the screen it is on.
fill- The filled part of the bar.
message- The line under the indicator — "Loading your workspace".
percent- No description in the source yet.
actions- No description in the source yet.
footer- Pinned to the bottom edge: a version, a byline, a legal line.
Props
open bindableboolean Whether the splash is up.
Leave it out and the component runs itself: it starts up and comes down when the page has loaded. Bind it — or pass a splash — and it is yours, auto-hide included.
splash SplashController A {@link SplashController}, for a boot sequence. It supplies open, progress and message; any of the three passed as a prop wins.
variant Defaults to 'background'
SplashScreenVariant The colour scheme.
backgroundsurfaceprimaryinverseglassimage
pattern Defaults to 'none'
SplashScreenPattern The decorative layer over it.
nonegradientaurorameshspotlightgriddots
size Defaults to 'md'
SplashScreenSize Mark and type size.
smmdlg
align Defaults to 'center'
SplashScreenAlign Where the block sits.
centertopbottom
indicator Defaults to 'spinner'
SplashScreenIndicator What loops while it is up.
exit Defaults to 'fade'
SplashScreenExit How it leaves.
enter Defaults to 'rise'
SplashScreenEnter How the content arrives.
title string The app's name, under the mark.
description string One line under the title. Say what is happening, not "Loading…".
message string The line under the indicator. Changing it fades the new one in.
progress number How far along, 0–100. Left out, the indicator is indeterminate — which is the honest answer unless you really are counting something.
percent Defaults to false
boolean Show the percentage beside the bar. Only with a real progress.
simulate Defaults to false
boolean Move an indeterminate bar along on its own, up to about 90%.
A lie, and a useful one: it says the app is working. It stops short of the end and only lands on 100 when the splash is actually done.
halo Defaults to false
boolean A ring that pulses out of the logo. Costs nothing, reads as alive.
image string Background photograph, for variant="image".
minDuration Defaults to 600
number The shortest time it may be on screen once shown, in ms. What stops a 40ms boot from flashing a splash at somebody.
appearAfter Defaults to 0
number Wait this long before showing it at all, in ms. An app that becomes ready inside the delay never shows a splash — the best splash screen is the one nobody had to see.
duration Defaults to 520
number How long the exit takes, in ms.
autoHide Defaults to true
boolean | number When an uncontrolled splash takes itself off: true waits for the page to finish loading, a number is a plain timeout in ms, false leaves it up until something sets open.
lockScroll Defaults to true
boolean Stop the page behind it scrolling while it is up.
portalTo PortalTarget Where the overlay is moved to. null keeps it where it is.
inline Defaults to false
boolean Inside its container instead of over the viewport.
label string What a screen reader calls it.
onhide () => void The exit has started.
onhidden () => void The splash is off screen and out of the DOM.
logo Snippet The mark. An <img>, an inline <svg>, anything.
children Snippet Extra content between the words and the indicator.
actions Snippet Buttons — a "Retry" for a boot that failed, usually.
footer Snippet Pinned to the bottom edge: version, byline, legal line.
class string classes SplashScreenClasses Per-part Tailwind overrides. class still covers the root.