Overview

keyframes(name, stops) injects a scoped @keyframes rule at module level and returns the CSS animation name (a string). Use it with inline animation styles or stylesheet rules — not as an animatedIf preset.

Call it once at module level (idempotent mount). For enter/exit WAAPI transitions, prefer animationPresets with animatedIf instead.

keyframes vs animationPresets

API
Returns
Wire-up
keyframes(name, stops) string (CSS name) style=`animation: ${name} 0.5s ease`
animationPresets.* AnimationPreset Pass to animatedIf / animatedRouter config.

Custom bounce with keyframes()

Define named @keyframes stops as a plain object. keyframes() injects the CSS once and returns the animation name — apply it via style.animation.

import { html, atom, keyframes, renderApp } from 'mates';

// keyframes() injects a named @keyframes rule and returns the CSS animation name
const bounceIn = keyframes('bounce-in', {
'0%': { transform: 'scale(0.3)', opacity: '0' },
'50%': { transform: 'scale(1.1)', opacity: '0.8' },
'70%': { transform: 'scale(0.95)' },
'100%': { transform: 'scale(1)', opacity: '1' },
});

const App = () => {
const show = atom(true);
return () => html`
<div class="m-col m-gap m-items-center center">
<button @click=${() => show.set(v => !v)}>${show() ? 'Hide' : 'Play bounce'}</button>
<div class="p-20">
${show()
? html`
<div class="card card-body" style="animation:${bounceIn} 0.55s ease both">
<span>🎉</span>
<p class="m-t-5">Custom keyframes!</p>
</div>
`
: html`<p class="label">Click to bounce in</p>`}
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));

Looping pulse

Use infinite animation for attention states.

import { html, atom, keyframes, renderApp } from 'mates';

const pulse = keyframes('docs-pulse', {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0.45' },
});

const App = () => {
const on = atom(true);
return () => html`
<div class="m-col m-gap m-items-center center">
<button @click=${() => on.set(v => !v)}>Toggle pulse</button>
<span class="tag" style=${on() ? `animation:${pulse} 1s ease infinite` : ''}>LIVE</span>
</div>
`;
};
renderApp(App, document.getElementById('app'));