feat: respect prefers-reduced-motion for Framer Motion animations
This commit is contained in:
12
src/App.tsx
12
src/App.tsx
@@ -8,7 +8,7 @@ import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import hljs from "highlight.js";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { motion, AnimatePresence, useReducedMotion } from "framer-motion";
|
||||
import TaskLists from "markdown-it-task-lists";
|
||||
import sup from "markdown-it-sup";
|
||||
import sub from "markdown-it-sub";
|
||||
@@ -39,7 +39,6 @@ const md: MarkdownIt = new MarkdownIt({
|
||||
}).use(TaskLists).use(sup).use(sub).use(mark);
|
||||
|
||||
const materialEase = [0.4, 0, 0.2, 1] as const;
|
||||
const smoothTransition = { duration: 0.2, ease: materialEase };
|
||||
|
||||
// Saved selection range — set in mousedown (before browser clears it), read in contextmenu handler
|
||||
let _savedSelectionRange: Range | null = null;
|
||||
@@ -361,6 +360,11 @@ function App() {
|
||||
const shortcutsModalRef = useRef<HTMLDivElement>(null);
|
||||
const aboutModalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const prefersReducedMotion = useReducedMotion();
|
||||
const smoothTransition = prefersReducedMotion
|
||||
? { duration: 0 }
|
||||
: { duration: 0.2, ease: materialEase };
|
||||
|
||||
useFocusTrap(showShortcutsModal, shortcutsModalRef);
|
||||
useFocusTrap(showAboutModal, aboutModalRef);
|
||||
|
||||
@@ -1052,7 +1056,7 @@ function App() {
|
||||
{isDraggingOver && (
|
||||
<motion.div className="drop-zone" role="region" aria-label="Drop zone for markdown files" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15, ease: materialEase }}>
|
||||
<motion.div className="drop-zone-content" initial={{ opacity: 0, scale: 0.92, y: 8 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.92, y: 8 }} transition={{ duration: 0.2, ease: materialEase }}>
|
||||
<motion.div className="drop-zone-icon-wrapper" animate={{ y: [0, -6, 0] }} transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}>
|
||||
<motion.div className="drop-zone-icon-wrapper" animate={prefersReducedMotion ? {} : { y: [0, -6, 0] }} transition={prefersReducedMotion ? { duration: 0 } : { duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}>
|
||||
<FileDown size={40} strokeWidth={1.5} />
|
||||
</motion.div>
|
||||
<div className="drop-zone-text">Drop markdown file here</div>
|
||||
@@ -1208,7 +1212,7 @@ function App() {
|
||||
<article className="markdown-content" style={{ maxWidth: contentWidth }} dangerouslySetInnerHTML={{ __html: highlightedHtml }} />
|
||||
) : (
|
||||
<motion.div className="welcome-screen" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} transition={smoothTransition}>
|
||||
<motion.div className="welcome-icon" animate={{ y: [0, -5, 0] }} transition={{ duration: 2, repeat: Infinity }}><FolderOpen size={64} /></motion.div>
|
||||
<motion.div className="welcome-icon" animate={prefersReducedMotion ? {} : { y: [0, -5, 0] }} transition={prefersReducedMotion ? { duration: 0 } : { duration: 2, repeat: Infinity }}><FolderOpen size={64} /></motion.div>
|
||||
<h1 className="welcome-title">Vesper</h1>
|
||||
<p className="welcome-subtitle">A beautiful markdown reader</p>
|
||||
<button className="welcome-button" onClick={handleOpenDialog}>Open File (Ctrl+O)</button>
|
||||
|
||||
Reference in New Issue
Block a user