feat: add "Get the app" header entry point with QR popover - #6384
feat: add "Get the app" header entry point with QR popover#6384tsahimatsliah wants to merge 9 commits into
Conversation
Desktop and extension visitors have no visible path to the mobile app - the only pointer lives buried in the profile menu. This adds a phone button to the header that opens a popover with a scannable QR code plus App Store and Google Play links. A desktop visitor cannot install a phone app from the machine they are on, so the QR is the primary affordance and the store links are the fallback. The code is a pre-rendered matrix rather than a runtime library, generated at error-correction level H so the centre can carry the daily.dev mark without becoming unscannable. Gated behind the header_get_app flag, default false. Rendered only at laptop width and up: the native iOS/Android wrappers run this same webapp shell and should not be told to install an app they are already running. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
capJavert
left a comment
There was a problem hiding this comment.
No code-level blocker found in the first pass, but this is not ready for final approval yet: the PR explicitly says experiment enrollment has not been requested and the component has only been verified in Storybook, not in a running webapp/extension with the GrowthBook flag. Please complete enrollment and the stated webapp + extension viewport sanity pass, then request final approval.
REVERT BEFORE MERGE.
GrowthBook has no `header_get_app` feature yet, and the DevTools extension
cannot attach on preview deploys because they build as production. Flipping
the committed default is the only way to see the entry point on the preview.
Merging this as-is ships the experiment to 100% of users with no rollback
short of a deploy. Restore `new Feature('header_get_app', false)`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The App Store / Google Play links were Secondary outlines, which read as tertiary next to the QR. They are the fallback path for anyone who cannot scan, so they get the solid Primary fill and the Medium size instead. Both icons draw in currentColor, so they invert with the fill and stay legible in either theme. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@capJavert thanks — fair call on both points. Here's where each stands. ✅ Webapp sanity pass — done, on the preview deployThe flag is now forced on via Verified on https://claude-app-promotion-button-4e84.preview.app.daily.dev/tags/javascript (logged out):
Viewport matrix
Absent below laptop is intended — the native iOS/Android wrappers render this same webapp shell, so the gate is what stops us telling someone holding the app to go install the app. Panel measures 384px and sits fully inside the viewport at 1020 ( One thing I found that is not a regression from this PRResizing from 1020 → 900 without a reload leaves the button mounted even though This is pre-existing and header-wide, not something this PR introduces: at 900px after a live resize the whole desktop
|
The Android robot is the OS logo, not the storefront, so it was the wrong mark next to "Google Play". Adds the official Play glyph as a local icon drawn in currentColor, matching how AndroidIcon was built. The two icons also read at different sizes because their artwork fills the viewBox differently: the Apple glyph spans ~67% of its box while the Play mark runs nearly edge to edge. At one shared size the Apple logo had 56% less ink area. Sizing Apple's box up to 20px against Play's 16px lands them within 4% of each other (ink 13.2x16.7 vs 14.4x16.0). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every header child is flex-shrink:0 via the global reset, so once the logged-in action rail outgrows its budget the header overflows instead of compressing: at 1020px the rail already overshoots the right edge by ~27px today and clips the logo on the left, and the new Get-the-app entry adds another 52px. The search is the one element that can give up width, so it now takes the remaining space (flex-1 + min-w-0) and its trigger caps at 26.25rem rather than being fixed there. Rendered width is unchanged at 420px wherever there is room - it only shrinks when the header would otherwise overflow. The laptop/laptopL max-widths removed from the trigger were dead: the fixed width never reached them. Measured against the real header with the rail reconstructed at true sizes: overshoot goes to 0 at 1020/1100/1280/1440 for profile clusters of 180/270/320px, and the logo is no longer clipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
capJavert
left a comment
There was a problem hiding this comment.
Blocking before merge: featureHeaderGetApp still defaults to true because temporary commit e35b4fa remains in the branch. As the PR notes, this bypasses the intended GrowthBook rollout and ships the entry point to everyone. Revert that commit/default to false, complete experiment enrollment, and finish the logged-in extension sanity pass before requesting final approval.
This reverts commit e35b4fa.
|
@capJavert addressing your three blockers — two are resolved, one has product context that changes it. ✅ 1. Temp commit — reverted
✅ 2. Experiment enrollment — not applicable, this isn't an experimentProduct decision: we're not running this as an A/B test. There's no hypothesis to measure and no variant analysis planned — it's a permanent entry point, like the store links in the sidebar.
So there's nothing to enroll in #experiments. I've updated the PR description's Experiment section to say exactly this, so the paper trail matches.
|
Changes
Adds a "Get the app" entry point to the desktop header, promoting the daily.dev mobile app to webapp and extension visitors. Today the only pointer lives buried in the profile menu.
GetAppButton(features/getApp) mounted inHeaderButtonsfor both the logged-out and logged-in headers.Float, neverPrimary, so it does not compete with Sign up.usePersistentContext. It nudges once and then goes quiet — deliberately not a recurring banner.Components → GetAppButton(5 stories, both themes) so the design is reviewable without touching GrowthBook.Placement rationale
Looked at how other products do this before choosing. TikTok ships an icon-only phone button (tooltip "Get App") in its header action rail next to the auth buttons; Spotify's web player uses a labelled "Open App". Both are persistent header affordances that open a QR, not banners.
Rejected alternatives:
QR implementation notes
https://daily.dev/apps?utm_source=header_qr. A single UTM param keeps the code at 37 modules; the two-param version pushed it to 45 and got noticeably denser.jsQRat 96 / 112 / 128 / 160px. All decode to the exact expected URL.Desktop-only by design
Gated on
useViewSize(ViewSize.Laptop)rather than platform detection. The native iOS/Android wrappers render this same webapp shell, so a viewport gate is what actually keeps us from telling someone holding the app to go install the app.Events
clicktarget_type: 'get app button'— fired when the popover opensdownload apptarget_type: 'get app button',target_id: 'ios' | 'android'— fired on a store link clickBoth reuse existing
LogEventmembers. Adds one newTargetType.GetAppButton. Analytics Taxonomy sheet still needs updating.Experiment
No — this is not an A/B experiment and no #experiments enrollment is needed. It's a permanent entry point with no hypothesis or variant analysis planned.
header_get_appis a plain GrowthBook feature flag used as a rollout kill-switch: defaultfalsein code, created in GrowthBook and ramped to 100% when we're ready, instantly rampable back down if anything looks off.Note
The branch briefly carried a temp commit (
e35b4fae4) forcing the default totrueso the entry point rendered on the preview deploy (GrowthBook DevTools can't attach on preview builds — they build as production). That commit has been reverted in92324b6dd; the committed default isfalseagain, so the merged code is inert until the flag is created and ramped in GrowthBook.Manual Testing
On those affected packages:
Did you test the modified components media queries?
Did you test on actual mobile devices?
Caveat for the reviewer: verification was done in Storybook (which forces the flag on via an
isFeatureEnabledprop) and through automated browser checks, not in a running webapp or extension with the flag flipped in GrowthBook. Worth a pass there before enrolment. Native mobile is not applicable — the component is gated off below laptop width — but that gate itself is worth confirming on a real device.Automated
features/getAppandcomponents/layout(6 new specs: flag off, below-laptop, pre-resolved flag, both store hrefs, label variant)scripts/typecheck-strict-changed.jscleanMyFeedPage › should request user feed, which passes on re-runTwo bugs found and fixed while verifying
flex-shrink: 0, somin-w-0alone never lets a flex child wrap — it needsflex-1too.Buttonderives its icon-only geometry from!children, so nesting the dot inside silently switched it to the labelled layout (pl-2 pr-4 gap-1). The dot is now a sibling.Open question
The logged-out row is currently
[Get the app] [Log in] [Sign up]— three text buttons, and "Get the app" is the widest. It pulls attention away from Sign up, which is the CTA that matters to a logged-out visitor.InHeaderLoggedOutCompactin Storybook shows the icon-only alternative against the same pair; droppingshowLabelinHeaderButtonsis a one-line switch if reviewers prefer it.🤖 Generated with Claude Code
Preview domain
https://claude-app-promotion-button-4e84.preview.app.daily.dev