Theming
The widget uses CSS custom properties (variables) for all visual styling. It provides full style isolation - your page styles won't leak in, and the widget styles won't leak out.
Interactive Playground
Preview all theme customizations in real time using the playground. Adjust colors, fonts, spacing, and border radius — then copy the generated code.
How It Works
The theming system has three layers, applied in order of priority:
- CSS Defaults - Base values defined in the widget's
:hoststyles - Backend Theme API - Runtime overrides fetched from the SayTV API
- Consumer Overrides - Inline
styleattribute on the<saytv-chat>element (highest priority)
Dark Mode
Set the theme attribute to switch between light and dark:
<saytv-chat app-id="your-app-id" theme="dark"></saytv-chat>The dark theme automatically overrides backgrounds, text colors, borders, and shadows.
Custom Colors
Override any CSS variable via inline styles:
<saytv-chat
app-id="your-app-id"
theme="light"
style="
--stv-color-primary: #4f46e5;
--stv-color-accent: #10b981;
--stv-bg-primary: #fafafa;
--stv-text-primary: #111827;
"
></saytv-chat>CSS Custom Properties Reference
Brand Colors
| Property | Light Default | Description |
|---|---|---|
--stv-color-primary | #7e171c | Primary brand color |
--stv-color-primary-dark | #5d1014 | Darker primary variant |
--stv-color-accent | #0143d9 | Accent / interactive color |
--stv-color-accent-yellow | #f8dc22 | Secondary accent |
--stv-color-danger | #e03333 | Error / destructive actions |
--stv-color-success | #1fa84a | Success indicators |
--stv-color-info | #3b82f6 | Informational elements |
--stv-color-warning | #f59e0b | Warning indicators |
Backgrounds
| Property | Light | Dark | Description |
|---|---|---|---|
--stv-bg-primary | #fafaf8 | #0e0e12 | Main background |
--stv-bg-secondary | #f2f0ec | #161619 | Secondary surfaces |
--stv-bg-light | #f5f3ef | #1c1c22 | Elevated surfaces |
--stv-bg-hover | #efede8 | #212128 | Hover state |
--stv-bg-input | #ffffff | #1a1a20 | Input fields |
--stv-bg-disabled | #eae8e3 | #252530 | Disabled elements |
--stv-bg-overlay | rgba(0,0,0,0.5) | - | Modal overlays |
Text
| Property | Light | Dark | Description |
|---|---|---|---|
--stv-text-primary | #0d0d0f | #f0eff5 | Primary text |
--stv-text-secondary | #4a4852 | #b0aebe | Secondary text |
--stv-text-muted | #8b8a96 | #706e80 | Muted / hint text |
--stv-text-light | #ffffff | - | Text on dark backgrounds |
--stv-text-hashtag | #1d4ed8 | - | Hashtag text |
--stv-text-link | #0143d9 | - | Link text |
--stv-text-danger | #e03333 | - | Error text |
Borders
| Property | Light | Dark | Description |
|---|---|---|---|
--stv-border-color | #e5e2db | #2a2932 | Default borders |
--stv-border-light | #edebe5 | #252430 | Subtle borders |
--stv-border-focus | #0143d9 | - | Focus ring color |
--stv-border-error | #e03333 | - | Validation error |
Border Radius
| Property | Default | Description |
|---|---|---|
--stv-border-radius-xs | 0.25rem | Extra small (4px) |
--stv-border-radius-sm | 0.375rem | Small (6px) |
--stv-border-radius-md | 0.625rem | Medium (10px) |
--stv-border-radius-lg | 1rem | Large (16px) |
--stv-border-radius-xl | 1.5rem | Extra large (24px) |
--stv-border-radius-full | 50% | Circle |
Typography
| Property | Default | Description |
|---|---|---|
--stv-font-primary | System sans-serif stack | Body font family |
--stv-font-heading | System rounded stack | Heading font family |
--stv-font-mono | "SF Mono", "Fira Code", monospace | Monospace font |
--stv-font-size-xs | 0.6875rem | 11px |
--stv-font-size-sm | 0.8125rem | 13px |
--stv-font-size-base | 0.875rem | 14px |
--stv-font-size-md | 1rem | 16px |
--stv-font-size-lg | 1.25rem | 20px |
--stv-font-size-xl | 1.5rem | 24px |
--stv-font-weight-normal | 400 | Normal weight |
--stv-font-weight-medium | 500 | Medium weight |
--stv-font-weight-semibold | 600 | Semibold weight |
--stv-font-weight-bold | 700 | Bold weight |
--stv-line-height-tight | 1.25 | Compact line height |
--stv-line-height-normal | 1.5 | Default line height |
Custom Fonts
To use custom fonts, override the font family variables:
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<saytv-chat
app-id="your-app-id"
style="--stv-font-primary: 'Inter', sans-serif;"
></saytv-chat>The font @import or <link> must be on the host page since the Shadow DOM can inherit font-face declarations from the parent document.
Spacing
| Property | Default | Description |
|---|---|---|
--stv-spacing-xs | 0.25rem | 4px |
--stv-spacing-sm | 0.5rem | 8px |
--stv-spacing-md | 1rem | 16px |
--stv-spacing-lg | 1.5rem | 24px |
--stv-spacing-xl | 2rem | 32px |
--stv-spacing-2xl | 3rem | 48px |
Shadows
| Property | Light | Dark |
|---|---|---|
--stv-shadow-sm | 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04) | Stronger |
--stv-shadow-md | 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.05) | Stronger |
--stv-shadow-lg | 0 8px 24px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.06) | Stronger |
--stv-shadow-xl | 0 16px 48px rgba(0,0,0,0.14), 0 8px 16px rgba(0,0,0,0.08) | Stronger |
Transitions
| Property | Default | Description |
|---|---|---|
--stv-transition-fast | 150ms ease | Hover, focus states |
--stv-transition-normal | 250ms ease | General animations |
--stv-transition-slow | 400ms ease | Page transitions |
Extended Tokens
| Property | Default | Description |
|---|---|---|
--stv-button-border-radius | var(--stv-border-radius-md) | Button corners |
--stv-input-border-radius | var(--stv-border-radius-lg) | Input field corners |
--stv-footer-label-size | 0.6875rem | Footer nav label size |
--stv-skeleton-duration | 1.8s | Loading skeleton animation |
Examples
Brand Customization
<saytv-chat
app-id="your-app-id"
style="
--stv-color-primary: #7c3aed;
--stv-color-primary-dark: #5b21b6;
--stv-color-accent: #06b6d4;
--stv-border-radius-md: 12px;
--stv-border-radius-lg: 16px;
"
></saytv-chat>Rounded Theme
<saytv-chat
app-id="your-app-id"
style="
--stv-border-radius-xs: 0.5rem;
--stv-border-radius-sm: 0.75rem;
--stv-border-radius-md: 1rem;
--stv-border-radius-lg: 1.5rem;
--stv-border-radius-xl: 2rem;
--stv-button-border-radius: 9999px;
--stv-input-border-radius: 9999px;
"
></saytv-chat>Compact Spacing
<saytv-chat
app-id="your-app-id"
style="
--stv-spacing-xs: 0.125rem;
--stv-spacing-sm: 0.25rem;
--stv-spacing-md: 0.5rem;
--stv-spacing-lg: 0.75rem;
--stv-spacing-xl: 1rem;
"
></saytv-chat>