---
title: Customize your brand and theme
description: Apply your product identity, colors, logos, and theme settings to the documentation site.
url: https://docs.thally.io/guides/branding-and-theming
---

# Customize your brand and theme

Apply your product identity, colors, logos, and theme settings to the documentation site.

## Choose where to set your brand

Thally reads branding from three supported surfaces:

1. **Thally Cloud → Settings → Branding** — set the visual defaults for a
   managed site. Saved changes publish through the managed release workflow.
2. **The built-in `/admin/branding` page** — apply supported runtime overrides
   on a self-hosted site. These settings layer over the file defaults.
3. **Source files** (`src/data/site.ts`, `docs.json`) — keep product identity
   and portable defaults under Git review. A new or unconnected deployment
   starts from these values.

> **Which should I use?:**
Use **Thally Cloud** for a managed site, the built-in admin for a self-hosted
runtime override, and **source files** when the setting must remain portable
and version-controlled. Do not edit framework-owned components to change the
brand.

The dashboard overrides the structural theme, colors (accent, primary, and background), the logo, and the favicon. When a custom background color is set, secondary surfaces — muted backgrounds, borders, form inputs, and secondary text — automatically harmonize with that background. Tokens not covered by a dashboard override are defined in the source files described below.

## Site configuration

Versioned site identity and links live in `src/data/site.ts`. Edit this file to
set the product name, description, repository URL, and navigation links. A
managed site's saved Cloud settings can override supported identity fields in
the published runtime.

```ts
export const siteConfig: SiteConfig = {
  name: 'Your Product',
  description: 'Documentation for Your Product.',
  repoUrl: 'https://github.com/your-org/your-repo',
  links: [
    { label: 'Get started', href: '/quickstart' },
    { label: 'Support', href: 'https://github.com/your-org/your-repo/issues' },
    { label: 'GitHub', href: 'https://github.com/your-org/your-repo' },
    { label: 'Changelog', href: '/changelog' },
  ],
  // ...brand config
}
```

## Brand presets

Thally ships with two color presets:

- **`primary`** — green accent (emerald tones)
- **`secondary`** — purple accent (violet tones)

Switch between them by changing the `brandPreset` variable:

```ts
const brandPreset: BrandPresetKey = 'primary'  // or 'secondary'
```

## Brand accent (the color picker)

For a managed site, use **Settings → Branding** in Thally Cloud to choose
separate light and dark accents, review the preview, and save. Saved changes
publish through the managed release workflow; configuration changes require a
build, and uploaded assets publish from their saved repository revision. On a
self-hosted site, `/admin/branding` provides supported runtime overrides.

This overrides the accent and focus ring. A custom background color, if set separately, overrides the page canvas, sidebar, and card surfaces and derives harmonized values for muted, input, border, and muted-foreground. Tokens not covered by a managed override come from the source-file presets below. Use the dashboard for a live adjustment and the files as the versioned baseline.

Card and tile icons use the brand accent by default. To make them neutral,
merge this setting into `docs.json`:

```json
"appearance": {
  "contentIcons": "neutral"
}
```

Use `iconColor="accent"` or `iconColor="neutral"` on an individual Card or Tile
to override the site-wide choice.

## Custom colors

To define your own palette, edit the `brandPresets` object in `site.ts`. Each preset has `light` and `dark` variants with these tokens:

| Token | Purpose |
| --- | --- |
| `background` | Page background |
| `card` | One-step elevated card and popover surface (optional) |
| `foreground` | Primary text color |
| `muted` | Subtle background and hover states |
| `mutedForeground` | Secondary text color (optional) |
| `border` | Border color |
| `accent` | Primary brand color (links, active states) |
| `accentForeground` | Text on accent backgrounds |
| `accent2` | Secondary accent for charts and restrained emphasis (optional) |
| `accent2Foreground` | Text on secondary-accent backgrounds (optional) |
| `input` | Form control surface (optional) |
| `sidebar` | Sidebar surface (optional) |
| `ring` | Focus ring color |
| `sidebarActiveBg` | Active sidebar item background |
| `sidebarActiveText` | Active sidebar item text |

