feat: add command palette, accessibility, scroll animations, and keyboard navigation
Implements COMP-139 (command palette), COMP-140 (accessibility), COMP-141 (scroll animations), COMP-145 (keyboard navigation) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
52
apps/web/src/hooks/use-scroll-animate.ts
Normal file
52
apps/web/src/hooks/use-scroll-animate.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
interface UseScrollAnimateOptions {
|
||||
threshold?: number;
|
||||
rootMargin?: string;
|
||||
once?: boolean;
|
||||
}
|
||||
|
||||
export function useScrollAnimate<T extends HTMLElement = HTMLDivElement>({
|
||||
threshold = 0.1,
|
||||
rootMargin = "0px 0px -60px 0px",
|
||||
once = true,
|
||||
}: UseScrollAnimateOptions = {}) {
|
||||
const ref = useRef<T>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
|
||||
const prefersReducedMotion = window.matchMedia(
|
||||
"(prefers-reduced-motion: reduce)"
|
||||
).matches;
|
||||
|
||||
if (prefersReducedMotion) {
|
||||
el.setAttribute("data-animate", "visible");
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.setAttribute("data-animate", "visible");
|
||||
if (once) {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
} else if (!once) {
|
||||
entry.target.setAttribute("data-animate", "hidden");
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold, rootMargin }
|
||||
);
|
||||
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, [threshold, rootMargin, once]);
|
||||
|
||||
return ref;
|
||||
}
|
||||
Reference in New Issue
Block a user