Branding

Branding

All Onelo hosted surfaces — Auth, Checkout card, Store, Customer portal, Feedback, Waitlist, and Roadmap — are styled from your Branding settings via CSS custom properties. This page covers both halves: the visual controls in the dashboard, and Custom CSS for anything they don't expose — injected into every surface automatically. No framework, no build step.

Most branding is done with the visual controls in Dashboard → App → Branding — colors, fonts, radius, logo, shadows, and the "Powered by Onelo" toggle. Reach for Custom CSS only when you need something the controls don't expose — the Custom CSS field itself requires a paid plan (on Free it renders locked).

The visual controls

Open Dashboard → App → Branding. Along the top are tabs for each surface — Auth, Checkout card, Store, Customer portal, Feedback, Waitlist and Roadmap — with a live preview beside the controls. Switch the preview between Desktop and Mobile at any time.

One theme, seven previews. Colors, fonts, radius, buttons, inputs and shadows are shared — change them on any tab and every surface follows, which is what keeps your product looking like one product. Only a handful of settings are genuinely per-surface: the logo's show/alignment, the Store's featured border, the Waitlist card width and border, and the card-border toggle. Those appear only on the tabs they apply to.

Logo

ControlDefaultCSS variableWhat it does
Logo(none)—Upload your logo image. Used by every surface that shows one.
Logo frameoff—Draws a border/background ring around the logo. Off = clean transparent logo.
Logo frame fill(empty)--onelo-logo-bgFill colour of that ring. Empty = border only. Only used when Logo frame is on.
Logo frame thickness4px--onelo-logo-frame-widthHow thick the ring/padding around the logo is (1–16px).

Below those, each surface gets its own show/hide and alignment — so you can centre the logo on the hosted Auth page but tuck it to the left in the compact checkout card, or hide it entirely on one surface without touching the others.

ControlDefaultCSS variableWhat it does
Authshown · center—Hosted sign-in / sign-up page.
Checkout cardshown · left—The card sits in a compact header row, hence left by default.
Storeshown · left—Managed here, not in the Store panel — one source of truth.
Customer portalshown · center—Subscription management page.
Feedbackshown · center—Hosted bug-report / feature-request form.
Waitlistshown · center—Hosted waitlist form and its iframe embed.
Roadmapshown · left—Public roadmap page.

Colors

ControlDefaultCSS variableWhat it does
Brand color#f97316--onelo-brandPrice, highlights, and the default button background.
Page background#111111--onelo-page-bgThe area behind the card on hosted pages.
Card background#1a1a1a--onelo-card-bgThe card / panel surface itself.
Text color#ffffff--onelo-textMain text colour, inherited by inputs unless you override them.
Border color#2a2a2a--onelo-borderDefault colour for card, input and button borders.

Buttons

ControlDefaultCSS variableWhat it does
Button background(empty)--onelo-button-bgLeave empty to follow Brand color.
Button text#ffffff--onelo-button-textLabel colour.
Button border width0px—0–4px. At 0 the border is removed entirely.
Button border color(empty)--onelo-button-borderEmpty = Border color. The field only appears once width is above 0.
Button radius14px--onelo-button-radius0–24px. Starts from Card radius, then overrides it.

Inputs

ControlDefaultCSS variableWhat it does
Input background(empty)--onelo-input-bgEmpty = an automatic subtle overlay that works on light and dark cards.
Input text(empty)--onelo-input-textEmpty = inherits Text color.
Input border color(empty)--onelo-input-border-colorEmpty = Border color. Set it to make fields differ from the card frame.
Input border width1px--onelo-field-border-width0–4px.
Field radius14px--onelo-field-radius0–24px. Starts from Card radius, then overrides it.
On the checkout card these same values are forwarded into Stripe's hosted card fields, so your inputs and the card-number field match — see Checkout card & Stripe below.

Card

ControlDefaultCSS variableWhat it does
Show card borderon—Checkout card and Customer portal only. Waitlist and Store have their own toggles.
Card border width1px--onelo-card-border-width0–4px.
Card radius14px--onelo-radiusRounds cards and panels. Button radius and Field radius are set separately.
Featured border width2px--onelo-featured-border-widthStore only — the ring around a featured product (0–8px).
Featured border color(empty)--onelo-featured-border-colorStore only. Empty = Brand color.

Typography

