diff --git a/mkdocs-website/docs/en/learn/guides/customising-windows.md b/mkdocs-website/docs/en/learn/guides/customising-windows.md
new file mode 100644
index 00000000000..d8217fb06e0
--- /dev/null
+++ b/mkdocs-website/docs/en/learn/guides/customising-windows.md
@@ -0,0 +1,111 @@
+# Customising Window Controls in Wails
+
+Wails provides an API to control the appearance and functionality of the controls of a window. 
+This functionality is available on Windows and macOS, but not on Linux.
+
+## Setting the Window Button States
+
+The button states are defined by the `ButtonState` enum:
+
+```go
+type ButtonState int
+
+const (
+    ButtonEnabled   ButtonState = 0
+    ButtonDisabled ButtonState = 1
+    ButtonHidden   ButtonState = 2
+)
+```
+
+- `ButtonEnabled`: The button is enabled and visible.
+- `ButtonDisabled`: The button is visible but disabled (grayed out).
+- `ButtonHidden`: The button is hidden from the titlebar.
+
+The button states can be set during window creation or at runtime.
+
+### Setting Button States During Window Creation
+
+When creating a new window, you can set the initial state of the buttons using the `WebviewWindowOptions` struct:
+
+```go
+package main
+
+import (
+	"github.com/wailsapp/wails/v3/pkg/application"
+)
+
+func main() {
+    app := application.New(application.Options{
+		Name:        "My Application",
+	})
+	
+    app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+        MinimiseButtonState: application.ButtonHidden,
+        MaximiseButtonState: application.ButtonDisabled,
+        CloseButtonState:    application.ButtonEnabled,
+	})
+	
+	app.Run()
+}
+```
+
+In the example above, the minimise button is hidden, the maximise button is inactive (grayed out), and the close button is active.
+
+### Setting Button States at Runtime
+
+You can also change the button states at runtime using the following methods on the `Window` interface:
+
+```go
+window.SetMinimiseButtonState(wails.ButtonHidden)
+window.SetMaximiseButtonState(wails.ButtonEnabled)
+window.SetCloseButtonState(wails.ButtonDisabled)
+```
+
+### Platform Differences
+
+The button state functionality behaves slightly differently on Windows and macOS:
+
+|                       | Windows                | Mac                    |
+|-----------------------|------------------------|------------------------|
+| Disable Min/Max/Close | Disables Min/Max/Close | Disables Min/Max/Close |
+| Hide Min              | Disables Min           | Hides Min button       |
+| Hide Max              | Disables Max           | Hides Max button       |
+| Hide Close            | Hides all controls     | Hides Close            |
+
+Note: On Windows, it is not possible to hide the Min/Max buttons individually.
+However, disabling both will hide both of the controls and only show the
+close button.
+
+### Controlling Window Style (Windows)
+
+To control the style of the titlebar on Windows, you can use the `ExStyle` field in the `WebviewWindowOptions` struct:
+
+Example:
+```go
+package main
+
+import (
+	"github.com/wailsapp/wails/v3/pkg/application"
+	"github.com/wailsapp/wails/v3/pkg/w32"
+)
+
+func main() {
+    app := application.New(application.Options{
+		Name: "My Application",
+	})
+	
+    app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+        Windows: application.WindowsWindow{
+            ExStyle: w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST,
+        },
+	})
+	
+	app.Run()
+}
+```
+
+Other options that affect the Extended Style of a window will be overridden by this setting:
+- HiddenOnTaskbar
+- AlwaysOnTop
+- IgnoreMouseEvents
+- BackgroundType
\ No newline at end of file
diff --git a/mkdocs-website/mkdocs.yml b/mkdocs-website/mkdocs.yml
index f85498cc3fe..7f9e36d5cf5 100644
--- a/mkdocs-website/mkdocs.yml
+++ b/mkdocs-website/mkdocs.yml
@@ -142,6 +142,8 @@ nav:
     - Learn More:
         - Runtime: learn/runtime.md
         - Plugins: learn/plugins.md
