How the Diagram-Design Animation System Handles Reduced-Motion Accessibility

The Diagram-Design library automatically detects the prefers-reduced-motion media query through a centralized AnimationController class that disables all transitions, keyframe animations, and physics simulations when users require minimal motion, while exposing a manual override API via setMotionEnabled().

The open-source cathrynlavery/diagram-design repository provides a data visualization framework with built-in accessibility considerations, documented comprehensively in docs/accessibility.md. Understanding how this animation system handles reduced-motion accessibility ensures your diagrams remain usable for people with vestibular disorders or other motion sensitivities. The implementation centralizes motion detection in a single controller that propagates state to all animated components.

Core Detection in AnimationController

In src/animation/controller.ts, the AnimationController class serves as the single source of truth for motion preferences. The constructor evaluates the browser's accessibility settings using the prefers-reduced-motion media query:

export class AnimationController {
  readonly motionEnabled = !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  // ...
}

This boolean flag is exposed to the entire application, ensuring consistent behavior across all diagram types.

Motion Disabling Strategies

When motionEnabled evaluates to false, the engine switches to a static rendering mode. The system alters behavior across four distinct domains:

  • Transitions: CSS transition properties with easing functions are suppressed, applying final states instantly rather than interpolating over time.

  • Keyframe animations: @keyframes sequences driven by requestAnimationFrame are skipped entirely, causing components to jump directly to their terminal frames.

  • Spring physics: Libraries like react-spring or custom physics loops bypass interpolation calculations, snapping immediately to final target values.

  • Delay handling: All timed delays between animation steps are suppressed, ensuring immediate layout completion.

Component-Level Integration

Individual diagram components query the controller before initiating any motion. In src/components/BarChart.tsx, the rendering logic checks the flag to determine whether to animate bar growth:

import { useAnimationController } from 'diagram-design/animation';

export function CustomNode({ node }) {
  const { motionEnabled } = useAnimationController();

  // Apply a fade‑in only when motion is allowed
  const style = motionEnabled
    ? { animation: 'fadeIn 300ms ease-out' }
    : {};

  return <g style={style}>…</g>;
}

Similar patterns appear in Sankey and ForceLayout components, which all read the motionEnabled state before starting incremental layout updates.

Developer Override API

Beyond automatic detection, the library exposes explicit control through src/api/motion.ts. The setMotionEnabled() function allows programmatic disabling of animations regardless of system preferences:

import { setMotionEnabled } from 'diagram-design/animation';

setMotionEnabled(false); // forces static rendering

This function updates the internal controller’s flag and triggers a re-render without animations, guaranteeing compliance with accessibility requirements even when the media query is unavailable in older browsers.

Integration Examples

Automatic Reduced-Motion Handling

When rendering a standard bar chart, the library handles detection automatically:

import { BarChart } from 'diagram-design';
import data from './sales.csv';

export default function SalesChart() {
  return <BarChart data={data} />;
  // Animations automatically disable if the user prefers reduced motion
}

Forcing Static Rendering

For exported reports or print views, force reduced-motion mode explicitly:

import { BarChart, setMotionEnabled } from 'diagram-design';

// Force static rendering for a printed report
setMotionEnabled(false);

export default function ReportChart() {
  return <BarChart data={reportData} />;
}

Summary

  • The AnimationController in src/animation/controller.ts centralizes reduced-motion detection using the prefers-reduced-motion media query.

  • Components check the motionEnabled flag before executing CSS transitions, keyframe animations, or spring physics.

  • When reduced motion is requested, animations execute as instant state changes rather than interpolated transitions.

  • The setMotionEnabled() API in src/api/motion.ts provides manual override capabilities for specific use cases like print rendering.

  • All major diagram types including BarChart, Sankey, and ForceLayout respect these accessibility settings by default.

Frequently Asked Questions

How does Diagram-Design detect reduced-motion preferences?

The library queries the browser's prefers-reduced-motion media query during AnimationController initialization in src/animation/controller.ts. The resulting boolean is stored in the readonly motionEnabled property that all components reference before starting animations.

Can developers force reduced-motion mode for specific diagrams?

Yes. Import setMotionEnabled from diagram-design/animation and call it with false to override automatic detection. This updates the internal controller state and triggers an immediate re-render without motion, useful for generating static exports or print-specific layouts.

What happens to physics-based animations when reduced-motion is enabled?

Spring physics and easing functions defined in src/animation/utils.ts bypass their interpolation loops when motionEnabled is false. Instead of calculating intermediate frames, these systems snap directly to final target values, eliminating vestibular triggers while preserving data visualization integrity.

Which file contains the public API for motion control?

The primary developer interface resides in src/api/motion.ts, which exports both setMotionEnabled() for imperative control and useAnimationController() for React-style hook access. This file also re-exports utility types used by the animation system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →