Token Variable and Generic Component Classes
This section explains the semantic theming tokens the portal exposes, the shared component classes, and how the portal scopes your custom CSS before it is applied.
Token variables
The portal exposes semantic theming tokens as CSS custom properties. Override them once on .sp-app (the consumer app root) and every component wired to them updates. Currently all dropdowns (.sp-dropdown*) and radio buttons (.sp-radio) are wired, with more components being wired over time.
Set them on .sp-app. The --sp-color-accent token additionally defaults to the portal's configured branding accent color when one is set. The palette value below is only the fallback when no branding accent is configured. An override in your custom CSS still wins over both.
Token | Light default | Dark default | Controls |
|---|---|---|---|
| branding accent, else #0077C4 | branding accent, else #4dc1fd | Accent color: radio checked border and dot, search product-selector hover, focus, and selected borders, product-tile visibility chip, and request-access border. |
| #005999 | #a6e0fe | Hover state of the accent. |
| #0066cc | #51a8ff | Link text color: breadcrumbs, side nav active items, table of contents active and hover, markdown content links, profile "manage account" link, doc anchor-link icon, and search loading spinner. |
| #004e9d | #75baff | Hover state of breadcrumb links. |
| #edf9ff | #383d41 | Accent-tinted backgrounds: selected mode tiles and profile menu item hover. |
| #fff | #1c2022 | Primary background: top bar, modals, dropdowns, and tiles. |
| #f0f1f1 | #2a2e30 | Raised surfaces: dropdown panel (dark) and profile menu items. |
| #f9f9f9 | #2a2e30 | Recessed surfaces: disabled tiles, search highlights, and top-bar stripes. |
| #e4e6e6 | #383d41 | Hover background on interactive rows (search results, side nav). |
| #b7bcbf | #6b757a | Standard borders: inputs, dropdowns, and tiles. |
| #e4e6e6 | #434b4f | Low-contrast dividers: modal and section separators. |
| #b7bcbf | #5f696e | High-contrast borders. |
| #1c2022 | #f0f1f1 | Primary body text. |
| #080a0b | #fff | Maximum-contrast text (headings). |
| #5f696e | #b7bcbf | Secondary text, labels, and muted icons. |
| #8c969a | #6b757a | Tertiary, disabled, and placeholder text. |
| #545d61 | #d2d6d7 | Hover background on "invert" style controls (cancel, close, and lock icon buttons, mobile login button). |
| #fff | #2a2e30 | Text color on top of |
| #545d61 at 50% | #6b757a at 50% | Modal and dialog backdrop overlay. |
| inherit | inherit | Reserved for font theming. |
| 0.25rem | 0.25rem | Corner radius of token-wired components. |
A ten-line rebrand:
.sp-app {
--sp-color-accent: #004831;
--sp-color-accent-hover: #003525;
--sp-color-border: #9db4ac;
--sp-border-radius: 0;
}
.dark .sp-app {
--sp-color-accent: #7fd1b9;
--sp-color-accent-hover: #a5e3d1;
}Generic component classes
Shared components carry context-neutral classes, so one rule covers every instance:
Class | Applied to |
|---|---|
| Every dropdown wrapper (topbar, sidenav, and search product selectors). |
| Every dropdown trigger button. |
| Every dropdown options panel. |
| Every radio button row (routing modal, mobile mode picker). |
| Every counter badge (search tab counts). |
Before, styling all dropdown panels took one rule per context:
.sp-topbar-product-selector-options { background-color: #0b1f33; }
.sp-sidenav-product-selector-options { background-color: #0b1f33; }
.sp-search-modal-dropdown { background-color: #0b1f33; }
.sp-search-mobile-product-selector-options { background-color: #0b1f33; }Now one rule covers them all:
.sp-dropdown-panel { background-color: #0b1f33; }Per-context selectors still exist and still win when you need a precision override on a single instance.
How it works
Every rule you write is automatically scoped to the consumer portal by the server. Before injection, each selector is prefixed with body.sp-consumer:
/* What you write */
.sp-topbar { background-color: #004831; }
/* What the server injects */
body.sp-consumer .sp-topbar { background-color: #004831; }This scoping does two things:
Prevents bleed into the admin UI. The admin interface shares the same origin, so unscoped rules could accidentally affect admin-only elements.
Raises specificity. The element and class combination in the scope gives your rules higher specificity than Tailwind utility classes, so they reliably win the cascade without requiring
!important.
You write normal .sp-* selectors; the prefix is applied transparently.