or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animation-controls.mdconfiguration.mddom-utilities.mdgestures.mdindex.mdlayout-presence.mdmotion-components.mdmotion-values.mdscroll-view.md
tile.json

dom-utilities.mddocs/

DOM Utilities

Direct DOM animation functions that work without React, for imperative animations and scroll tracking.

Capabilities

Animate Function

Direct DOM animation function using Web Animations API or fallback engine.

/**
 * Animate DOM elements directly without React
 * @param element - Element to animate (selector string or Element)
 * @param keyframes - Animation keyframes or single keyframe object
 * @param options - Animation options
 * @returns Animation playback controls
 */
function animate(
  element: string | Element,
  keyframes: Record<string, any> | Record<string, any>[],
  options?: AnimationOptions
): AnimationPlaybackControls;

/**
 * Create scoped animate function bound to a container
 * @param scope - Container element for scoped queries
 * @returns Scoped animate function
 */
function createScopedAnimate(scope: Element): typeof animate;

interface AnimationOptions {
  /**
   * Animation duration in seconds
   */
  duration?: number;
  
  /**
   * Delay before animation starts in seconds
   */
  delay?: number;
  
  /**
   * Easing function or WAAPI easing string
   */
  ease?: string | number[] | ((t: number) => number);
  
  /**
   * Number of times to repeat animation
   */
  repeat?: number;
  
  /**
   * Type of repeat behavior
   */
  repeatType?: "loop" | "reverse" | "mirror";
  
  /**
   * Delay between repeats in seconds
   */
  repeatDelay?: number;
  
  /**
   * Direction of animation
   */
  direction?: "normal" | "reverse" | "alternate" | "alternate-reverse";
  
  /**
   * Fill mode for animation
   */
  fill?: "none" | "forwards" | "backwards" | "both";
  
  /**
   * Animation end behavior
   */
  endDelay?: number;
  
  /**
   * Start time offset
   */
  startTime?: number;
  
  /**
   * Animation timing function
   */
  composite?: "replace" | "add" | "accumulate";
}

interface AnimationPlaybackControls {
  /**
   * Stop the animation
   */
  stop(): void;
  
  /**
   * Pause the animation
   */
  pause(): void;
  
  /**
   * Resume the animation
   */
  play(): void;
  
  /**
   * Reverse the animation direction
   */
  reverse(): void;
  
  /**
   * Cancel the animation and reset to initial state
   */
  cancel(): void;
  
  /**
   * Complete the animation immediately
   */
  finish(): void;
  
  /**
   * Get current animation time
   */
  currentTime: number;
  
  /**
   * Get animation duration
   */
  duration: number;
  
  /**
   * Get animation playback rate
   */
  playbackRate: number;
  
  /**
   * Set animation playback rate
   */
  updatePlaybackRate(rate: number): void;
  
  /**
   * Promise that resolves when animation completes
   */
  then(onResolve?: () => void, onReject?: (error: any) => void): Promise<void>;
  
  /**
   * Attach event listeners
   */
  addEventListener(event: string, handler: EventListener): void;
  
  /**
   * Remove event listeners
   */
  removeEventListener(event: string, handler: EventListener): void;
}

Usage Examples:

import { animate, createScopedAnimate } from "framer-motion/dom";

// Basic DOM animation
function basicDOMAnimation() {
  // Animate single element
  animate(".box", { x: 100, rotate: 45 }, { duration: 1 });
  
  // Animate with keyframes
  animate(".circle", [
    { scale: 1, backgroundColor: "#ff0000" },
    { scale: 1.5, backgroundColor: "#00ff00" },
    { scale: 1, backgroundColor: "#0000ff" }
  ], { 
    duration: 2, 
    repeat: Infinity, 
    repeatType: "reverse" 
  });
  
  // Animate multiple elements
  document.querySelectorAll(".item").forEach((element, index) => {
    animate(element, 
      { y: -50, opacity: [0, 1] }, 
      { duration: 0.5, delay: index * 0.1 }
    );
  });
}

// Animation with controls
function controlledDOMAnimation() {
  const controls = animate(".animated-element", 
    { rotate: 360 }, 
    { duration: 2, repeat: Infinity }
  );
  
  // Control playback
  document.getElementById("pause")?.addEventListener("click", () => {
    controls.pause();
  });
  
  document.getElementById("play")?.addEventListener("click", () => {
    controls.play();
  });
  
  document.getElementById("reverse")?.addEventListener("click", () => {
    controls.reverse();
  });
  
  document.getElementById("stop")?.addEventListener("click", () => {
    controls.stop();
  });
  
  // Promise-based completion
  controls.then(() => {
    console.log("Animation completed");
  });
}

// Scoped animations
function scopedDOMAnimation() {
  const container = document.getElementById("animation-container");
  if (container) {
    const scopedAnimate = createScopedAnimate(container);
    
    // Animate elements within container only
    scopedAnimate(".box", { x: 200 }, { duration: 1 });
    scopedAnimate(".circle", { scale: 1.5 }, { duration: 0.8 });
  }
}

