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.
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.
Logo
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Logo | (none) | — | Upload your logo image. Used by every surface that shows one. |
| Logo frame | off | — | Draws a border/background ring around the logo. Off = clean transparent logo. |
| Logo frame fill | (empty) | --onelo-logo-bg | Fill colour of that ring. Empty = border only. Only used when Logo frame is on. |
| Logo frame thickness | 4px | --onelo-logo-frame-width | How 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.
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Auth | shown · center | — | Hosted sign-in / sign-up page. |
| Checkout card | shown · left | — | The card sits in a compact header row, hence left by default. |
| Store | shown · left | — | Managed here, not in the Store panel — one source of truth. |
| Customer portal | shown · center | — | Subscription management page. |
| Feedback | shown · center | — | Hosted bug-report / feature-request form. |
| Waitlist | shown · center | — | Hosted waitlist form and its iframe embed. |
| Roadmap | shown · left | — | Public roadmap page. |
Colors
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Brand color | #f97316 | --onelo-brand | Price, highlights, and the default button background. |
| Page background | #111111 | --onelo-page-bg | The area behind the card on hosted pages. |
| Card background | #1a1a1a | --onelo-card-bg | The card / panel surface itself. |
| Text color | #ffffff | --onelo-text | Main text colour, inherited by inputs unless you override them. |
| Border color | #2a2a2a | --onelo-border | Default colour for card, input and button borders. |
Buttons
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Button background | (empty) | --onelo-button-bg | Leave empty to follow Brand color. |
| Button text | #ffffff | --onelo-button-text | Label colour. |
| Button border width | 0px | — | 0–4px. At 0 the border is removed entirely. |
| Button border color | (empty) | --onelo-button-border | Empty = Border color. The field only appears once width is above 0. |
| Button radius | 14px | --onelo-button-radius | 0–24px. Starts from Card radius, then overrides it. |
Inputs
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Input background | (empty) | --onelo-input-bg | Empty = an automatic subtle overlay that works on light and dark cards. |
| Input text | (empty) | --onelo-input-text | Empty = inherits Text color. |
| Input border color | (empty) | --onelo-input-border-color | Empty = Border color. Set it to make fields differ from the card frame. |
| Input border width | 1px | --onelo-field-border-width | 0–4px. |
| Field radius | 14px | --onelo-field-radius | 0–24px. Starts from Card radius, then overrides it. |
Card
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Show card border | on | — | Checkout card and Customer portal only. Waitlist and Store have their own toggles. |
| Card border width | 1px | --onelo-card-border-width | 0–4px. |
| Card radius | 14px | --onelo-radius | Rounds cards and panels. Button radius and Field radius are set separately. |
| Featured border width | 2px | --onelo-featured-border-width | Store only — the ring around a featured product (0–8px). |
| Featured border color | (empty) | --onelo-featured-border-color | Store only. Empty = Brand color. |
Typography
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Heading font | Inter | --onelo-heading-font | 15 web fonts, plus system-ui (uses the device font, loads nothing). |
| Heading size | 26px | --onelo-heading-size | 18 → 48px. |
| Body font | Inter | --onelo-body-font | Same list as headings — pick the same one for a quieter look. |
| Body size | 14px | --onelo-body-size | 12 → 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.
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Shadow | off | — | Master switch. Off = no shadow anywhere, whatever the per-element values say. |
| Shadow color | #000000 | --onelo-shadow-color | Shared by every element. |
| Card shadow | on · 0/12px · blur 48 · 15% | — | Per-element: enable, offset X/Y, blur, opacity. |
| Buttons shadow | on · 0/4px · blur 16 · 20% | — | Tighter and stronger than the card — buttons sit closer to the surface. |
| Inputs shadow | on · 0/2px · blur 8 · 10% | — | Subtlest of the three. |
Waitlist-only
| Control | Default | CSS variable | What it does |
|---|---|---|---|
| Card max width | 360px | --onelo-card-max-width | 240 → 600px. Widen it when your form collects more than an email. |
| Show card border | on | — | The Waitlist card has its own border toggle, separate from the Card one. |
Footer & Custom CSS
| Control | Default | CSS variable | What 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).
: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).
| Variable | Dark page | Light page | Set by | What it carries |
|---|---|---|---|---|
| --onelo-logo-bg | transparent | transparent | Logo frame fill | Logo frame fill (when the logo frame is on) |
| --onelo-logo-frame-width | 4px | 4px | Logo frame thickness | Logo frame thickness |
| --onelo-brand | #f97316 | #f97316 | Brand color | Brand accent — prices, highlights, links, the spinner arc |
| --onelo-page-bg | #111111 | #ffffff | Page background | Page background behind the card |
| --onelo-card-bg | #1a1a1a | #ffffff | Card background | Card, panel and dialog background (unset: follows a light or dark page) |
| --onelo-text | #ffffff | #111111 | Text color | Body text (unset: dark on a light page, white on a dark one) |
| --onelo-border | #2a2a2a | #e4e4e7 | Border color | Card frame and divider colour (unset: follows a light or dark page) |
| --onelo-button-text | #ffffff | #ffffff | Button text | Label colour on primary buttons |
| --onelo-button-radius | 14px | 14px | Button radius | Button corner radius |
| --onelo-field-radius | 14px | 14px | Field radius | Input corner radius |
| --onelo-field-border-width | 1px | 1px | Input border width | Input border width |
| --onelo-card-border-width | 1px | 1px | Card border width | Card and panel border width |
| --onelo-radius | 14px | 14px | Card radius | Card 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-serif | Heading font | Heading font stack |
| --onelo-heading-size | 26px | 26px | Heading size | Heading size |
| --onelo-body-font | 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif | 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif | Body font | Body font stack |
| --onelo-body-size | 14px | 14px | Body size | Base body text size |
| --onelo-shadow-color | #000000 | #000000 | Shadow color | The one colour every enabled shadow uses |
| --onelo-button-bg | #f97316 | #f97316 | derived | Primary button fill (Button background; unset = Brand color) |
| --onelo-button-border | none | none | derived | Primary button border — none unless Button border width is above 0 |
| --onelo-input-bg | rgba(255,255,255,0.06) | rgba(0,0,0,0.04) | derived | Input fill (unset: a subtle overlay readable on light and dark cards) |
| --onelo-input-text | #ffffff | #111111 | derived | Input text (unset = Text) |
| --onelo-input-border-color | #2a2a2a | #e4e4e7 | derived | Input border (unset = Border color) |
| --onelo-logo-border | none | none | derived | Logo frame border |
| --onelo-card-shadow | none | none | derived | Card box-shadow — none unless shadows are on |
| --onelo-button-shadow | none | none | derived | Button box-shadow — none unless shadows are on |
| --onelo-input-shadow | none | none | derived | Input box-shadow — none unless shadows are on |
| --onelo-text-muted | rgba(255,255,255,0.6) | rgba(17,17,17,0.65) | derived | Secondary text — hints, captions, fine print (derived from Text) |
| --onelo-secondary-bg | rgba(255,255,255,0.04) | rgba(0,0,0,0.03) | derived | Secondary button and quiet-panel fill (derived overlay) |
| --onelo-spinner-track | rgba(255,255,255,0.18) | rgba(0,0,0,0.12) | derived | Loading spinner track (the arc is --onelo-brand) |
| --onelo-scrim | rgba(0,0,0,0.6) | rgba(0,0,0,0.4) | derived | Backdrop behind dialogs |
| --onelo-focus-ring | #f97316 | #000000 | derived | Keyboard 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 | #15803d | derived | Success status colour (named status token, not your brand) |
| --onelo-danger | #f87171 | #dc2626 | derived | Error and destructive-action status colour |
| --onelo-warning | #fbbf24 | #b45309 | derived | Warning status colour |
| --onelo-footer-color | #ffffff | #111111 | derived | "Powered by Onelo" text colour (read-only: the mark cannot be restyled) |
| --onelo-footer-opacity | 0.75 | 0.75 | derived | "Powered by Onelo" opacity |
| --onelo-footer-pill-bg | rgba(255,255,255,0.08) | rgba(0,0,0,0.05) | derived | "Powered by Onelo" pill background |
| --onelo-featured-border-width | 2px | 2px | Featured border width | Featured product card border width (Store only) |
| --onelo-featured-border-color | #f97316 | #f97316 | Featured border color | Featured product card border colour (Store only) |
| --onelo-card-max-width | 360px | 360px | Card max width | Waitlist 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 field | Example | What it carries |
|---|---|---|
| version | 1 | Schema 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_radius | 14 | Button corner radius, px |
| field_radius | 14 | Input corner radius, px |
| card_radius | 14 | Card 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_bg | false | true 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).
| Selector | Surface | What it targets |
|---|---|---|
| #onelo-auth | Auth | Sign-in / sign-up form root |
| #onelo-store | Store | Pricing / products page root |
| #onelo-portal | Customer portal | Subscription / billing portal root |
| #onelo-waitlist | Waitlist | Waitlist widget root |
| #onelo-roadmap | Roadmap | Public roadmap root |
Element hooks you can target (with !important for concrete properties):
| Selector | Surface | What it targets |
|---|---|---|
| .checkout-card | Checkout card | The card container (the card has no #onelo-* root id — scope card-only rules to this) |
| .onelo-button | All hosted pages | Primary buttons (CTA, submit, purchase) — Button background, Button text, Button radius |
| .onelo-button-secondary | All hosted pages | Secondary buttons (Go back, Apply) — secondary fill, Border colour outline, Button radius |
| .onelo-button-ghost | All hosted pages | Quiet actions (Skip, Not now) — no fill, Text colour, Button radius |
| .onelo-button-danger | All hosted pages | Destructive actions (Cancel plan) — the danger status colour, Button radius |
| .onelo-link | All hosted pages | Inline text actions (Terms, Resend) — Brand colour, no box |
| .onelo-input | All hosted pages | Text inputs, textareas and selects — also bridged into the Stripe card fields |
| .onelo-card | All hosted pages | Cards, panels and dialogs — Card background, Card border, Card radius, Card shadow |
| .onelo-toggle | All hosted pages | Segmented toggles (Monthly / Yearly) — the selected segment looks like the CTA |
| .onelo-badge | All hosted pages | Badges and pills (Save 20%, Popular, Active) |
| .onelo-notice | All hosted pages | Inline messages — info (Brand colour) or success / warning / danger |
| [data-onelo-feedback-input] | Feedback | Feedback message / details inputs |
| [data-onelo-feedback-submit] | Feedback | Feedback submit button |
| [data-onelo-feedback-chip] | Feedback | Feedback type chips |
| .portal-btn | Customer portal | All portal buttons (base) |
| .portal-btn-primary / -secondary / -danger / -ghost | Customer portal | A specific portal button style |
| .portal-card / .portal-section | Customer portal | Portal cards & sections |
| [data-onelo-product-card] | Store | Each store product card |
| [data-onelo-card-divider] | Checkout card | The left/right column separator (set border-color to recolor it) |
#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.#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;
}!important. Overriding a --onelo-* variable scoped to a root (e.g. #onelo-auth { --onelo-text: #eee }) does not need it..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 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
#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.
@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.