From d55958cb2107c1c51b141c9563e11003809bb703 Mon Sep 17 00:00:00 2001 From: Tsahi Matsliah Date: Mon, 2 Feb 2026 08:41:14 +0200 Subject: [PATCH 1/9] feat: improve feed layout with full-width cards and reduced gaps - Remove max-width constraints on feed container for full-width layout - Reduce default grid gap from gap-8 (32px) to gap-4 (16px) - Unify page padding to laptop:p-10 on all sides Co-authored-by: Cursor --- packages/shared/src/components/Feed.module.css | 8 ++------ packages/shared/src/components/feeds/FeedContainer.tsx | 4 +++- .../shared/src/components/layout/PageWrapperLayout.tsx | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/shared/src/components/Feed.module.css b/packages/shared/src/components/Feed.module.css index 89c074beb5..4ebfb00288 100644 --- a/packages/shared/src/components/Feed.module.css +++ b/packages/shared/src/components/Feed.module.css @@ -2,14 +2,10 @@ grid-template-columns: 100%; } .container { - @screen laptopL { - max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1)); - } + max-width: 100%; } .cards { - @screen mobileL { - max-width: calc(20rem * var(--num-cards) + var(--feed-gap) * (var(--num-cards) - 1)); - } + max-width: 100%; } .feedRow { diff --git a/packages/shared/src/components/feeds/FeedContainer.tsx b/packages/shared/src/components/feeds/FeedContainer.tsx index ea3ba62fe4..2925cbe4d2 100644 --- a/packages/shared/src/components/feeds/FeedContainer.tsx +++ b/packages/shared/src/components/feeds/FeedContainer.tsx @@ -69,7 +69,9 @@ const cardListClass = { export const getFeedGapPx = { 'gap-2': 8, 'gap-3': 12, + 'gap-4': 16, 'gap-5': 20, + 'gap-6': 24, 'gap-8': 32, 'gap-12': 48, 'gap-14': 56, @@ -87,7 +89,7 @@ export const gapClass = ({ if (isFeedLayoutList) { return ''; } - return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-8'; + return isList ? listGaps[space] ?? 'gap-2' : gridGaps[space] ?? 'gap-4'; }; const cardClass = ({ diff --git a/packages/shared/src/components/layout/PageWrapperLayout.tsx b/packages/shared/src/components/layout/PageWrapperLayout.tsx index f8abfe8a99..e10ca3c603 100644 --- a/packages/shared/src/components/layout/PageWrapperLayout.tsx +++ b/packages/shared/src/components/layout/PageWrapperLayout.tsx @@ -2,7 +2,7 @@ import type { ComponentProps, PropsWithChildren, ReactElement } from 'react'; import React from 'react'; import classNames from 'classnames'; -export const pageMainClassNames = 'tablet:p-4 laptop:px-10 laptop:py-5'; +export const pageMainClassNames = 'tablet:p-4 laptop:p-10'; export const PageWrapperLayout = ({ children, From 2a30e578f9eb184e98beb98be7ac3c753d5ae5b1 Mon Sep 17 00:00:00 2001 From: Tsahi Matsliah Date: Mon, 2 Feb 2026 12:18:32 +0200 Subject: [PATCH 2/9] fix: header, sidebar UI and animations - Make header fixed instead of sticky for YouTube-like behavior - Add hidden scrollbar that shows on hover - Improve sidebar collapse animation with synchronized 300ms transitions - Adjust sidebar padding and font sizes for consistency - Make plus icon always visible in section headers Co-authored-by: Cursor --- packages/shared/src/components/MainLayout.tsx | 4 +- .../shared/src/components/feeds/FeedNav.tsx | 43 +--------- .../components/layout/MainLayoutHeader.tsx | 4 +- .../shared/src/components/sidebar/Section.tsx | 79 +++++++++++++------ .../src/components/sidebar/SidebarDesktop.tsx | 37 ++++++--- .../src/components/sidebar/SidebarItem.tsx | 11 ++- .../components/sidebar/SidebarMenuIcon.tsx | 31 +++----- .../shared/src/components/sidebar/common.tsx | 26 +++--- .../sidebar/sections/BookmarkSection.tsx | 22 ++---- .../sidebar/sections/CustomFeedSection.tsx | 26 +++--- .../sidebar/sections/MainSection.tsx | 19 +++-- .../sidebar/sections/NetworkSection.tsx | 22 ++---- packages/shared/src/styles/base.css | 21 ++++- 13 files changed, 178 insertions(+), 167 deletions(-) diff --git a/packages/shared/src/components/MainLayout.tsx b/packages/shared/src/components/MainLayout.tsx index 7c130bee0e..5db49bce10 100644 --- a/packages/shared/src/components/MainLayout.tsx +++ b/packages/shared/src/components/MainLayout.tsx @@ -196,14 +196,14 @@ function MainLayoutComponent({ />
{isAuthReady && showSidebar && ( diff --git a/packages/shared/src/components/feeds/FeedNav.tsx b/packages/shared/src/components/feeds/FeedNav.tsx index 81a480c03d..e52421fd12 100644 --- a/packages/shared/src/components/feeds/FeedNav.tsx +++ b/packages/shared/src/components/feeds/FeedNav.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; import type { ReactElement } from 'react'; -import React, { useMemo, useState, useTransition } from 'react'; +import React, { useMemo } from 'react'; import { useRouter } from 'next/router'; import { Tab, TabContainer } from '../tabs/TabContainer'; import { useActiveFeedNameContext } from '../../contexts'; import useActiveNav from '../../hooks/useActiveNav'; -import { useEventListener, useFeeds, useViewSize, ViewSize } from '../../hooks'; +import { useFeeds, useViewSize, ViewSize } from '../../hooks'; import usePersistentContext from '../../hooks/usePersistentContext'; import { algorithmsList, @@ -31,7 +31,6 @@ import { SharedFeedPage } from '../utilities'; import PlusMobileEntryBanner from '../banners/PlusMobileEntryBanner'; import { TargetType } from '../../lib/log'; import usePlusEntry from '../../hooks/usePlusEntry'; -import { useAlertsContext } from '../../contexts/AlertContext'; enum FeedNavTab { ForYou = 'For you', @@ -52,17 +51,12 @@ const StickyNavIconWrapper = classed( 'sticky flex h-14 pt-1 -translate-y-16 items-center justify-end bg-gradient-to-r from-transparent via-background-default via-40% to-background-default pr-4', ); -const MIN_SCROLL_BEFORE_HIDING = 60; - function FeedNav(): ReactElement { const router = useRouter(); - const [, startTransition] = useTransition(); - const [isHeaderVisible, setIsHeaderVisible] = useState(true); const { feedName } = useActiveFeedNameContext(); const { sortingEnabled } = useSettingsContext(); const { isSortableFeed } = useFeedName({ feedName }); const { home, bookmarks } = useActiveNav(feedName); - const { alerts } = useAlertsContext(); const isMobile = useViewSize(ViewSize.MobileL); const [selectedAlgo, setSelectedAlgo] = usePersistentContext( DEFAULT_ALGORITHM_KEY, @@ -82,8 +76,6 @@ function FeedNav(): ReactElement { isMobile && ((sortingEnabled && isSortableFeed) || feedName === SharedFeedPage.Custom); - const hasOpportunityAlert = !!alerts.opportunityId; - const urlToTab: Record = useMemo(() => { const customFeeds = sortedFeeds.reduce((acc, { node: feed }) => { const isEditingFeed = @@ -127,45 +119,16 @@ function FeedNav(): ReactElement { isCustomDefaultFeed, ]); - const previousScrollY = React.useRef(0); - - useEventListener(globalThis, 'scroll', () => { - // when scrolled down we should hide the header - // when scrolled up, we should bring it back - const { scrollY } = window; - const shouldHeaderBeVisible = scrollY < previousScrollY.current; - - previousScrollY.current = scrollY; - - if (shouldHeaderBeVisible === isHeaderVisible) { - return; - } - - if (!shouldHeaderBeVisible && scrollY < MIN_SCROLL_BEFORE_HIDING) { - return; - } - - startTransition(() => { - setIsHeaderVisible(shouldHeaderBeVisible); - }); - }); const shouldRenderNav = home || (isMobile && bookmarks); if (!shouldRenderNav || router?.pathname?.startsWith('/posts/[id]')) { return null; } - const headerTransitionClasses = - isMobile && hasOpportunityAlert - ? '-translate-y-[7.5rem] duration-[800ms]' - : '-translate-y-26 duration-[800ms]'; - return (
{isMobile && } diff --git a/packages/shared/src/components/layout/MainLayoutHeader.tsx b/packages/shared/src/components/layout/MainLayoutHeader.tsx index ecb3331fe5..e2c0a30466 100644 --- a/packages/shared/src/components/layout/MainLayoutHeader.tsx +++ b/packages/shared/src/components/layout/MainLayoutHeader.tsx @@ -76,7 +76,7 @@ function MainLayoutHeader({ if (loadedSettings && !isLaptop) { if (isSearchPage) { return ( -
+
{!isSearch && }
@@ -93,7 +93,7 @@ function MainLayoutHeader({ return (
{ @@ -24,6 +24,7 @@ interface SectionProps extends SectionCommonProps { items: SidebarMenuItem[]; isItemsButton: boolean; isAlwaysOpenOnMobile?: boolean; + onAdd?: () => void; } export function Section({ @@ -36,6 +37,7 @@ export function Section({ className, flag, isAlwaysOpenOnMobile, + onAdd, }: SectionProps): ReactElement { const { flags, updateFlag } = useSettingsContext(); const { sidebarRendered } = useSidebarRendered(); @@ -48,31 +50,63 @@ export function Section({ isVisible.current = !isVisible.current; }; - return ( - - {title && ( - + + {onAdd && ( + + )} +
+ ); + + return ( + + {title && ( + + {sidebarExpanded ? ( + headerContent + ) : ( + +
+ + )} )} - {(isVisible.current || shouldAlwaysBeVisible) && - items.map((item) => ( +
+ {items.map((item) => ( ))} +
); } diff --git a/packages/shared/src/components/sidebar/SidebarDesktop.tsx b/packages/shared/src/components/sidebar/SidebarDesktop.tsx index 38851c1850..4fdef24aa5 100644 --- a/packages/shared/src/components/sidebar/SidebarDesktop.tsx +++ b/packages/shared/src/components/sidebar/SidebarDesktop.tsx @@ -10,7 +10,7 @@ import { CustomFeedSection } from './sections/CustomFeedSection'; import { DiscoverSection } from './sections/DiscoverSection'; import { SidebarMenuIcon } from './SidebarMenuIcon'; import { CreatePostButton } from '../post/write'; -import { ButtonSize } from '../buttons/Button'; +import { ButtonSize, ButtonVariant } from '../buttons/Button'; import { BookmarkSection } from './sections/BookmarkSection'; import { NetworkSection } from './sections/NetworkSection'; @@ -57,38 +57,53 @@ export const SidebarDesktop = ({
+ + {/* Primary Navigation - Always visible */} + + {/* User Content Sections */} + + {/* Discovery Section */} + { return (
- Menu - + - {onAdd && ( + {addHref && ( + + + + + + )} + {!addHref && onAdd && (