SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%
4.5 KB · 158 lines markdown
Rendered Raw Blame History
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# UI Animation Patterns — Copy-Paste Reference78## Contents9- Motion tokens10- Micro-interaction (button press/hover)11- Enter/exit transition (dialog, toast)12- Scroll reveal with IntersectionObserver13- Page-load sequence (staggered)14- Web Animations API sequencing15- Reduced-motion gate16- Gotchas1718## Motion tokens1920```css21:root {22  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);   /* enter: fast start, soft landing */23  --ease-in:  cubic-bezier(0.7, 0, 0.84, 0);   /* exit: accelerate away */24  --dur-fast: 150ms;   /* hover, press feedback */25  --dur-base: 250ms;   /* enters, exits, toggles */26  --dur-slow: 450ms;   /* scene-level changes */27}28```2930## Micro-interaction (button press/hover)3132```css33.btn {34  transition: transform var(--dur-fast) var(--ease-out),35              box-shadow var(--dur-fast) var(--ease-out);36}37.btn:hover  { transform: translateY(-1px); }38.btn:active { transform: translateY(0) scale(0.98); }39```4041## Enter/exit transition (dialog, toast)4243Enter with ease-out, exit faster with ease-in.4445```css46.toast {47  transition: transform var(--dur-base) var(--ease-out),48              opacity   var(--dur-base) var(--ease-out);49}50.toast[data-state="closed"] {51  transform: translateY(8px);52  opacity: 0;53  transition-duration: var(--dur-fast);54  transition-timing-function: var(--ease-in);55}56```5758Animating from `display: none` (modern CSS):5960```css61dialog[open] {62  opacity: 1;63  transform: none;64  transition: opacity var(--dur-base) var(--ease-out),65              transform var(--dur-base) var(--ease-out),66              display var(--dur-base) allow-discrete;67  @starting-style { opacity: 0; transform: translateY(12px); }68}69```7071## Scroll reveal with IntersectionObserver7273Content is fully visible without JS; the class only *enables* the hidden start state.7475```html76<section data-reveal>…</section>77```7879```css80.js [data-reveal]          { opacity: 0; transform: translateY(16px); }81.js [data-reveal].is-shown {82  opacity: 1; transform: none;83  transition: opacity var(--dur-slow) var(--ease-out),84              transform var(--dur-slow) var(--ease-out);85}86```8788```js89document.documentElement.classList.add('js');90const io = new IntersectionObserver((entries) => {91  for (const e of entries) if (e.isIntersecting) {92    e.target.classList.add('is-shown');93    io.unobserve(e.target);            // reveal once94  }95}, { threshold: 0.15 });               // fire when 15% visible96document.querySelectorAll('[data-reveal]').forEach(el => io.observe(el));97```9899## Page-load sequence (staggered)100101One orchestrated moment: hero elements enter in order, 60ms apart.102103```css104.hero > * {105  opacity: 0;106  transform: translateY(12px);107  animation: enter var(--dur-slow) var(--ease-out) forwards;108  animation-delay: calc(var(--i) * 60ms);109}110@keyframes enter { to { opacity: 1; transform: none; } }111```112113```html114<div class="hero">115  <h1 style="--i:0">…</h1>116  <p  style="--i:1">…</p>117  <a  style="--i:2" class="btn">…</a>118</div>119```120121## Web Animations API sequencing122123For interruption-safe, runtime-computed motion.124125```js126const panel = document.querySelector('.panel');127const anim = panel.animate(128  [{ transform: 'translateX(100%)' }, { transform: 'none' }],129  { duration: 250, easing: 'cubic-bezier(0.16,1,0.3,1)', fill: 'forwards' }130);131// interruptible: reverse mid-flight instead of restarting132closeBtn.onclick = () => anim.reverse();133await anim.finished;                   // sequencing point134```135136## Reduced-motion gate137138```css139@media (prefers-reduced-motion: reduce) {140  [data-reveal], .hero > * { opacity: 1 !important; transform: none !important;141                             animation: none !important; transition: none !important; }142}143```144145```js146const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;147if (!reduced) el.animate(/* … */);148```149150## Gotchas151152- `transition: all` invites accidental layout animations and future regressions — always list properties.153- `animation-fill-mode: forwards` keeps the element on its final keyframe, which can trap it in a stale state after class changes; prefer transitioning real end states.154- `will-change` left permanently on many elements consumes GPU memory — add before heavy animation, remove after.155- Staggers multiply duration: 10 items × 100ms delay = a 1s+ sequence. Cap total sequence time near 700ms.156- `height: auto` can't transition; animate `grid-template-rows: 0fr → 1fr` on a wrapper, or use WAAPI with measured pixel values.157- IntersectionObserver with `threshold: 1.0` never fires on elements taller than the viewport.158