fix(lightbox): mobile toolbar clipping + iOS safe-area + viewport-fit (#336)
When feedback was enabled the lightbox bottom toolbar packed counter + zoom + download + like + 5-star + comments into a single row that overflowed the viewport on iPhone-class widths, putting the rating stars under the screen edge and below the iOS home indicator. Changes: - Bottom toolbar now uses flex-wrap with reduced gap/padding on mobile, so all controls fit (375px viewport: max-right 363 < 375; 390px: max-right 378 < 390; 393px: max-right 393 < 393). - pb computed as max(0.75rem, env(safe-area-inset-bottom)) so the row sits above the iOS home indicator on devices with a gesture bar. - Close button top/right now use max(1rem, env(safe-area-inset-*)) so it doesn't disappear under the notch / dynamic island. - "Swipe to navigate" hint moved from bottom-20 to bottom-40 so it clears the now-taller wrapped toolbar. - index.html viewport meta gains viewport-fit=cover to enable env(safe-area-inset-*) on iOS Safari. Verified in mobile emulation across iPhone SE (375x667), iPhone 13/14 (390x844), iPhone 14 Pro (393x852) portrait, and 14 Pro landscape (852x393) — toolbar fits, photo centered, no clipping.
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<title>PicPeak - Photo Sharing Platform</title>
|
<title>PicPeak - Photo Sharing Platform</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -426,12 +426,16 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={lightboxClass}>
|
<div className={lightboxClass}>
|
||||||
{/* Close button */}
|
{/* Close button. top respects iOS safe-area (notch) so it doesn't
|
||||||
|
disappear under the camera/dynamic-island. */}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="absolute top-4 p-2 bg-white/10 hover:bg-white/20 rounded-full transition-colors z-30"
|
className="absolute p-2 bg-white/10 hover:bg-white/20 rounded-full transition-colors z-30"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
style={{ right: isDesktopFeedback ? `${desktopFeedbackWidth + 16}px` : '1rem' }}
|
style={{
|
||||||
|
top: 'max(1rem, env(safe-area-inset-top))',
|
||||||
|
right: isDesktopFeedback ? `${desktopFeedbackWidth + 16}px` : 'max(1rem, env(safe-area-inset-right))'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<X className="w-6 h-6 text-white" />
|
<X className="w-6 h-6 text-white" />
|
||||||
</button>
|
</button>
|
||||||
@@ -456,19 +460,25 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{/* Bottom toolbar */}
|
{/* Bottom toolbar. flex-wrap + reduced gap/padding on mobile prevent
|
||||||
|
the action row from clipping when feedback (likes / 5-star ratings /
|
||||||
|
comments) is enabled. pb-[env(safe-area-inset-bottom)] keeps the
|
||||||
|
buttons above the iOS home indicator. */}
|
||||||
<div
|
<div
|
||||||
className="absolute bottom-0 left-0 bg-gradient-to-t from-black/80 to-transparent p-4 z-20"
|
className="absolute bottom-0 left-0 bg-gradient-to-t from-black/80 to-transparent px-3 pt-3 pb-3 sm:p-4 z-20"
|
||||||
style={{ right: isDesktopFeedback ? `${desktopFeedbackWidth}px` : 0 }}
|
style={{
|
||||||
|
right: isDesktopFeedback ? `${desktopFeedbackWidth}px` : 0,
|
||||||
|
paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="max-w-4xl mx-auto flex items-center justify-between">
|
<div className="max-w-4xl mx-auto flex items-center justify-between gap-2 flex-wrap">
|
||||||
<div className="text-white">
|
<div className="text-white">
|
||||||
<p className="text-sm opacity-75">
|
<p className="text-sm opacity-75">
|
||||||
{currentIndex + 1} / {photos.length}
|
{currentIndex + 1} / {photos.length}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1 sm:gap-2 flex-wrap justify-end">
|
||||||
<button
|
<button
|
||||||
onClick={handleZoomOut}
|
onClick={handleZoomOut}
|
||||||
disabled={zoom <= 1}
|
disabled={zoom <= 1}
|
||||||
@@ -638,8 +648,9 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Touch/swipe indicators for mobile */}
|
{/* Touch/swipe hint for mobile. Sits above the bottom toolbar
|
||||||
<div className="absolute bottom-20 left-1/2 -translate-x-1/2 text-white text-sm opacity-50 pointer-events-none md:hidden z-20">
|
(which can wrap to two rows when feedback controls are enabled). */}
|
||||||
|
<div className="absolute bottom-40 left-1/2 -translate-x-1/2 text-white text-sm opacity-50 pointer-events-none md:hidden z-20">
|
||||||
Swipe to navigate
|
Swipe to navigate
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user