ControlDefaultCSS variableWhat it does
Heading fontInter--onelo-heading-font15 web fonts, plus system-ui (uses the device font, loads nothing).
Heading size26px--onelo-heading-size18 → 48px.
Body fontInter--onelo-body-fontSame list as headings — pick the same one for a quieter look.
Body size14px--onelo-body-size12 → 18px.

Shadow

Shadows are off by default. The master Shadow toggle turns the whole system on; from there each element carries its own geometry, which you set by dragging a light source around a 2D pad rather than typing offsets.

ControlDefaultCSS variableWhat it does
Shadowoff—Master switch. Off = no shadow anywhere, whatever the per-element values say.
Shadow color#000000--onelo-shadow-colorShared by every element.
Card shadowon · 0/12px · blur 48 · 15%—Per-element: enable, offset X/Y, blur, opacity.
Buttons shadowon · 0/4px · blur 16 · 20%—Tighter and stronger than the card — buttons sit closer to the surface.
Inputs shadowon · 0/2px · blur 8 · 10%—Subtlest of the three.

Waitlist-only

ControlDefaultCSS variableWhat it does
Card max width360px--onelo-card-max-width240 → 600px. Widen it when your form collects more than an email.
Show card borderon—The Waitlist card has its own border toggle, separate from the Card one.

Footer & Custom CSS

ControlDefaultCSS variableWhat it does
Show "Powered by Onelo"on—Turning it off requires a paid plan. On Free the footer always renders.
Custom CSS(empty)—The Advanced field the rest of this page is about. Requires a paid plan — on Free it renders locked.

Custom CSS — how it works

Onelo renders each surface on its own URL and sets a base set of --onelo-* CSS variables from your Branding settings. Your Custom CSS is appended on top.

Custom CSS is automatically scoped to the surface it runs on — every rule you write is prefixed with that surface's root so it can never leak into the Onelo dashboard or another surface. You can also scope a rule yourself to a single surface using its root ID (e.g. #onelo-auth).

The dashboard live preview reflects your edits instantly. The desktop preview does not accept pointer input, so hover and focus states (:hover, :focus) don't show there — check them in the mobile preview or on the hosted URL.

Brand tokens

