pota/use/animatepota/use/animate swaps CSS classes (or part tokens) on an element
and returns a promise that resolves when the resulting CSS animation
finishes — or immediately, if no animation is running. Useful for
chaining state changes after an enter / exit transition without
watching animationend by hand. It also bundles a few animation
utilities: cancel running animations, introspect @keyframes, and an
owned requestAnimationFrame loop.
animateClassTo(el, oldClass, newClass)
— swap classes; resolves when the animation endsanimatePartTo(el, oldPart, newPart)
— same, for part tokensstopAnimations(el) — cancel every
running animation; returns themdocumentKeyframes() — map of
every @keyframes rule by nameuseAnimationFrame(fn) — owned
rAF loop with { start, stop }animateClassTo swaps classes and
resolves on animationend, so state changes can be chained without
watching the event by hand. It resolves immediately when no animation
runs, making it safe to await either way.
import { ref, render, signal } from 'pota'
import { animateClassTo } from 'pota/use/animate'
function App() {
const box = ref()
const state = signal('idle')
const toggle = async () => {
if (state.read() !== 'idle') return
state.write('sliding out')
// `.out` runs an animation — resolves on `animationend`
await animateClassTo(box(), 'idle', 'out')
state.write('snapping back')
// `.idle` runs none — resolves immediately
await animateClassTo(box(), 'out', 'idle')
state.write('idle')
}
return (
<>
<style>{`
.idle { background: #2a9d8f; }
.out { background: #e76f51; animation: slide .4s forwards; }
@keyframes slide { to { transform: translateX(120px); } }
`}</style>
<button on:click={toggle}>animate</button>
<div
use:ref={box}
class="idle"
style={{
width: '120px',
padding: '1rem',
color: 'white',
'margin-top': '1rem',
}}
>
state: {state.read}
</div>
</>
)
}
render(App)
useAnimationFrame returns
{ start, stop }, never starts on its own, and auto-stops on scope
dispose. fn can call stop() to break the loop on the same tick.
import { render, signal } from 'pota'
import { useAnimationFrame } from 'pota/use/animate'
function App() {
const frames = signal(0)
const loop = useAnimationFrame(() => {
frames.update(n => n + 1)
if (frames.read() >= 100) loop.stop()
})
return (
<>
<button on:click={() => loop.start()}>start</button>
<button on:click={() => loop.stop()}>stop</button>
<p>frames: {frames.read}</p>
</>
)
}
render(App)