feat: reel UI improvements — pointer events, gestures, bottom row layout#40
Merged
GraysonCAdams merged 33 commits intomainfrom Mar 2, 2026
Merged
feat: reel UI improvements — pointer events, gestures, bottom row layout#40GraysonCAdams merged 33 commits intomainfrom
GraysonCAdams merged 33 commits intomainfrom
Conversation
- Add sheetOpen store that tracks the count of mounted BaseSheet instances - BaseSheet increments/decrements the count on mount/unmount - Bottom nav gets display:none via .sheet-hidden when anySheetOpen is true, eliminating the z-index stacking context conflict on iOS Safari - Feed pull-to-refresh ignores touchstart events while a sheet is open, preventing the drag-to-dismiss gesture from triggering a feed refresh - CommentsSheet auto-focus is now decoupled from the async comments fetch so the keyboard appears promptly after the sheet animation on iOS
move comment bar to bottom (nav+8px), progress bar above it (nav+36px), captions/sidebar above progress bar (nav+92px). music disc aligns on same row as comment bar. CommentPrompt extracted to own component. ClipOverlay baseline simplified to safe-area-only.
…badge with back button
Simplifies REACTIONS to a plain string[] and REACTION_MAP to a Set. ReactionPicker now renders emoji chars directly instead of SVG components. Removes the custom DoubleExclamationIcon SVG component.
Adds --reel-bg / --reel-bg-elevated tokens (always dark regardless of theme) so empty/end states in the feed stay immersive. Applies html.feed-context on the feed route (both at hydration and via inline script at cold start) so the iOS black-translucent status bar shows the correct dark background color.
Adds /favorites route with a 3-column thumbnail grid. Tapping a cell opens a full-screen reel with snap-scroll. Adds Faves tab to the bottom nav. Improves theme-color sync to use --reel-bg on the feed vs --bg-primary elsewhere.
Favorites moved to its own dedicated route (/favorites). Removes the 'favorites' option from the feed filter bar and updates labels to New/Seen. Adds a push notification enable button on the feed end-slide for users who haven't enabled notifications yet.
Replace top: 50% / transform approach with a calculated top: 5px that aligns the icon center with the text baseline regardless of UA-assigned container height. Icons switch to bottom-anchored when input grows past single-line.
Disabling user scaling is a WCAG 2.1 SC 1.4.4 violation. The existing touch-action: manipulation already prevents double-tap zoom, making user-scalable=no redundant and harmful for accessibility.
…ture Replace split mouse + touch event handlers with a single pointer event model. setPointerCapture ensures move/up events reach the element even when the pointer leaves its bounds during a scrub, eliminating the need for document-level mousemove/mouseup listeners. Add touch-action: none so the browser yields all gestures on the scrubber to JS.
Replace ontouchstart/ontouchend/ontouchcancel with the pointer events equivalent. Add onpointerleave to cancel the long-press popover when the finger slides off the element, matching the touch cancel behavior.
Pull-to-refresh and horizontal filter swipe both used raw touch events with passive:false touchmove to call preventDefault(). Replace with pointer events (filtering to pointerType=touch for PTR). Drop the preventDefault() calls — touch-action: pan-y on .feed-slide declares that horizontal touches are owned by JS, so the browser yields without needing an explicit cancel. Also remove deprecated -webkit-overflow-scrolling: touch (iOS handles this automatically now).
Replace touchstart/touchmove/touchend with pointer events and remove the preventDefault() on horizontal moves. Add touch-action: pan-y so the browser knows horizontal touches are handled by JS, eliminating the need for a non-passive listener.
Dragging the handle bar downward now follows the pointer in real time (transition disabled during drag for responsiveness). Releasing past 120px dismisses the sheet; releasing short of the threshold snaps it back. A small movement (<5px) is treated as a tap and also dismisses, preserving the existing click-to-close behavior. touch-action: none on the handle prevents browser scroll interference during the drag.
Replace the calculated top offset with transform: translateY(-50%) for cleaner vertical centering. The multi-line override resets the transform and pins to the bottom as before.
Wrap the handle bar and title header in a single drag zone so users can initiate a drag-to-dismiss gesture from anywhere in the card header. Uses lazy pointer capture — the drag is only committed (and the pointer captured) after 6px of clearly-downward movement, so tapping the title text does nothing. Tapping the grip bar still dismisses via its own onclick handler.
… entry The overlay now slides in from the right (matching the left-swipe-to-dismiss direction), making the gesture feel like a native navigation push/pop. A 300ms isEntering guard prevents accidental swipe tracking while the CSS entry animation is running, avoiding a jarring jump if the user touches the screen immediately after the overlay appears.
Glyphs sit above the geometric midpoint of their line box, so a slightly larger top padding (11px vs 7px) shifts the perceived text center down to align with the vertically-centered icons.
Previously AddVideoModal was rendered only inside the feed page, so the + tab on non-feed pages navigated away. Now it lives in the layout and the + tab always opens the modal regardless of the current page.
…ions Adds a pill-shaped comment prompt bone at the bottom so the skeleton matches the real reel layout. Also moves the sidebar and overlay bones upward to leave room above the comment bar.
… opens GifPicker gains a compact=true mode that renders a horizontal scrolling carousel instead of the masonry grid. CommentsSheet switches to full-dvh height (with an animated transition) when the GIF picker is open so the grid is never hidden behind the keyboard.
…ayback - MusicDisc moves from absolute-positioned overlay into ActionSidebar, replacing the external-link button for music clips and removing the need for a separate uiHidden prop - CommentPrompt switches from absolute to flex child of a new .bottom-row container so the prompt and disc sit on the same baseline - Auto-scroll defers when the user is engaged (comments/picker/viewers) and fires after a 3 s cooldown when engagement ends; forceLoop keeps media looping during that window - Scrub-seek uses a "seek on seeked" state machine to prevent mobile seek queue buildup; ProgressBar tracks displayTime locally for instant visual feedback independent of video decode lag
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.
Summary
setPointerCapturefor reliable multi-device behaviorbottom-rowflex container, replacing fragile absolute positioning; MusicDisc moved into ActionSidebar (replaces external-link for music clips)forceLoopkeeps media looping during the waitcompactmode (horizontal scroll row) for use inside the comments sheet; CommentsSheet expands to full viewport height when the GIF picker is openTest plan