In [Building a Desktop App](/docs/2.0/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](/docs/2.0/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](/docs/2.0/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](/docs/2.0/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](/docs/2.0/text-editing-and-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](/docs/2.0/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](/docs/2.0/input-method-editors) | Chinese, Japanese, and Korean text entry through OS input method editors works in pages without extra code. |
| [Windows, Monitors, and DPI](/docs/2.0/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.

```cpp
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](/docs/2.0/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`.

```cpp
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.

```cpp
// 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](/docs/2.0/page-wiring-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](/docs/2.0/floating-panels).

### Create the Popup Window

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

```cpp
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.

```cpp
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](/docs/2.0/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.

```cpp
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.

```cpp
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](/docs/2.0/popups-and-dialogs).

## What's Next

From here, [Custom Window Chrome](/docs/2.0/custom-window-chrome) walks through drawing the window's title bar and caption buttons in HTML.
