Native Liquid Glass effect for macOS Qt6 applications
Instantiates AppKitβs NSGlassEffectView directly through Objective-C runtime, with private variant selectors for extended styles.
- Native Glass Effects β Real
NSGlassEffectViewintegration with private extended variants andNSVisualEffectViewfallback for older macOS. - Qt Integration β Works seamlessly with
QWidgetandQMainWindow. - 15 Materials β Sidebar, HUD, Popover, Frosted, ClearGlass, Chromatic, and more.
- Appearance Control β Force light/dark mode or follow the system automatically.
- Interaction States β Normal and hovered states for interactive glass surfaces.
- Custom Shapes β Experimental
QPainterPathgeometry withQTransformsupport on compatible runtimes. - Safe Cleanup β Effects are invalidated when their Qt or native host is destroyed, and explicit removal restores captured native state.
- Standard and Frameless Windows β Supports standard Qt windows and includes a best-effort native wrapping path for frameless windows (
Qt::FramelessWindowHint).
This is the simplest way to get started. Clone the repository into your project structure.
git clone https://github.com/fsalinas26/qt-liquid-glass.git# In your CMakeLists.txt
add_subdirectory(QtLiquidGlass)
target_link_libraries(YourApp PRIVATE QtLiquidGlass)If you prefer to install the library system-wide or use it across multiple projects:
-
Build and Install:
git clone https://github.com/fsalinas26/qt-liquid-glass.git cd qt-liquid-glass && mkdir build && cd build cmake .. sudo cmake --install .
-
Use in your project:
find_package(QtLiquidGlass 0.4.0 REQUIRED) target_link_libraries(YourApp PRIVATE QtLiquidGlass::QtLiquidGlass)
- macOS 26 (Tahoe) for
NSGlassEffectView(Liquid Glass). Falls back toNSVisualEffectViewon macOS 10.14+. - Qt 6.2+ (Core, Widgets)
- CMake 3.16+
Note: This package only works on macOS. On other platforms, it compiles but performs safe no-ops.
#include "QtLiquidGlass/QtLiquidGlass.h"
#include <QApplication>
#include <QMainWindow>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 1. Create your window
QMainWindow window;
window.setWindowFlags(Qt::Window);
// 2. Configure glass options
QtLiquidGlass::Options opts;
opts.cornerRadius = 16.0;
opts.tintColor = "#80FFFFFF";
opts.appearance = QtLiquidGlass::AdaptiveAppearance::Auto;
// 3. Apply the Liquid Glass effect
int id = QtLiquidGlass::addGlassEffect(&window, QtLiquidGlass::Material::Sidebar, opts);
window.resize(600, 400);
window.show();
return a.exec();
}Note: By default, the library sets
WA_TranslucentBackground, makes theNSWindowtransparent, and uses a transparent full-size titlebar so glass can extend behind the title area. In this default mode, AppKit background dragging is enabled automatically to keep the seamless titlebar interactive.
Apply the effect before the widget's native window is created. If your application calls winId() or otherwise realizes the native handle first, set Qt::WA_TranslucentBackground before that happens; Qt cannot reliably change an already-created opaque QNSWindow into a translucent one.
The included example demonstrates how to switch materials and configure properties in real-time.
| Method | Description |
|---|---|
addGlassEffect(widget, material, options) |
Applies the glass effect behind the widget. Returns an int ID. |
configure(id, options) |
Updates the existing effect's options without replacing its native view. |
setIntProperty(id, key, value) |
Sets a low-level property by name. Keys: variant, material, scrimState, subduedState, contentLensing, appearance, interaction, blendingMode. |
remove(id) |
Removes the effect and cleans up native views. |
Call QtLiquidGlass from Qt's GUI thread. Calling remove(id) detaches the native effect and restores the window and container state captured when it was added. Destroying the host QWidget or native view also invalidates the effect automatically. Operations using a removed or otherwise stale ID are safe no-ops; experimental operations return false.
Explicit removal remains useful when an application wants to stop the effect before destroying its host. It is safe to call remove() more than once for the same ID.
| Field | Type | Default | Description |
|---|---|---|---|
cornerRadius |
double |
0.0 |
Corner radius of the glass effect. |
tintColor |
QString |
"" |
Tint overlay in #RRGGBB or #AARRGGBB format. Empty = no tint. |
opaque |
bool |
false |
Adds a solid backing layer behind the glass. |
titlebarStyle |
TitlebarStyle |
TransparentFullSize |
TransparentFullSize keeps the existing seamless-titlebar behavior; Preserve avoids changing AppKit titlebar flags. |
dragBehavior |
WindowDragBehavior |
Auto |
Auto enables AppKit background dragging with TransparentFullSize; Preserve leaves native drag policy unchanged; MovableByWindowBackground enables it; NotMovableByWindowBackground disables it. |
blendingMode |
BlendingMode |
BehindWindow |
Selects the blending mode on the NSVisualEffectView fallback. The tested NSGlassEffectView runtime does not implement this property. |
appearance |
AdaptiveAppearance |
Auto |
Light, Dark, or Auto (follows system). |
interaction |
InteractionState |
Normal |
Normal or Hovered (elevated highlight state). |
If the transparent full-size titlebar interferes with native title dragging or title rendering, preserve the AppKit titlebar behavior:
QtLiquidGlass::Options opts;
opts.titlebarStyle = QtLiquidGlass::TitlebarStyle::Preserve;For simple frameless or background-draggable windows, you can opt into AppKit background dragging:
opts.dragBehavior = QtLiquidGlass::WindowDragBehavior::MovableByWindowBackground;To keep the previous native drag policy even with a transparent full-size titlebar:
opts.dragBehavior = QtLiquidGlass::WindowDragBehavior::Preserve;configure() updates every field in Options. Material is passed separately to addGlassEffect() and still requires replacing the effect.
| Enum | Description |
|---|---|
Material::Sidebar |
Thick, vibrant blur (Standard macOS sidebar) |
Material::Titlebar |
"Abutted Sidebar" - blends seamlessly with sidebars |
Material::Inspector |
Sidebar material for detail/inspector panels |
Material::Widgets |
macOS Big Sur+ widget background style |
Material::Sheet |
Lighter blur for modal sheets |
Material::Hud |
Dark, satiny material for HUDs |
Material::Popover |
Standard popover material |
Material::Menu |
Notification-center style glass for menus |
Material::WindowBackground |
Subtle background blur |
Material::FullscreenUI |
Deep blur for fullscreen content |
Material::ControlCenter |
Modern, translucent module background |
Material::Tooltip |
"Loupe" material for hover cards |
Material::Frosted |
Softest, strongest blur, bright diffusion |
Material::ClearGlass |
Almost no blur, crisp transparency with light RGB refraction |
Material::Chromatic |
Frosted look with chromatic aberration blur |
Note: AppKit publicly documents
NSGlassEffectViewwithRegularandClearstyles. The additional material variants above use private_variantvalues discovered at runtime and may change across macOS releases.
On supported macOS 26 runtimes, a glass surface can follow an arbitrary QPainterPath. The API is runtime-gated because it uses AppKit's private _setPath: selector. The bundled ShapePlayground example β the morphing media player shown above β demonstrates it animated: click the player or press Space to toggle the interruptible spring transition.
| Method | Description |
|---|---|
Experimental::supportsCustomShapes(id) |
Reports whether the active native backend supports arbitrary glass paths. |
Experimental::setShape(id, path) |
Applies a QWidget-local QPainterPath to the glass surface. |
Experimental::clearShape(id) |
Restores the default rectangular glass surface. |
Experimental::setClipsToBounds(id, enabled) |
Controls native clipping for the glass surface. |
#include "QtLiquidGlass/QtLiquidGlass.h"
#include <QApplication>
#include <QMainWindow>
#include <QPainterPath>
#include <QTransform>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 1. Create your window and add a glass effect
QMainWindow window;
window.setWindowFlags(Qt::Window);
int id = QtLiquidGlass::addGlassEffect(&window, QtLiquidGlass::Material::Sidebar);
// 2. Build a QWidget-local path (origin top-left, Y increases downward)
QPainterPath pill;
pill.addRoundedRect(QRectF(0, 0, 240, 64), 32, 32);
// 3. Apply the shape if the runtime supports it
if (QtLiquidGlass::Experimental::supportsCustomShapes(id)) {
QTransform transform;
transform.translate(12.0, 8.0);
transform.rotate(4.0);
QtLiquidGlass::Experimental::setShape(id, transform.map(pill));
QtLiquidGlass::Experimental::setClipsToBounds(id, true);
}
// Later, restore the default rectangle: Experimental::clearShape(id);
window.resize(600, 400);
window.show();
return a.exec();
}Note: Paths use QWidget-local coordinates (origin top-left, Y increasing downward); the library converts them to AppKit's Y-up space while preserving lines and cubic curves. Reapply the path after resizing the glass host, and use
QTransformfor animated translation, scaling, rotation, skewing, or morphing.
Custom paths are unavailable on the NSVisualEffectView fallback. Shape capability checks, path application, and path clearing return false when the effect ID is invalid or _setPath: is unsupported. Clipping is checked independently and may remain available on a fallback view when its runtime implements setClipsToBounds:.
The Material enum is stable library API, while most of its native NSGlassEffectView mappings use private AppKit variants. The Experimental namespace is additive but runtime-dependent and should always be guarded by its capability check.
Build and run the ShapePlayground example with:
cmake -S . -B build
cmake --build build --target ShapePlayground
./build/examples/ShapePlayground/ShapePlaygroundQt-liquid-glass uses a native injection strategy to place the glass layer behind Qt's rendering surface for standard windows, child widgets, and best-effort frameless windows.
- Native Backend: Uses Objective-C++ to inject a native
NSViewinto the window hierarchy. - Smart Injection:
- Standard Windows: Injects the glass view as a sibling behind the Qt root view in the
NSThemeFrame. - Frameless Windows: Uses a best-effort native wrapper around the Qt root view so the glass can sit behind the Qt rendering layer.
- Child Widgets: Injects the glass view directly inside the widget's native view, behind its children.
- Lifetime Tracking: Uses Qt destruction callbacks and Objective-C associated objects to prevent registry entries from outliving their hosts.
- Duplicate Detection: Automatically removes an existing effect before re-adding to the same native host.
- Standard Windows: Injects the glass view as a sibling behind the Qt root view in the
- Fallback: On macOS versions without
NSGlassEffectView, falls back toNSVisualEffectViewwith mapped material values.
This library is built upon the research by the electron-liquid-glass project. We gratefully acknowledge their discovery and reverse-engineering of Apple's private NSGlassEffectView API documentation and the mapping of its variant properties, which provided the foundation for this native Qt port.
MIT