Minimal Animation Function

Ultra-lightweight animation function for simple WAAPI animations.

/**
 * Minimal WAAPI-based animation function
 * @param element - Element to animate
 * @param keyframes - Animation keyframes
 * @param options - WAAPI options
 * @returns Native Web Animation object
 */
function animateMini(
  element: string | Element,
  keyframes: Keyframe[] | PropertyIndexedKeyframes,
  options?: KeyframeAnimationOptions
): Animation;

/**
 * Sequence multiple animations with precise timing
 * @param animations - Array of animation definitions
 * @param options - Sequence options
 * @returns Promise resolving when sequence completes
 */
function animateSequence(
  animations: Array<[string | Element, Keyframe[] | PropertyIndexedKeyframes, KeyframeAnimationOptions?]>,
  options?: SequenceOptions
): Promise<void>;

interface SequenceOptions {
  /**
   * Default duration for animations without explicit duration
   */
  defaultDuration?: number;
  
  /**
   * Default easing for animations
   */
  defaultEasing?: string;
  
  /**
   * Delay between animations
   */
  stagger?: number;
}

Usage Examples:

import { animateMini, animateSequence } from "framer-motion/dom-mini";

// Minimal WAAPI animation
function minimalAnimation() {
  const element = document.querySelector(".element");
  if (element) {
    const animation = animateMini(element, [
      { transform: "translateX(0px) scale(1)" },
      { transform: "translateX(100px) scale(1.2)" },
      { transform: "translateX(0px) scale(1)" }
    ], {
      duration: 1000,
      easing: "ease-in-out",
      iterations: Infinity
    });
    
    // Native Web Animation methods
    animation.pause();
    animation.play();
    animation.addEventListener("finish", () => {
      console.log("Animation finished");
    });
  }
}

// Animation sequences
async function sequencedAnimations() {
  await animateSequence([
    [".header", [{ opacity: 0 }, { opacity: 1 }], { duration: 500 }],
    [".content", [{ y: 50 }, { y: 0 }], { duration: 600 }],
    [".footer", [{ scale: 0.8 }, { scale: 1 }], { duration: 400 }]
  ], {
    stagger: 100 // 100ms delay between each animation
  });
  
  console.log("Sequence completed");
}

Scroll Utilities

Direct DOM scroll tracking utilities that work without React.

/**
 * Track scroll with callback (already documented in scroll-view.md)
 */
function scroll(
  onScroll: (info: ScrollInfo) => void,
  options?: ScrollOptions
): () => void;

/**
 * Advanced scroll tracking with detailed information
 */
function scrollInfo(
  onScroll: (info: DetailedScrollInfo) => void,
  options?: ScrollOptions
): () => void;

interface ScrollInfo {
  x: number;
  y: number;
  xProgress: number;
  yProgress: number;
  velocity: number;
}

interface DetailedScrollInfo extends ScrollInfo {
  targetBounds: DOMRect;
  containerBounds: DOMRect;
  intersectionRatio: number;
}

Viewport Utilities

Direct DOM intersection observer utilities.

/**
 * Observe element intersection with viewport (already documented in scroll-view.md)
 */
function inView(
  element: string | Element,
  onStart: (entry: IntersectionObserverEntry) => void | (() => void),
  options?: InViewOptions
): () => void;

interface InViewOptions {
  root?: Element;
  margin?: string;
  amount?: number | number[] | "some" | "all";
}

Transform Utilities

Utilities for working with CSS transforms.

/**
 * Build CSS transform string from transform object
 * @param transform - Transform properties object
 * @param transformOrigin - Transform origin values
 * @returns CSS transform string
 */
function buildTransform(
  transform: Record<string, string | number>,
  transformOrigin?: string
): string;

/**
 * Parse transform string into object
 * @param transformString - CSS transform string
 * @returns Object with transform properties
 */
function parseTransform(transformString: string): Record<string, number>;

/**
 * Interpolate between transform states
 * @param from - Starting transform object
 * @param to - Ending transform object
 * @param progress - Interpolation progress (0-1)
 * @returns Interpolated transform object
 */
function interpolateTransforms(
  from: Record<string, number>,
  to: Record<string, number>,
  progress: number
): Record<string, number>;

Usage Examples:

import { buildTransform, parseTransform } from "framer-motion/dom";

// Build transform strings
function transformUtilities() {
  // Build transform from object
  const transformString = buildTransform({
    x: 100,
    y: 50,
    rotate: 45,
    scale: 1.2,
    skewX: 10
  }, "center center");
  
  console.log(transformString); 
  // "translateX(100px) translateY(50px) rotate(45deg) scale(1.2) skewX(10deg)"
  
  // Apply to element
  const element = document.querySelector(".element");
  if (element) {
    (element as HTMLElement).style.transform = transformString;
    (element as HTMLElement).style.transformOrigin = "center center";
  }
  
  // Parse existing transform
  const existing = (element as HTMLElement)?.style.transform || "";
  const parsed = parseTransform(existing);
  console.log(parsed); // { translateX: 100, translateY: 50, rotate: 45, ... }
}

