You can adapt pages to the user's preferred light or dark appearance. Pages style themselves using standard CSS media queries— the same way they do in a desktop browser.

You tell the library which color scheme is active, and views update whenever that setting changes.

## Styling Pages for Each Scheme

Pages define default styles for the light theme and override them inside the CSS `prefers-color-scheme` media query:

```css
body {
  background: white;
  color: #222;
}

@media (prefers-color-scheme: dark) {
  body {
    background: #1e1e1e;
    color: #ddd;
  }
}
```

> 🚧 Built-in Elements Stay Light
>
> The color scheme is only a signal to the page. Built-in elements like form controls, scrollbars, and the default canvas keep their light appearance under a dark scheme. The page must style those controls itself.

## Managing the System Scheme

Each `Renderer` maintains a single system color scheme, which defaults to `ColorScheme::Light`.

By default, every `View` uses `ColorScheme::Auto` and follows the system scheme. The library never inspects the OS theme directly— without AppCore, pages remain light until you set a new scheme.

When the system scheme changes, the renderer re-evaluates media queries across every view following the system setting. Styles recalculate and `matchMedia` change listeners fire during the next rendering update.

### Automatic Tracking in AppCore

When you use AppCore, `App::Create()` reads the OS appearance at startup and forwards it to the renderer. On Windows and macOS, AppCore monitors appearance changes and updates the system scheme automatically.

For details on what `App::Create()` initializes, see [App Lifecycle and Settings](/docs/2.0/app-lifecycle-and-settings).

Tracking behavior varies by OS:

- **Windows** — Tracks changes live by following the Windows app mode setting.
- **macOS** — Tracks changes live by following the system appearance setting.
- **Linux** — Reads the `GTK_THEME` environment variable once at startup, setting dark if the variable names a dark theme and light otherwise. AppCore does not track changes afterward on Linux— call `Renderer::set_system_color_scheme()` when your desktop environment signals a theme change.

### Setting the Scheme Manually

If you don't use AppCore, or when handling theme changes on Linux, call `Renderer::set_system_color_scheme()`:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

RefPtr<Renderer> renderer;

void ApplySystemTheme(bool is_dark) {
  ///
  /// Call this at startup and again whenever the OS tells us the theme
  /// changed. Every View still on Auto picks up the new scheme.
  ///
  renderer->set_system_color_scheme(
      is_dark ? ColorScheme::Dark : ColorScheme::Light);
}
```

> 🚧 AppCore Overwrites Manual Changes
>
> On Windows and macOS, AppCore overwrites any scheme you set here on the next OS appearance change. To force a scheme under AppCore, override the view instead— see [Overriding the View Scheme](#content-overriding-the-view-scheme).

## Overriding the View Scheme

To override the color scheme for an individual view (such as a dark tool panel or preview pane), call `View::set_preferred_color_scheme()`:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

///
/// Called from an in-app Appearance setting. Passing ColorScheme::Auto
/// hands the View back to the system scheme.
///
void SetAppearance(View* view, ColorScheme scheme) {
  view->set_preferred_color_scheme(scheme);
}
```

To set the scheme when creating a view, assign `ColorScheme::Light` or `ColorScheme::Dark` to `ViewConfig::preferred_color_scheme`.

A view forced to light or dark ignores changes to the system scheme— changing the view's scheme re-evaluates media queries on that page during the next rendering update.

> 👍 Matching Window Backdrops
>
> When using an AppCore window with a backdrop material, `BackdropOptions::theme` sets the material's light or dark look and controls the title bar on Windows and macOS. The backdrop setting is separate from the page scheme— force both together so the material and the page match. For a walkthrough, see [Native Look and Feel](/docs/2.0/native-look-and-feel).

## Preventing White Loading Flashes

To prevent a view from flashing white while a page loads, configure a dark base background color:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

ViewConfig MakeDarkViewConfig() {
  ///
  /// A dark base under the page, so nothing flashes white while it loads.
  ///
  ViewConfig view_config;
  view_config.background_color = "#1e1e1e";
  return view_config;
}
```

The base color renders beneath the page before styling applies, and remains visible wherever the page paints nothing. If left unset, the base color defaults to opaque white.

If `ViewConfig::is_transparent` is set to `true`, transparency takes precedence and the view keeps a fully transparent base.

To style the native window frame and title bar around the view, see [Native Window Styling](/docs/2.0/native-window-styling).