Most colors are hex values (e.g. `#10B981`) which are auto-converted to HSL. Optional tokens fall back to their nearest core surface, so existing palettes remain valid. The sidebar state tokens (`sidebarActiveBg`, `sidebarActiveText`) may also use raw HSL strings with opacity (e.g. `'152 60% 88% / 0.55'`) for translucent effects.

## Site background colors

In **Settings → Branding → Site background** in Thally Cloud, choose a separate
background color for light and dark mode. Review the previews and contrast
readouts before saving. The override covers the page canvas, header, sidebar,
and cards, and derives companion muted, input, border, and secondary-text tones.
Primary text and other semantic colors retain their existing palette, so check
readability in both themes. Choose **Use site default** to remove an override
and return to the palette in `src/data/site.ts`. Changes apply in the next
published version; upgrade older runtimes before using background overrides.

## Dark mode and appearance locking

Dark mode is handled automatically. By default, readers toggle it with the theme switch in the top bar. Your brand palette defines both light and dark variants, so both themes stay on-brand.

You can control the initial color mode and whether readers can change it by adding `appearance` settings to `docs.json`:

```json
{
  "appearance": {
    "default": "dark",
    "showToggle": false,
    "contentIcons": "accent"
  }
}
```

| Key | Type | Default | Purpose |
| --- | --- | --- | --- |
| `default` | `"system"`, `"light"`, or `"dark"` | `"system"` | Initial color mode for new readers. |
| `showToggle` | boolean | `true` | Show the theme toggle in the top bar. When `false`, the mode set by `default` is enforced and the toggle is hidden. |
| `contentIcons` | `"neutral"` or `"accent"` | `"accent"` | Card and tile icon treatment. |

When `showToggle` is `false`, a flash-free pre-paint script runs in the document head so the locked mode is applied before the page renders. Reader preferences stored from a previous visit are ignored while the toggle is hidden.

With `"default": "system"` and the toggle hidden, the site follows OS theme
changes. Thally Cloud managed sites can override these settings per field under
**Settings → Branding**, taking effect in the next published version.

## Background images and decoration

Add a site-wide background behind the document surface with the `background` object in `docs.json`:

```json
{
  "background": {
    "image": "/images/bg-light.webp",
    "imageDark": "/images/bg-dark.webp",
    "decoration": "grid"
  }
}
```

| Key | Type | Default | Purpose |
| --- | --- | --- | --- |
| `image` | string | — | Background image for light mode. A path under `public/` or an HTTPS URL. Public paths must use PNG, JPG, JPEG, or WebP. HTTPS URLs need no file extension. |
| `imageDark` | string | — | Background image for dark mode. Falls back to `image` when omitted. |
| `decoration` | `"none"`, `"grid"`, or `"gradient"` | `"none"` | Overlay decoration rendered on top of the background image. |

The background image is rendered as a fixed, full-viewport layer behind the page. When a background image is active, content and sidebar surfaces become translucent to keep text legible over the imagery. Omit the `background` object entirely (or set all fields to their defaults) to leave the site surfaces unchanged.

HTTPS images load directly in the reader’s browser and cannot include URL
credentials. Public-relative paths cannot contain traversal segments; local SVG
backgrounds are unsupported. A dark image without a shared image appears only
in dark mode. Decorations also work without an image.

Thally Cloud managed sites can supply background images and decoration under
**Settings → Branding**, with managed values taking per-field precedence over
the repository config in the next published version. Upgrade older runtimes
before using these options.

## Logo & favicon (light and dark)

For a managed site, upload the logo and favicon under **Settings → Branding**.
The accepted formats and upload limits are shown beside each control. Saved
assets take effect in the next published version. For a self-hosted runtime
override, use `/admin/branding`.

You can upload **separate assets for light and dark mode** — both a logo and a favicon per mode. The site serves the variant that matches the viewer's theme; if you upload only the light-mode asset, dark mode falls back to it, so a single upload still works. This is how a wordmark that reads on a white header can be swapped for a light-on-dark version at night.

