Styling the Website Widgets
For the developer looking after a restaurant's website. This page covers what you can and cannot change about the SSP menu and booking widgets, and where each control actually lives.
Read this first: your CSS cannot reach the widget
Both widgets render inside a sandboxed, cross-origin <iframe>. Your stylesheet, your CSS variables and your JavaScript stop at the frame boundary. There is no class you can target, no !important that will win, and no way to inject a stylesheet into the frame.
This is deliberate. The frame is served from the SSP API on a different origin so that a change to your site cannot break bookings, and a fault in the widget cannot reach your page or your analytics.
So if you have been writing selectors and seeing nothing happen — that is why, and no amount of specificity will fix it. Appearance is configured in SSP Manager instead, and this page tells you where.
Everything visual about the widget's interior is set in Manager → Web Menu → Appearance. Everything about its place on your page is yours.
What you control from your own page
You own the element the snippet sits in. Wrap the <script> tag in a container and style that:
<div class="menu-embed">
<script
src="https://YOUR-SSP-HOST/embed/v1/loader.js"
data-ssp-site-key="sk_web_…"
async
></script>
</div>
.menu-embed {
max-width: 1100px; /* see "Give it room" below */
margin: 0 auto;
padding: 0; /* the widget brings its own internal padding */
}
Get the exact snippets for your restaurant from Manager → Your website. Both widgets share one key; the booking widget is the same tag with data-ssp-widget="booking" added.
Do not set a fixed height
The widget measures its own content and asks the page to resize the frame, using a postMessage on the ssp-embed channel. The loader you pasted handles that for you. A fixed height on the container or the iframe will clip the menu or leave a gap under it.
Give it room
The widget lays out in columns and needs width to do it. Below roughly 800px it degrades, and a narrow content column is the usual cause of a menu that looks cramped or overlapped on an otherwise wide page.
If your theme puts content in a narrow column, give the embed container its own wider one. On genuinely small screens the widget switches to its mobile layout, which is a separate setting (columnsMobile).
What you control in Manager → Appearance
The Appearance tab writes a set of design tokens, which are injected into the frame as CSS custom properties named --ssp-<token> in kebab-case. surfaceAlt becomes --ssp-surface-alt, contentMaxWidth becomes --ssp-content-max-width.
You cannot set these from your page — they are listed so you know the vocabulary, can ask for a specific change, and can tell whether the effect you want is achievable at all.
Colour
| Token | CSS property | What it paints |
|---|---|---|
surface | --ssp-surface | The widget background |
surfaceAlt | --ssp-surface-alt | Item cards, raised areas |
textPrimary | --ssp-text-primary | Item names, headings |
textSecondary | --ssp-text-secondary | Descriptions, supporting text |
accent | --ssp-accent | Buttons, selected states |
accentContrast | --ssp-accent-contrast | Text on top of the accent |
border | --ssp-border | Card and divider lines |
price | --ssp-price | Prices |
unavailable | --ssp-unavailable | Sold-out items |
error | --ssp-error | Error messages |
Type
| Token | CSS property |
|---|---|
fontFamily | --ssp-font-family |
headingFontFamily | --ssp-heading-font-family |
baseFontSize | --ssp-base-font-size |
scaleRatio | --ssp-scale-ratio |
headingWeight | --ssp-heading-weight |
bodyWeight | --ssp-body-weight |
Spacing and shape
| Token | CSS property |
|---|---|
baseUnit | --ssp-base-unit |
sectionGap | --ssp-section-gap |
itemGap | --ssp-item-gap |
itemPadding | --ssp-item-padding |
contentMaxWidth | --ssp-content-max-width |
cornerRadius | --ssp-corner-radius |
buttonRadius | --ssp-button-radius |
Layout and content
| Token | CSS property | Notes |
|---|---|---|
layout | --ssp-layout | Overall arrangement |
columnsDesktop | --ssp-columns-desktop | Columns on a wide viewport |
columnsMobile | --ssp-columns-mobile | Columns on a narrow one |
imageAspect | --ssp-image-aspect | Item image shape |
imageSize | --ssp-image-size | Item image size |
buttonFill | --ssp-button-fill | Solid or outlined buttons |
categoryNav | --ssp-category-nav | The category chips at the top |
descriptionStyle | --ssp-description-style | How descriptions render |
stickyHeader | --ssp-sticky-header | Whether the header follows the scroll |
showImages | --ssp-show-images | Booleans arrive as 1 or 0 |
showDescriptions | --ssp-show-descriptions | |
showPrices | --ssp-show-prices |
The booking widget reads the same theme as the menu, so a restaurant that has styled one has styled both. What differs between them is structure, not palette — nothing about a menu's item grid maps onto a date and a list of times.
Where the widget will not appear
The widget only renders on sites listed under Manager → Your website. This is a browser-level restriction: a page not on the list is refused by the browser before any of your code runs, so it fails silently rather than with an error you can catch.
An empty list allows nothing, not everything. If both widgets are blank everywhere, check that list first.
Search engines
Content inside an embed is not indexed. Manager → Web Menu → Publish provides a plain, crawlable copy of the menu on an SSP URL, plus a JSON-LD block you can paste into your own page's <head>. Link to the crawlable copy from your site so search engines can find it.
Troubleshooting
| What you see | Most likely cause |
|---|---|
| Nothing renders, no console error | Your site is not on the allowed list in Manager → Your website |
| Nothing renders, 404 in the network tab | The code was replaced. Generating a new code invalidates the old one for both widgets — get the current one from Manager → Your website |
| Menu appears, booking form does not | The booking widget has its own switch under Manager → Reservations → Settings, and reservations must also be on in Location Settings |
| Cramped or overlapping layout | The container is too narrow — see "Give it room" |
| Clipped at the bottom, or a gap underneath | A fixed height somewhere; let the widget size itself |
| Your CSS has no effect | Expected — see the top of this page |