A second way to share
A general "share the game" button joins the main menu, distinct from the post-game score share, plus a dependency catch-up that ran into a release-age policy of its own.
The game only had one way to share itself: the button at the end of a finished round, built to boast a score. There was no way to hand someone the game itself without playing first. A wayfinder map settled the shape of a second, more general entry point, and a footer button now opens it from the main menu.
Deciding what “general” means#
The map fixed the destination before any code was written: a text-only “Share” trigger in the footer’s support-links row, between Ko-fi and Blog, opening a minimal modal with a title, a QR code, a copyable link, and one adaptive action button that calls the browser’s native share sheet where available and falls back to a clipboard copy. Two questions needed their own tickets first. One decided the ref value the shared link carries for tracking, a fixed menu-share rather than a scheme that varies by language or entry point, exported alongside the existing GAME_URL constant. The other produced a rough prototype of the trigger’s position and the modal’s layout, reusing the PvP lobby’s existing QR-code-and-copy-link cluster as a starting shape and the confirmation dialog’s accessibility pattern, focus trap, aria-modal, Escape-to-close, for the shell, minus its two-button footer since this modal has nothing to confirm.
A general invite is not a finished game’s boast.
What makes it a second surface rather than an extension of the first is scope. The existing share button always shares a specific result: today’s score, a streak, a board state worth showing off. The new button shares nothing but the game itself, the bare homepage with a ref tag, and a generic bilingual invite line instead of score-flavored copy. Reusing that component would have meant threading a “no score” mode through code built around always having one, so the modal became its own self-contained component instead of an extension of ShareButton.svelte.
A two-axis review of the result found one real violation and one naming smell: a hardcoded color: white that should have been the design token for text on an accent background, and a doAction handler that said nothing about what it did until renamed to shareOrCopyLink. Two other flagged duplications, primary-button styling repeated instead of shared, and the modal shell echoing the confirmation dialog’s, were left alone. DESIGN.md already documents no shared <Button> component as a deliberate consequence of how Svelte scopes component styles, so that duplication is the pattern, not an oversight.
A dependency catch-up, delayed on arrival#
A pair of routine dependency bumps landed first and failed every CI job at the install step, rejected by pnpm’s own default minimum-release-age policy: packages published less than a day before the run get refused outright, no compatibility problem required. Five updates across two dependabot PRs, @playwright/test, colorjs.io, svelte, svelte-check, and a major jsdom bump, sat blocked until each package cleared the window, with no actual compatibility failure ever exercised. The fix was mechanical: wait it out and recreate the same five bumps as individual commits once each qualified.
The share button gives the game a second front door, one that does not require finishing anything first. Whether it gets used enough to justify a third remains to be seen.