Theming
Every element renders inside a shadow root styled from --dc-* CSS custom
properties. That isolation runs both ways: your page’s CSS cannot reach into
the widget, and the widget’s cannot leak out. Restyling with your own
selectors will not work — set the tokens instead.
Tokens
Eight always render:
| Token | CSS property | Default |
|---|---|---|
accent | --dc-accent | #0e1726 |
accentForeground | --dc-accent-fg | #ffffff |
surface | --dc-surface | #ffffff |
ink | --dc-ink | #0e1726 |
inkMuted | --dc-ink-muted | rgba(14, 23, 38, 0.6) |
border | --dc-border | rgba(0, 0, 0, 0.10) |
radius | --dc-radius | soft |
density | --dc-leading | comfortable |
radius takes sharp (4px), soft (12px), pill (9999px), or a plain number
of pixels. density takes comfortable (line-height 1.5) or compact (1.35).
Two more render only when you set them:
| Token | CSS property |
|---|---|
fontFamily | --dc-font-family |
fontDisplay | --dc-font-display |
And three are runtime signals rather than CSS variables: fontUrl, locale,
currency.
The default accent is a near-black navy, not a blue. If you set nothing, the widget looks deliberately neutral rather than branded.
Setting a theme
DriveCars.init({
session,
onRefreshToken,
theme: {
accent: '#ff6b00',
accentForeground: '#ffffff',
radius: 'pill',
density: 'compact',
},
});Or for one element only:
DriveCars.mount('search', el, { theme: { accent: '#0055aa' } });Precedence
Most specific wins:
mount({ theme }) > init({ theme }) > session theme_overrides > DriveCars defaulttheme_overrides comes from your partner record, so we can set your brand
colours once and every embed picks them up with no code from you at all. Ask
us to set them rather than repeating a palette across pages.
Each tier merges token by token. Overriding accent alone leaves the other
seven where the tier below put them — you never have to restate a whole theme
to change one value.
Fonts
fontFamily and fontDisplay are ordinary CSS font stacks. They style text but
do not load anything.
fontUrl loads a stylesheet — typically a Google Fonts or self-hosted CSS URL —
and is injected as a <link> once per document, shared across every element
on the page rather than fetched per element.
theme: {
fontUrl: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap',
fontFamily: '"Inter", system-ui, sans-serif',
}fontUrl must be an https: URL. Anything else — http:, a javascript:
URL, a protocol-relative path — is refused with a console error and simply not
injected. Set both fontUrl and fontFamily: the first loads the file, the
second is what actually applies it.
Ordering, and why it does not bite you
Elements resolve their theme once, when they first connect, and bake it into
their shadow root. In the integration you will actually write, the loader
injects the element bundle the moment it sees the tag — so that bake usually
happens while your session fetch is still in flight, before init() has run.
The SDK corrects for this: init() re-applies the resolved theme to every
<drivecars-*> already in the document as inline custom properties, which
outrank the shadow root’s own rule. An element that upgrades later resolves the
same tiers itself. Both orderings end in the same place, so you do not have to
sequence anything.
This is why theme survives an element that mounted before init() even
though the product selection does not — see
Web SDK. Keep the elements in your
HTML and call init() afterwards regardless.
Right-to-left
Set lang="ar" (on the element, or init({ lang: 'ar' })) and the widget
renders right-to-left. This is a locale setting, not a theme token — there is no
direction token to set.
Check your own container CSS under RTL: the widget flips its own layout, but a
fixed margin-left on your wrapper will not.