### Source-controlled fallbacks in docs.json

When no managed or admin asset is configured, Thally checks `docs.json` for
portable logo and favicon paths before falling back to the built-in defaults.

**Logo.** Set `navbar.logo` to control the logo image, site name visibility,
and the right-of-logo label:

```json
"navbar": {
  "logo": {
    "light": "/brand/logo-light.svg",
    "dark": "/brand/logo-dark.svg",
    "showTitle": true,
    "rightText": "Docs"
  }
}
```

Set `showTitle` to `false` to hide the site name text beside the logo (the
Cloud control is **Hide site name text**).

See [Customize the navbar and footer](/guides/navbar-and-footer) for all options.

**Favicon.** Set `favicon` at the root level of `docs.json`:

```json
{
  "favicon": {
    "light": "/brand/favicon-light.svg",
    "dark": "/brand/favicon-dark.svg"
  }
}
```

Paths must point to files inside `public/`. External URLs, path traversal,
query strings, and backslashes are rejected. When no source is configured at
any level, the runtime uses its built-in mark and the name from `siteConfig`.
Keep framework-owned logo components unchanged; use the branding controls or
the docs.json configuration instead.

## Fonts

The body and heading fonts are set from a `fonts` block in `docs.json` — any Google Font, no code changes:

```json
"fonts": {
  "body": { "family": "Plus Jakarta Sans", "weight": ["400", "500", "600", "700"] },
  "heading": { "family": "Outfit", "weight": ["600", "700"] }
}
```

The defaults are **Inter** for body text and **JetBrains Mono** for code. See
[Custom fonts](/guides/custom-fonts) for the supported configuration.

## Version switcher

If your docs have multiple versions, add a `versions` array to `siteConfig`. A dropdown appears in the top bar automatically.

```ts
export const siteConfig: SiteConfig = {
  // ...
  versions: [
    { label: 'v2.0', href: '/', current: true },
    { label: 'v1.0', href: 'https://v1.docs.yourproduct.com' },
  ],
}
```

- **`label`** — text shown in the dropdown
- **`href`** — URL for that version (`/` for the current site, or a full URL for older versions)
- **`current`** — marks the active version (exactly one should be `true`)

The switcher is hidden when fewer than two versions are defined.

## Edit on GitHub

When `repoUrl` in `siteConfig` points to a real GitHub repository, doc pages can show "Edit this page" and "Report an issue" links in the page-actions rail (beside the table of contents, or below the article on centered and wide layouts). Managed sites show each link only when its corresponding **Settings → Feedback** option (edit suggestions or issue reporting) is enabled. The edit link opens the MDX source file on the `main` branch; the issue link opens a pre-titled GitHub issue.

```ts
export const siteConfig: SiteConfig = {
  repoUrl: 'https://github.com/your-org/your-docs',
  // ...
}
```

## Structural themes

Structural themes control the shape and density of the UI — border radius, sidebar active item style, and navigation tab appearance. They are independent of brand colors, so any color preset works with any structural theme.

Set the baseline theme in `docs.json`, or choose it under **Settings → Branding**
in Thally Cloud and publish the updated configuration:

```json
{
  "theme": "maple"
}
```

| Theme | Character | Border radius | Sidebar active | Nav tabs |
| --- | --- | --- | --- | --- |
| `default` | Balanced | Medium (1 rem) | Left indicator + tinted background | Pill with underline accent |
| `maple` | Warm, rounded | Large (1.25–2 rem) | Tinted background pill only | Full-width pill, no underline |
| `sharp` | Technical, precise | Tight (0.25 rem) | Left indicator only, no background | Flat tabs, underline accent |
| `minimal` | Content-first | None (0) | Text color change only | Text tabs, underline accent |

> **Tip:**
Structural themes and brand colors are orthogonal. You can use the `maple` theme with either the `primary` (green) or `secondary` (purple) brand preset.

## Layout tokens

Fine-tune spacing and widths in `src/config/layout.ts`:

- Sidebar width
- Content max-width
- Padding and gaps
- Typography scale for meta text