Pin reader and tab bars to their own compositing layer
Build and Push Docker Image / build-and-push (push) Successful in 5m1s

iOS Safari/PWA can strand fixed and sticky bars mid-screen after the
toolbar collapses or the page rubber-bands. Add a .fixed-bar class
(translateZ(0), will-change: transform) to the reader top/bottom bars
and the phone tab bar.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-27 15:13:18 -04:00
1 parent 593769b4f6
commit d9891ff9fe
3 files changed
+11 -3

No files matched your search

+2 -2
View File
@@ -8,7 +8,7 @@ const iconBtn = 'flex h-9 w-9 items-center justify-center rounded-lg text-[var(-
export function TopBar({ title, onBack, onOpenNavigator, onSearch, onBookmarks, bookmarkCount, onHighlights, highlightCount, onCompare, compareActive, onSettings, onDoneDrawing }) { export function TopBar({ title, onBack, onOpenNavigator, onSearch, onBookmarks, bookmarkCount, onHighlights, highlightCount, onCompare, compareActive, onSettings, onDoneDrawing }) {
return ( return (
<div className="safe-top sticky top-0 z-30 border-b border-[var(--r-line)] bg-[var(--r-bar)] font-sans"> <div className="fixed-bar safe-top sticky top-0 z-30 border-b border-[var(--r-line)] bg-[var(--r-bar)] font-sans">
<div className="mx-auto flex h-12 max-w-4xl items-center gap-1 px-2"> <div className="mx-auto flex h-12 max-w-4xl items-center gap-1 px-2">
<HomeButton onClick={onBack} /> <HomeButton onClick={onBack} />
<button type="button" onClick={onOpenNavigator} <button type="button" onClick={onOpenNavigator}
@@ -44,7 +44,7 @@ export function BottomBar({
}) { }) {
const active = 'text-[var(--r-accent)]'; const active = 'text-[var(--r-accent)]';
return ( return (
<div className="fixed inset-x-0 bottom-0 z-30 border-t border-[var(--r-line)] bg-[var(--r-bar)] pb-[env(safe-area-inset-bottom)] font-sans"> <div className="fixed-bar fixed inset-x-0 bottom-0 z-30 border-t border-[var(--r-line)] bg-[var(--r-bar)] pb-[env(safe-area-inset-bottom)] font-sans">
<div className="mx-auto flex h-14 max-w-4xl items-center gap-0.5 px-2"> <div className="mx-auto flex h-14 max-w-4xl items-center gap-0.5 px-2">
<button type="button" onClick={onPrev} disabled={prevDisabled} className={iconBtn} aria-label="Previous chapter" data-tip="Previous chapter (←)"><ChevronLeft /></button> <button type="button" onClick={onPrev} disabled={prevDisabled} className={iconBtn} aria-label="Previous chapter" data-tip="Previous chapter (←)"><ChevronLeft /></button>
<button type="button" onClick={onAudio} className={`${iconBtn} ${audioActive ? active : ''}`} aria-label="Listen" data-tip="Listen to this chapter"><HeadphonesIcon /></button> <button type="button" onClick={onAudio} className={`${iconBtn} ${audioActive ? active : ''}`} aria-label="Listen" data-tip="Listen to this chapter"><HeadphonesIcon /></button>
+1 -1
View File
@@ -132,7 +132,7 @@ export default function PageShell({ title, onBack, actions, active = null, maxWi
{phone && ( {phone && (
<> <>
<div aria-hidden="true" className="h-[calc(5rem+env(safe-area-inset-bottom,0px))]" /> <div aria-hidden="true" className="h-[calc(5rem+env(safe-area-inset-bottom,0px))]" />
<nav aria-label="Main" className="fixed inset-x-0 bottom-0 z-30 grid grid-cols-5 border-t border-[var(--r-line)] bg-[var(--r-bar)] pb-[env(safe-area-inset-bottom)]"> <nav aria-label="Main" className="fixed-bar fixed inset-x-0 bottom-0 z-30 grid grid-cols-5 border-t border-[var(--r-line)] bg-[var(--r-bar)] pb-[env(safe-area-inset-bottom)]">
{NAV.map((n) => ( {NAV.map((n) => (
<button key={n.id} type="button" onClick={go[n.id]} aria-current={active === n.id ? 'page' : undefined} aria-label={n.label} <button key={n.id} type="button" onClick={go[n.id]} aria-current={active === n.id ? 'page' : undefined} aria-label={n.label}
className={`flex min-h-[52px] flex-col items-center justify-center gap-0.5 text-[11px] ${active === n.id ? 'text-[var(--r-text)]' : 'text-[var(--r-sub)]'}`}> className={`flex min-h-[52px] flex-col items-center justify-center gap-0.5 text-[11px] ${active === n.id ? 'text-[var(--r-text)]' : 'text-[var(--r-sub)]'}`}>
+8
View File
@@ -79,6 +79,14 @@ input,
/* Top bars leave room for the status bar / notch when the app is installed or full-screen /* Top bars leave room for the status bar / notch when the app is installed or full-screen
(env() is 0 in an ordinary browser tab). */ (env() is 0 in an ordinary browser tab). */
/* iOS Safari/PWA can leave fixed and sticky bars stranded mid-screen after the toolbar collapses or the page
rubber-bands. Giving them their own compositing layer keeps them pinned to the viewport edge. */
.fixed-bar {
transform: translateZ(0);
-webkit-transform: translateZ(0);
will-change: transform;
}
.safe-top { .safe-top {
padding-top: env(safe-area-inset-top, 0px); padding-top: env(safe-area-inset-top, 0px);
} }