+        - Guides:
+            - Customising Windows: learn/guides/customising-windows.md
     - Feedback: getting-started/feedback.md
   - Feedback: getting-started/feedback.md
   - What's New in v3?: whats-new.md
diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go
index f427d44946a..4b705ac5b9b 100644
--- a/v3/examples/window/main.go
+++ b/v3/examples/window/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	_ "embed"
 	"fmt"
+	"github.com/wailsapp/wails/v3/pkg/w32"
 	"log"
 	"math/rand"
 	"runtime"
@@ -64,12 +65,7 @@ func main() {
 		myMenu.Add("New WebviewWindow (Disable Minimise)").
 			OnClick(func(ctx *application.Context) {
 				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
-					Windows: application.WindowsWindow{
-						DisableMinimiseButton: true,
-					},
-					Mac: application.MacWindow{
-						DisableMinimiseButton: true,
-					},
+					MinimiseButtonState: application.ButtonDisabled,
 				}).
 					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
 					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
@@ -80,12 +76,29 @@ func main() {
 		myMenu.Add("New WebviewWindow (Disable Maximise)").
 			OnClick(func(ctx *application.Context) {
 				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
-					Windows: application.WindowsWindow{
-						DisableMaximiseButton: true,
-					},
-					Mac: application.MacWindow{
-						DisableMaximiseButton: true,
-					},
+					MaximiseButtonState: application.ButtonDisabled,
+				}).
+					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
+					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
+					SetURL("https://wails.io").
+					Show()
+				windowCounter++
+			})
+		myMenu.Add("New WebviewWindow (Hide Minimise)").
+			OnClick(func(ctx *application.Context) {
+				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+					MinimiseButtonState: application.ButtonHidden,
+				}).
+					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
+					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
+					SetURL("https://wails.io").
+					Show()
+				windowCounter++
+			})
+		myMenu.Add("New WebviewWindow (Hide Maximise)").
+			OnClick(func(ctx *application.Context) {
+				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+					MaximiseButtonState: application.ButtonHidden,
 				}).
 					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
 					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
@@ -94,13 +107,22 @@ func main() {
 				windowCounter++
 			})
 	}
-	if runtime.GOOS == "darwin" {
+	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
 		myMenu.Add("New WebviewWindow (Disable Close)").
 			OnClick(func(ctx *application.Context) {
 				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
-					Mac: application.MacWindow{
-						DisableCloseButton: true,
-					},
+					CloseButtonState: application.ButtonDisabled,
+				}).
+					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
+					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
+					SetURL("https://wails.io").
+					Show()
+				windowCounter++
+			})
+		myMenu.Add("New WebviewWindow (Hide Close)").
+			OnClick(func(ctx *application.Context) {
+				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+					CloseButtonState: application.ButtonHidden,
 				}).
 					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
 					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
@@ -110,6 +132,22 @@ func main() {
 			})
 
 	}
+	if runtime.GOOS == "windows" {
+		myMenu.Add("New WebviewWindow (Custom ExStyle)").
+			OnClick(func(ctx *application.Context) {
+				app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+					Windows: application.WindowsWindow{
+						DisableMenu: true,
+						ExStyle:     w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST,
+					},
+				}).
+					SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
+					SetRelativePosition(rand.Intn(1000), rand.Intn(800)).
+					SetURL("https://wails.io").
+					Show()
+				windowCounter++
+			})
+	}
 
 	myMenu.Add("New WebviewWindow (Hides on Close one time)").
 		SetAccelerator("CmdOrCtrl+H").
@@ -278,12 +316,6 @@ func main() {
 			w.SetMinSize(200, 200)
 		})
 	})
