redesign: add-bookmark flow as bottom sheet (mobile) / dialog (desktop)#46
Merged
Merged
Conversation
Redesigns the add-bookmark flow from first principles around its one job: capture a link fast. New bookmarks no longer use the inline draft card at the top of the list; they go through a new AddBookmarkDialog. Why: the inline card scrolled with the list under the sticky header, so on phones the tags row and Save/Cancel actions routinely ended up under the soft keyboard, Enter-to-save was the only reliable affordance, and multi-line notes were impossible (Enter saved, Shift+Enter doesn't exist on touch). The URL - the only required field - was a 16px line at the top of a form that gave title and description equal billing. The new dialog: - Bottom sheet on mobile: pinned to the viewport bottom with a sticky footer, so the full-width Save button stays visible above the keyboard (verified at 390x450 keyboard-up viewport - the body scrolls, the footer doesn't). index.html now sets interactive-widget=resizes-content so Android Chrome resizes the layout viewport. Centered top-anchored dialog on sm+. - URL first: the only required field, hero position, one-tap Paste button inside the field, Save disabled until a URL is entered, inline errors for invalid/unreadable clipboard. - Content suggestions finally reach manual adds: committing a URL (paste or blur) auto-fills empty title/description and offers suggested tags as one-tap chips when the opt-in feature is enabled. - Notes are a real textarea: Enter inserts a newline, Cmd/Ctrl+Enter saves from anywhere; Enter in URL/title saves (enterKeyHint go/done). - Draft protection: tapping outside a non-empty draft no longer discards it; Cancel and Esc still do. - All touch targets >=44px; Radix Dialog provides focus trap, scroll lock and aria wiring; entrance animations respect prefers-reduced-motion. BookmarkInlineCard is now edit-only (the URL was never editable there anyway since URLs are immutable after create), dropping its isNew branches, paste button and draft badge. Draft-card tests moved to AddBookmarkDialog.test.jsx and extended (suggestions, disabled save, newline-in-notes); inline card tests trimmed to edit behaviors. Verified with Playwright at 390x844 (touch), 390x450 (keyboard up) and 1440x900: paste -> autofill -> tag -> read-later -> save -> toast -> appears in list and in the Read Later filter; invalid URL and dirty- draft dismissal probed. npm run test:coverage passes all thresholds (52 files, 1282 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NibfKDyuyzU7MPgyeNe7yD
✅ Deploy Preview for hypermarkk ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Opening the add-bookmark dialog on a real iPhone zoomed the page and pushed the sheet off screen. iOS Safari auto-zooms whenever a focused form control's font-size is under 16px, and the dialog autofocuses its URL field, which (like every input in the app) rendered at 14px (text-sm) — so the zoom fired on open, before any tap on the field. Force all text inputs, selects and textareas to 16px below the sm breakpoint (!important to beat Tailwind's .text-sm utility). This also covers the search bar, tag inputs and the inline edit card, which would trigger the same zoom on focus. Desktop keeps the compact 14px sizes. Verified via Playwright: computed font-size is 16px for URL/title/ notes/tags/search at 390px and 14px at 1440px, with the sheet layout unchanged. Full test suite passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NibfKDyuyzU7MPgyeNe7yD
On iPhone the sheet's save footer disappeared behind the keyboard. iOS Safari ignores interactive-widget=resizes-content and overlays the keyboard on the layout viewport, so a bottom-0 fixed sheet stays pinned under it. While the dialog is open, track window.visualViewport (resize + scroll) and compute how much of the layout viewport the keyboard covers (innerHeight - vv.height - vv.offsetTop, so it also handles Safari scrolling the visual viewport). The inset is written to CSS variables on the sheet element, consumed only by the <640px positioning rules: bottom lifts by the keyboard height and max-height caps to the visible area. With no keyboard the variables resolve to the original bottom-pinned sheet; desktop positioning never reads them. The element handle uses callback-ref state rather than a plain ref - Radix assigns the forwarded ref after parent effects run, so a useRef was still null on the effect's first pass. Verified in Chromium with a scripted visualViewport fake: 336px keyboard -> sheet bottom lifts 336px and the Save button sits above the keyboard line; dismiss -> returns to bottom:0; desktop dialog position unchanged. Full suite passes (52 files, 1284 tests) with new unit tests covering the inset math and keyboard dismissal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NibfKDyuyzU7MPgyeNe7yD
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.
Redesigns the add-bookmark flow from first principles around its one
job: capture a link fast. New bookmarks no longer use the inline draft
card at the top of the list; they go through a new AddBookmarkDialog.
Why: the inline card scrolled with the list under the sticky header, so
on phones the tags row and Save/Cancel actions routinely ended up under
the soft keyboard, Enter-to-save was the only reliable affordance, and
multi-line notes were impossible (Enter saved, Shift+Enter doesn't
exist on touch). The URL - the only required field - was a 16px line at
the top of a form that gave title and description equal billing.
The new dialog:
footer, so the full-width Save button stays visible above the
keyboard (verified at 390x450 keyboard-up viewport - the body
scrolls, the footer doesn't). index.html now sets
interactive-widget=resizes-content so Android Chrome resizes the
layout viewport. Centered top-anchored dialog on sm+.
button inside the field, Save disabled until a URL is entered,
inline errors for invalid/unreadable clipboard.
(paste or blur) auto-fills empty title/description and offers
suggested tags as one-tap chips when the opt-in feature is enabled.
saves from anywhere; Enter in URL/title saves (enterKeyHint go/done).
discards it; Cancel and Esc still do.
lock and aria wiring; entrance animations respect
prefers-reduced-motion.
BookmarkInlineCard is now edit-only (the URL was never editable there
anyway since URLs are immutable after create), dropping its isNew
branches, paste button and draft badge. Draft-card tests moved to
AddBookmarkDialog.test.jsx and extended (suggestions, disabled save,
newline-in-notes); inline card tests trimmed to edit behaviors.
Verified with Playwright at 390x844 (touch), 390x450 (keyboard up) and
1440x900: paste -> autofill -> tag -> read-later -> save -> toast ->
appears in list and in the Read Later filter; invalid URL and dirty-
draft dismissal probed. npm run test:coverage passes all thresholds
(52 files, 1282 tests).
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01NibfKDyuyzU7MPgyeNe7yD