Animation Utilities

Additional utilities for animation management.

/**
 * Delay execution by specified time
 * @param duration - Delay duration in seconds
 * @returns Promise that resolves after delay
 */
function delay(duration: number): Promise<void>;

/**
 * Create delayed function
 * @param fn - Function to delay
 * @param duration - Delay duration in seconds
 * @returns Delayed function
 */
function delay<T extends (...args: any[]) => any>(
  fn: T, 
  duration: number
): DelayedFunction<T>;

interface DelayedFunction<T extends (...args: any[]) => any> {
  (...args: Parameters<T>): Promise<ReturnType<T>>;
  cancel(): void;
}

/**
 * RAF-based animation loop
 * @param callback - Function to call each frame
 * @returns Cleanup function to stop the loop
 */
function animationLoop(callback: (time: number, delta: number) => void): () => void;

/**
 * Throttle function calls to animation frames
 * @param fn - Function to throttle
 * @returns Throttled function
 */
function throttleToAnimationFrame<T extends (...args: any[]) => any>(fn: T): T;

Usage Examples:

import { delay, animationLoop, throttleToAnimationFrame } from "framer-motion/dom";

// Delay utilities
async function delayExample() {
  console.log("Starting...");
  await delay(1); // Wait 1 second
  console.log("After 1 second");
  
  // Delayed function execution
  const delayedLog = delay(console.log, 0.5);
  delayedLog("This appears after 500ms");
  
  // Cancel delayed execution
  const cancelable = delay(() => console.log("This won't run"), 2);
  cancelable.cancel();
}

// Animation loop
function customAnimationLoop() {
  let rotation = 0;
  const element = document.querySelector(".rotating-element") as HTMLElement;
  
  const stopLoop = animationLoop((time, delta) => {
    rotation += delta * 0.1; // Rotate based on time delta
    if (element) {
      element.style.transform = `rotate(${rotation}deg)`;
    }
  });
  
  // Stop after 5 seconds
  setTimeout(stopLoop, 5000);
}

// Throttled event handling
function throttledScrollExample() {
  const handleScroll = throttleToAnimationFrame((event: Event) => {
    const scrollY = window.scrollY;
    const header = document.querySelector(".header") as HTMLElement;
    if (header) {
      header.style.transform = `translateY(${Math.min(0, -scrollY * 0.5)}px)`;
    }
  });
  
  window.addEventListener("scroll", handleScroll);
  
  // Cleanup
  return () => window.removeEventListener("scroll", handleScroll);
}

Integration Patterns

Combining with React:

import { animate } from "framer-motion/dom";
import { useEffect, useRef } from "react";

function HybridAnimation() {
  const ref = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    if (ref.current) {
      // Use DOM animate for complex sequences
      const controls = animate(ref.current, [
        { x: 0, y: 0 },
        { x: 100, y: 0 },
        { x: 100, y: 100 },
        { x: 0, y: 100 },
        { x: 0, y: 0 }
      ], {
        duration: 4,
        repeat: Infinity,
        ease: "linear"
      });
      
      return () => controls.stop();
    }
  }, []);
  
  return <div ref={ref} className="w-16 h-16 bg-blue-500" />;
}

Performance Optimization:

// Batch DOM animations
function batchAnimations() {
  const elements = document.querySelectorAll(".animate-me");
  
  // Start all animations simultaneously for better performance
  elements.forEach((element, index) => {
    animate(element, 
      { y: -100, opacity: [0, 1] },
      { 
        duration: 0.6,
        delay: index * 0.05, // Small stagger
        ease: "easeOut"
      }
    );
  });
}

// Memory-efficient cleanup
function animationWithCleanup() {
  const cleanupFunctions: (() => void)[] = [];
  
  // Store cleanup functions
  cleanupFunctions.push(
    scroll((info) => {
      // Handle scroll
    })
  );
  
  cleanupFunctions.push(
    inView(".target", (entry) => {
      // Handle intersection
    })
  );
  
  // Clean up all listeners
  return () => {
    cleanupFunctions.forEach(cleanup => cleanup());
  };
}

Progressive Enhancement:

// Feature detection and fallbacks
function progressiveAnimation() {
  const element = document.querySelector(".element") as HTMLElement;
  
  if ("animate" in document.createElement("div")) {
    // Use Web Animations API
    animate(element, { x: 100 }, { duration: 1 });
  } else {
    // Fallback to CSS transitions
    element.style.transition = "transform 1s ease";
    element.style.transform = "translateX(100px)";
  }
}