Dark mode theming
Dark mode theming
Bilberry widgets support a first-class dark mode. When enabled, the widgets adapt their surfaces, text, dividers, overlays, and disabled states for dark backgrounds — all derived from a small set of theme properties you provide.
Dark mode is opt-in via an explicit flag. Tenants that do not set the flag get exactly the same behavior they did before — nothing changes for existing light-mode integrations.
Enabling dark mode
Set darkMode: true on window.BilberryCustomTheme and provide the two
surface tokens that dark mode is built on:
window.BilberryCustomTheme = { darkMode: true, widgetBackgroundColor: '#2a2a3e', // the widget surface widgetTextColor: '#e0e0e0', // primary text color on that surface // …your other theme properties};That is the minimum configuration. With these three values set, all widgets will render with a dark surface and light text.
darkMode
- Type:
boolean - Default:
false(or unset)
When true, the widgets switch to dark-mode rendering. Leave unset or set
to false for normal light mode.
widgetBackgroundColor
- Type: hex color string
- Default:
#ffffff
The widget’s primary surface color. In dark mode this is the background color used for most widget panels (date pickers, dialogs, overlays, inputs).
widgetTextColor
- Type: hex color string
- Default: unset (falls back to
primaryColorfor primary text)
The primary text color used on top of widgetBackgroundColor. Set this to a
light tone (e.g. #e0e0e0 or #ffffff) when using a dark
widgetBackgroundColor.
Essential tenant adjustments for a polished dark theme
The darkMode: true flag handles most things for you. But a handful of
tenant-level properties still matter for a coherent dark design. The most
common pitfalls are below.
1. Keep lightestGrey lighter than widgetBackgroundColor
In dark themes, elevation reads as lighter — cards that sit “on top of” a
surface should be lighter than that surface, not darker. Many nested
surfaces in widgets (the checkout summary card, related-products cards)
derive their background from lightestGrey.
// ❌ Collapses into the outer widget surface — no visual elevation:widgetBackgroundColor: '#2a2a3e',lightestGrey: '#2a2a3e',
// ✅ Elevated cards clearly raise from the surface:widgetBackgroundColor: '#2a2a3e',lightestGrey: '#4a4a5e',A value roughly 10–30% lighter than widgetBackgroundColor works well.
2. Keep primaryColorContrast distinct from your page background
primaryColorContrast is used as a background color for several button
variants (e.g. the outlined “Book now” button in nested checkout cards). If
it matches your page background, those buttons will look like they sink into
the page.
// ❌ button bg (primaryColorContrast) matches page bg — button disappears:primaryColorContrast: '#1a1a2e', // and body bg is also #1a1a2e
// ✅ button bg lifts from the page:primaryColorContrast: '#1a1a2e', // and body bg is a different dark toneIf you have control over the surrounding page, ensure the page color and
primaryColorContrast are visibly different. Otherwise choose a
primaryColorContrast that contrasts with your page.
3. Provide a dark-tuned bookingSearchFormInputColor
By default the booking-search form has a white-background input. On a dark booking-search-form surface, white inputs are jarring. Explicitly set:
bookingSearchFormInputColor: '#2a2a3e',bookingSearchFormInputTextColor: '#e0e0e0',4. Adjust linkColor for dark backgrounds
The default light-mode link tone (usually a deep blue) may be hard to read on a dark surface. Pick a brighter link color:
linkColor: '#6ba3ff', // brighter blue, readable on dark surfacesAlso consider setting productCardLinkColor — it controls links rendered
inside product cards and does not inherit from linkColor if unset:
productCardLinkColor: '#6ba3ff',5. Use brighter heading colors
The default h1Color–h6Color values assume text on white. For dark mode,
set them explicitly:
h1Color: '#ffffff',h2Color: '#e0e0e0',h3Color: '#d0d0d0',h4Color: '#d0d0d0',h5Color: '#d0d0d0',h6Color: '#c0c0c0',bodyColor: '#e0e0e0',6. Nets / Nexi payment iframe — not yet supported
If you process payments through Nets (Nexi) on the inline embedded
checkout, the payment form runs inside an iframe served by Nets. Theming
that iframe to match a dark surface is not currently supported — the
widget does not forward any styling values to the Nets payment session, so
the iframe always renders in its light default regardless of darkMode.
Expect a light Nets iframe inside an otherwise-dark widget for now.
What dark mode adjusts automatically
When darkMode: true is set, you do not need to configure the following
— the widgets handle them for you:
- Menu, dialog, tooltip, popover backgrounds — derived from
widgetBackgroundColor. - Divider, hover, focus, and disabled-state colors — switched to dark-tuned defaults.
- Secondary text (labels, helper text, captions) — set to a readable bright on-surface tone so it stays legible without you having to override it.
- Loading skeletons and shimmer overlays — use a dark-mode default overlay.
- Date picker day, weekday label, and arrow colors — routed through
bodyColorand the dark-mode palette.
Common pitfalls
- Forgetting
darkMode: truewhen setting dark surface colors. Without the flag, the widgets stay in light mode and many components (menus, tooltips, disabled states) will render with light defaults on top of your dark surface. - Using the same value for
widgetBackgroundColorandlightestGrey— collapses the elevation hierarchy, making nested cards invisible. - Matching
primaryColorContrastto your page background — makes outlined CTAs (like “Book now”) blend into the page. - Leaving
bodyColor,h1Color…h6Colorat light-mode values — produces near-invisible dark-on-dark text. - Not setting
widgetTextColor— primary text falls back toprimaryColor, which is usually your brand accent color, not a readable text color. - Expecting the Nets / Nexi iframe to follow
darkMode— it doesn’t. Theming the inline Nets checkout is not yet supported; the iframe always renders in its light default.
Full working example
The example below uses the Bilberry 2026 brand palette (deep navy primary, warm-grey neutrals, Inter Tight typography) as a starting point. Replace the brand colors with your own and adjust surface tones to taste.
<script> window.BilberryCustomTheme = { // Dark mode opt-in + surface tokens darkMode: true, widgetBackgroundColor: '#1d2435', // dark navy widget surface widgetTextColor: '#ffffff',
// Brand — Bilberry Blue primaryColor: '#3d6da4', // mid-blue accent primaryColorContrast: '#ffffff', secondaryColor: '#788274', // warm grey secondaryColorContrast: '#ffffff',
// Elevated surface — clearly lighter than widgetBackgroundColor lightestGrey: '#3c423c',
// Booking widget bookingWidgetColor: '#162c4c', // deeper navy for visual weight bookingWidgetColorContrast: '#ffffff', bookingWidgetPrimaryColor: '#3d6da4',
// Booking search form — darkest navy, dark inputs bookingSearchFormColor: '#0e2645', bookingSearchFormInputColor: '#1d2435', bookingSearchFormInputTextColor: '#ffffff',
// Accommodation search form accommodationSearchFormColor: '#0e2645', accommodationSearchFormColorContrast: '#ffffff', accommodationSearchFormInputColor: '#1d2435', accommodationSearchFormInputTextColor: '#ffffff',
// Checkout header — distinct from page bg checkoutHeaderColor: '#081830', checkoutHeaderColorContrast: '#ffffff', checkoutHeaderPrimaryColor: '#3d6da4',
// Basket icon basketIconColor: '#3d6da4', basketIconTextColor: '#ffffff', basketIconBorderColor: '#3d6da4', basketIconCountColor: '#ffffff',
// Product cards — elevated surface productCardColor: '#162c4c', productCardTextColor: '#ffffff', productCardPrimaryColor: '#0e2645', // text on inline-summary CTA productCardPrimaryColorContrast: '#3d6da4', // bg on inline-summary CTA productCardAccentColor: '#3d6da4', productCardAccentColorContrast: '#ffffff', productCardIconColor: '#5e8bbc', productCardLinkColor: '#8aaad0',
// Input fields inputFieldTextColor: '#ffffff', inputFieldLabelColor: '#bfc7b9', inputFieldBorderColor: 'rgba(255, 255, 255, 0.2)',
// Typography fontFamily: '"Inter Tight", "Helvetica", "Arial", sans-serif', bodyColor: '#ffffff', h1Color: '#ffffff', h2Color: '#ffffff', h3Color: '#edf1e9', h4Color: '#edf1e9', h5Color: '#bfc7b9', h6Color: '#bfc7b9',
// Links — readable on dark linkColor: '#8aaad0',
// Status errorColor: '#f43f5e', warningColor: '#f59e0b', informationColor: '#382f50', };</script><script type="module" src="https://bilberry-widgets.b-cdn.net/v4/main.js"></script>Switching between light and dark at runtime
BilberryOverrideTheme can flip darkMode on the fly, which is useful if
your site has a theme toggle:
// Switch to dark modewindow.BilberryOverrideTheme({ darkMode: true, widgetBackgroundColor: '#2a2a3e', widgetTextColor: '#e0e0e0', // …other dark overrides});
// Switch back to lightwindow.BilberryOverrideTheme({ darkMode: false, widgetBackgroundColor: '#ffffff', // …your usual light theme});Only the specified properties are applied; other theme properties retain their current values. If you maintain two separate theme objects (light and dark), pass the entire object you want to switch to.