QueueUp / docs

Customize the widget

Theme tokens, layouts, and copy. Every dial the embed widget exposes.

The widget’s appearance is driven by a small set of tokens you set in the dashboard’s Customize section. The same tokens can also be passed inline via the theme attribute on the <queueup-form> element. For deeper control, override CSS variables or target the widget’s ::part hooks from your stylesheet.

Live editor

Open the dashboard and navigate to the waitlist’s Setup → Customize section. The page shows a live preview of the widget rendered against your current settings; every change updates instantly. When you’re happy, click Save to persist.

Tokens

TokenTypeDefaultDescription
brand_colorhex #RRGGBB#f97316Button background and accents.
text_colorhex #RRGGBB#000000Text colour for input and button label.
border_radiusinteger (0 to 32)6Corner radius for input and button, in pixels.
layout"stacked" or "inline""stacked"Vertical (input above button) or horizontal (input + button on one row).
button_labelstring (1 to 32)"Join waitlist"Button text.
placeholderstring (0 to 64)"[email protected]"Email input placeholder.

Inline theme

The theme attribute on <queueup-form> carries the JSON object you copied from the dashboard. The widget reads it on mount and renders against those values:

<queueup-form
  waitlist-id="wl_abc123"
  theme='{"brand_color":"#10b981","layout":"inline","button_label":"Get notified"}'
></queueup-form>

You don’t need to specify every token. Anything you omit falls back to the system default. Drop the attribute (or pass theme="none") to render with system defaults entirely.

Custom styling

Theme tokens cover the common dials. For full control (custom borders, gradients, font stack, hover states), the widget exposes CSS hooks you can target from your page’s stylesheet.

::part hooks

The widget renders inside a Shadow DOM, so your page’s CSS can’t reach into it directly. Every meaningful element carries a part attribute. Target those with the ::part() selector:

queueup-form::part(input) {
  border: 2px solid #111;
  font-family: "Inter", sans-serif;
}

queueup-form::part(input):focus {
  border-color: #f97316;
  box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.2);
}

queueup-form::part(button) {
  background: linear-gradient(135deg, #f97316, #ef4444);
  font-weight: 600;
  letter-spacing: 0.02em;
}

queueup-form::part(button):hover {
  filter: brightness(1.05);
}

queueup-form::part(message-error) {
  color: #dc2626;
}

queueup-form::part(message-success) {
  color: #16a34a;
}

queueup-form::part(brand) {
  display: none;
}

Available parts

PartWhat it targets
formThe <form> element wrapping the input and button.
form-successAdded to form after a successful submission.
fieldA wrapper around the input. Useful for adornments.
inputThe email input.
buttonThe submit button.
button-successThe success label inside the button (the “You’re on the list” state).
footerThe row containing the message slot and brand.
messageThe message slot (empty by default).
message-errorAdded to message when an error is shown.
message-successAdded to message when a success message is shown.
brandThe “Powered by QueueUp” line.

CSS variables

The widget reads its colours, radius, and spacing from CSS custom properties on the host element. You can override any of them:

queueup-form {
  --qu-brand: #111111;
  --qu-brand-fg: #ffffff;
  --qu-text: #1f2937;
  --qu-bg: #fafafa;
  --qu-border: rgba(0, 0, 0, 0.15);
  --qu-radius: 12px;
  --qu-gap: 12px;
  --qu-focus-ring: rgba(17, 17, 17, 0.25);
  --qu-input-padding-y: 12px;
  --qu-input-padding-x: 14px;
  --qu-button-padding-y: 12px;
  --qu-button-padding-x: 20px;
}
VariableDefaultWhat it controls
--qu-brandfrom themeButton background and focus accents.
--qu-brand-fgauto-contrastButton text colour.
--qu-textfrom themeForm text colour.
--qu-bg#ffffffInput background.
--qu-bordertranslucent slateInput border.
--qu-radiusfrom themeCorner radius for input and button.
--qu-gap8pxGap between input and button.
--qu-focus-ringtranslucent blueFocus outline ring.
--qu-input-padding-y / -x10px / 12pxInput padding.
--qu-button-padding-y / -x10px / 16pxButton padding.

Fonts

The host element uses font: inherit, so the form picks up whatever font you set on <queueup-form> or any ancestor. There’s no separate font token; just style the host:

queueup-form {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
}

Headless mode

If you want to start from a blank slate and style everything yourself, set mode="headless" on the element. The default stylesheet is skipped entirely; only the ::part hooks remain. You’re then responsible for every visual rule.

<queueup-form
  waitlist-id="wl_abc123"
  mode="headless"
></queueup-form>

In headless mode, the widget still renders the same DOM structure (form, field, input, button, footer, message, brand), so your existing ::part selectors keep working.

Events

The widget dispatches DOM events on success and failure so you can react to signups without owning the form markup.

EventWhendetail shape
queueup:readyAfter mount, before submit.{ waitlistId }
queueup:submitWhen the user submits, before the network call. Cancellable.{ email }
queueup:successAfter a successful signup.{ email, statusToken, referralCode, statusUrl }
queueup:errorOn any error.{ code, message, retryable }

A typical handler that surfaces the user’s referral link inline:

<queueup-form waitlist-id="wl_abc123"></queueup-form>
<div id="share" hidden>
  Share to skip ahead: <code id="share-url"></code>
</div>

<script>
  document.querySelector("queueup-form").addEventListener("queueup:success", (e) => {
    const { statusUrl, referralCode } = e.detail;
    if (!referralCode) return;
    const share = document.getElementById("share-url");
    share.textContent = `https://yoursite.example/?queueup_ref=${referralCode}`;
    document.getElementById("share").hidden = false;
  });
</script>

The statusToken and statusUrl fields are populated for both new signups and idempotent re-signups, so a returning user sees the same share UI as a first-time visitor. See Referrals for the full flow.

Snippet versioning

Every save in the dashboard bumps a version counter on the theme. The Embed section tracks which version you last copied; if the saved version is newer, you’ll see an Out of date banner suggesting you re-copy.

This matters because the embed inlines the theme JSON into the snippet, and the widget doesn’t fetch theme data at runtime. A change to the saved theme won’t appear on your site until you update the snippet on the page.