FesaComponents
Más módulos

Motion y hooks

Animaciones sin dependencias que respetan reduced-motion, y los hooks base del ecosistema.

import { AnimatedSection, AnimatedCounter, BackToTop } from "@fesa/components/motion";
import { useInView, useIsMobile, useHydrated } from "@fesa/components/hooks";

Todo CSS puro + requestAnimationFrame — cero librerías de animación en el bundle — y respetando prefers-reduced-motion.

AnimatedSection

Revela su contenido al entrar en viewport.

<AnimatedSection variant="fade-up" delay={100}>
  <FeatureCard />
</AnimatedSection>
PropTipoDefaultDescripción
variant'fade' | 'fade-up' | 'fade-down' | 'fade-left' | 'fade-right' | 'scale' | 'slide-up''fade-up'Tipo de entrada
delaynumber0Retardo en ms (para escalonar listas: delay={index * 100})
thresholdnumberCuánto del elemento debe verse para disparar
asReact.ElementType'div'Elemento contenedor

StaggerChildren — variante que anima a los hijos en cascada con staggerDelay.

AnimatedCounter

Contador animado al entrar en viewport; parsea prefijo/sufijo/decimales del string ("+1.500 clientes", "99,9%").

<AnimatedCounter value="+1.500" className="text-3xl font-bold" />

BackToTop

Botón flotante de volver arriba, con throttle por rAF (aparece tras scrollear). Etiqueta accesible por prop.

Hooks

useInView

IntersectionObserver con ref-callback de React 19 (cleanup automático).

const { ref, isInView, hasBeenInView } = useInView({ triggerOnce: true, threshold: 0.2 });

Antes existía en cuatro copias byte-idénticas — es el símbolo de por qué existe esta librería.

useIsMobile

Breakpoint (< 768px) vía useSyncExternalStore sobre matchMedia: sin flash de SSR, sin useEffect, sin re-render extra.

useHydrated

true tras la hidratación — para gates de contenido client-only sin parpadeo.

useReducedMotionStore

La preferencia prefers-reduced-motion como store de zustand, compartida entre componentes.

En esta pagina