hide fullscreen controls on iOS/devices without Fullscreen API

iOS Safari doesn't support requestFullscreen. Detect support and:
- Intro page: show single START button instead of FULLSCREEN/WINDOWED
- AppLayout: hide the global fullscreen button entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
main
name 2026-04-12 01:55:57 -07:00
parent 3190891d6e
commit e5552e72f7
3 changed files with 44 additions and 22 deletions

View File

@ -1,15 +1,18 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { Outlet } from 'react-router-dom' import { Outlet } from 'react-router-dom'
import { supportsFullscreen } from '../lib/platform'
import styles from './AppLayout.module.css' import styles from './AppLayout.module.css'
export function AppLayout() { export function AppLayout() {
const canFullscreen = supportsFullscreen()
const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement) const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement)
useEffect(() => { useEffect(() => {
if (!canFullscreen) return
const onChange = () => setIsFullscreen(!!document.fullscreenElement) const onChange = () => setIsFullscreen(!!document.fullscreenElement)
document.addEventListener('fullscreenchange', onChange) document.addEventListener('fullscreenchange', onChange)
return () => document.removeEventListener('fullscreenchange', onChange) return () => document.removeEventListener('fullscreenchange', onChange)
}, []) }, [canFullscreen])
const goFullscreen = useCallback(() => { const goFullscreen = useCallback(() => {
document.documentElement.requestFullscreen().catch(() => {}) document.documentElement.requestFullscreen().catch(() => {})
@ -20,7 +23,7 @@ export function AppLayout() {
<main className={styles.main}> <main className={styles.main}>
<Outlet /> <Outlet />
</main> </main>
{!isFullscreen && ( {canFullscreen && !isFullscreen && (
<button <button
type="button" type="button"
className={styles.fullscreenBtn} className={styles.fullscreenBtn}

View File

@ -0,0 +1,3 @@
export function supportsFullscreen(): boolean {
return !!document.documentElement.requestFullscreen
}

View File

@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { ROUTE_PATHS } from '../app/route-paths' import { ROUTE_PATHS } from '../app/route-paths'
import { playSound, SFX_BUTTON_DEFAULT } from '../lib/sounds' import { playSound, SFX_BUTTON_DEFAULT } from '../lib/sounds'
import { supportsFullscreen } from '../lib/platform'
import styles from './IntroPage.module.css' import styles from './IntroPage.module.css'
const SPONSORS = [ const SPONSORS = [
@ -121,26 +122,41 @@ export function IntroPage() {
{cameraReady && ( {cameraReady && (
<div className={styles.actionsRow}> <div className={styles.actionsRow}>
<button {supportsFullscreen() ? (
type="button" <>
className={`${styles.btn} ${styles.btnPrimary} font-title`} <button
onClick={() => { type="button"
playSound(SFX_BUTTON_DEFAULT) className={`${styles.btn} ${styles.btnPrimary} font-title`}
goFullscreen() onClick={() => {
}} playSound(SFX_BUTTON_DEFAULT)
> goFullscreen()
FULLSCREEN }}
</button> >
<button FULLSCREEN
type="button" </button>
className={`${styles.btn} ${styles.btnSecondary} font-title`} <button
onClick={() => { type="button"
playSound(SFX_BUTTON_DEFAULT) className={`${styles.btn} ${styles.btnSecondary} font-title`}
goWindowed() onClick={() => {
}} playSound(SFX_BUTTON_DEFAULT)
> goWindowed()
WINDOWED }}
</button> >
WINDOWED
</button>
</>
) : (
<button
type="button"
className={`${styles.btn} ${styles.btnPrimary} font-title`}
onClick={() => {
playSound(SFX_BUTTON_DEFAULT)
goWindowed()
}}
>
START
</button>
)}
</div> </div>
)} )}