You can style an AppCore window beyond the standard OS frame using HTML chrome, native backdrop materials, and per-pixel transparency. The window keeps standard system behaviors like dragging, edge resizing, and window snapping.

Styling behavior varies by OS— each section notes its platform requirements and fallbacks.

## Drawing Custom Chrome

Set `WindowFlags::CustomChrome` when creating a window to draw the title bar and caption buttons using HTML and CSS. The hosted page covers the full window surface, eliminating the need for `WindowFlags::Titled`.

The window retains standard frame behaviors, including edge resizing and window snapping. AppCore ignores `WindowFlags::CustomChrome` with a warning on popup or fullscreen windows, and when combined with `WindowFlags::Borderless`.

> 👍 Custom Chrome Walkthrough
>
> For a step-by-step walkthrough covering HTML title bars, drag regions, caption buttons, and state changes, see [Custom Window Chrome](/docs/2.0/custom-window-chrome).

### Custom Chrome API

Native code and the page coordinate window dragging, caption buttons, and window state through several methods, properties, and listeners:

| API | Description |
|---|---|
| `Window::SetHitTestRegions()` | Registers caption drag areas and button regions in window content logical pixels. Each call replaces the full list, so register them again whenever the chrome changes layout (eg, in `WindowListener::OnResize()`). |
| `app-region: drag` / `app-region: no-drag` | Marks draggable and non-draggable areas directly inside the page's CSS. |
| `Window::SetWindowButtons()` | Controls which window actions are available to the user across caption buttons, title-bar double-clicks, the window menu, OS shortcuts, and snap gestures. |
| `Window::BeginDragMove()` / `Window::BeginDragResize()` | Starts an OS window move or resize operation while you handle a left mouse-button press. |
| `Window::window_control_bounds()` / `Window::SetWindowControlInset()` | Queries the area covered by the native window buttons and adjusts their position on macOS. macOS is the only platform that keeps native buttons floating over page content. |
| `WindowListener::OnWindowStateChanged()` / `WindowListener::OnActivationChanged()` | Notifies you when the window maximizes, restores, or becomes active or inactive so the chrome can update its appearance. |

### Defining Hit-Test Regions

Hit-test regions follow several interaction and precedence rules:

- **Caption regions** — Dragging moves the window, and double-clicking maximizes or restores it. The page receives no mouse input over these regions.
- **Button regions** — Pointer hover events pass through to the page so CSS `:hover` states work, but clicking performs the window action.
- **Overlapping regions** — Later entries in the array take precedence over earlier ones.
- **Resize edges** — The window's native resize edges always take precedence over custom regions.
- **Native precedence** — Regions registered through `Window::SetHitTestRegions()` override CSS `app-region` definitions where they overlap.
- **Disabled buttons** — A button region is ignored if its corresponding action was disabled with `Window::SetWindowButtons()`.

### Platform Differences

Custom chrome behaviors and window controls vary across desktop platforms:

| Behavior | Windows | macOS | Linux |
|---|---|---|---|
| Frame shadow and rounded corners | Preserved. | Preserved. | No system shadow (the window manager controls corner rounding). |
| Right-click on a caption region | Opens the system window menu. | Passed to the page. | Passed to the page. |
| Hovering a maximize region | Windows 11 displays the snap layouts flyout. | No effect. | No effect. |
| Native window buttons | None (the page renders its own buttons). | Floats native buttons over page content. Query `Window::window_control_bounds()` after calling `Window::SetWindowControlInset()` and inside `WindowListener::OnResize()`. | None (the page renders its own buttons). |
| `Window::BeginDragResize()` | Supported. | Unsupported (the OS provides its own resize edges). | Supported. |
| Display server | Any. | Any. | X11 or XWayland only (native Wayland is unsupported). |

## Applying Backdrop Materials

Call `Window::SetBackdrop()` to render a native OS material behind the window's content:

```cpp
///
/// Back the main window with the native window material, tinted by a
/// translucent dark background.
///
RefPtr<Window> window = Window::Create(App::instance()->main_monitor(), 1024,
    768, false, WindowFlags::Titled | WindowFlags::Resizable);
window->SetBackdrop(BackdropMaterial::Window);
window->SetBackgroundColor("rgba(32, 32, 32, 0.5)");
```

Choose the material that matches the purpose of the window. The host OS determines how each surface looks.

| Material | Purpose |
|---|---|
| `BackdropMaterial::Window` | Main application windows. |
| `BackdropMaterial::Popup` | Menus, dropdowns, and flyout panels. See [Popups and Dialogs](/docs/2.0/popups-and-dialogs). |
| `BackdropMaterial::Overlay` | Heads-up displays and on-screen overlays (eg, a volume indicator). |
| `BackdropMaterial::None` | No material (the default). |

### Setting Transparency and Tint

A backdrop material shows only where content above it is transparent— both the hosted `View` and the web page must have transparent backgrounds.

Leaving the window background color unset displays the material untinted. Calling `Window::SetBackgroundColor()` with a translucent color tints the material (lower alpha values allow more of the underlying material to show through).

### Backdrop Options

Pass `BackdropOptions` as the second argument to `Window::SetBackdrop()` to adjust appearance, theme, and fallback styling:

| Option | Description |
|---|---|
| `variant` | Sets the visual style. `BackdropVariant::Frosted` creates a live blur of content behind the window, while `BackdropVariant::Matte` produces an opaque surface tinted by the desktop wallpaper and theme. |
| `theme` | Sets `BackdropTheme::Light` or `BackdropTheme::Dark` to force a specific appearance regardless of the active OS theme. |
| `fallback_color` | Specifies a solid fill color used when the platform cannot render live materials. Leaving this unset uses a color derived from the active theme. |
| `rendition` | Sets `BackdropRendition::Vivid` for a more colorful style when using opaque window materials. |