Onelo resolves your Branding once into brand tokens — every colour, radius, font and shadow, with the defaults and light/dark-page rules applied — and every hosted surface, email and SDK paints from them. On a hosted page the tokens are these CSS variables, set on the surface root. The table below is generated from the code that sets them, with the value an app that changed nothing gets on the default dark page and on a light one (Page background #ffffff).

Change them with the visual controls in Branding. In Custom CSS, read them (e.g. box-shadow: var(--onelo-card-shadow)) or re-declare one scoped to a surface root for fine tuning. Derived values such as --onelo-button-bg are already resolved on the root, so to change one element reliably override its property directly with !important (see Cookbook).

VariableDark pageLight pageSet byWhat it carries
--onelo-logo-bgtransparenttransparentLogo frame fillLogo frame fill (when the logo frame is on)
--onelo-logo-frame-width4px4pxLogo frame thicknessLogo frame thickness
--onelo-brand#f97316#f97316Brand colorBrand accent — prices, highlights, links, the spinner arc
--onelo-page-bg#111111#ffffffPage backgroundPage background behind the card
--onelo-card-bg#1a1a1a#ffffffCard backgroundCard, panel and dialog background (unset: follows a light or dark page)
--onelo-text#ffffff#111111Text colorBody text (unset: dark on a light page, white on a dark one)
--onelo-border#2a2a2a#e4e4e7Border colorCard frame and divider colour (unset: follows a light or dark page)
--onelo-button-text#ffffff#ffffffButton textLabel colour on primary buttons
--onelo-button-radius14px14pxButton radiusButton corner radius
--onelo-field-radius14px14pxField radiusInput corner radius
--onelo-field-border-width1px1pxInput border widthInput border width
--onelo-card-border-width1px1pxCard border widthCard and panel border width
--onelo-radius14px14pxCard radiusCard and panel corner radius (unset: the larger of Button and Field radius)
--onelo-heading-font'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serifHeading fontHeading font stack
--onelo-heading-size26px26pxHeading sizeHeading size
--onelo-body-font'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serifBody fontBody font stack
--onelo-body-size14px14pxBody sizeBase body text size
--onelo-shadow-color#000000#000000Shadow colorThe one colour every enabled shadow uses
--onelo-button-bg#f97316#f97316derivedPrimary button fill (Button background; unset = Brand color)
--onelo-button-bordernonenonederivedPrimary button border — none unless Button border width is above 0
--onelo-input-bgrgba(255,255,255,0.06)rgba(0,0,0,0.04)derivedInput fill (unset: a subtle overlay readable on light and dark cards)
--onelo-input-text#ffffff#111111derivedInput text (unset = Text)
--onelo-input-border-color#2a2a2a#e4e4e7derivedInput border (unset = Border color)
--onelo-logo-bordernonenonederivedLogo frame border
--onelo-card-shadownonenonederivedCard box-shadow — none unless shadows are on
--onelo-button-shadownonenonederivedButton box-shadow — none unless shadows are on
--onelo-input-shadownonenonederivedInput box-shadow — none unless shadows are on
--onelo-text-mutedrgba(255,255,255,0.6)rgba(17,17,17,0.65)derivedSecondary text — hints, captions, fine print (derived from Text)
--onelo-secondary-bgrgba(255,255,255,0.04)rgba(0,0,0,0.03)derivedSecondary button and quiet-panel fill (derived overlay)
--onelo-spinner-trackrgba(255,255,255,0.18)rgba(0,0,0,0.12)derivedLoading spinner track (the arc is --onelo-brand)
--onelo-scrimrgba(0,0,0,0.6)rgba(0,0,0,0.4)derivedBackdrop behind dialogs
--onelo-focus-ring#f97316#000000derivedKeyboard focus outline on links, buttons and fields (Brand color; black or white when the brand lacks 3:1 contrast against the page or card)
--onelo-success#22c55e#15803dderivedSuccess status colour (named status token, not your brand)
--onelo-danger#f87171#dc2626derivedError and destructive-action status colour
--onelo-warning#fbbf24#b45309derivedWarning status colour
--onelo-footer-color#ffffff#111111derived"Powered by Onelo" text colour (read-only: the mark cannot be restyled)
--onelo-footer-opacity0.750.75derived"Powered by Onelo" opacity
--onelo-footer-pill-bgrgba(255,255,255,0.08)rgba(0,0,0,0.05)derived"Powered by Onelo" pill background
--onelo-featured-border-width2px2pxFeatured border widthFeatured product card border width (Store only)
--onelo-featured-border-color#f97316#f97316Featured border colorFeatured product card border colour (Store only)
--onelo-card-max-width360px360pxCard max widthWaitlist card maximum width (Waitlist only)

Brand tokens in the SDKs

The SDKs paint their native chrome — the loading skeleton, spinner, retry button and close button shown around a hosted page — from the same tokens. /api/sdk/config and the store, portal, manage and upgrade responses carry a versioned brand_tokens object; before the first response an SDK uses a neutral grey, never Onelo's colours. Example values are for an app that changed nothing.

brand_tokens fieldExampleWhat it carries
version1Schema version (1). Bumped only when a field changes meaning
page_bg"#111111"Page background — native container, WebView and skeleton ground
brand"#f97316"Brand accent — spinners and highlights
card_bg"#1a1a1a"Card background — skeleton blocks and sheets
text"#ffffff"Text colour
text_muted"rgba(255,255,255,0.6)"Secondary text (may be rgba())
border"#2a2a2a"Border colour
button_bg"#f97316"Primary button fill (retry / sign-in buttons)
button_text"#ffffff"Primary button label colour
button_radius14Button corner radius, px
field_radius14Input corner radius, px
card_radius14Card corner radius, px
body_font"'Inter', -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"Body font stack (CSS)
heading_font"'Inter', -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"Heading font stack (CSS)
spinner_track"rgba(255,255,255,0.18)"Spinner track colour (may be rgba())
light_bgfalsetrue when the page background is light

Selectors

These surfaces expose a root ID — scope a rule to one surface by prefixing it with the ID (e.g. #onelo-store .onelo-button).

SelectorSurfaceWhat it targets
#onelo-authAuthSign-in / sign-up form root
#onelo-storeStorePricing / products page root
#onelo-portalCustomer portalSubscription / billing portal root
#onelo-waitlistWaitlistWaitlist widget root
#onelo-roadmapRoadmapPublic roadmap root

Element hooks you can target (with !important for concrete properties):

SelectorSurfaceWhat it targets
.checkout-cardCheckout cardThe card container (the card has no #onelo-* root id — scope card-only rules to this)
.onelo-buttonAll hosted pagesPrimary buttons (CTA, submit, purchase) — Button background, Button text, Button radius
.onelo-button-secondaryAll hosted pagesSecondary buttons (Go back, Apply) — secondary fill, Border colour outline, Button radius
.onelo-button-ghostAll hosted pagesQuiet actions (Skip, Not now) — no fill, Text colour, Button radius
.onelo-button-dangerAll hosted pagesDestructive actions (Cancel plan) — the danger status colour, Button radius
.onelo-linkAll hosted pagesInline text actions (Terms, Resend) — Brand colour, no box
.onelo-inputAll hosted pagesText inputs, textareas and selects — also bridged into the Stripe card fields
.onelo-cardAll hosted pagesCards, panels and dialogs — Card background, Card border, Card radius, Card shadow
.onelo-toggleAll hosted pagesSegmented toggles (Monthly / Yearly) — the selected segment looks like the CTA
.onelo-badgeAll hosted pagesBadges and pills (Save 20%, Popular, Active)
.onelo-noticeAll hosted pagesInline messages — info (Brand colour) or success / warning / danger
[data-onelo-feedback-input]FeedbackFeedback message / details inputs
[data-onelo-feedback-submit]FeedbackFeedback submit button
[data-onelo-feedback-chip]FeedbackFeedback type chips
.portal-btnCustomer portalAll portal buttons (base)
.portal-btn-primary / -secondary / -danger / -ghostCustomer portalA specific portal button style
.portal-card / .portal-sectionCustomer portalPortal cards & sections
[data-onelo-product-card]StoreEach store product card
[data-onelo-card-divider]Checkout cardThe left/right column separator (set border-color to recolor it)
Checkout card and Feedback have no root ID — a rule without a surface-specific selector applies to every surface. To isolate them, target their element hooks above.
The "Powered by Onelo" mark (#onelo-footer / [data-onelo-powered-by]) cannot be styled or hidden via Custom CSS — those rules are stripped. Hide it with the plan-gated toggle in Branding → Powered by Onelo instead.
You cannot target a surface's outermost wrapper directly — Onelo prefixes every rule with the surface root, so a selector must match a descendant (a root ID like #onelo-store, the .checkout-card container, or an element hook).

Per-surface & per-element targeting

Because every rule is auto-scoped to the surface it runs on, and each surface exposes a root ID plus element hooks, you can style one element type on one surface and nothing else — and give each surface its own independent settings. Combine a root ID with an element hook: #surface .element.

Shadow on Auth buttons only — inputs untouched

#onelo-auth .onelo-button {
  box-shadow: 0 6px 24px rgba(255, 0, 255, 0.5) !important;
}

Different settings per surface

Each #surface .element block is independent — style Auth and Portal buttons completely differently.

/* Auth buttons — magenta glow */
#onelo-auth .onelo-button {
  box-shadow: 0 6px 24px rgba(255, 0, 255, 0.5) !important;
}

/* Portal primary button — different glow + sharper corners */
#onelo-portal .portal-btn-primary {
  box-shadow: 0 2px 8px rgba(0, 255, 0, 0.4) !important;
  border-radius: 4px !important;
}

/* Store CTA buttons — yet another look */
#onelo-store .onelo-button {
  text-transform: uppercase !important;
  letter-spacing: 0.06em !important;
}
Buttons and inputs apply their background / border / shadow as inline styles, so override those concrete properties with !important. Overriding a --onelo-* variable scoped to a root (e.g. #onelo-auth { --onelo-text: #eee }) does not need it.
Scoping is automatic and airtight — a rule written under one surface's ID can never leak to another surface. A rule with no surface ID (e.g. a bare .onelo-button) applies to that element on every surface that has it.

Checkout card & Stripe

The checkout card is split in two. The left column (logo, product name, price) is rendered by Onelo and is fully stylable. The right column — every payment field (Email, Card number, Sort code…) and the payment-method tabs (Card / Bacs Direct Debit / …) — is rendered by Stripe inside an iframe. Custom CSS cannot reach inside it — a hard browser-security boundary.

How to style the Stripe fields & tabs anyway

Onelo bridges your input CSS into Stripe's appearance. A rule matching .onelo-input (or input / textarea) has its background, color, border, border-radius and box-shadow copied onto the Stripe fields and the unselected payment-method tabs. So this restyles them:

.onelo-input {
  background: #11001a !important;
  border: 2px solid #2a0040 !important;
  color: #ffffff !important;
}
The selected payment-method tab (its border + text) uses your Brand color — change it in Branding → Brand color, not via CSS. Individual tabs and their hover/selected states can't be targeted separately (they live inside Stripe).

The column divider line

The vertical line between the two columns is the left panel's border. Its color comes from Branding → Border color (applied inline). To recolor it from CSS, target the panel by its inline border — this works regardless of layout/build and leaves the lower divider untouched:

.checkout-card [data-onelo-card-divider] {
  border-color: #00ff88 !important;
}
[data-onelo-card-divider] is the supported hook for the card's separator lines — use it instead of matching inline styles, which breaks whenever the card's markup changes.

Cookbook

Recolor buttons across surfaces

Buttons apply their background inline, so override the property with !important. A rule with no surface ID applies everywhere.

.onelo-button {
  background: #7c3aed !important;
  color: #ffffff !important;
}

Different button per surface

Prefix with a root ID to scope to one surface.

#onelo-auth .onelo-button {
  background: #6d28d9 !important;
}

