Theming
FanFest supports full visual customization through a two-layer theming system. Admins can customize every color in the interface using the built-in theme editor, apply community-specific themes, or select from pre-built theme presets.
Theming Architecture
Two-Layer System
FanFest themes operate on two token layers:
V1 (legacy) --- 12 brand tokens (
--brand-primary,--brand-bg-main, etc.) that control the high-level brand palette. These are the original theming tokens and are still read by older components.V2 (design tokens) --- 64 granular tokens covering Background, Surface, Fill, Text, Border, and Icon categories. These provide fine-grained control over every UI element. See the Design Tokens reference for the full list.
Both layers are stored together in a single theme configuration record. When a theme is loaded, both V1 and V2 tokens are applied as CSS custom properties on the root element, so all components --- legacy and current --- render correctly.
Theme Hierarchy
Themes apply at three levels, with more specific levels overriding broader ones:
- Channel theme --- The default theme for the entire channel. Set by the Admin in Channel Settings.
- Community theme --- An optional override for a specific community within the channel. When a fan selects a community, its theme replaces the channel theme.
- Show theme --- A per-show override chosen during show creation. The Host can pick the channel theme, a community theme, or a fully custom theme for each show.
When the frontend loads, the theme store fetches the appropriate theme via the getBrandTheme GraphQL query and injects all token values as CSS custom properties.
Theme Editor
The built-in theme editor is available to Admins in Channel Settings. It provides a visual interface for customizing all 64 V2 design tokens with live preview.
Accessing the Theme Editor
- Navigate to your Channel Settings page
- Open the Appearance or Theme section
- Click Customize Theme to open the color editor modal
How It Works
The editor organizes tokens into sections matching the design token categories:
- Background ---
bg,bg_secondary - Surface ---
surface,surface_hover,surface_secondary,surface_secondary_hover,surface_accent - Fill --- All 20 fill tokens (brand, dark, status, medal colors)
- Text --- All 14 text tokens
- Border --- All 15 border tokens
- Icon --- All 8 icon tokens
Each token is displayed with its CSS custom property name (e.g., --fill-brand), a hex color input field, and a color picker circle. You can type hex values directly or use the visual picker.
The editor validates input in real time --- invalid hex values are highlighted and cannot be saved.
Saving Themes
When you click Save, the editor calls the upsertBrandTheme GraphQL mutation, which persists all token values to the database. The theme store then refetches the active theme so the entire interface updates immediately.
Community Themes
Each community within a channel can have its own theme. This is useful for sports organizations where each fan community (team) has distinct brand colors.
How Community Themes Work
- Every community is automatically assigned a theme configuration when the channel loads community themes
- Admins can customize each community's theme independently through Channel Settings
- When a fan selects a community, the theme switches to that community's colors
- The community theme overrides the channel theme across all surfaces
Setting a Community Theme
- Go to Channel Settings
- Navigate to the Communities section
- Select a community to customize
- Use the theme editor to set that community's colors
Show Themes
When creating a show, the Host can choose which theme to apply:
- Channel theme --- Use the channel's default colors
- Community theme --- Select from any community theme available on the channel via a dropdown
- Custom theme --- Pick a custom background tint color for that specific show
The show theme is saved via the upsertShowTheme GraphQL mutation and applies only to that show's viewer experience.
Programmatic Theming
Fetching the Active Theme
query BrandTheme($channel_id: String, $community_id: String) {
getBrandTheme(channel_id: $channel_id, community_id: $community_id) {
... on BrandTheme {
id
fill_brand
fill_brand_hover
bg
bg_secondary
text
text_secondary
# ... all 64 V2 tokens + 12 V1 tokens
}
... on NotFoundError {
error
}
}
}Pass channel_id to get the channel theme, or community_id to get a community-specific theme.
Updating a Theme
mutation UpsertBrandTheme($data: UpsertBrandThemeInput!) {
upsertBrandTheme(data: $data) {
id
}
}The UpsertBrandThemeInput accepts all 64 V2 token fields plus the 12 V1 legacy fields as hex color strings. Pass the theme id to update an existing theme, or omit it to create a new one.
Admin Permission Required
The upsertBrandTheme mutation requires Admin permissions on the channel.
Applying Tokens in Code
Tokens are applied as CSS custom properties on the root element by the theme store. To use them:
In CSS:
.my-component {
background: var(--fill-brand);
color: var(--text-on-brand);
}In Tailwind:
<div class="bg-fill-brand text-text-on-brand border border-border-brand">
Themed content
</div>Reading values in JavaScript:
const brandColor = getComputedStyle(document.documentElement)
.getPropertyValue('--fill-brand')
.trim();Theme Defaults
When no custom theme is configured, FanFest uses a default dark theme with the following key values:
| Token | Default | Description |
|---|---|---|
--bg | #100B1E | Deep purple-black background |
--fill-brand | #875AF9 | FanFest indigo (primary brand) |
--text | #FFFFFF | White primary text |
--fill-success | #0DD32D | Green |
--fill-critical | #ED3024 | Red |
--fill-warning | #FF8D3A | Orange |
See the full default value table in Design Tokens.