-	sizeMenu.Add("Set Max Size (600,600)").OnClick(func(ctx *application.Context) {
-		currentWindow(func(w *application.WebviewWindow) {
-			w.SetFullscreenButtonEnabled(false)
-			w.SetMaxSize(600, 600)
-		})
-	})
 	sizeMenu.Add("Get Current WebviewWindow Size").OnClick(func(ctx *application.Context) {
 		currentWindow(func(w *application.WebviewWindow) {
 			width, height := w.Size()
@@ -297,12 +329,6 @@ func main() {
 		})
 	})
 
-	sizeMenu.Add("Reset Max Size").OnClick(func(ctx *application.Context) {
-		currentWindow(func(w *application.WebviewWindow) {
-			w.SetMaxSize(0, 0)
-			w.SetFullscreenButtonEnabled(true)
-		})
-	})
 	positionMenu := menu.AddSubmenu("Position")
 	positionMenu.Add("Set Relative Position (0,0)").OnClick(func(ctx *application.Context) {
 		currentWindow(func(w *application.WebviewWindow) {
@@ -346,6 +372,52 @@ func main() {
 			w.Center()
 		})
 	})
+	titleBarMenu := menu.AddSubmenu("Controls")
+	titleBarMenu.Add("Disable Minimise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMinimiseButtonState(application.ButtonDisabled)
+		})
+	})
+	titleBarMenu.Add("Enable Minimise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMinimiseButtonState(application.ButtonEnabled)
+		})
+	})
+	titleBarMenu.Add("Hide Minimise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMinimiseButtonState(application.ButtonHidden)
+		})
+	})
+	titleBarMenu.Add("Disable Maximise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMaximiseButtonState(application.ButtonDisabled)
+		})
+	})
+	titleBarMenu.Add("Enable Maximise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMaximiseButtonState(application.ButtonEnabled)
+		})
+	})
+	titleBarMenu.Add("Hide Maximise").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetMaximiseButtonState(application.ButtonHidden)
+		})
+	})
+	titleBarMenu.Add("Disable Close").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetCloseButtonState(application.ButtonDisabled)
+		})
+	})
+	titleBarMenu.Add("Enable Close").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetCloseButtonState(application.ButtonEnabled)
+		})
+	})
+	titleBarMenu.Add("Hide Close").OnClick(func(ctx *application.Context) {
+		currentWindow(func(w *application.WebviewWindow) {
+			w.SetCloseButtonState(application.ButtonHidden)
+		})
+	})
 	stateMenu := menu.AddSubmenu("State")
 	stateMenu.Add("Minimise (for 2 secs)").OnClick(func(ctx *application.Context) {
 		currentWindow(func(w *application.WebviewWindow) {
diff --git a/v3/pkg/application/messageprocessor_window.go b/v3/pkg/application/messageprocessor_window.go
index ce69d541f08..1ea5db44153 100644
--- a/v3/pkg/application/messageprocessor_window.go
+++ b/v3/pkg/application/messageprocessor_window.go
@@ -249,14 +249,6 @@ func (m *MessageProcessor) processWindowMethod(method int, rw http.ResponseWrite
 		}
 		window.SetFrameless(*frameless)
 		m.ok(rw)
-	case WindowSetFullscreenButtonEnabled:
-		enabled := args.Bool("enabled")
-		if enabled == nil {
-			m.Error("Invalid SetFullscreenButtonEnabled Message: 'enabled' value required")
-			return
-		}
-		window.SetFullscreenButtonEnabled(*enabled)
-		m.ok(rw)
 	case WindowSetMaxSize:
 		width := args.Int("width")
 		if width == nil {
diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go
index 0e4f4e4367d..783a12ca971 100644
--- a/v3/pkg/application/webview_window.go
+++ b/v3/pkg/application/webview_window.go
@@ -71,9 +71,6 @@ type (
 		isNormal() bool
 		isVisible() bool
 		isFocused() bool
-		setFullscreenButtonEnabled(enabled bool)
-		setMinimiseButtonEnabled(enabled bool)
-		setMaximiseButtonEnabled(enabled bool)
 		focus()
 		show()
 		hide()
@@ -90,6 +87,9 @@ type (
 		flash(enabled bool)
 		handleKeyEvent(acceleratorString string)
 		getBorderSizes() *LRTB
+		setMinimiseButtonState(state ButtonState)
+		setMaximiseButtonState(state ButtonState)
+		setCloseButtonState(state ButtonState)
 	}
 )
 
@@ -540,31 +540,31 @@ func (w *WebviewWindow) Fullscreen() Window {
 	return w
 }
 
-func (w *WebviewWindow) SetFullscreenButtonEnabled(enabled bool) Window {
-	w.options.FullscreenButtonEnabled = enabled
+func (w *WebviewWindow) SetMinimiseButtonState(state ButtonState) Window {
+	w.options.MinimiseButtonState = state
 	if w.impl != nil {
 		InvokeSync(func() {
-			w.impl.setFullscreenButtonEnabled(enabled)
+			w.impl.setMinimiseButtonState(state)
 		})
 	}
 	return w
 }
 
-func (w *WebviewWindow) SetMinimiseButtonEnabled(enabled bool) Window {
-	w.options.FullscreenButtonEnabled = enabled
+func (w *WebviewWindow) SetMaximiseButtonState(state ButtonState) Window {
+	w.options.MaximiseButtonState = state
 	if w.impl != nil {
 		InvokeSync(func() {
-			w.impl.setMinimiseButtonEnabled(enabled)
+			w.impl.setMaximiseButtonState(state)
 		})
 	}
 	return w
 }
 
-func (w *WebviewWindow) SetMaximiseButtonEnabled(enabled bool) Window {
-	w.options.FullscreenButtonEnabled = enabled
+func (w *WebviewWindow) SetCloseButtonState(state ButtonState) Window {
+	w.options.CloseButtonState = state
 	if w.impl != nil {
 		InvokeSync(func() {
-			w.impl.setMaximiseButtonEnabled(enabled)
+			w.impl.setCloseButtonState(state)
 		})
 	}
 	return w
diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go
index 32ba6765576..40d493d728c 100644
--- a/v3/pkg/application/webview_window_darwin.go
+++ b/v3/pkg/application/webview_window_darwin.go
@@ -641,22 +641,38 @@ static void windowHide(void *window) {
 	[(WebviewWindow*)window orderOut:nil];
 }
 
-static void enableMinimiseButton(void *window, bool enabled) {
+// setButtonState sets the state of the given button
+// 0 = enabled
+// 1 = disabled
+// 2 = hidden
+static void setButtonState(void *button, int state) {
+	if (button == nil) {
+		return;
+	}
+	NSButton *nsbutton = (NSButton*)button;
+	nsbutton.hidden = state == 2;
+	nsbutton.enabled = state != 1;
+}
+
+// setMinimiseButtonState sets the minimise button state
+static void setMinimiseButtonState(void *window, int state) {
 	WebviewWindow* nsWindow = (WebviewWindow*)window;
 	NSButton *minimiseButton = [nsWindow standardWindowButton:NSWindowMiniaturizeButton];
-	minimiseButton.enabled = enabled;
+	setButtonState(minimiseButton, state);
 }
 
-static void enableMaximiseButton(void *window, bool enabled) {
+// setMaximiseButtonState sets the maximise button state
+static void setMaximiseButtonState(void *window, int state) {
 	WebviewWindow* nsWindow = (WebviewWindow*)window;
 	NSButton *maximiseButton = [nsWindow standardWindowButton:NSWindowZoomButton];
-	maximiseButton.enabled = enabled;
+	setButtonState(maximiseButton, state);
 }
 
-static void enableCloseButton(void *window, bool enabled) {
+// setCloseButtonState sets the close button state
+static void setCloseButtonState(void *window, int state) {
 	WebviewWindow* nsWindow = (WebviewWindow*)window;
 	NSButton *closeButton = [nsWindow standardWindowButton:NSWindowCloseButton];
-	closeButton.enabled = enabled;
+	setButtonState(closeButton, state);
 }
 
 // windowShowMenu opens an NSMenu at the given coordinates
@@ -1131,15 +1147,10 @@ func (w *macosWebviewWindow) run() {
 		case MacBackdropNormal:
 		}
 
-		if macOptions.DisableMinimiseButton {
-			w.setMinimiseButtonEnabled(false)
-		}
-		if macOptions.DisableMaximiseButton {
-			w.setMaximiseButtonEnabled(false)
-		}
-		if macOptions.DisableCloseButton {
-			w.setCloseButtonEnabled(false)
-		}
+		// Initialise the window buttons
+		w.setMinimiseButtonState(options.MinimiseButtonState)
+		w.setMaximiseButtonState(options.MaximiseButtonState)
+		w.setCloseButtonState(options.CloseButtonState)
 
 		if options.IgnoreMouseEvents {
 			C.windowIgnoreMouseEvents(w.nsWindow, C.bool(true))
@@ -1270,14 +1281,14 @@ func (w *macosWebviewWindow) startDrag() error {
 	return nil
 }
 
-func (w *macosWebviewWindow) setMinimiseButtonEnabled(enabled bool) {
-	C.enableMinimiseButton(w.nsWindow, C.bool(enabled))
+func (w *macosWebviewWindow) setMinimiseButtonState(state ButtonState) {
+	C.setMinimiseButtonState(w.nsWindow, C.int(state))
 }
 
-func (w *macosWebviewWindow) setMaximiseButtonEnabled(enabled bool) {
-	C.enableMaximiseButton(w.nsWindow, C.bool(enabled))
+func (w *macosWebviewWindow) setMaximiseButtonState(state ButtonState) {
+	C.setMaximiseButtonState(w.nsWindow, C.int(state))
 }
 
-func (w *macosWebviewWindow) setCloseButtonEnabled(enabled bool) {
-	C.enableCloseButton(w.nsWindow, C.bool(enabled))
+func (w *macosWebviewWindow) setCloseButtonState(state ButtonState) {
+	C.setCloseButtonState(w.nsWindow, C.int(state))
 }
diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go
index cbf8edee643..726b6f1e171 100644
--- a/v3/pkg/application/webview_window_linux.go
+++ b/v3/pkg/application/webview_window_linux.go
@@ -337,3 +337,18 @@ func (w *linuxWebviewWindow) handleKeyEvent(acceleratorString string) {
 	// }
 	w.parent.processKeyBinding(acceleratorString)
 }
+
+// SetMinimiseButtonState is unsupported on Linux
+func (w *linuxWebviewWindow) SetMinimiseButtonState(state ButtonState) Window {
+	return w
+}
+
+// SetMaximiseButtonState is unsupported on Linux
+func (w *linuxWebviewWindow) SetMaximiseButtonState(state ButtonState) Window {
+	return w
+}
+
+// SetFullscreenButtonState is unsupported on Linux
+func (w *linuxWebviewWindow) SetCloseButtonState(state ButtonState) Window {
+	return w
+}
diff --git a/v3/pkg/application/webview_window_options.go b/v3/pkg/application/webview_window_options.go
index 2f220e3f1d1..cad27dba8bb 100644
--- a/v3/pkg/application/webview_window_options.go
+++ b/v3/pkg/application/webview_window_options.go
@@ -14,6 +14,14 @@ const (
 	WindowStateFullscreen
 )
 
+type ButtonState int
+
+const (
+	ButtonEnabled  ButtonState = 0
+	ButtonDisabled ButtonState = 1
+	ButtonHidden   ButtonState = 2
+)
+
 type WebviewWindowOptions struct {
 	// Name is a unique identifier that can be given to a window.
 	Name string
@@ -108,6 +116,11 @@ type WebviewWindowOptions struct {
 	// Linux options
 	Linux LinuxWindow
 
+	// Toolbar button states
+	MinimiseButtonState ButtonState
+	MaximiseButtonState ButtonState
+	CloseButtonState    ButtonState
+
 	// ShouldClose is called when the window is about to close.
 	// Return true to allow the window to close, or false to prevent it from closing.
 	ShouldClose func(window *WebviewWindow) bool
@@ -284,11 +297,8 @@ type WindowsWindow struct {
 	// Permissions map for WebView2. If empty, default permissions will be granted.
 	Permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState
 
-	// Disables the minimise button
-	DisableMinimiseButton bool
-
-	// Disables the maximise button
-	DisableMaximiseButton bool
+	// ExStyle is the extended window style
+	ExStyle int
 }
 
 type Theme int
@@ -370,15 +380,6 @@ type MacWindow struct {
 
 	// WebviewPreferences contains preferences for the webview
 	WebviewPreferences MacWebviewPreferences
-
-	// Disables the minimise button
-	DisableMinimiseButton bool
-
-	// Disables the maximise button
-	DisableMaximiseButton bool
-
-	// Disables the close button
-	DisableCloseButton bool
 }
 
 // MacWebviewPreferences contains preferences for the Mac webview
diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go
index 0a313e236c4..9a28b91183a 100644
--- a/v3/pkg/application/webview_window_windows.go
+++ b/v3/pkg/application/webview_window_windows.go
@@ -192,8 +192,7 @@ func (w *windowsWebviewWindow) run() {
 
 	w.chromium = edge.NewChromium()
 
-	var exStyle uint
-	exStyle = w32.WS_EX_CONTROLPARENT
+	exStyle := w32.WS_EX_CONTROLPARENT
 	if options.BackgroundType != BackgroundTypeSolid {
 		exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP
 		if w.parent.options.IgnoreMouseEvents {
@@ -210,6 +209,10 @@ func (w *windowsWebviewWindow) run() {
 		exStyle |= w32.WS_EX_APPWINDOW
 	}
 
+	if options.Windows.ExStyle != 0 {
+		exStyle = options.Windows.ExStyle
+	}
+
 	// ToDo: X, Y should also be scaled, should it be always relative to the main monitor?
 	var startX, _ = lo.Coalesce(options.X, w32.CW_USEDEFAULT)
 	var startY, _ = lo.Coalesce(options.Y, w32.CW_USEDEFAULT)
@@ -234,7 +237,7 @@ func (w *windowsWebviewWindow) run() {
 	var style uint = w32.WS_OVERLAPPEDWINDOW
 
 	w.hwnd = w32.CreateWindowEx(
-		exStyle,
+		uint(exStyle),
 		windowClassName,
 		w32.MustStringToUTF16Ptr(options.Title),
 		style,
@@ -255,9 +258,10 @@ func (w *windowsWebviewWindow) run() {
 
 	w.setupChromium()
 
-	// Min/max buttons
-	w.setMinimiseButtonEnabled(!options.Windows.DisableMinimiseButton)
-	w.setMaximiseButtonEnabled(!options.Windows.DisableMaximiseButton)
+	// Initialise the window buttons
+	w.setMinimiseButtonState(options.MinimiseButtonState)
+	w.setMaximiseButtonState(options.MaximiseButtonState)
+	w.setCloseButtonState(options.CloseButtonState)
 
 	// Register the window with the application
 	getNativeApplication().registerWindow(w)
@@ -1680,3 +1684,41 @@ func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (w32.HICON, error
 	}
 	return result, err
 }
+
+func (w *windowsWebviewWindow) setMinimiseButtonState(state ButtonState) {
+	switch state {
+	case ButtonDisabled, ButtonHidden:
+		w.setStyle(false, w32.WS_MINIMIZEBOX)
+	case ButtonEnabled:
+		w.setStyle(true, w32.WS_SYSMENU)
+		w.setStyle(true, w32.WS_MINIMIZEBOX)
+
+	}
+}
+
+func (w *windowsWebviewWindow) setMaximiseButtonState(state ButtonState) {
+	switch state {
+	case ButtonDisabled, ButtonHidden:
+		w.setStyle(false, w32.WS_MAXIMIZEBOX)
+	case ButtonEnabled:
+		w.setStyle(true, w32.WS_SYSMENU)
+		w.setStyle(true, w32.WS_MAXIMIZEBOX)
+	}
+}
+
+func (w *windowsWebviewWindow) setCloseButtonState(state ButtonState) {
+	switch state {
+	case ButtonEnabled:
+		w.setStyle(true, w32.WS_SYSMENU)
+		_ = w32.EnableCloseButton(w.hwnd)
+	case ButtonDisabled:
+		w.setStyle(true, w32.WS_SYSMENU)
+		_ = w32.DisableCloseButton(w.hwnd)
+	case ButtonHidden:
+		w.setStyle(false, w32.WS_SYSMENU)
+	}
+}
+
+func (w *windowsWebviewWindow) setGWLStyle(style int) {
+	w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, uint32(style))
+}
diff --git a/v3/pkg/application/window.go b/v3/pkg/application/window.go
index 57b85dbfac9..e8d7f214cde 100644
--- a/v3/pkg/application/window.go
+++ b/v3/pkg/application/window.go
@@ -55,8 +55,10 @@ type Window interface {
 	SetAlwaysOnTop(b bool) Window
 	SetBackgroundColour(colour RGBA) Window
 	SetFrameless(frameless bool) Window
-	SetFullscreenButtonEnabled(enabled bool) Window
 	SetHTML(html string) Window
+	SetMinimiseButtonState(state ButtonState) Window
+	SetMaximiseButtonState(state ButtonState) Window
+	SetCloseButtonState(state ButtonState) Window
 	SetMaxSize(maxWidth, maxHeight int) Window
 	SetMinSize(minWidth, minHeight int) Window
 	SetRelativePosition(x, y int) Window
diff --git a/v3/pkg/w32/window.go b/v3/pkg/w32/window.go
index 545b4213542..5a862965904 100644
--- a/v3/pkg/w32/window.go
+++ b/v3/pkg/w32/window.go
@@ -13,6 +13,21 @@ import (
 	"unsafe"
 )
 
+const (
+	SC_CLOSE    = 0xF060
+	SC_MOVE     = 0xF010
+	SC_MAXIMIZE = 0xF030
+	SC_MINIMIZE = 0xF020
+	SC_SIZE     = 0xF000
+	SC_RESTORE  = 0xF120
+)
+
+var (
+	user32         = syscall.NewLazyDLL("user32.dll")
+	getSystemMenu  = user32.NewProc("GetSystemMenu")
+	enableMenuItem = user32.NewProc("EnableMenuItem")
+)
+
 const (
 	GCLP_HBRBACKGROUND int32 = -10
 	GCLP_HICON         int32 = -14
@@ -251,3 +266,31 @@ func EnumChildWindows(hwnd HWND, callback func(hwnd HWND, lparam LPARAM) LRESULT
 	r, _, _ := procEnumChildWindows.Call(hwnd, syscall.NewCallback(callback), 0)
 	return r
 }
+
+func DisableCloseButton(hwnd HWND) error {
+	hSysMenu, _, err := getSystemMenu.Call(hwnd, 0)
+	if hSysMenu == 0 {
+		return err
+	}
+
+	r1, _, err := enableMenuItem.Call(hSysMenu, SC_CLOSE, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED)
+	if r1 == 0 {
+		return err
+	}
+
+	return nil
+}
+
+func EnableCloseButton(hwnd HWND) error {
+	hSysMenu, _, err := getSystemMenu.Call(hwnd, 0)
+	if hSysMenu == 0 {
+		return err
+	}
+
+	r1, _, err := enableMenuItem.Call(hSysMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED)
+	if r1 == 0 {
+		return err
+	}
+
+	return nil
+}
diff --git a/v3/wep/proposals/titlebar-buttons/proposal.md b/v3/wep/proposals/titlebar-buttons/proposal.md
new file mode 100644
index 00000000000..8bd6b8c4fdc
--- /dev/null
+++ b/v3/wep/proposals/titlebar-buttons/proposal.md
@@ -0,0 +1,137 @@
+# Wails Enhancement Proposal (WEP)
+
+## Customising Window Controls in Wails
+
+**Author**: Lea Anthony
+**Created**: 2024-05-20
+
+## Summary
+
+This is a proposal for an API to control the appearance and functionality of window controls in Wails.
+This will only be available on Windows and macOS.
+
+## Motivation
+
+We currently do not fully support the ability to customise window controls. 
+
+## Detailed Design
+
+### Controlling Button State
+
+1. A new enum will be added:
+
+```go
+      type ButtonState int
+
+      const (
+          ButtonEnabled  ButtonState = 0
+          ButtonDisabled ButtonState = 1
+          ButtonHidden   ButtonState = 2
+      )
+```
+
+2. These options will be added to the `WebviewWindowOptions` option struct:
+
+```go
+   MinimiseButtonState ButtonState
+   MaximiseButtonState ButtonState
+   CloseButtonState    ButtonState
+```
+
+3. These options will be removed from the current Windows/Mac options:
+
+- DisableMinimiseButton
+- DisableMaximiseButton
+- DisableCloseButton
+
+4. These methods will be added to the `Window` interface:
+
+```go
+   SetMinimizeButtonState(state ButtonState)
+   SetMaximizeButtonState(state ButtonState)
+   SetCloseButtonState(state ButtonState)
+```
+
+The settings translate to the following functionality on each platform:
+
+|                       | Windows                | Mac                    |
+|-----------------------|------------------------|------------------------|
+| Disable Min/Max/Close | Disables Min/Max/Close | Disables Min/Max/Close |
+| Hide Min              | Disables Min           | Hides Min button       |
+| Hide Max              | Disables Max           | Hides Max button       |
+| Hide Close            | Hides all controls     | Hides Close            |
+
+Note: On Windows, it is not possible to hide the Min/Max buttons individually.
+However, disabling both will hide both of the controls and only show the 
+close button. 
+
+### Controlling Window Style (Windows)
+
+As Windows currently does not have much in the way of controlling the style of the
+titlebar, a new option will be added to the `WebviewWindowOptions` option struct:
+
+```go
+   ExStyle int
+```
+
+If this is set, then the new Window will use the style specified in the `ExStyle` field.
+
+Example:
+```go
+package main
+
+import (
+	"github.com/wailsapp/wails/v3/pkg/application"
+	"github.com/wailsapp/wails/v3/pkg/w32"
+)
+
+func main() {
+    app := application.New(application.Options{
+		Name:        "My Application",
+	})
+	
+    app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
+        Windows: application.WindowsWindow{
+            ExStyle: w32.WS_EX_TOOLWINDOW | w32.WS_EX_NOREDIRECTIONBITMAP | w32.WS_EX_TOPMOST,
+        },
+	})
+	
+	app.Run()
+}
+```
+
+## Pros/Cons
+
+### Pros
+
+- We bring much needed customisation capabilities to both macOS and Windows
+
+### Cons
+
+- Windows works slightly different to macOS
+- No Linux support (doesn't look like it's possible regardless of the solution)
+
+## Alternatives Considered
+
+The alternative is to draw your own titlebar, but this is a lot of work and often doesn't look good.
+
+## Backwards Compatibility
+
+This is not backwards compatible as we remove the old "disable button" options.
+
+## Test Plan
+
+As part of the implementation, the window example will be updated to test the functionality.
+
+## Reference Implementation
+
+There is a reference implementation as part of this proposal.
+
+## Maintenance Plan
+
+This feature will be maintained and supported by the Wails developers.
+
+## Conclusion
+
+This API would be a leap forward in giving developers greater control over their application window appearances.
+