feat(mobile): the phone shell - #6767
Open
tsahimatsliah wants to merge 24 commits into
Open
tsahimatsliah wants to merge 24 commits into
tsahimatsliah wants to merge 24 commits into
Conversation
Step 0 of the mobile shell work (Storybook, Mobile UX / 10. The build): the fixes that need no design decision, shipped together. TabContainer's swipe decided from the total movement at touch end with a 40px threshold, so a thumb scrolling at a slight angle switched the Highlights channel. The swipe now locks its axis on the first 10px, commits past 56px inside a 27 degree cone or on a fast flick past 32px, and the surface takes touch-action: pan-y. The numbers live in shell/constants.ts. FooterNavBarLayout waited for the window load event before rendering the bar; it renders once hydrated. The footer tabs fire a click with the footer as target and the tab in extra, and Activity fires click notification icon with NotificationTarget.Footer, which existed and was never used. HighlightCardOptions and SquadOptionsButton were hidden until hover on every device, so their actions did not exist on phones; they use the laptop-and-mouse-only hover class the post cards use. The Explore period drawer gets a Close, the notification menu icon stops being rotated, Unfollow uses the remove-user icon, "Manade Ad" is "Manage ad" and the settings menu labels match their page titles. shell.css carries the shell's two curves and four durations and the press class (scale 0.96, 150ms) applied to the footer tabs, the plus button, the header controls, the feed chips and the post bar; the post titles balance. Two pre-existing strict type errors in the touched files are fixed so the strict guard passes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Step 1 of the mobile shell work (Storybook, Mobile UX / 10. The build, PRs 1.1 to 1.8 together): the shell under 656px. Tablet, laptop, layout v2 and the extension keep the code they run today. The floating cluster replaces the docked footer: Home, Explore, Squads and Activity on the flat blur material, the Create square beside it opening the composer, the brand bubble on Activity, the shrink on scroll, the lit tab from the root that owns the URL, visitors sent to sign up from Home, Activity and Create. It is in the server HTML so it paints with the page. The top block replaces the phone header: the brand row on Home (logo, streak, the Plus door for free members, the square avatar opening the new You page), a name row on Explore, Squads and Activity, and back, title and actions on every leaf, fed by GoBackHeaderMobile and the page headers that drew their own bars. It is a fixed overlay that hides as one piece while reading down and returns on any short scroll up; content keeps a constant top padding from --shell-top. One scroll reader drives block and cluster. Drawer becomes the sheet (travel curve in, faster out, a grabber, drag to dismiss), DropdownMenu renders as a modal bottom sheet on phones, the gated sign-up closes with an X and names what the action gets you, and people's pictures are rounded squares where they were circles. Deleted: MobileFooterNavbar, FooterNavBarTabs, FooterNavBarItem, FooterPlusButton, MobileFeedActions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # packages/shared/src/components/MainFeedLayout.tsx # packages/shared/src/components/feeds/FeedNav.spec.tsx # packages/shared/src/components/feeds/FeedNav.tsx # packages/shared/src/components/feeds/MobileFeedActions.tsx # packages/shared/src/components/post/GoBackHeaderMobile.tsx # packages/shared/src/components/profile/Header.tsx
On review Tsahi asked for the header's rows in the same PR so no page shows a second header. ShellRow carries the three weights of the 28px chip (segments, chips, a menu label) and a sheet of choices. Home's row is For you, Happening now with its channel sheet, Following, the member's custom feeds and the plus; the Highlights page shows the channel on its own line instead of its title and tab strip. Explore's row is the search field and the places, and its sort is a text menu on the feed line instead of the sticky tab strip. Squads root puts its categories in the block, Activity its types, Bookmarks its lists as segments under a title; Tags, Sources, Leaderboard and History take their names in the block and lose their in-page navbars. Tablet keeps every old row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On review every phone page still kept a button row of its own under the block: New Squad under the Squads title, the channel line and copy link under Happening now, Search and the menu in the squad hero, Follow, Award and the menu twice on a profile, Sort and Share in the Bookmarks header, the full-width Filters on search results. They move into the block. ShellPage now portals its actions into a slot the block exposes instead of passing the nodes through context, because the block renders outside the page's providers and the squad menu reads SquadPageContext. The hero copies are gated by viewport in JS rather than CSS so each label exists once in the DOM, which is also what the page tests query. The streak was never hidden: the local proxy's fake member had no streak response. The proxy stubs one now so the shots show it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A route-by-route pass at 393px over the member app found the pages that still drew a header row of their own under the block: Sources (a full-width Suggest new source button), Jobs, the Core wallet, the briefings, squad moderation, post analytics, the gear rankings, the best-of archives (breadcrumbs and a copy link row), the experience lists (a second back arrow), the squad form (Cancel and Create under a back square), the leaderboard detail (no title at all), the tag explore page (no title), and the composer page (the block over the composer's own chrome). Each now registers its title and actions with the block on a phone and keeps its row for tablet and up; the composer hides the block. FormWrapper gets an inBlock mode for page forms so the squad form's submit rides in the block and the back square replaces Cancel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # packages/webapp/pages/sources/index.tsx # packages/webapp/pages/users.tsx
# Conflicts: # packages/shared/src/components/MainLayout.tsx
The 72% rest opacity sat on the whole tab, so the unread bubble dimmed with the glyph and read as disabled next to the lit tab. The dim moves onto the glyph alone; the bubble is the same colour in both states. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
BootProvider.spec "refetch boot on focus within the interval when another tab changed the Plus status" counted three boot requests instead of two on CI; it passes alone and this branch does not touch contexts/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…finger The lit tab now has a soft pill behind it (the Instagram reference Tsahi gave), sliding on the travel curve when the root changes. Press and hold the bar, move along it, and the pill follows the finger with the glyph under it lit; lifting selects that tab. A 6px travel threshold keeps a tap a tap, and the click the browser fires for the release is swallowed so the drag's choice is not navigated twice. No glass: the flat material only. The Squads root also loses most of its top padding on phones, since the chips moved into the block. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Suggest new source, New Squad and Job preferences need an account, so their squares in the block render for members only; visitors keep Log in and Open app in the row and nothing they cannot use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A finger on the bar scales the whole bar to 1.04 from its bottom edge for as long as it stays down (150ms ease-out in, 220ms on the no-bounce curve out), the lift Instagram's and iOS 26's bars give; the per-tab 0.96 press goes, since the bar now answers as one piece. The pill slides on the same no-bounce curve, per chapter 9l. Reduced motion drops the lift. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The finger is measured on screen while the bar is scaled to 1.04, but the pill moves in the bar's own pixels, so at the far right it ran past the edge by the scale difference. The finger now maps through the layout width, so both edges clamp the same. Also: a color-scheme meta keyed to the theme beside theme-color, and a valid apple-mobile-web-app-status-bar-style value (black or default) in place of the hex it had, so Safari's chrome and the standalone status bar follow the app's background. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Chris's review: a fast diagonal flick that happened to lock sideways in its first 10px could still change the channel through the velocity path, which skipped the cone. The cone now gates both paths, with a spec for that flick; the swipe specs stamp event.timeStamp (react-swipeable's clock) so the distance and speed paths are each tested on their own. Also from the review: the swipe surface keeps pinch zoom (touch-pinch-zoom); the footer tabs log their own target (mobile footer nav) with whether the tap was a member's, so they separate from the app footer's events; the shell constants and stylesheet carry only what this PR consumes (the motion values come with their consumers in the shell PR), and the explanatory blocks moved to the PR description. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings Chris's review fixes: the swipe cone on both paths, the stamped swipe specs, pinch zoom on the swipe surface, and the footer tabs' own analytics target. The bar logs the same target (mobile footer nav) with logged_in in the extra. The shell constants and stylesheet keep this branch's versions, since their consumers live here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 1 of the mobile shell work (Storybook
Mobile UX / 10. The build, PRs 1.1 to 1.8 delivered together as agreed): the phone shell under 656px. Tablet, laptop, layout v2 and the extension keep the code they run today.Built on top of #6765 (the four fixes); merge that one first and this diff shrinks to the shell alone.
What changed
The bottom cluster (1.1)
ShellClusterreplaces the docked footer: Home · Explore (compass) · Squads · Activity as a floating bar on the flat blur material, the Create square beside it opening the composer directly, the brand bubble on Activity only, Tint selection (filled glyph, the rest at 72%), the shrink on scroll (56→44px, inset 20→40, radius 22→18 over 96px), re-tap the lit tab to scroll to top, the lit tab from the root that owns the URL (Squads lights on a squad page, Home on a post). Visitors: Explore and Squads open; Home, Activity and Create open the sign-up with a context line. The cluster is in the server HTML (tablet:hidden), so it paints with the page. The Charm footer still replaces it at its trigger; the post action bar sits above it as before. Deleted:MobileFooterNavbar,FooterNavBarTabs,FooterNavBarItem,FooterPlusButton,MobileFeedActions.The You page (1.2)
/you, behind the avatar: name row, View profile, then daily.dev Plus (Upgrade / Manage / Renew), Custom feeds, My squads, Following, Bookmarks, History; Your progress (Achievements, Streak as the streak sheet, DevCard, Hot takes, Game center); Core wallet, Invite friends, Settings; Help as the top action. The settings gear leaves the Home row.The top block on roots (1.3)
ShellBlockreplaces FeedNav's phone branch: Home = logo (Plus mark for members), streak, the Plus square for free members (opens Plus, firesUpgradeSubscription), the 38px square avatar; Explore, Squads and Activity get the name row they never had (Activity with the notification-settings square). Today's rows ride under it (the chip strip on Home, the Spotlight trigger on Explore). The block is a fixed overlay; content takes--shell-toppadding; it hides as one solid piece while reading down (64px travel, 96px dead zone, 24/8px tolerances, 300ms stop, 220ms snap on the interaction curve) and returns on any short scroll up, never on arrival, and reveals when a field takes focus.useShellScrollis the one scroll reader for block and cluster.The top block on pages (1.4) The page row: back (38px square), the title (20px), the page's actions.
GoBackHeaderMobilefeeds it under 656px (title prop for the profile sub-pages), as do the profile header, the no-sidebar layout, the squad sub-page header and the settings container (its sections menu becomes a ⋮ square). Every leaf has a back now; back is history when the previous entry is ours, otherwise the owning root. The old sticky bars stay for tablet.Sign-up, two touches (1.5) On a phone the gated sign-up closes with an X top left (not a chevron) and shows one line under the title keyed by the trigger (Follow, Bookmark, Upvote, Comment, Join squad, Create…). Providers, order, email, terms, flow and analytics untouched.
The sheet (1.6) Drawer: 300ms in on the travel curve, 200ms out, scrim 150ms, a grabber on bottom sheets, drag to dismiss past a third of the height or on a fast flick (up barely moves, back springs in 220ms), inner lists keep scrolling. Every Drawer consumer gets it.
Menus as sheets (1.7)
DropdownMenuon a phone is modal and renders its Radix content as a bottom sheet (48px rows, grabber, the sheet's timings) throughshell-menu-sheet; it no longer closes on the scroll the collapsing address bar fires. Items, handlers and events are untouched, which keepsPostOptionButtonand the other 35 consumers as they are.People are rounded squares (1.8) Referral slots, embedded tweets, profile metadata and the feedback widget drop their circles; squads and sources keep theirs.
The rows (added on review, from 2.1, 2.2, 2.4, 2.5 and 2.8)
ShellRowwith the three weights of the 28px chip: Segments (which list, replacing history), Chips (narrow this list), MenuLabel (a sheet). Home's row is For you · Happening now ▾ · Following · your custom feeds · +; the chevron on Happening now opens the channel sheet (Headlines, All, every channel), and the Highlights page shows the channel on its own line instead of its old title and tab strip. Explore's row is the search field and the places (Tags, Sources, Leaderboard, Discussions); its sort is a text menu on the feed line (Popular ▾, with the period inside for the two sorts that have one) instead of the sticky tab strip. Squads root puts its categories in the block as chips; Activity its types; Bookmarks its lists as segments under a Bookmarks title; Tags, Sources, Leaderboard and History get their names in the block and lose their in-page navbars and breadcrumbs. The old strip's other chips are reachable from Explore, You or the bar. Tablet keeps every old row.The buttons (second review) A page keeps no button row of its own above the content once the block exists. The Squads root carries New Squad as a plus square (the Primary button under the title is gone on phones). Home shows the streak and the quest count beside the Plus square. Happening now loses the channel line and the copy link under the segments; the chevron sheet is the one channel control and marks the channel you are on. Search results are a page: back, the query as the title, a Filters square (the same drawer), the field in the row. The squad page block carries Search and the menu; the hero keeps the link and Join. The profile block carries the menu only; the hero keeps Follow and Award. Bookmarks carries Sort, Share and the folder menu in the block; the search field stays in the content. Explore loses the 40px band the old sticky tabs reserved.
ShellPagenow portalsactionsinto the block's slot from the page's own tree, so an action keeps its page's providers (the squad menu needsSquadPageContext); the hero copies are gated by viewport in JS so a label exists once in the DOM.Every page (third review) A route-by-route pass over 110 member routes at 393px. The pages that still drew a header row of their own under the block now register with it: Sources (Suggest new source as a plus square), Jobs (title, How it works, preferences), the Core wallet (title, Buy Cores), the briefings (title, Generate, settings), squad moderation, post analytics (title, Boost), the gear rankings, the best-of archives (title and copy link in the block, breadcrumbs gone on phones), the experience lists (the second back arrow gone), the squad form (title and Create Squad in the block, the back square replaces Cancel), the leaderboard detail and the tag explore page (titles). The composer page hides the block over its own chrome.
FormWrappergained aninBlockmode for page forms. Pages with their own standalone layout (Plus, Cores) are unchanged: they never render the block.Plus:
shell/constants.ts(the numbers),shell.css(material, hit areas, sheet timings, menu sheet),ShellSquare,ShellPageContext,ShellRow,HomeSegments,ExploreSortMenu.Verified
useShellScroll(dead zone, tolerance, hide, snap, reveal),ShellCluster(lit root, badge, both events, composer, visitor sign-up),ShellBlock(brand row, root names, page title and actions, hidden pages, the slide). Shared and webapp suites green, strict typecheck and lint clean, extension builds.Known and deferred (by the plan)
safeArea.css); the no-frame look there is step 4.🤖 Generated with Claude Code
Preview domain
https://claude-mobile-shell-1.preview.app.daily.dev