Skip to content

Instantly share code, notes, and snippets.

@rockinghelvetica
Created December 19, 2025 10:13
Show Gist options
  • Select an option

  • Save rockinghelvetica/8a4d0ddb0faf98f2e22b21744fd625a7 to your computer and use it in GitHub Desktop.

Select an option

Save rockinghelvetica/8a4d0ddb0faf98f2e22b21744fd625a7 to your computer and use it in GitHub Desktop.
Utility for simulated motion preferences
// DEBUG TOOL FOR MOTION PREF
// WILL CREATE DUPLICATE RULES BASED ON '(prefers-reduced-motion: reduce)' block exactly
// WILL NOT ADDRESS STALE matchMedia objects / handlers created before this runs
(function initMotionToggle() {
// Create and inject the checkbox UI
const toggleContainer = document.createElement('div');
toggleContainer.innerHTML = `
<label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
<input type="checkbox" id="motion-toggle" style="cursor: pointer;">
<span>Simulate prefers-reduced-motion</span>
</label>
`;
Object.assign(toggleContainer.style, {
position: 'fixed',
bottom: '1rem',
left: '1rem',
zIndex: '9999',
background: 'white',
padding: '0.75rem 1rem',
border: '2px solid #ccc',
borderRadius: '6px',
fontFamily: 'monospace',
fontSize: '14px',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)'
});
document.body.appendChild(toggleContainer);
const motionToggle = document.getElementById('motion-toggle');
let mockMotionEnabled = false;
const listeners = new Set();
// Override matchMedia for JS
const originalMatchMedia = window.matchMedia;
window.matchMedia = function(query) {
if (query === '(prefers-reduced-motion: reduce)') {
return {
matches: mockMotionEnabled,
media: query,
addEventListener: (event, callback) => {
if (event === 'change') listeners.add(callback);
},
removeEventListener: (event, callback) => {
if (event === 'change') listeners.delete(callback);
}
};
}
return originalMatchMedia.call(window, query);
};
// AUTO-GENERATE CSS RULES FROM MEDIA QUERY
function injectMockReducedMotionCSS() {
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.type === CSSRule.MEDIA_RULE &&
rule.conditionText === '(prefers-reduced-motion: reduce)') {
const mockRules = [];
for (const innerRule of rule.cssRules) {
if (innerRule.type === CSSRule.STYLE_RULE) {
const selector = innerRule.selectorText;
const cssText = innerRule.style.cssText;
let newSelector;
if (selector === ':root') {
newSelector = ':root.mock-reduced-motion';
} else {
newSelector = ':root.mock-reduced-motion ' + selector;
}
mockRules.push(`${newSelector} { ${cssText} }`);
}
}
const styleEl = document.createElement('style');
styleEl.textContent = '/* Auto-generated mock reduced motion */\n' + mockRules.join('\n');
document.head.appendChild(styleEl);
console.log('✓ Generated mock-reduced-motion CSS rules');
return;
}
}
} catch (e) {
// Cross-origin stylesheets will throw
}
}
}
// Generate the CSS on init
injectMockReducedMotionCSS();
// Connect checkbox
motionToggle.addEventListener('change', (e) => {
mockMotionEnabled = e.target.checked;
document.documentElement.classList.toggle('mock-reduced-motion', mockMotionEnabled);
const event = { matches: mockMotionEnabled };
listeners.forEach(callback => callback(event));
console.log('prefers-reduced-motion:', mockMotionEnabled);
});
// Initialize from system preference
const actualPref = originalMatchMedia.call(window, '(prefers-reduced-motion: reduce)');
motionToggle.checked = actualPref.matches;
mockMotionEnabled = actualPref.matches;
if (actualPref.matches) {
document.documentElement.classList.add('mock-reduced-motion');
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment