Overview

animatedIf(condition, trueTpl, falseTpl?, config?) uses the Web Animations API to animate content in and out. Pass animationPresets.* factories into config.enter / config.exit instead of writing keyframes by hand.

Presets return an AnimationPreset ({ keyframes, options }) compatible with animatedIf, animatedRouter, and animate(). Swap preset objects at runtime — they are plain data.

How it differs

Approach
Best for
Notes
animationPresets.* WAAPI enter/exit Use with animatedIf / animatedRouter.
keyframes(name, stops) CSS @keyframes Returns a CSS animation name for style.animation — not an AnimationPreset.
Raw WAAPI keyframes Custom motion Pass your own { keyframes, options } when presets are not enough.

Switching between animation presets

Select a preset from the buttons, then toggle visibility. Each preset is a plain object with enter and exit AnimationPreset values — swap them freely at runtime.

import { html, atom, animatedIf, animationPresets, nothing, renderApp } from 'mates';
const App = () => {
const show = atom(true);
const preset = atom('fade');
const presets = {
fade: { enter: animationPresets.fadeIn(350), exit: animationPresets.fadeOut(250) },
slide: { enter: animationPresets.slideIn('up', '24px', 350), exit: animationPresets.slideOut('down', '24px', 250) },
scale: { enter: animationPresets.scaleIn(0, 350), exit: animationPresets.scaleOut(0, 250) },
};
return () => html`
<div class="m-col m-gap m-items-center center">
<div class="m-flex m-items-center m-justify-center m-gap-sm">
${Object.keys(presets).map(p => html`
<button class=${preset() === p ? 'btn-primary' : 'btn-ghost'} @click=${() => preset.set(p)}>${p}</button>
`)}
</div>
<button @click=${() => show.set(v => !v)}>${show() ? 'Hide' : 'Show'} content</button>
<div class="p-20">
${animatedIf(show(),
html`<div class="card card-body p-20 center">
<h1>✨ ${preset()} animation</h1>
</div>`,
nothing,
presets[preset()]
)}
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));

withStagger on a short list

withStagger(preset, index, delayMs) offsets each item's enter animation.

import { html, atom, animatedIf, animationPresets, nothing, renderApp } from 'mates';

const App = () => {
const show = atom(true);
const items = ['One', 'Two', 'Three'];
return () => html`
<div class="m-col m-gap">
<button @click=${() => show.set(v => !v)}>${show() ? 'Hide' : 'Show'}</button>
${items.map((label, i) =>
animatedIf(
show(),
html`<div class="card card-body">${label}</div>`,
nothing,
{
enter: animationPresets.withStagger(animationPresets.fadeIn(280), i, 70),
exit: animationPresets.fadeOut(120),
},
),
)}
</div>
`;
};
renderApp(App, document.getElementById('app'));