17 — Embeddable Booking Widget Specification
Surface: Drop-in JavaScript widget for tenant external sites (WordPress, Wix, custom) Phase: P2 / R2 Competitive reference: SiteMinder Booking Engine, Cloudbeds Booking Widget, Mews Distributor, Rezdy
1. Overview
Many tenants keep their existing marketing website (WordPress, Wix, Squarespace) and want to embed Melmastoon's booking funnel as a widget — "powered by Melmastoon" — without redirecting to a separate URL.
Embedding model: A lightweight <script> snippet added to the tenant's site. The widget renders inside an <iframe> (shadow-DOM isolation is not used — iframe provides better CSP/CORS isolation).
2. Snippet format
Tenants add to their website:
<div id="melmastoon-widget"></div>
<script src="https://widget.melmastoon.com/v1/embed.js"
data-tenant="hotel-kabul-palace"
data-lang="ps"
data-currency="AFN"
data-theme="light"
async>
</script>
That's it. The script handles everything else.
3. Widget modes
| Mode | Description | data-mode value |
|---|---|---|
| Search bar (default) | Compact search bar (dates + guests + "Check availability") that expands to full funnel | search-bar |
| Full funnel | Full booking flow embedded in the host page | full-funnel |
| Availability calendar | Monthly calendar showing available dates and lowest rate | availability-calendar |
| Floating button | Fixed "Book now" button that opens a modal with the search bar | floating-button |
4. Iframe vs shadow-DOM decision
Decision: iframe
| Consideration | Iframe | Shadow DOM |
|---|---|---|
| CSS isolation | ✅ Complete | ✅ |
| Host page style bleed | ✅ None | ⚠️ Can leak through custom properties |
| Communication with host | postMessage | Direct JS |
| CSP compliance | ✅ iframe src allowlisted | ⚠️ Shadow DOM is in same origin |
| Third-party cookie handling | Managed by widget domain | Same-origin; easier |
| Tenant custom fonts | Loaded inside iframe | Loaded inside shadow root |
Tradeoff: iframe's postMessage communication is slightly more complex but provides complete isolation, which is the correct choice for a third-party embed.
5. Theming inheritance
The widget loads tenant theme tokens from theme-config-service using the data-tenant attribute. Theme tokens are applied inside the iframe — the host page's CSS does not affect the widget.
Host page background adaptation: The widget iframe's background color can be set to transparent (via allow-transparent attribute) so the widget blends with the host page's background. Font is always the tenant's configured font, not the host page font.
6. postMessage protocol
The embed.js script communicates with the iframe via postMessage:
// Host page → iframe
{ type: 'WIDGET_CONFIG', payload: { lang, currency, theme } }
{ type: 'WIDGET_RESIZE', payload: { width, height } }
// Iframe → host page
{ type: 'BOOKING_STARTED', payload: { search: { checkin, checkout, guests } } }
{ type: 'BOOKING_COMPLETE', payload: { booking_reference, total } }
{ type: 'WIDGET_HEIGHT_CHANGE', payload: { height: number } } // for auto-resize
{ type: 'BOOKING_ABANDONED', payload: { step, reason } }
Auto-resize: The iframe posts WIDGET_HEIGHT_CHANGE whenever its content height changes. The embed.js script resizes the <iframe> element on the host page accordingly, eliminating double scrollbars.
7. Conversion telemetry
All standard booking funnel telemetry events (from C1-telemetry-event-dictionary.md) fire inside the iframe. The embed.js script additionally fires host-page events via window.postMessage for host-page analytics (GA4, GTM):
window.dataLayer?.push({ event: 'melmastoon_booking_complete', booking_reference: '...' });
8. CSP requirements
Tenants' sites need to allowlist:
Content-Security-Policy:
frame-src https://widget.melmastoon.com;
script-src https://widget.melmastoon.com;
connect-src https://api.melmastoon.com;
embed.js includes a helper comment explaining the CSP additions required.
9. Accessibility
The widget iframe has title="Melmastoon hotel booking". All content inside the iframe meets WCAG 2.2 AA (same as 13-tenant-booking-web-specification.md). The widget does not trap keyboard focus outside the iframe when closed.
10. Performance budgets
| Metric | Target |
|---|---|
embed.js size | ≤ 10 kB gzipped |
| Widget initial render (search bar) | ≤ 1.5 s |
| Widget full funnel load | ≤ 2.5 s |
11. Distribution
Widget CDN: https://widget.melmastoon.com/v1/embed.js — immutable versioned URL at v1. Breaking changes require v2.
Tenants access their snippet in the operator desktop app under Settings → Direct booking → Widget embed code.