#onelo-store .onelo-button {
  background: linear-gradient(135deg, #7c3aed, #a855f7) !important;
  box-shadow: 0 4px 20px rgba(124, 58, 237, 0.35) !important;
}

Tune colors that read a variable live

Variables like --onelo-text and --onelo-border are read live, so re-declaring them scoped to a root works without !important.

#onelo-waitlist {
  --onelo-text: #e8e8e8;
  --onelo-border: rgba(14, 165, 233, 0.3);
}

Button hover animation

Hover states are not visible in the dashboard preview — test in the hosted URL.
#onelo-auth .onelo-button,
#onelo-waitlist .onelo-button {
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

#onelo-auth .onelo-button:hover,
#onelo-waitlist .onelo-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(124, 58, 237, 0.45) !important;
}

Light mode adaptation

Media queries are supported and scoped automatically.

@media (prefers-color-scheme: light) {
  #onelo-auth,
  #onelo-waitlist {
    --onelo-border: rgba(0, 0, 0, 0.12);
    --onelo-text: #111111;
  }
}

Mobile tweaks

@media (max-width: 400px) {
  #onelo-store .onelo-button {
    font-size: 13px;
    padding: 10px;
  }
}

Fonts

Choose your heading and body fonts from the pickers in Branding → Typography — 15 web fonts are built in (Inter, Poppins, Plus Jakarta Sans, Figtree, Manrope, and more) and are loaded for you on every surface, plus system-ui, which uses the device's own font and loads nothing.

