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:
- Detect the preference once at load time using
matchMedia("(prefers-reduced-motion: reduce)") - Store the result in a descriptive boolean (
reducedorreduceMotion) - Guard all animation entry points with
if (!reduced) { ... }or early return statements - Avoid CSS-only animations that cannot be toggled via JavaScript; Hallmark already marks
.revealelements as visible on page load to prevent scroll-triggered motion
Summary
- Centralize detection by creating a global
reducedflag in your main entry file usingmatchMedia - Guard view transitions by checking
!reduced && document.startViewTransitionbefore calling the API - Follow existing patterns in
site/js/main.jsand example demos liketally/app.jsfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →