Branding
Customize how your tour looks and feels
Built-in branding and theming system gives you complete control over the visual appearance. Change colors, fonts, and styling to match your brand or create a unique experience.
Default theme
There is one main theme with two variants:
Default Light
Clean, distraction-free light aesthetic with high legibility and soft gray accents.
Default Dark
Refined dark aesthetic with grayscale palette and sans-serif typography.
Using a theme
Set the theme in your metadata.json:
{
"id": "barcelona",
"themeId": "default-light"
}Or "default-dark" for the dark theme.
That's it! The entire app adopts the theme—cards, buttons, player, sheets, everything.
What you can customize
Themes control virtually every visual aspect:
| Area | What You Control |
|---|---|
| Colors | Backgrounds, text, borders, accents |
| Typography | Font families, sizes, weights |
| Cards | Background, borders, shadows, corner radius |
| Buttons | Primary, secondary, and download button styles |
| Mini Player | Progress bar, controls, transcription text |
| Bottom Sheets | Background, handle color, title styling |
| Step Indicators | Active, inactive, and completed states |
| Branding | Custom logo on start screen |
You can tweak individual properties or create an entirely new theme from scratch. It's up to you how far you want to go.
Theme structure
Themes are TypeScript objects with a specific structure. Here's a simplified view:
const myTheme = {
id: 'my-theme',
name: 'My Theme',
header: { /* colors, progress bar */ },
mainContent: { /* background */ },
cards: { /* colors, typography, shadows */ },
buttons: { /* primary, secondary styles */ },
miniPlayer: { /* player controls, progress */ },
sheets: { /* bottom sheet styles */ },
typography: { /* font families */ },
colors: { /* semantic color palette */ },
// ...and more
};Each section controls a specific part of the UI. We'll cover all of them in Theme Reference.
Quick customization
Want to just change the accent color? You don't need a full custom theme.
The easiest approach is to copy an existing theme and modify it:
Copy a theme file
cp src/theme/themes/default-light.ts src/theme/themes/my-brand.tsChange the accent color
Find all instances of the primary color (like #6366F1) and replace with your brand color.
Register the theme
Add to src/theme/themes/index.ts:
import { myBrandTheme } from './my-brand';
export const themes = {
'default-light': defaultLightTheme,
'default-dark': defaultDarkTheme,
'my-brand': myBrandTheme,
};Use it
Set "themeId": "my-brand" in your metadata.json.
Per-language themes
You can use different themes for different languages by overriding in the language file:
// es.json
{
"id": "barcelona",
"language": "es",
"themeId": "warm-theme"
}Spanish visitors would see warm-theme while others see whatever's in metadata.json.
Custom fonts
Themes support custom fonts from Google Fonts (or self-hosted):
typography: {
fontFamily: {
sans: ['Roboto', 'system-ui', 'sans-serif'],
heading: ['Playfair Display', 'serif'],
numbers: ['JetBrains Mono', 'monospace'],
},
}Remember to add the font to index.html:
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&display=swap" rel="stylesheet">Branding: Custom logo
Display your logo on the tour start screen:
branding: {
logoUrl: 'https://your-domain.com/logo.svg',
showLogoBorder: true,
logoSize: 'fit', // or 'original'
}| Option | Effect |
|---|---|
showLogoBorder: true | Logo in a rounded rectangle with shadow |
showLogoBorder: false | Logo displayed directly, no container |
logoSize: 'fit' | Constrained to 48x48px |
logoSize: 'original' | Natural image dimensions |
What you can't customize
Some things are intentionally fixed:
- Component layout - Where elements are positioned
- Animation types - Transitions use spring physics
- Icon shapes - Only colors are themeable
- Spacing between elements - Some spacing is hardcoded
These constraints ensure the app remains usable and visually consistent regardless of theme choices.