You cannot load an external font with @import in Custom CSS — @import is stripped for security. Use the built-in font picker instead.

What you cannot do with CSS

  • ✕Hide or recolor the "Powered by Onelo" mark — Footer-targeted rules are stripped; hide it via the plan-gated Branding toggle
  • ✕Load external fonts or stylesheets with @import — @import is removed during sanitization — use the built-in font picker
  • ✕Change field order (e.g. name before email) — HTML structure is controlled by Onelo
  • ✕Add custom fields to the Auth form — Use Waitlist for custom data collection
  • ✕Change button labels ("Sign in", "Join Waitlist") — Text is managed by Onelo
  • ✕Reach inside the Stripe iframe with CSS — Payment fields + method tabs are cross-origin — style them via the .onelo-input bridge instead (see “Checkout card & Stripe”)

Tips

  • —Override variables, not concrete properties, where you can — variable overrides cascade naturally and never need !important.
  • —Use !important only when overriding a concrete property (background, box-shadow, border) that a widget applies as an inline style — e.g. on .onelo-button.
  • —Avoid structural selectors like > div > div — widget HTML may change between releases. Stick to .onelo-button, .onelo-input, the data-onelo-* hooks, or the root IDs.
  • —The dashboard preview is a snapshot — :hover and :focus states are not visible there. Test them on the hosted URL.
  • —All surfaces share one Custom CSS field — use the root IDs (#onelo-auth, #onelo-store…) to scope rules to specific surfaces.
Branding — Onelo Docs