diff --git a/v3/Taskfile.yaml b/v3/Taskfile.yaml
index 6f9899091..04afd60f4 100644
--- a/v3/Taskfile.yaml
+++ b/v3/Taskfile.yaml
@@ -184,6 +184,7 @@ tasks:
html-dnd-api
ignore-mouse
keybindings
+ liquid-glass
menu
notifications
panic-handling
@@ -308,6 +309,7 @@ tasks:
html-dnd-api
ignore-mouse
keybindings
+ liquid-glass
menu
notifications
panic-handling
diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md
index 8e4648038..57112da92 100644
--- a/v3/UNRELEASED_CHANGELOG.md
+++ b/v3/UNRELEASED_CHANGELOG.md
@@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file
## Added
+- Add native Liquid Glass effect support for macOS with NSGlassEffectView (macOS 15.0+) and NSVisualEffectView fallback, including comprehensive material customization options by @leaanthony in [#4534](https://github.com/wailsapp/wails/pull/4534)
## Changed
diff --git a/v3/examples/liquid-glass/.gitignore b/v3/examples/liquid-glass/.gitignore
new file mode 100644
index 000000000..45d2406b0
--- /dev/null
+++ b/v3/examples/liquid-glass/.gitignore
@@ -0,0 +1 @@
+liquid-glass-demo
diff --git a/v3/examples/liquid-glass/README.md b/v3/examples/liquid-glass/README.md
new file mode 100644
index 000000000..33875af54
--- /dev/null
+++ b/v3/examples/liquid-glass/README.md
@@ -0,0 +1,70 @@
+# Liquid Glass Demo for Wails v3
+
+This demo showcases the native Liquid Glass effect available in macOS 15.0+ with fallback to NSVisualEffectView for older systems.
+
+## Features Demonstrated
+
+### Window Styles
+
+1. **Light Glass** - Clean, light appearance with no tint
+2. **Dark Glass** - Dark themed glass effect
+3. **Vibrant Glass** - Enhanced vibrant effect for maximum transparency
+4. **Tinted Glass** - Blue tinted glass with custom RGBA color
+5. **Sheet Material** - Using specific NSVisualEffectMaterialSheet
+6. **HUD Window** - Ultra-light HUD window material
+7. **Content Background** - Content background material with warm tint
+
+### Customization Options
+
+- **Style**: `LiquidGlassStyleAutomatic`, `LiquidGlassStyleLight`, `LiquidGlassStyleDark`, `LiquidGlassStyleVibrant`
+- **Material**: Direct NSVisualEffectMaterial selection (when NSGlassEffectView is not available)
+ - `NSVisualEffectMaterialAppearanceBased`
+ - `NSVisualEffectMaterialLight`
+ - `NSVisualEffectMaterialDark`
+ - `NSVisualEffectMaterialSheet`
+ - `NSVisualEffectMaterialHUDWindow`
+ - `NSVisualEffectMaterialContentBackground`
+ - `NSVisualEffectMaterialUnderWindowBackground`
+ - `NSVisualEffectMaterialUnderPageBackground`
+ - And more...
+- **CornerRadius**: Rounded corners (0 for square corners)
+- **TintColor**: Custom RGBA tint overlay
+- **GroupID**: For grouping multiple glass windows (future feature)
+- **GroupSpacing**: Spacing between grouped windows (future feature)
+
+### Running the Demo
+
+```bash
+go build -o liquid-glass-demo .
+./liquid-glass-demo
+```
+
+### Requirements
+
+- macOS 10.14+ (best experience on macOS 26.0+ with native NSGlassEffectView)
+- Wails v3
+
+### Implementation Details
+
+The implementation uses:
+- Native `NSGlassEffectView` on macOS 26.0+ for authentic glass effect
+- Falls back to `NSVisualEffectView` on older systems
+- Runtime detection using `NSClassFromString` for compatibility
+- Key-Value Coding (KVC) for dynamic property setting
+
+### Example Usage
+
+```go
+window := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500, // Make window draggable
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleLight,
+ Material: application.NSVisualEffectMaterialHUDWindow,
+ CornerRadius: 20.0,
+ TintColor: &application.RGBA{Red: 0, Green: 100, Blue: 200, Alpha: 50},
+ },
+ },
+})
+```
\ No newline at end of file
diff --git a/v3/examples/liquid-glass/index.html b/v3/examples/liquid-glass/index.html
new file mode 100644
index 000000000..7f9ff4545
--- /dev/null
+++ b/v3/examples/liquid-glass/index.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Wails Liquid Glass
+
+
+
+
+

+
LIQUID GLASS
+
+
+
\ No newline at end of file
diff --git a/v3/examples/liquid-glass/main.go b/v3/examples/liquid-glass/main.go
new file mode 100644
index 000000000..9562c5930
--- /dev/null
+++ b/v3/examples/liquid-glass/main.go
@@ -0,0 +1,235 @@
+package main
+
+import (
+ _ "embed"
+ "encoding/base64"
+ "log"
+ "runtime"
+ "strings"
+
+ "github.com/wailsapp/wails/v3/pkg/application"
+)
+
+//go:embed index.html
+var indexHTML string
+
+//go:embed wails-logo.png
+var wailsLogo []byte
+
+func main() {
+ app := application.New(application.Options{
+ Name: "Wails Liquid Glass Demo",
+ Description: "Demonstrates the native Liquid Glass effect on macOS",
+ })
+
+ // Check if running on macOS
+ if runtime.GOOS != "darwin" {
+ // Show dialog for non-macOS platforms
+ application.InfoDialog().
+ SetTitle("macOS Only Demo").
+ SetMessage("The Liquid Glass effect is a macOS-specific feature that uses native NSGlassEffectView (macOS 15.0+) or NSVisualEffectView.\n\nThis demo is not available on " + runtime.GOOS + ".").
+ Show()
+ return
+ }
+
+ // Convert logo to base64 data URI
+ logoDataURI := "data:image/png;base64," + base64.StdEncoding.EncodeToString(wailsLogo)
+
+ // Create different HTML for each window
+ lightHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ lightHTML = strings.Replace(lightHTML, "LIQUID GLASS", "Light Style", 1)
+
+ darkHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ darkHTML = strings.Replace(darkHTML, "LIQUID GLASS", "Dark Style", 1)
+
+ vibrantHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ vibrantHTML = strings.Replace(vibrantHTML, "LIQUID GLASS", "Vibrant Style", 1)
+
+ tintedHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ tintedHTML = strings.Replace(tintedHTML, "LIQUID GLASS", "Blue Tint", 1)
+
+ sheetHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ sheetHTML = strings.Replace(sheetHTML, "LIQUID GLASS", "Sheet Material", 1)
+
+ hudHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ hudHTML = strings.Replace(hudHTML, "LIQUID GLASS", "HUD Window", 1)
+
+ contentHTML := strings.Replace(indexHTML, "wails-logo.png", logoDataURI, 1)
+ contentHTML = strings.Replace(contentHTML, "LIQUID GLASS", "Content Background", 1)
+
+ // Window 1: Light style with no tint
+ window1 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Light Glass",
+ Width: 350,
+ Height: 280,
+ X: 100,
+ Y: 100,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: lightHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleLight,
+ Material: application.NSVisualEffectMaterialAuto,
+ CornerRadius: 20.0,
+ TintColor: nil,
+ },
+ },
+ })
+
+ // Window 2: Dark style
+ window2 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Dark Glass",
+ Width: 350,
+ Height: 280,
+ X: 500,
+ Y: 100,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: darkHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleDark,
+ Material: application.NSVisualEffectMaterialAuto,
+ CornerRadius: 20.0,
+ TintColor: nil,
+ },
+ },
+ })
+
+ // Window 3: Vibrant style
+ window3 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Vibrant Glass",
+ Width: 350,
+ Height: 280,
+ X: 900,
+ Y: 100,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: vibrantHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleVibrant,
+ Material: application.NSVisualEffectMaterialAuto,
+ CornerRadius: 20.0,
+ TintColor: nil,
+ },
+ },
+ })
+
+ // Window 4: Blue tinted glass
+ window4 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Tinted Glass",
+ Width: 350,
+ Height: 280,
+ X: 300,
+ Y: 420,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: tintedHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleLight,
+ Material: application.NSVisualEffectMaterialAuto,
+ CornerRadius: 25.0, // Different corner radius
+ TintColor: &application.RGBA{Red: 0, Green: 100, Blue: 200, Alpha: 50}, // Blue tint
+ },
+ },
+ })
+
+ // Window 5: Using specific NSVisualEffectMaterial
+ window5 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Sheet Material",
+ Width: 350,
+ Height: 280,
+ X: 700,
+ Y: 420,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: sheetHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleAutomatic, // Automatic style
+ Material: application.NSVisualEffectMaterialSheet, // Specific material
+ CornerRadius: 15.0, // Different corner radius
+ TintColor: nil,
+ },
+ },
+ })
+
+ // Window 6: HUD Window Material (very light, translucent)
+ window6 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "HUD Window",
+ Width: 350,
+ Height: 280,
+ X: 100,
+ Y: 740,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: hudHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleAutomatic,
+ Material: application.NSVisualEffectMaterialHUDWindow, // HUD Window material - very light
+ CornerRadius: 30.0, // Larger corner radius
+ TintColor: nil,
+ },
+ },
+ })
+
+ // Window 7: Content Background Material
+ window7 := app.Window.NewWithOptions(application.WebviewWindowOptions{
+ Title: "Content Background",
+ Width: 350,
+ Height: 280,
+ X: 500,
+ Y: 740,
+ Frameless: true,
+ EnableDragAndDrop: false,
+ HTML: contentHTML,
+ InitialPosition: application.WindowXY,
+ Mac: application.MacWindow{
+ Backdrop: application.MacBackdropLiquidGlass,
+ InvisibleTitleBarHeight: 500,
+ LiquidGlass: application.MacLiquidGlass{
+ Style: application.LiquidGlassStyleAutomatic,
+ Material: application.NSVisualEffectMaterialContentBackground, // Content background
+ CornerRadius: 10.0, // Smaller corner radius
+ TintColor: &application.RGBA{Red: 0, Green: 200, Blue: 100, Alpha: 30}, // Warm tint
+ },
+ },
+ })
+
+ // Show all windows
+ window1.Show()
+ window2.Show()
+ window3.Show()
+ window4.Show()
+ window5.Show()
+ window6.Show()
+ window7.Show()
+
+ // Run the application
+ err := app.Run()
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/v3/examples/liquid-glass/wails-logo.png b/v3/examples/liquid-glass/wails-logo.png
new file mode 100644
index 000000000..e65c582ff
Binary files /dev/null and b/v3/examples/liquid-glass/wails-logo.png differ
diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go
index 8d5a85a8e..dfab963cb 100644
--- a/v3/pkg/application/webview_window_darwin.go
+++ b/v3/pkg/application/webview_window_darwin.go
@@ -4,7 +4,7 @@ package application
/*
#cgo CFLAGS: -mmacosx-version-min=10.13 -x objective-c
-#cgo LDFLAGS: -framework Cocoa -framework WebKit
+#cgo LDFLAGS: -framework Cocoa -framework WebKit -framework QuartzCore
#include "application_darwin.h"
#include "webview_window_darwin.h"
@@ -1247,6 +1247,8 @@ func (w *macosWebviewWindow) run() {
case MacBackdropTranslucent:
C.windowSetTranslucent(w.nsWindow)
C.webviewSetTransparent(w.nsWindow)
+ case MacBackdropLiquidGlass:
+ w.applyLiquidGlass()
case MacBackdropNormal:
}
@@ -1351,6 +1353,55 @@ func (w *macosWebviewWindow) setBackgroundColour(colour RGBA) {
C.windowSetBackgroundColour(w.nsWindow, C.int(colour.Red), C.int(colour.Green), C.int(colour.Blue), C.int(colour.Alpha))
}
+func (w *macosWebviewWindow) applyLiquidGlass() {
+ options := w.parent.options.Mac.LiquidGlass
+
+ // Validate corner radius
+ if options.CornerRadius < 0 {
+ options.CornerRadius = 0
+ }
+
+ globalApplication.debug("Applying Liquid Glass effect", "window", w.parent.id)
+
+ // Check if liquid glass is supported
+ if !C.isLiquidGlassSupported() {
+ // Fallback to translucent
+ C.windowSetTranslucent(w.nsWindow)
+ C.webviewSetTransparent(w.nsWindow)
+ globalApplication.debug("Liquid Glass not supported on this macOS version, falling back to translucent", "window", w.parent.id)
+ return
+ }
+
+ // Prepare tint color values (already clamped by uint8 type)
+ var r, g, b, a C.int
+ if options.TintColor != nil {
+ r = C.int(options.TintColor.Red)
+ g = C.int(options.TintColor.Green)
+ b = C.int(options.TintColor.Blue)
+ a = C.int(options.TintColor.Alpha)
+ }
+
+ // Prepare group ID
+ var groupIDCStr *C.char
+ if options.GroupID != "" {
+ groupIDCStr = C.CString(options.GroupID)
+ defer C.free(unsafe.Pointer(groupIDCStr))
+ }
+
+ // Apply liquid glass effect
+ C.windowSetLiquidGlass(
+ w.nsWindow,
+ C.int(options.Style),
+ C.int(options.Material),
+ C.double(options.CornerRadius),
+ r, g, b, a,
+ groupIDCStr,
+ C.double(options.GroupSpacing),
+ )
+
+ globalApplication.debug("Applied Liquid Glass effect", "window", w.parent.id, "style", options.Style)
+}
+
func (w *macosWebviewWindow) relativePosition() (int, int) {
var x, y C.int
InvokeSync(func() {
diff --git a/v3/pkg/application/webview_window_darwin.h b/v3/pkg/application/webview_window_darwin.h
index f0f58d896..0743fd6d9 100644
--- a/v3/pkg/application/webview_window_darwin.h
+++ b/v3/pkg/application/webview_window_darwin.h
@@ -34,4 +34,12 @@
void windowSetScreen(void* window, void* screen, int yOffset);
+// Liquid Glass support functions
+bool isLiquidGlassSupported();
+void windowSetLiquidGlass(void* window, int style, int material, double cornerRadius,
+ int r, int g, int b, int a,
+ const char* groupID, double groupSpacing);
+void windowRemoveVisualEffects(void* window);
+void configureWebViewForLiquidGlass(void* window);
+
#endif /* WebviewWindowDelegate_h */
diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m
index 033792c97..199556bc6 100644
--- a/v3/pkg/application/webview_window_darwin.m
+++ b/v3/pkg/application/webview_window_darwin.m
@@ -1,6 +1,7 @@
//go:build darwin
#import
#import
+#import
#import "webview_window_darwin.h"
#import "../events/events_darwin.h"
extern void processMessage(unsigned int, const char*);
@@ -9,6 +10,14 @@ extern void processDragItems(unsigned int windowId, char** arr, int length, int
extern void processWindowKeyDownEvent(unsigned int, const char*);
extern bool hasListeners(unsigned int);
extern bool windowShouldUnconditionallyClose(unsigned int);
+
+// Define NSGlassEffectView style constants (these match the Go constants)
+typedef NS_ENUM(NSInteger, NSGlassEffectViewStyle) {
+ NSGlassEffectViewStyleAutomatic = 0,
+ NSGlassEffectViewStyleLight = 1,
+ NSGlassEffectViewStyleDark = 2,
+ NSGlassEffectViewStyleVibrant = 3
+};
@implementation WebviewWindow
- (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation;
{
@@ -321,14 +330,11 @@ extern bool windowShouldUnconditionallyClose(unsigned int);
- (BOOL)windowShouldClose:(NSWindow *)sender {
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[sender delegate];
- NSLog(@"[DEBUG] windowShouldClose called for window %d", delegate.windowId);
// Check if this window should close unconditionally (called from Close() method)
if (windowShouldUnconditionallyClose(delegate.windowId)) {
- NSLog(@"[DEBUG] Window %d closing unconditionally (Close() method called)", delegate.windowId);
return true;
}
// For user-initiated closes, emit WindowClosing event and let the application decide
- NSLog(@"[DEBUG] Window %d close requested by user - emitting WindowClosing event", delegate.windowId);
processWindowEvent(delegate.windowId, EventWindowShouldClose);
return false;
}
@@ -544,13 +550,11 @@ extern bool windowShouldUnconditionallyClose(unsigned int);
}
}
- (void)windowDidOrderOffScreen:(NSNotification *)notification {
- NSLog(@"[DEBUG] Window %d ordered OFF screen (hidden)", self.windowId);
if( hasListeners(EventWindowDidOrderOffScreen) ) {
processWindowEvent(self.windowId, EventWindowDidOrderOffScreen);
}
}
- (void)windowDidOrderOnScreen:(NSNotification *)notification {
- NSLog(@"[DEBUG] Window %d ordered ON screen (shown)", self.windowId);
if( hasListeners(EventWindowDidOrderOnScreen) ) {
processWindowEvent(self.windowId, EventWindowDidOrderOnScreen);
}
@@ -626,7 +630,6 @@ extern bool windowShouldUnconditionallyClose(unsigned int);
}
}
- (void)windowWillClose:(NSNotification *)notification {
- NSLog(@"[DEBUG] Window %d WILL close (window is actually closing)", self.windowId);
if( hasListeners(EventWindowWillClose) ) {
processWindowEvent(self.windowId, EventWindowWillClose);
}
@@ -672,13 +675,11 @@ extern bool windowShouldUnconditionallyClose(unsigned int);
}
}
- (void)windowWillOrderOffScreen:(NSNotification *)notification {
- NSLog(@"[DEBUG] Window %d WILL order off screen (about to hide)", self.windowId);
if( hasListeners(EventWindowWillOrderOffScreen) ) {
processWindowEvent(self.windowId, EventWindowWillOrderOffScreen);
}
}
- (void)windowWillOrderOnScreen:(NSNotification *)notification {
- NSLog(@"[DEBUG] Window %d WILL order on screen (about to show)", self.windowId);
if( hasListeners(EventWindowWillOrderOnScreen) ) {
processWindowEvent(self.windowId, EventWindowWillOrderOnScreen);
}
@@ -815,3 +816,234 @@ void windowSetScreen(void* window, void* screen, int yOffset) {
// Set the frame which moves the window to the new screen
[nsWindow setFrame:frame display:YES];
}
+
+// Check if Liquid Glass is supported on this system
+bool isLiquidGlassSupported() {
+ // Check for macOS 26.0+ and NSGlassEffectView availability
+ if (@available(macOS 26.0, *)) {
+ return NSClassFromString(@"NSGlassEffectView") != nil;
+ }
+ return false;
+}
+
+// Remove any existing visual effects from the window
+void windowRemoveVisualEffects(void* nsWindow) {
+ WebviewWindow* window = (WebviewWindow*)nsWindow;
+ NSView* contentView = [window contentView];
+
+ // Get NSGlassEffectView class if available (avoid hard reference)
+ Class glassEffectViewClass = nil;
+ if (@available(macOS 26.0, *)) {
+ glassEffectViewClass = NSClassFromString(@"NSGlassEffectView");
+ }
+
+ // Remove all NSVisualEffectView and NSGlassEffectView subviews
+ NSArray* subviews = [contentView subviews];
+ for (NSView* subview in subviews) {
+ if ([subview isKindOfClass:[NSVisualEffectView class]] ||
+ (glassEffectViewClass && [subview isKindOfClass:glassEffectViewClass])) {
+ [subview removeFromSuperview];
+ }
+ }
+}
+
+// Configure WebView for liquid glass effect
+void configureWebViewForLiquidGlass(void* nsWindow) {
+ WebviewWindow* window = (WebviewWindow*)nsWindow;
+ WKWebView* webView = window.webView;
+
+ // Make WebView background transparent
+ [webView setValue:@NO forKey:@"drawsBackground"];
+ if (@available(macOS 10.12, *)) {
+ [webView setValue:[NSColor clearColor] forKey:@"backgroundColor"];
+ }
+
+ // Ensure WebView is above glass layer
+ if (webView.layer) {
+ webView.layer.zPosition = 1.0;
+ webView.layer.shouldRasterize = YES;
+ webView.layer.rasterizationScale = [[NSScreen mainScreen] backingScaleFactor];
+ }
+}
+
+// Apply Liquid Glass effect to window
+void windowSetLiquidGlass(void* nsWindow, int style, int material, double cornerRadius,
+ int r, int g, int b, int a,
+ const char* groupID, double groupSpacing) {
+ WebviewWindow* window = (WebviewWindow*)nsWindow;
+
+ // Ensure we're on the main thread for UI operations
+ if (![NSThread isMainThread]) {
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ windowSetLiquidGlass(nsWindow, style, material, cornerRadius, r, g, b, a, groupID, groupSpacing);
+ });
+ return;
+ }
+
+ // Remove any existing visual effects
+ windowRemoveVisualEffects(nsWindow);
+
+ // Try to use NSGlassEffectView if available
+ NSView* glassView = nil;
+
+ if (@available(macOS 26.0, *)) {
+ Class NSGlassEffectViewClass = NSClassFromString(@"NSGlassEffectView");
+ if (NSGlassEffectViewClass) {
+ // Create NSGlassEffectView (autoreleased)
+ glassView = [[[NSGlassEffectViewClass alloc] init] autorelease];
+
+ // Set corner radius if the property exists
+ if (cornerRadius > 0 && [glassView respondsToSelector:@selector(setCornerRadius:)]) {
+ [glassView setValue:@(cornerRadius) forKey:@"cornerRadius"];
+ }
+
+ // Set tint color if the property exists and color is specified
+ if (a > 0 && [glassView respondsToSelector:@selector(setTintColor:)]) {
+ NSColor* tintColor = [NSColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/255.0];
+ // Use performSelector to safely set tintColor if the setter exists
+ [glassView performSelector:@selector(setTintColor:) withObject:tintColor];
+ }
+
+ // Set style if the property exists
+ if ([glassView respondsToSelector:@selector(setStyle:)]) {
+ // For vibrant style, try to use Light style for a lighter effect
+ int lightStyle = (style == NSGlassEffectViewStyleVibrant) ? NSGlassEffectViewStyleLight : style;
+ [glassView setValue:@(lightStyle) forKey:@"style"];
+ }
+
+ // Set group identifier if the property exists and groupID is specified
+ if (groupID && strlen(groupID) > 0) {
+ if ([glassView respondsToSelector:@selector(setGroupIdentifier:)]) {
+ NSString* groupIDString = [NSString stringWithUTF8String:groupID];
+ [glassView performSelector:@selector(setGroupIdentifier:) withObject:groupIDString];
+ } else if ([glassView respondsToSelector:@selector(setGroupName:)]) {
+ NSString* groupIDString = [NSString stringWithUTF8String:groupID];
+ [glassView performSelector:@selector(setGroupName:) withObject:groupIDString];
+ }
+ }
+
+ // Set group spacing if the property exists and spacing is specified
+ if (groupSpacing > 0 && [glassView respondsToSelector:@selector(setGroupSpacing:)]) {
+ [glassView setValue:@(groupSpacing) forKey:@"groupSpacing"];
+ }
+ }
+ }
+
+ // Fallback to NSVisualEffectView if NSGlassEffectView is not available
+ if (!glassView) {
+ NSVisualEffectView* effectView = [[[NSVisualEffectView alloc] init] autorelease];
+ glassView = effectView; // Use effectView as glassView for the rest of the function
+
+ // If a custom material is specified, use it directly
+ if (material >= 0) {
+ [effectView setMaterial:(NSVisualEffectMaterial)material];
+ [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ } else {
+ // Configure the visual effect based on style
+ switch(style) {
+ case 1: // Light
+ if (@available(macOS 15.0, *)) {
+ [effectView setMaterial:NSVisualEffectMaterialUnderPageBackground];
+ } else if (@available(macOS 10.14, *)) {
+ [effectView setMaterial:NSVisualEffectMaterialHUDWindow];
+ } else {
+ [effectView setMaterial:NSVisualEffectMaterialLight];
+ }
+ [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ break;
+ case 2: // Dark
+ if (@available(macOS 15.0, *)) {
+ [effectView setMaterial:NSVisualEffectMaterialHeaderView];
+ } else if (@available(macOS 10.14, *)) {
+ [effectView setMaterial:NSVisualEffectMaterialFullScreenUI];
+ } else {
+ [effectView setMaterial:NSVisualEffectMaterialDark];
+ }
+ [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ break;
+ case 3: // Vibrant
+ if (@available(macOS 11.0, *)) {
+ // Use the lightest material available - similar to dock
+ [effectView setMaterial:NSVisualEffectMaterialHUDWindow];
+ } else if (@available(macOS 10.14, *)) {
+ [effectView setMaterial:NSVisualEffectMaterialSheet];
+ } else {
+ [effectView setMaterial:NSVisualEffectMaterialLight];
+ }
+ // Use behind window for true transparency
+ [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ break;
+ default: // Automatic
+ if (@available(macOS 10.14, *)) {
+ // Use content background for lighter automatic effect
+ [effectView setMaterial:NSVisualEffectMaterialContentBackground];
+ } else {
+ [effectView setMaterial:NSVisualEffectMaterialAppearanceBased];
+ }
+ [effectView setBlendingMode:NSVisualEffectBlendingModeWithinWindow];
+ break;
+ }
+ }
+
+ // Use followsWindowActiveState for automatic adjustment
+ [effectView setState:NSVisualEffectStateFollowsWindowActiveState];
+
+ // Don't emphasize - it makes the effect too dark
+ if (@available(macOS 10.12, *)) {
+ [effectView setEmphasized:NO];
+ }
+
+ // Apply corner radius if specified
+ if (cornerRadius > 0) {
+ [effectView setWantsLayer:YES];
+ effectView.layer.cornerRadius = cornerRadius;
+ effectView.layer.masksToBounds = YES;
+ }
+ }
+
+ // Get the content view
+ NSView* contentView = [window contentView];
+
+ // Set up the glass view
+ [glassView setFrame:contentView.bounds];
+ [glassView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ // Check if this is a real NSGlassEffectView with contentView property
+ BOOL hasContentView = [glassView respondsToSelector:@selector(contentView)];
+
+ if (hasContentView) {
+ // NSGlassEffectView: Add it to window and webView goes in its contentView
+ [contentView addSubview:glassView positioned:NSWindowBelow relativeTo:nil];
+
+ // Safely reparent the webView to the glass view's contentView
+ WKWebView* webView = window.webView;
+ NSView* glassContentView = [glassView valueForKey:@"contentView"];
+
+ // Only proceed if both webView and glassContentView are non-nil
+ if (webView && glassContentView) {
+ // Always remove from current superview to avoid exceptions
+ [webView removeFromSuperview];
+
+ // Add to the glass view's contentView
+ [glassContentView addSubview:webView];
+ [webView setFrame:glassContentView.bounds];
+ [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ }
+ } else {
+ // NSVisualEffectView: Add glass as bottom layer, webView on top
+ [contentView addSubview:glassView positioned:NSWindowBelow relativeTo:nil];
+
+ WKWebView* webView = window.webView;
+ if (webView) {
+ [webView removeFromSuperview];
+ [contentView addSubview:webView positioned:NSWindowAbove relativeTo:glassView];
+ }
+ }
+
+ // Configure WebView for liquid glass
+ configureWebViewForLiquidGlass(nsWindow);
+
+ // Make window transparent
+ [window setOpaque:NO];
+ [window setBackgroundColor:[NSColor clearColor]];
+}
diff --git a/v3/pkg/application/webview_window_options.go b/v3/pkg/application/webview_window_options.go
index 89be6c878..4c9c0f57a 100644
--- a/v3/pkg/application/webview_window_options.go
+++ b/v3/pkg/application/webview_window_options.go
@@ -383,6 +383,8 @@ const (
MacBackdropTransparent
// MacBackdropTranslucent - The window will have a translucent background, with the content underneath it being "fuzzy" or "frosted"
MacBackdropTranslucent
+ // MacBackdropLiquidGlass - The window will use Apple's Liquid Glass effect (macOS 15.0+ with fallback to translucent)
+ MacBackdropLiquidGlass
)
// MacToolbarStyle is the style of toolbar for macOS
@@ -401,6 +403,67 @@ const (
MacToolbarStyleUnifiedCompact
)
+// MacLiquidGlassStyle defines the style of the Liquid Glass effect
+type MacLiquidGlassStyle int
+
+const (
+ // LiquidGlassStyleAutomatic - System determines the best style
+ LiquidGlassStyleAutomatic MacLiquidGlassStyle = iota
+ // LiquidGlassStyleLight - Light glass appearance
+ LiquidGlassStyleLight
+ // LiquidGlassStyleDark - Dark glass appearance
+ LiquidGlassStyleDark
+ // LiquidGlassStyleVibrant - Vibrant glass with enhanced effects
+ LiquidGlassStyleVibrant
+)
+
+// NSVisualEffectMaterial represents the NSVisualEffectMaterial enum for macOS
+type NSVisualEffectMaterial int
+
+const (
+ // NSVisualEffectMaterial values from macOS SDK
+ NSVisualEffectMaterialAppearanceBased NSVisualEffectMaterial = 0
+ NSVisualEffectMaterialLight NSVisualEffectMaterial = 1
+ NSVisualEffectMaterialDark NSVisualEffectMaterial = 2
+ NSVisualEffectMaterialTitlebar NSVisualEffectMaterial = 3
+ NSVisualEffectMaterialSelection NSVisualEffectMaterial = 4
+ NSVisualEffectMaterialMenu NSVisualEffectMaterial = 5
+ NSVisualEffectMaterialPopover NSVisualEffectMaterial = 6
+ NSVisualEffectMaterialSidebar NSVisualEffectMaterial = 7
+ NSVisualEffectMaterialHeaderView NSVisualEffectMaterial = 10
+ NSVisualEffectMaterialSheet NSVisualEffectMaterial = 11
+ NSVisualEffectMaterialWindowBackground NSVisualEffectMaterial = 12
+ NSVisualEffectMaterialHUDWindow NSVisualEffectMaterial = 13
+ NSVisualEffectMaterialFullScreenUI NSVisualEffectMaterial = 15
+ NSVisualEffectMaterialToolTip NSVisualEffectMaterial = 17
+ NSVisualEffectMaterialContentBackground NSVisualEffectMaterial = 18
+ NSVisualEffectMaterialUnderWindowBackground NSVisualEffectMaterial = 21
+ NSVisualEffectMaterialUnderPageBackground NSVisualEffectMaterial = 22
+ NSVisualEffectMaterialAuto NSVisualEffectMaterial = -1 // Use auto-selection based on Style
+)
+
+// MacLiquidGlass contains configuration for the Liquid Glass effect
+type MacLiquidGlass struct {
+ // Style of the glass effect
+ Style MacLiquidGlassStyle
+
+ // Material to use for NSVisualEffectView (when NSGlassEffectView is not available)
+ // Set to NSVisualEffectMaterialAuto to use automatic selection based on Style
+ Material NSVisualEffectMaterial
+
+ // Corner radius for the glass effect (0 for square corners)
+ CornerRadius float64
+
+ // Tint color for the glass (optional, nil for no tint)
+ TintColor *RGBA
+
+ // Group identifier for merging multiple glass windows
+ GroupID string
+
+ // Spacing between grouped glass elements (in points)
+ GroupSpacing float64
+}
+
// MacWindow contains macOS specific options for Webview Windows
type MacWindow struct {
// Backdrop is the backdrop type for the window
@@ -425,6 +488,9 @@ type MacWindow struct {
// WindowLevel sets the window level to control the order of windows in the screen
WindowLevel MacWindowLevel
+
+ // LiquidGlass contains configuration for the Liquid Glass effect
+ LiquidGlass MacLiquidGlass
}
type MacWindowLevel string