Skip to content

feat(launchpad): official store badges, a linked title, and an app-website link on the header - #69

Merged
onamfc merged 1 commit into
mainfrom
feat/launchpad-badges-title-website
Oct 8, 2026
Merged

onamfc merged 1 commit into
mainfrom
feat/launchpad-badges-title-website

Conversation

@onamfc

@onamfc onamfc commented Oct 8, 2026

Copy link
Copy Markdown
Member

Description

Three improvements to the Launchpad page, the page a visitor sees when a link can't open the app:

  1. Official store badges. The App Store and Google Play buttons are now each company's official badge instead of styled text buttons.
  2. A linked title. On the default hero, the title links to the web destination. "Continue on the web" stays as the explicit link.
  3. An app-website link on the header. A new launchpad.appWebsiteUrl setting: when set, the header's icon and name link to the app's own website.

For hosts that draw their own hero, resolveContent now receives a third argument, { webUrl }, so they can link their title the same way.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update
  • ♻️ Code refactoring
  • ⚡ Performance improvement
  • ✅ Test addition or update

Related Issues

None filed publicly.

Changes Made

  • src/lib/store-badges.ts (new): the badge artwork as data URIs.
    • Apple: the official SVG from Apple's marketing badge service (black, en-US). Only the whitespace between tags was removed; the artwork is unchanged.
    • Google: the official PNG (646×250, unmodified). Google publishes no public SVG.
    • A trademark notice excludes the artwork from the MIT license and states the one permitted use: linking to the app's own store listing.
  • src/lib/launchpad.ts:
    • Badges replace the store text buttons. They keep the cta_ios / cta_android events, are drawn at the same visible height (CSS pulls in the transparent clear space built into Google's PNG), and have alt text.
    • "Open in app", when present, stays the one primary button.
    • The default hero's <h1> links to webUrl (cta_web). With no web destination, or an unsafe one, it stays plain text. A host-supplied heroHtml is left alone.
    • New appWebsiteUrl in LaunchpadSettings. readLaunchpadSettings keeps only http(s) values. The header wraps the icon and name in a link to it (cta_app_website), and stays unlinked when it's unset. It's deliberately explicit rather than derived from a link's web fallback, which may be a third-party page.
  • src/routes/redirect.ts: passes { webUrl } to resolveContent (additive, non-breaking), and appWebsiteUrl to the page. The beacon doc comment now lists every event; it was missing cta_web.
  • README: the badges, the title link, appWebsiteUrl, the hook's new argument, and the full beacon event list.

Testing

  • Tests pass locally (npm test): 422 passed, 2 skipped
  • New tests added for new functionality
  • Existing tests updated (if needed): none needed
  • Type checking passes (npm run typecheck)

Test Coverage

  • Badges: each links to its own store with its click event; only the stores given are shown; "Open in app" remains primary; the embedded artwork decodes to Apple's SVG (viewBox 0 0 119.66407 40) and a 646×250 PNG.
  • Title: it links to the web destination and "Continue on the web" is kept; it stays plain with no web destination or an unsafe one; a host hero is left alone.
  • Header: it's linked when appWebsiteUrl is set, and unlinked when unset or unsafe; readLaunchpadSettings drops non-http(s) values.
  • Route level: resolveContent receives the decorated webUrl, and the header links to the workspace's appWebsiteUrl.
  • Visual: I rendered and screenshotted the page at desktop and phone widths in light and dark mode. The badges line up at the same visible height, and the title and header look like plain text until hovered.
npm test -- src/lib/launchpad.test.ts src/routes/redirect.launchpad.test.ts

Screenshots/Examples

{ "launchpad": { "appName": "Example", "appWebsiteUrl": "https://example.com" } }

Checklist

  • My code follows the project's code style
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published
  • I have used conventional commit messages (e.g., feat:, fix:)
  • I have signed the Contributor License Agreement (the CLA bot will prompt you)

Breaking Changes

None. resolveContent's new third argument is additive. The store buttons' markup changes (badge images instead of text), so anything that scraped the page for the button text will see alt text instead.

Additional Notes

  • App-open is unaffected. This changes page content only. The page still never sits in front of a Universal Link / App Link redirect, and the redirect's routing is untouched.
  • Page weight: about 12 KB on a phone (one badge) and about 26 KB on desktop (both badges plus the QR block), with no extra requests.
  • Beacon endpoints: a host that validates beacon event names should add cta_app_website. Until it does, those taps just aren't counted.

…bsite link on the header

- The App Store and Google Play buttons are now the official badges: Apple's
  SVG and Google's PNG (Google publishes no public SVG), unmodified and
  embedded as data URIs, so the page makes no third-party request. Each links
  only to its own store listing; a notice keeps the artwork out of the MIT
  license. Open in app stays the primary button.
- The default hero's title links to the web destination, beside Continue on
  the web rather than instead of it.
- New launchpad.appWebsiteUrl setting: when set, the header's icon and name
  link to the app's own website (cta_app_website). Explicit rather than
  derived from a link's web fallback, which may be a third-party page.
- resolveContent receives a third argument, { webUrl }, so a host that draws
  its own hero can link its title the same way.

App-open routing is unchanged: page content only.
@codecov

codecov Bot commented Oct 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@onamfc
onamfc merged commit 972b12c into main Oct 8, 2026
13 of 14 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant