How to Implement Reduced Motion Support in Hallmark: A Complete Guide

Hallmark respects the prefers-reduced-motion media query by detecting the user preference once at load time and guarding all animations with a global boolean flag.

The Hallmark repository by Nutlope provides a reference implementation for accessibility-first motion design. By centralizing the reduced motion check in site/js/main.js, the project ensures that every interactive component—from theme transitions to hero animations—respects user accessibility preferences.

Detecting the prefers-reduced-motion Preference

Hallmark detects the user's motion preference immediately upon initialization. In site/js/main.js, the code creates a global constant that evaluates the media query:

const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;

This single detection point (lines 5–6) stores the result in a boolean variable named reduced. All subsequent animation logic throughout the codebase references this flag, ensuring consistent behavior without repeated media query evaluations.

Guarding Core Animations in main.js

The primary implementation guards motion-heavy operations using early returns or conditional blocks. When swapping themes, Hallmark conditionally uses the View Transitions API only when motion is not reduced:

if (!reduced && document.startViewTransition) {
  document.startViewTransition(apply);
}

This pattern appears at lines 761–762 in site/js/main.js. The code checks both the reduced flag and API availability before invoking the transition. If the user prefers reduced motion, the theme applies instantly without the morphing animation.

Implementing Reduced Motion in Example Demos

Individual examples in the Hallmark repository follow the same detection and guarding pattern, demonstrating how to apply the pattern across different animation contexts.

Tally Demo Animation Guard

In site/examples/tally/app.js, the demo reads the media query into a specifically named boolean:

const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

// Later in animation logic
if (reduceMotion) {
  // Skip motion-heavy transitions
  return;
}

Riso Demo Hero Animation

The site/examples/riso-01/script.js file creates a reduced flag and conditionally runs the hero entrance animation:

const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;

function animateHero() {
  if (reduced) return;
  // Perform entrance animation logic
}

Custom-05 Scroll Behaviors

In site/examples/custom-05/script.js, the implementation adds explicit comments explaining that when reduced motion is preferred, the component disables the "traveling playhead" and scroll-linked animations. This documents the accessibility rationale directly in the source code.

Best Practices for Adding New Components

When extending Hallmark with new interactive elements, follow the established four-step pattern:

  1. Detect the preference once at load time using matchMedia("(prefers-reduced-motion: reduce)")
  2. Store the result in a descriptive boolean (reduced or reduceMotion)
  3. Guard all animation entry points with if (!reduced) { ... } or early return statements
  4. Avoid CSS-only animations that cannot be toggled via JavaScript; Hallmark already marks .reveal elements as visible on page load to prevent scroll-triggered motion

Summary

  • Centralize detection by creating a global reduced flag in your main entry file using matchMedia
  • Guard view transitions by checking !reduced && document.startViewTransition before calling the API
  • Follow existing patterns in site/js/main.js and example demos like tally/app.js for consistent implementation
  • Document accessibility choices with comments explaining why specific animations are disabled
  • Provide instant fallbacks that execute immediately when motion is reduced, ensuring functionality remains accessible

Frequently Asked Questions

How does Hallmark detect reduced motion preferences?

Hallmark uses window.matchMedia("(prefers-reduced-motion: reduce)").matches at application startup to create a boolean flag. This detection occurs in site/js/main.js and stores the result in a const reduced variable that the entire application references.

What happens to theme transitions when reduced motion is enabled?

When reduced is true, Hallmark skips the document.startViewTransition API call and executes the theme change logic immediately. This prevents the morphing visual effect while maintaining instant functional feedback for the user.

Can I use CSS animations instead of JavaScript guards?

Hallmark specifically avoids CSS-only animation triggers for motion-sensitive elements. The repository explicitly marks elements with the .reveal class as already visible on page load to prevent scroll-based CSS animations from running, ensuring JavaScript maintains full control over motion rendering.

Which files should I reference when implementing reduced motion in my own Hallmark components?

Reference site/js/main.js for the global flag pattern and theme transition logic, site/examples/tally/app.js for per-component motion detection, and site/examples/riso-01/script.js for animation entry point guarding. These files demonstrate the complete implementation spectrum from global to local scope.

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 →