a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

move mobile nav to the bottom

celine (May 7, 2026, 2:20 PM EDT) 9d93e76e 0ce160a8

+53 -41
+7
app/globals.css
··· 545 545 padding-bottom: var(--safe-padding-bottom); 546 546 } 547 547 548 + .pwa-padding-x { 549 + @media (display-mode: standalone) { 550 + margin-left: 1rem; 551 + margin-right: 1rem; 552 + } 553 + } 554 + 548 555 /* animation for atmosphere logo carousel */ 549 556 @keyframes logo-scroll { 550 557 0% {
+38 -33
components/ActionBar/MobileNavigation.tsx
··· 14 14 search?: React.ReactNode; 15 15 mobileActions?: React.ReactNode; 16 16 pageTitle: string; 17 + hiddenOnScroll?: boolean; 17 18 }) => { 18 19 let [state, setState] = useState<"search" | "default">("default"); 19 20 let [hidden, setHidden] = useState(false); 20 - let [sticky, setSticky] = useState(false); 21 - let [scrollPos, setScrollPos] = useState(0); 21 + let [distFromBottom, setDistFromBottom] = useState(1000); 22 22 let lastScrollY = useRef(0); 23 - let stickyRef = useRef(false); 24 23 let cardBorderHidden = useCardBorderHidden(); 24 + let hiddenOnScroll = props.hiddenOnScroll; 25 25 26 26 useEffect(() => { 27 27 const homeContent = document.getElementById("home-content"); 28 28 if (!homeContent) return; 29 29 30 + const computeDist = () => 31 + Math.max( 32 + 0, 33 + homeContent.scrollHeight - 34 + homeContent.clientHeight - 35 + homeContent.scrollTop, 36 + ); 37 + 38 + setDistFromBottom(computeDist()); 39 + 30 40 const handleScroll = () => { 31 41 const currentScrollY = homeContent.scrollTop; 42 + const dist = computeDist(); 32 43 const delta = currentScrollY - lastScrollY.current; 33 44 lastScrollY.current = currentScrollY; 34 - setScrollPos(currentScrollY); 45 + setDistFromBottom(dist); 35 46 36 - if (currentScrollY === 0) { 37 - stickyRef.current = false; 38 - setSticky(false); 47 + if (!hiddenOnScroll) { 48 + setHidden(false); 49 + return; 50 + } 51 + 52 + if (dist <= 0) { 39 53 setHidden(false); 40 54 return; 41 55 } 42 56 43 57 if (delta > 8) { 44 - if (stickyRef.current) setHidden(true); 45 - } else if (delta < -1 && currentScrollY > 0) { 46 - if (!stickyRef.current) { 47 - stickyRef.current = true; 48 - setSticky(true); 49 - setHidden(true); 50 - requestAnimationFrame(() => { 51 - requestAnimationFrame(() => setHidden(false)); 52 - }); 53 - } else { 54 - setHidden(false); 55 - } 58 + setHidden(true); 59 + } else if (delta < -1) { 60 + setHidden(false); 56 61 } 57 62 }; 58 63 59 64 homeContent.addEventListener("scroll", handleScroll, { passive: true }); 60 65 return () => homeContent.removeEventListener("scroll", handleScroll); 61 - }, []); 66 + }, [hiddenOnScroll]); 62 67 63 68 let headerBGColor = cardBorderHidden ? "var(--bg-leaflet)" : "var(--bg-page)"; 69 + let atBottom = distFromBottom < 20; 64 70 65 71 return ( 66 72 <MediaContents 67 73 mobile={true} 68 - className={`mobilePageHeader z-20 w-full transition-transform duration-200 ${sticky ? "sticky top-0" : ""} ${sticky && hidden ? "-translate-y-[80px] " : ""}`} 74 + className={`mobilePageHeader z-20 fixed left-0 bottom-6 right-0 transition-transform duration-200 ${hidden ? "translate-y-[120px]" : ""}`} 75 + style={{ bottom: "var(--safe-padding-bottom)" }} 69 76 > 70 77 <div 71 78 style={ 72 - sticky && scrollPos < 20 79 + atBottom 73 80 ? { 74 - paddingLeft: `calc(${scrollPos / 20}*8px)`, 75 - paddingRight: `calc(${scrollPos / 20}*8px)`, 81 + paddingLeft: `calc(${distFromBottom / 20}*8px + 12px)`, 82 + paddingRight: `calc(${distFromBottom / 20}*8px + 12px)`, 76 83 } 77 - : !sticky 78 - ? { paddingLeft: 0, paddingRight: 0 } 79 - : { paddingLeft: "8px", paddingRight: "8px" } 84 + : { paddingLeft: "20px", paddingRight: "20px" } 80 85 } 81 86 > 82 87 <div 83 - className={`mobilePageHeaderContent rounded-lg text-secondary flex gap-2 border justify-between items-center py-1 w-full ${cardBorderHidden && scrollPos < 20 ? "border-transparent" : " border-border-light"}`} 88 + className={`mobilePageHeaderContent pwa-padding-x rounded-lg text-secondary flex gap-2 border justify-between items-center py-1 w-full ${cardBorderHidden && atBottom ? "border-transparent " : " border-border-light"}`} 84 89 style={ 85 - scrollPos < 20 90 + atBottom 86 91 ? { 87 92 paddingLeft: cardBorderHidden 88 - ? `calc(${scrollPos / 20}*8px)` 93 + ? `calc(${distFromBottom / 20}*8px)` 89 94 : "8px", 90 95 paddingRight: cardBorderHidden 91 - ? `calc(${scrollPos / 20}*8px)` 96 + ? `calc(${distFromBottom / 20}*8px)` 92 97 : "8px", 93 98 backgroundColor: !cardBorderHidden 94 - ? `rgba(${headerBGColor}, ${scrollPos / 60 + 0.75})` 95 - : `rgba(${headerBGColor}, ${scrollPos / 20})`, 99 + ? `rgba(${headerBGColor}, ${distFromBottom / 60 + 0.75})` 100 + : `rgba(${headerBGColor}, ${distFromBottom / 20})`, 96 101 } 97 102 : { 98 103 paddingLeft: "8px",
+7 -7
components/PageLayouts/DashboardPageLayout.tsx
··· 16 16 17 17 return ( 18 18 <div 19 - className={`dashboardPage w-full h-full flex flex-col gap-2 relative overflow-y-scroll pt-3 pb-3 px-3 sm:pt-6 sm:pb-6 sm:pl-8 sm:pr-4`} 19 + className={`dashboardPage w-full h-full flex flex-col gap-2 relative overflow-y-scroll pt-3 pb-[calc(var(--safe-padding-bottom)+56px)] px-3 sm:pt-6 sm:pb-6 sm:pl-8 sm:pr-4`} 20 20 ref={ref} 21 21 id="home-content" 22 22 > 23 - <MobileNavigation 24 - pageTitle={props.pageTitle} 25 - mobileActions={props.mobileActions} 26 - search={props.search} 27 - /> 28 - 29 23 {props.showHeader && ( 30 24 <PageHeader> 31 25 <div className={`sm:block ${props.publication && "hidden"} grow`}> ··· 34 28 </PageHeader> 35 29 )} 36 30 {props.children} 31 + 32 + <MobileNavigation 33 + pageTitle={props.pageTitle} 34 + mobileActions={props.mobileActions} 35 + search={props.search} 36 + /> 37 37 </div> 38 38 ); 39 39 }
+1 -1
app/[leaflet_id]/actions/PublishButton.tsx
··· 55 55 labelOnMobile 56 56 className="sm:w-full! w-fit!" 57 57 icon={<PublishSmall className="shrink-0" />} 58 - label={"Publish!!!"} 58 + label={"Publish!"} 59 59 onClick={() => { 60 60 router.push(`/${params.leaflet_id}/publish`); 61 61 }}