diff --git a/src/hooks/useIsMobile.ts b/src/hooks/useIsMobile.ts index a2df937f..db619f28 100644 --- a/src/hooks/useIsMobile.ts +++ b/src/hooks/useIsMobile.ts @@ -28,3 +28,11 @@ export function useIsMobile(horizontal?: boolean) { isMobile, }; } + +export function useIsPWA() { + return window.matchMedia("(display-mode: standalone)").matches; +} + +export function useIsIOS() { + return /iPad|iPhone|iPod/.test(navigator.userAgent); +} diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index d0aaf9be..699bb92c 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -22,7 +22,7 @@ import { Heading1 } from "@/components/utils/Text"; import { Transition } from "@/components/utils/Transition"; import { useAuth } from "@/hooks/auth/useAuth"; import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; -import { useIsMobile } from "@/hooks/useIsMobile"; +import { useIsIOS, useIsMobile, useIsPWA } from "@/hooks/useIsMobile"; import { useSettingsState } from "@/hooks/useSettingsState"; import { AccountActionsPart } from "@/pages/parts/settings/AccountActionsPart"; import { AccountEditPart } from "@/pages/parts/settings/AccountEditPart"; @@ -60,11 +60,17 @@ function SettingsLayout(props: { const searchRef = useRef(null); const bannerSize = useBannerSize(); + const isPWA = useIsPWA(); + const isIOS = useIsIOS(); + const isIOSPWA = isIOS && isPWA; + // Navbar height is 80px (h-20) const navbarHeight = 80; // On desktop: inline with navbar (same top position + 14px adjustment) // On mobile: below navbar (navbar height + banner) - const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14; + const topOffset = isMobile + ? navbarHeight + bannerSize + (isIOSPWA ? 34 : 0) + : bannerSize + 14; return ( diff --git a/src/pages/parts/home/HeroPart.tsx b/src/pages/parts/home/HeroPart.tsx index 982dd198..6f967d00 100644 --- a/src/pages/parts/home/HeroPart.tsx +++ b/src/pages/parts/home/HeroPart.tsx @@ -6,7 +6,7 @@ import { SearchBarInput } from "@/components/form/SearchBar"; import { ThinContainer } from "@/components/layout/ThinContainer"; import { useSlashFocus } from "@/components/player/hooks/useSlashFocus"; import { HeroTitle } from "@/components/text/HeroTitle"; -import { useIsMobile } from "@/hooks/useIsMobile"; +import { useIsIOS, useIsMobile, useIsPWA } from "@/hooks/useIsMobile"; import { useIsTV } from "@/hooks/useIsTv"; import { useRandomTranslation } from "@/hooks/useRandomTranslation"; import { useSearchQuery } from "@/hooks/useSearchQuery"; @@ -55,11 +55,17 @@ export function HeroPart({ [setIsSticky], ); + const isPWA = useIsPWA(); + const isIOS = useIsIOS(); + const isIOSPWA = isIOS && isPWA; + // Navbar height is 80px (h-20) const navbarHeight = 80; - // On desktop: inline with navbar (same top position) + // On desktop: inline with navbar (same top position + 14px adjustment) // On mobile: below navbar (navbar height + banner) - const topOffset = isMobile ? navbarHeight + bannerSize : bannerSize + 14; + const topOffset = isMobile + ? navbarHeight + bannerSize + (isIOSPWA ? 34 : 0) + : bannerSize + 14; const time = getTimeOfDay(new Date()); const title = randomT(`home.titles.${time}`);