Native Window Styling
Customize desktop windows with HTML chrome, native backdrop materials, transparency, and frame styles.
On this page
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.
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
:hoverstates 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 CSSapp-regiondefinitions 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:
///
/// 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. |
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.
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:
///
/// 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 hostedView(is_transparent = trueinViewConfig), 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:
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:
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::Squareremoves 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.