To adapt page content to the current OS theme, see [Dark Mode and Color Schemes](/docs/2.0/dark-mode-and-color-schemes).

### Handling Material Fallbacks

When an OS version or window configuration cannot render the requested material, the library substitutes the closest available appearance, falling back to a solid fill in the theme color. Windows that cannot display any material report `BackdropMaterial::None`.

Call `Window::effective_backdrop()` and `Window::effective_backdrop_variant()` to inspect the material and variant currently rendering on screen. To respond when the active material changes at runtime (eg, if the user toggles OS transparency settings), implement `WindowListener::OnBackdropChanged()`.

### Setting the Backdrop State

By default, a backdrop material follows the window's activation— displaying its active look while the window is active and its inactive look while it is inactive.

Call `Window::SetBackdropState()` with `BackdropState::Active` or `BackdropState::Inactive` to force that look regardless of activation. Not every platform honors this setting— see the platform differences below.

### Platform Differences

Backdrop material behaviors and options vary across desktop platforms:

| Behavior | Windows | macOS | Linux |
|---|---|---|---|
| Setting a background color over a material | Stops the material from tracking later OS theme changes. | Stops the material from tracking later OS theme changes. | Continues updating when the OS theme changes. |
| `BackdropOptions::theme` | Sets the light or dark appearance of both the material and the title bar. | Sets the light or dark appearance of both the material and the title bar. | Sets the light or dark appearance of the material only. |
| `BackdropRendition::Vivid` | Uses the colorful style found in apps like File Explorer. | Ignored. | Ignored. |
| Fullscreen and CPU-rendered windows | Show no material and report `BackdropMaterial::None`. | Show no material and report `BackdropMaterial::None`. | Show at least the fallback fill and report the requested material. |
| Inactive look and `Window::SetBackdropState()` | Shows the fallback fill when inactive. Titled windows on newer Windows 11 builds ignore `Window::SetBackdropState()` because the OS controls the inactive look. | Honored. | Ignored. |

## Creating Transparent Windows

Pass `WindowFlags::Transparent` during creation to enable per-pixel transparency for shaped windows or soft-edged overlays:

```cpp
///
/// A borderless, see-through window hosting a transparent View.
///
RefPtr<Window> window = Window::Create(App::instance()->main_monitor(), 400,
    300, false, WindowFlags::Borderless | WindowFlags::Transparent);

ViewConfig config;
config.is_transparent = true;
window->AddPanel({}, config)->view()->LoadURL("file:///shaped.html");
```

You can only set `WindowFlags::Transparent` when creating the window— it cannot be toggled later. Transparent pixels reveal underlying desktop content, allowing irregular window silhouettes and shaped cutouts.

Leaving the window background color unset leaves transparent areas completely see-through. Setting a background color with `Window::SetBackgroundColor()` draws that color across the window while honoring its alpha channel.

> 📘 Transparent Layers
>
> Per-pixel transparency requires three layers to be transparent: the window (`WindowFlags::Transparent`), the hosted `View` (`is_transparent = true` in `ViewConfig`), and the page styles (`html, body { background: transparent; }`). Web pages draw on an opaque white background by default.

> 🚧 Mouse Input and Transparency
>
> Mouse input isn't tested against pixel transparency— clicks over transparent pixels still reach the window rather than passing through to whatever is behind it. CPU-rendered windows on Windows are the only exception.

### Platform Differences

Transparent windows behave differently depending on the host OS and renderer:

| Platform and Renderer | Behavior |
|---|---|
| Windows (CPU-rendered) | Always borderless— frame flags are ignored with a warning. Clicks over fully transparent pixels pass through to the window beneath, and the window never receives a shadow. |
| Windows (GPU-rendered) | The window receives mouse input across its entire rectangle. Windows start with square corners and no shadow, but rounding the window with `Window::SetCornerStyle()` adds the system shadow. |
| macOS | The window receives a system shadow shaped to its visible content. Calling `Window::SetCornerStyle()` with `CornerStyle::Square` removes the shadow. |

## Setting the Title Bar Color

Call `Window::SetCaptionColor()` to tint the window's system title bar:

```cpp
window->SetCaptionColor("#282828");
```

The library automatically selects a contrasting title text color.

Setting a caption color covers any backdrop material across the title bar. Passing an unset color restores the default system appearance— allowing any backdrop material beneath to show through.

> 📘 Platform Support
>
> Setting the title bar color works on Windows 11 only. macOS, Linux, and windows without a system title bar (such as custom-chrome windows) ignore the call.

## Setting Corner Styles

Call `Window::SetCornerStyle()` to adjust the window's corner geometry:

```cpp
window->SetCornerStyle(CornerStyle::Rounded);
```

Pass `CornerStyle::Rounded` or `CornerStyle::Square` to change the corner shape, or `CornerStyle::Default` to follow the OS standard.

On transparent windows, the library rounds hosted web content to match the chosen corners.

### Platform Differences

Corner styling and window shadows vary by platform:

- **Windows** — On custom-chrome windows and GPU-rendered transparent windows, the system shadow requires rounded corners. Setting `CornerStyle::Square` removes both the rounded corners and the shadow.
- **macOS** — Corner styles affect transparent windows only. Other windows retain standard OS corners and shadows.
- **Linux** — Window managers control all window corners and decorations.
