Server rendering
Wardic renders on the server with nothing to set up. Both bindings ship a server build, which SolidStart picks on its own, and the provider takes DomLive there as in the browser: a server render builds no runtime, and runs no animation.
Where an element starts
The server gives each element its starting style, its initial, over its own style. The page hydrates with the same values, so the element never flashes at its target first, and the client switches to its own style in the commit that mounts it, with no paint in between.
- With
initial={false}, it starts atanimate. - A variant child starts at its own
initial, or at its parent'sinitialname in its ownvariants. - A key that
animatesets andinitialdoesn't starts from the element's own value, which only the browser can read: give it aninitialto render it.
Every element form renders on the server: w.*, w.create, and a ward spread on an element. Two attach in the browser only, so their element can flash: the use:w directive on Solid 1, and a ward given as ref on Solid 2.
Entrances before the JavaScript
An element whose whole entrance the server knows plays it from the first paint. The server samples it from the same springs and tweens the browser runs, writes it into the page's head as a CSS animation, and the element plays it before any JavaScript has loaded. As the page hydrates, the client takes it over where the CSS has got to, velocity included. With the JavaScript off, it plays to its end and stays there.
<WardicProvider env={DomLive}>
{/* Plays from the first paint, and the client takes it over. */}
<w.h1 initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }}>
Hello
</w.h1>
{/* Waits for the JavaScript, as on a page rendered in the browser. */}
<w.div appear={false} initial={{ scale: 0.9 }} animate={{ scale: 1 }} />
</WardicProvider>A banner that arrives before the JavaScript shows it, with links to hold the JavaScript back or turn it off.
What plays
- Every key it animates starts from
initial, and CSS can blend its start and target: notauto, not avar(), and not two units, such as10pxto50%. - Springs, tweens with a named ease (Motion's names,
cubicBezier,steps), keyframes andarc()paths, arepeat, and a tween'srepeat: Infinity. - Variants, with their
staggerChildren,delayChildrenandwhen: the whole group plays, or none of it. - Layout elements, drawn where the client would draw them.
- On SVG, the attributes that are CSS properties too:
opacity,fill,strokeand its width and dashes (so a line drawing itself withpathLength), and the stop and flood colours.cx,cy,r,rxandryplay where the browser draws them from CSS;d,widthandheightwait.
The rest waits for the JavaScript: an ease given as a function, an element whose own style sets animation, and anything over 60 s. appear={false} turns it off for one element, and on the WardicProvider for the whole app. A transitionEnd is set when the client takes the animation over, so not at all with the JavaScript off.
To see why an element waits, give the provider appear="explain". Each element still plays as before, and the server logs a warning for each one that waits, naming its tag, the keys it animates and the reason:
wardic.appear: <div> (width) waits for the JavaScript: width's start and target don't take one shape
Reduced motion and skipped animations
Under the default reducedMotion="user", the page carries both plays, the reduced one under @media (prefers-reduced-motion: reduce), so the browser picks one before any script has run. "always" and "never" pick one on the server. Under <WardicConfig skipAnimations>, each element is drawn at its end from the first paint.
Into the page's head
- On Solid 1, the rules go in through Solid's
useAssets, which SolidStart's document renders. A render with no</head>leaves them out, and its elements wait at their starting style. - On Solid 2, they go in through
@solidjs/web's head, which a document with a<head>renders.
Content streamed after the page's shell, under a Suspense (Loading on Solid 2) waiting on data, plays too: its chunk brings its own rules, and its entrances start when it lands.
On the server
Hooks give their server values, which the page keeps until it mounts:
createScroll,createVelocityandcreateTimegive 0, andcreateSpringandcreateFollowValuetheir source's value.createHover,createPress,createFocusandcreateInViewgivefalse, andcreatePageInViewtrue.createReducedMotiongivesfalse, where Motion'suseReducedMotiongivesnull.
What it costs
- On the server, about 9 µs for each element, and 8 µs more for each one that plays from the first paint. The first render of an entrance samples it, in about 0.1 ms, and every request after that reuses it.
- In the page, 1 to 2 KB of CSS for each distinct entrance, and about 70 bytes for each element, before compression.
- In the browser, one read for each element as it hydrates, and nothing for each frame.