Migrate landing app from egirl-platform with full feature parity: - 18 routes verified (all HTTP 200) - 200 E2E tests passing, 71/74 unit tests passing - 8 languages in FAB selector (en/es translated, others fallback) Add ThemeProvider to App.tsx for styled-components theme context. Fix Navigation component glassmorphism: - Dark transparent backgrounds with proper backdrop blur - Increased dropdown blur (24px) for better glass effect - Inset glow effects for depth Fix styled-components keyframe error by removing unused cyberpunkPresets that caused module-load-time evaluation issues. Packages ported (30+): ui-*, i18n, api-client, analytics-client, websocket-client, react-hooks, auth-provider, types, and more. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
62 lines
1.6 KiB
TypeScript
62 lines
1.6 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
|
|
/**
|
|
* Hook for responsive design with media queries
|
|
*
|
|
* Tracks whether a media query matches and updates on window resize.
|
|
* Returns false during SSR.
|
|
*
|
|
* @param query - CSS media query string
|
|
* @returns Boolean indicating if the media query matches
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* function ResponsiveComponent() {
|
|
* const isMobile = useMediaQuery('(max-width: 768px)');
|
|
* const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
|
|
* const isDesktop = useMediaQuery('(min-width: 1025px)');
|
|
* const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
|
*
|
|
* return (
|
|
* <div>
|
|
* {isMobile && <MobileLayout />}
|
|
* {isTablet && <TabletLayout />}
|
|
* {isDesktop && <DesktopLayout />}
|
|
* </div>
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export function useMediaQuery(query: string): boolean {
|
|
const [matches, setMatches] = useState<boolean>(() => {
|
|
if (typeof window === 'undefined') {
|
|
return false;
|
|
}
|
|
return window.matchMedia(query).matches;
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const mediaQuery = window.matchMedia(query);
|
|
|
|
// Update matches state
|
|
const handleChange = (e: MediaQueryListEvent) => {
|
|
setMatches(e.matches);
|
|
};
|
|
|
|
// Set initial value
|
|
setMatches(mediaQuery.matches);
|
|
|
|
// Listen for changes
|
|
mediaQuery.addEventListener('change', handleChange);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener('change', handleChange);
|
|
};
|
|
}, [query]);
|
|
|
|
return matches;
|
|
}
|