Skip to content

fix(web,widget): embed checkout styles and no silent host fallback - #171

Open
Mamavee001 wants to merge 1 commit into
determined-001:mainfrom
Mamavee001:fix-embed-checkout-styles-and-host
Open

fix(web,widget): embed checkout styles and no silent host fallback#171
Mamavee001 wants to merge 1 commit into
determined-001:mainfrom
Mamavee001:fix-embed-checkout-styles-and-host

Conversation

@Mamavee001

Copy link
Copy Markdown
Contributor

closes #156

What changed

1. Real .shell--embed / .panel--embed rules (apps/web/app/globals.css) - previously neither class existed, so only the full-page .shell rules applied (max-width centering + 32-80px page padding) inside the widget's 440x680 iframe. The widget's own outer <div> (packages/widget/src/modal.ts) already supplies the rounded corners/border/shadow around the iframe, so the page rendered inside it only needs to fill the frame - it shouldn't also look like a card floating inside a card.

2. Checked the embed rendering at that exact size and tightened what assumed a full page - CheckoutClient now takes an embed prop (threaded from pay/[id]/page.tsx's existing isEmbed flag) so it can:

  • apply a .checkout--embed class (tighter heading scale, QR-wrap margins, memo-note spacing, and status-rail/icon sizing - globals.css), and
  • use a smaller QR code (140px vs 180px) - the one thing CSS alone can't resize, since size is a real prop on QRCodeSVG, not a stylable dimension.

I sized/checked this against the "active - waiting for payment" render specifically, since it's the tallest of the checkout states (title + amount + QR + memo note + status rail, no masthead since that's already hidden in embed mode).

3. packages/widget/src/modal.ts: deleted the DEFAULT_HOST fallback (https://quay-web.vercel.app - the maintainer's own deployment). openModal() now resolves the host via an explicit host option or the widget's own <script src="...widget.js"> tag; if neither works, it throws a clear error naming both fixes, rather than silently pointing a self-hoster's integration at someone else's backend. I also moved host resolution to the very top of openModal(), before any state mutation - a failed call is now a true no-op, rather than one that closes (and fails to replace) whatever modal was already open.

4. Updated the widget quickstart in README.md to show the host option for self-hosters, stating the new fail-loud behavior explicitly.

A real, necessary test update

The existing widget tests (packages/widget/test/widget.test.ts) called Quay.open("lnk_test_123") with no host and no script tag in the DOM - under the old code this silently succeeded via the window.location.origin fallback (itself removed too, since it's just a different flavor of the same "guess a host" problem - a third-party site embedding the widget would have window.location.origin resolve to their own origin, not Quay's, which is just as wrong as the hardcoded fallback). Under the new code these calls would throw. I updated them to supply an explicit host (or a real script tag, for the button-binding test), and added new coverage for: the throw case, script-tag inference, explicit-host-overrides-script-tag precedence, and the no-op-on-failure guarantee.

Test plan

No Node.js/npm/pnpm/vitest runtime is available in the environment this was authored in, so none of this - including the updated and new widget tests - has actually been executed. The CSS sizing was checked by reasoning through the actual component markup and existing style rules (real qr-wrap padding values, amount-hero font sizes, the existing <=560px media query) against a 440x680 frame, not by rendering it in a browser. Please:

  • Run pnpm --filter @checkout/widget test and pnpm --filter @checkout/web build before merging.
  • Visually verify the embed at exactly 440x680 (e.g. via the widget demo or a manual iframe) for the QR, heading, and status-rail sizing I reasoned through rather than saw.

…ssue 5.10)

- globals.css: real .shell--embed / .panel--embed rules - no page-level
  max-width/centering/32-80px padding inside the widget's 440x680 iframe
  (the outer modal <div> in packages/widget/src/modal.ts already supplies
  the rounded corners/border/shadow, so the page inside only needs to
  fill the frame). Added .checkout--embed overrides tightening heading
  scale, QR wrap margins, memo-note spacing, and the status-rail/icon
  sizes specifically for the narrower embed context - checked against
  the "active - waiting for payment" render, the tallest of the states.
- CheckoutClient now takes an `embed` prop (threaded from
  pay/[id]/page.tsx's existing isEmbed flag) so it can apply the
  .checkout--embed class and use a smaller QR size (140px vs 180px) -
  the one thing CSS alone can't resize, since it's a real prop on
  QRCodeSVG, not a stylable dimension.
- packages/widget/src/modal.ts: deleted the DEFAULT_HOST fallback
  (https://quay-web.vercel.app - the maintainer's own deployment).
  openModal() now resolves the host via an explicit `host` option or the
  widget's own <script src="...widget.js"> tag; if neither works, it
  throws a clear error naming both ways to fix it, rather than silently
  pointing a self-hoster's integration at someone else's backend. Also
  moved host resolution to the very top of openModal(), before any state
  mutation, so a failed call is a true no-op - it doesn't close (and
  fail to replace) a modal that was already open.
- README.md: added the `host` option to the widget quickstart snippet
  for self-hosters, with the new fail-loud behavior stated explicitly.
- packages/widget/test/widget.test.ts: updated the existing tests to
  supply a host (they'd have hit the new throw otherwise, since none of
  them had a real host to detect), and added new coverage for the throw
  case, script-tag inference, explicit-host precedence, and the
  no-op-on-failure guarantee.

No Node.js/npm/pnpm/vitest runtime is available in the environment this
was authored in, so none of this - including the updated and new widget
tests - has actually been executed. The CSS sizing was checked by
reasoning through the actual component markup and existing style rules
(qr-wrap padding, amount-hero font sizes, the existing <=560px
breakpoint) against a 440x680 frame, not by rendering it - please verify
visually at that exact size before merging.
@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Mamavee001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Mamavee001 is attempting to deploy a commit to the determined's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5.10 - Embedded checkout has no embed styles and a hardcoded fallback host

1 participant