# theme_color and background_color manifest members

> theme_color tints the title bar, status bar, and task switcher of an installed PWA; background_color fills the splash screen and window until the first paint.

`theme_color` is the colour the operating system and browser use for the chrome around an
installed app: the title bar on desktop, the status bar and task-switcher card on Android, and
the accent of the install dialog. `background_color` is the colour painted behind the app
icon on the splash screen and inside the window from launch until the page's own CSS has
painted. The two members exist so that nothing white flashes between the icon tap and the
first frame.

Chrome 46, Edge 79, Samsung Internet 5.0, Android WebView 46, and Firefox 79 on Android read
both members; Safari reads `theme_color` from iOS 15 and macOS 17 but has no consumer for
`background_color` (BCD `html.manifest.theme_color` and `html.manifest.background_color`).
Desktop Firefox installs no apps from the manifest and uses neither.

## Member

- **Type**: string holding a CSS `<color>`: a named colour, hex, `rgb()`, `hsl()`, or any
  other form the engine's CSS parser accepts. Chromium stores the parsed value as an opaque
  colour, so an alpha channel is discarded.
- **Default**: absent. With no `theme_color` the engine uses its own chrome colour; with no
  `background_color` the splash screen is white (Chromium) or the system background.
- **Example value**: `"theme_color": "#1a1a2e"`, `"background_color": "#1a1a2e"`.

A value the CSS parser rejects is dropped with `property 'theme_color' ignored, 'bluish' is not
a valid color.` (the same message names `background_color`), and the member then behaves as
absent (`manifest_parser.cc`).

The manifest value is the colour of record only until a page is showing. Once loaded, a
`<meta name="theme-color">` element in the document overrides `theme_color` for that page,
and because the element accepts a `media` attribute the page can declare one colour per
colour scheme, which the manifest cannot (MDN, `<meta name="theme-color">`). Chromium reads
`background_color` only for the splash screen and the pre-paint window fill, so the page's
`body` background must be set to the same value or a visible colour step appears at first
paint.

:::observed
Chrome 155 on macOS 26 (English UI), DevTools > Application > Manifest, **Presentation**
section: the rows **Theme color** and **Background color** render a colour swatch next to the
resolved value, and a manifest with `"theme_color": "bluish"` lists
`property 'theme_color' ignored, 'bluish' is not a valid color.` under **Errors and
warnings** while the **Theme color** row is left blank.
:::

## Examples

The examples share one dark-navy brand colour, `#1a1a2e`, and a light variant, `#f5f5fa`.

### One manifest colour, two page colours for light and dark mode

The manifest carries the colour used before any page exists, so it holds the default scheme.
The two `<meta>` elements take over once the page loads and switch with the user's system
setting.

```json
{
  "theme_color": "#1a1a2e",
  "background_color": "#1a1a2e"
}
```

```html
<meta name="theme-color" content="#f5f5fa" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#1a1a2e" media="(prefers-color-scheme: dark)">
```

On Android the status bar follows the matching `<meta>` as soon as the page paints; during
the splash screen it shows the manifest value regardless of the scheme.

### A splash screen that hands off without a colour step

Chromium composes the splash from `background_color`, the largest `icons` entry, and `name`.
Setting the document background to the same colour, before any stylesheet that might load
late, keeps the handoff from splash to page invisible.

```css
:root {
  color-scheme: dark;
  background: #1a1a2e;
}
@media (prefers-color-scheme: light) {
  :root {
    background: #f5f5fa;
  }
}
```

A light-scheme user still sees the navy splash, because the manifest has one value; the step
from navy to `#f5f5fa` happens at first paint and lasts one frame, which is the trade-off for
not shipping two manifests.

### Keeping the live theme colour and the page in sync

Nothing at runtime reports which colour the OS chrome adopted, so the page reads back its own
active `<meta>` element and uses that value for in-page surfaces such as a sticky header. When
no element matches, the code falls back to the manifest's documented colour.

```js
function activeThemeColor() {
  const dark = matchMedia('(prefers-color-scheme: dark)').matches;
  const metas = [...document.querySelectorAll('meta[name="theme-color"]')];
  const match = metas.find((m) => !m.media || matchMedia(m.media).matches === true);
  return match?.content ?? (dark ? '#1a1a2e' : '#f5f5fa');
}

document.querySelector('header').style.background = activeThemeColor();
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
  document.querySelector('header').style.background = activeThemeColor();
});
```

The listener matters on desktop, where the user can flip the system scheme while the app is
open; the OS title bar updates by itself, and this keeps the header with it.

## See also

- [icons manifest member](/reference/manifest/icons/)
- [name and short_name manifest members](/reference/manifest/name-short-name/)
- [PWAs on iOS and Safari](/reference/platforms/ios-safari/)
- [Web Application Manifest: theme_color member](https://www.w3.org/TR/appmanifest/#theme_color-member) (w3.org)
- [Customize your app's theme and background colors](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/How_to/Customize_your_app_colors) (developer.mozilla.org)