docs

Native Look and Feel

Match the native look and feel of the host OS in desktop applications.

On this page

In Building a Desktop App, we introduced the options for matching the look and feel of the host OS.

This tutorial walks you through the 10-native-look-and-feel sample to show how native behaviors, backdrop materials, popup menus, theme tracking, and message boxes work on a window.

The sample also draws custom window chrome in HTML, which we cover in Custom Window Chrome.

Build and Run the Sample

First, build and run the sample to see the interface in action.

👍 Build the samples

If you haven't built the SDK samples yet, see Trying the Samples.

Open main.cpp, assets/chrome.html, and assets/menu.html from the SDK's samples folder to follow along with the code.

When you run the sample, try opening the Appearance menu, switching color schemes from the menu or OS settings, and clicking the button that triggers the message box.

Built-in Native Behaviors

Calling App::Create() and adding a panel with Window::AddPanel() provides several native behaviors without requiring extra code.

Feature Description
Text Rendering and Fonts Text appearance matches the OS rendering preset, and generic CSS font families (eg, sans-serif and monospace) resolve to platform browser defaults (Settings::auto_font_profile and Settings::auto_font_families, both true by default).
Text Editing and the Clipboard Selection and editing follow OS conventions (eg, non-directional selections on macOS), controlled by Settings::match_native_editing_behavior (default true).
Dark Mode and Color Schemes The OS color scheme is passed to pages at startup, tracked live on Windows and macOS, and read once at startup on Linux.
Cursors The OS cursor updates automatically to match page hover states (eg, link hands, text I-beams, and CSS cursor rules).
Input Method Editors Chinese, Japanese, and Korean text entry through OS input method editors works in pages without extra code.
Windows, Monitors, and DPI Layout dimensions use logical pixels, scaling automatically to match each monitor's display scale factor.

Add a Backdrop Material

A backdrop material is the blurred, theme-aware surface that the OS renders behind native windows.

Request the Material

Call Window::SetBackdrop() with BackdropMaterial::Window to request the native backdrop material on the main window.

C++
window_->SetBackdrop(BackdropMaterial::Window);

Leaving the window background color unset displays the material untinted— the sample applies translucent tints through its HTML and CSS instead.

To configure backdrop options, custom tint colors, and fallback fills, see Native Window Styling.

Make the View and Page Transparent

The backdrop material shows through only where both the view and the HTML page are transparent.

Enable transparency on the view by setting ViewConfig::is_transparent to true.

C++
ViewConfig config = window_->default_view_config();
config.is_transparent = true;
panel_ = window_->AddPanel({}, config);

In assets/chrome.html, set the background color of the root elements to transparent.

CSS
html, body {
  background: transparent;
}

👍 Inherit window defaults

Passing a default ViewConfig to Window::AddPanel() loses the automatic OS font families and editing behaviors. Start from Window::default_view_config() instead, and change only the settings you need.

Connect Page Buttons to Native Code

The sample uses a dom::Triggers instance, chrome_listeners_, to bind native click handlers to page elements matched by CSS selectors.

Attach the trigger set to the view before loading the page so the initial document receives every registered listener.

C++
// Register every click handler (the steps below) before this point.

if (chrome_listeners_.AttachTo(panel_->view().get()))
  panel_->view()->LoadURL("file:///chrome.html");

To learn more about binding DOM listeners across navigations, see DOM Triggers and Navigation.

Open a Popup Menu

The sample displays the Appearance menu using a popup window.

A popup window is a native OS window owned by the main window— it can extend past the main window's edges and never takes focus from the owner.

The library dismisses the popup automatically when the user clicks outside it, presses Esc, or moves the owner window. For content that stays inside the window (eg, toasts or in-window dialogs), see Floating Panels.

Create the Popup Window

Call Window::CreatePopup() with WindowFlags::Hidden to create the popup window once at startup.

C++
menu_ = Window::CreatePopup(window_, 0, 0, 240, 182, WindowFlags::Hidden);
menu_->set_listener(this);
menu_->SetBackdrop(BackdropMaterial::Popup);

ViewConfig menu_config = menu_->default_view_config();
menu_config.is_transparent = true;
menu_panel_ = menu_->AddPanel({}, menu_config);
menu_panel_->view()->LoadURL("file:///menu.html");

BackdropMaterial::Popup requests the OS's native menu backdrop material.

Popups are always transparent windows— the view must also set ViewConfig::is_transparent to true for the material to show through.

Position and Show the Menu

In the menu button's click handler, measure the button's position and show the popup.

C++
chrome_listeners_.On("#menu-button", "click", [this](dom::Element button) {
  dom::DOMRect rect = button.getBoundingClientRect();
  menu_->MoveTo(rect.x + rect.width - 240, rect.bottom() + 6);
  menu_->Show();
});

Popup coordinates are relative to the owner window's content area— the same coordinate space getBoundingClientRect() measures. The button's bounds pass directly to Window::MoveTo() without conversion.

Dismissing a popup only hides it, so calling Window::Show() displays the window again on the next click.

👍 Prevent immediate reopening

Pressing the menu button while the menu is open causes the library to auto-dismiss the popup before the click event fires, which would immediately reopen it. To avoid this, the sample compares the timestamps of the button's mousedown event and WindowListener::OnDismiss(), skipping Window::Show() if they occur within 150 ms.

To learn more about dismissal rules, keyboard navigation, and popup lifetimes, see Popups and Dialogs.

Light and Dark Themes

Desktop windows can adapt automatically to the user's OS color scheme or lock to a specific theme.

Follow the OS Theme

Use the standard CSS prefers-color-scheme media query to adapt translucent surface colors when the OS theme changes.

CSS
:root {
  --panel: rgba(255, 255, 255, 0.55);
  --text: #1b2030;
}

@media (prefers-color-scheme: dark) {
  :root {
    --panel: rgba(23, 26, 35, 0.55);
    --text: #e6e8ef;
  }
}

The sample defines translucent tints for each color scheme, allowing the active backdrop material to show through in both light and dark modes.

Force Light or Dark Theme

The Appearance menu allows the user to force light mode, force dark mode, or return to following the system theme.

Native window materials and web pages have separate theme settings— BackdropOptions::theme controls the material, while View::set_preferred_color_scheme() controls the page.

Update both settings together so that the backdrop material and the HTML content stay in sync.

C++
void SetScheme(ColorScheme scheme, BackdropTheme theme) {
  window_->SetBackdrop(BackdropMaterial::Window, { .theme = theme });
  menu_->SetBackdrop(BackdropMaterial::Popup, { .theme = theme });
  panel_->view()->set_preferred_color_scheme(scheme);
  menu_panel_->view()->set_preferred_color_scheme(scheme);
}

Passing ColorScheme::Auto and BackdropTheme::Auto restores automatic tracking of the OS theme.

Show a Native Message Box

Call ShowMessageBox() from <AppCore/Dialogs.h> to display a modal message box using the OS's native styling.

C++
chrome_listeners_.On("#dialog-button", "click", [] {
  ShowMessageBox("Native Look and Feel",
                 "This is the platform's own message box.");
});

ShowMessageBox() takes the dialog's own title and message text, separate from the main window's title bar.

The call blocks until dismissed, returning a ButtonResult that indicates which button the user selected.

For dialog icons and button sets, see Popups and Dialogs.

What's Next

From here, Custom Window Chrome walks through drawing the window's title bar and caption buttons in HTML.