---
title: Frameless Windows
description: Create custom window chrome with frameless windows
sidebar:
  order: 4
---

import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";

## Frameless Windows

Wails provides **frameless window support** with CSS-based drag regions and platform-native behaviour. Remove the platform-native title bar for complete control over window chrome, custom designs, and unique user experiences whilst maintaining essential functionality like dragging, resizing, and system controls.

## Quick Start

```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Title:     "Frameless App",
    Width:     800,
    Height:    600,
    Frameless: true,
})
```

**CSS for draggable title bar:**

```css
.titlebar {
    --wails-draggable: drag;
    height: 40px;
    background: #333;
}

.titlebar button {
    --wails-draggable: no-drag;
}
```

**HTML:**

```html
<div class="titlebar">
    <span>My Application</span>
    <button onclick="window.close()">×</button>
</div>
```

**That's it!** You have a custom title bar.

## Creating Frameless Windows

### Basic Frameless Window

```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Frameless: true,
    Width:     800,
    Height:    600,
})
```

**What you get:**
- No title bar
- No window borders
- No system buttons
- Transparent background (optional)

**What you need to implement:**
- Draggable area
- Close/minimise/maximise buttons
- Resize handles (if resizable)

### With Transparent Background

```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Frameless:      true,
    BackgroundType: application.BackgroundTypeTransparent,
})
```

**Use cases:**
- Rounded corners
- Custom shapes
- Overlay windows
- Splash screens

## Drag Regions

### CSS-Based Dragging

Use the `--wails-draggable` CSS property:

```css
/* Draggable area */
.titlebar {
    --wails-draggable: drag;
}

/* Non-draggable elements within draggable area */
.titlebar button {
    --wails-draggable: no-drag;
}
```

**Values:**
- `drag` - Area is draggable
- `no-drag` - Area is not draggable (even if parent is)

### Complete Title Bar Example

```html
<div class="titlebar">
    <div class="title">My Application</div>
    <div class="controls">
        <button class="minimize">−</button>
        <button class="maximize">□</button>
        <button class="close">×</button>
    </div>
</div>
```

```css
.titlebar {
    --wails-draggable: drag;
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 40px;
    background: #2c2c2c;
    color: white;
    padding: 0 16px;
}

.title {
    font-size: 14px;
    user-select: none;
}

.controls {
    display: flex;
    gap: 8px;
}

.controls button {
    --wails-draggable: no-drag;
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    color: white;
    font-size: 16px;
    cursor: pointer;
    border-radius: 4px;
}

.controls button:hover {
    background: rgba(255, 255, 255, 0.1);
}

.controls .close:hover {
    background: #e81123;
}
```

**JavaScript for buttons:**

```javascript
import { Window } from '@wailsio/runtime'

document.querySelector('.minimize').addEventListener('click', () => Window.Minimise())
document.querySelector('.maximize').addEventListener('click', () => Window.Maximise())
document.querySelector('.close').addEventListener('click', () => Window.Close())
```

## System Buttons

### Implementing Close/Minimise/Maximise

**Go side:**

```go
type WindowControls struct {
    window *application.WebviewWindow
}

func (wc *WindowControls) Minimise() {
    wc.window.Minimise()
}

func (wc *WindowControls) Maximise() {
    if wc.window.IsMaximised() {
        wc.window.UnMaximise()
    } else {
        wc.window.Maximise()
    }
}

func (wc *WindowControls) Close() {
    wc.window.Close()
}
```

**JavaScript side:**

```javascript
import { Minimise, Maximise, Close } from './bindings/WindowControls'

document.querySelector('.minimize').addEventListener('click', Minimise)
document.querySelector('.maximize').addEventListener('click', Maximise)
document.querySelector('.close').addEventListener('click', Close)
```

**Or use runtime methods:**

```javascript
import { Window } from '@wailsio/runtime'

document.querySelector('.minimize').addEventListener('click', () => Window.Minimise())
document.querySelector('.maximize').addEventListener('click', () => Window.Maximise())
document.querySelector('.close').addEventListener('click', () => Window.Close())
```

### Toggle Maximise State

Track maximise state for button icon:

```javascript
import { Window } from '@wailsio/runtime'

async function toggleMaximise() {
    const isMaximised = await Window.IsMaximised()

    if (isMaximised) {
        await Window.Restore()
    } else {
        await Window.Maximise()
    }

    updateMaximiseButton()
}

async function updateMaximiseButton() {
    const isMaximised = await Window.IsMaximised()
    const button = document.querySelector('.maximize')
    button.textContent = isMaximised ? '❐' : '□'
}
```

## Resize Handles

### CSS-Based Resize

Wails provides automatic resize handles for frameless windows:

```css
/* Enable resize on all edges */
body {
    --wails-resize: all;
}

/* Or specific edges */
.resize-top {
    --wails-resize: top;
}

.resize-bottom {
    --wails-resize: bottom;
}

.resize-left {
    --wails-resize: left;
}

.resize-right {
    --wails-resize: right;
}

/* Corners */
.resize-top-left {
    --wails-resize: top-left;
}

.resize-top-right {
    --wails-resize: top-right;
}

.resize-bottom-left {
    --wails-resize: bottom-left;
}

.resize-bottom-right {
    --wails-resize: bottom-right;
}
```

**Values:**
- `all` - Resize from all edges
- `top`, `bottom`, `left`, `right` - Specific edges
- `top-left`, `top-right`, `bottom-left`, `bottom-right` - Corners
- `none` - No resize

### Resize Handle Example

```html
<div class="window">
    <div class="titlebar">...</div>
    <div class="content">...</div>
    <div class="resize-handle resize-bottom-right"></div>
</div>
```

```css
.resize-handle {
    position: absolute;
    width: 16px;
    height: 16px;
}

.resize-bottom-right {
    --wails-resize: bottom-right;
    bottom: 0;
    right: 0;
    cursor: nwse-resize;
}
```

## Platform-Specific Behaviour

<Tabs syncKey="platform">
  <TabItem label="Windows" icon="seti:windows">
    **Windows frameless windows:**
    
    ```go
    window := app.Window.NewWithOptions(application.WebviewWindowOptions{
        Frameless: true,
        Windows: application.WindowsOptions{
            DisableFramelessWindowDecorations: false,
        },
    })
    ```
    
    **Features:**
    - Automatic drop shadow
    - Snap layouts support (Windows 11)
    - Aero Snap support
    - DPI scaling
    
    **Disable decorations:**
    ```go
    Windows: application.WindowsOptions{
        DisableFramelessWindowDecorations: true,
    },
    ```
    
    **Snap Assist:**
    ```go
    // Trigger Windows 11 Snap Assist
    window.SnapAssist()
    ```
    
    **Custom title bar height:**
    Windows automatically detects drag regions from CSS.
  </TabItem>

  <TabItem label="macOS" icon="apple">
    **macOS frameless windows:**
    
    ```go
    window := app.Window.NewWithOptions(application.WebviewWindowOptions{
        Frameless: true,
        Mac: application.MacOptions{
            TitleBarAppearsTransparent: true,
            InvisibleTitleBarHeight:    40,
        },
    })
    ```
    
    **Features:**
    - Native fullscreen support
    - Traffic light buttons (optional)
    - Vibrancy effects
    - Transparent title bar
    
    **Hide traffic lights:**
    ```go
    Mac: application.MacOptions{
        TitleBarStyle: application.MacTitleBarStyleHidden,
    },
    ```
    
    **Invisible title bar:**
    Allows dragging whilst hiding the title bar. This only takes effect when the window is frameless or uses `AppearsTransparent`:
    ```go
    Mac: application.MacOptions{
        InvisibleTitleBarHeight: 40,
    },
    ```
  </TabItem>

  <TabItem label="Linux" icon="linux">
    **Linux frameless windows:**
    
    ```go
    window := app.Window.NewWithOptions(application.WebviewWindowOptions{
        Frameless: true,
    })
    ```
    
    **Features:**
    - Basic frameless support
    - CSS drag regions
    - Varies by desktop environment
    
    **Desktop environment notes:**
    - **GNOME:** Good support
    - **KDE Plasma:** Good support
    - **XFCE:** Basic support
    - **Tiling WMs:** Limited support
    
    **Compositor required:**
    Transparency requires a compositor (most modern DEs have one).
  </TabItem>
</Tabs>

## Common Patterns

### Pattern 1: Modern Title Bar

```html
<div class="modern-titlebar">
    <div class="app-icon">
        <img src="/icon.png" alt="App Icon">
    </div>
    <div class="title">My Application</div>
    <div class="controls">
        <button class="minimize">−</button>
        <button class="maximize">□</button>
        <button class="close">×</button>
    </div>
</div>
```

```css
.modern-titlebar {
    --wails-draggable: drag;
    display: flex;
    align-items: center;
    height: 40px;
    background: linear-gradient(to bottom, #3a3a3a, #2c2c2c);
    border-bottom: 1px solid #1a1a1a;
    padding: 0 16px;
}

.app-icon {
    --wails-draggable: no-drag;
    width: 24px;
    height: 24px;
    margin-right: 12px;
}

.title {
    flex: 1;
    font-size: 13px;
    color: #e0e0e0;
    user-select: none;
}

.controls {
    display: flex;
    gap: 1px;
}

.controls button {
    --wails-draggable: no-drag;
    width: 46px;
    height: 32px;
    border: none;
    background: transparent;
    color: #e0e0e0;
    font-size: 14px;
    cursor: pointer;
    transition: background 0.2s;
}

.controls button:hover {
    background: rgba(255, 255, 255, 0.1);
}

.controls .close:hover {
    background: #e81123;
    color: white;
}
```

### Pattern 2: Splash Screen

```go
splash := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Title:          "Loading...",
    Width:          400,
    Height:         300,
    Frameless:      true,
    AlwaysOnTop:    true,
    BackgroundType: application.BackgroundTypeTransparent,
})
```

```css
body {
    background: transparent;
    display: flex;
    justify-content: center;
    align-items: center;
}

.splash {
    background: white;
    border-radius: 12px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    padding: 40px;
    text-align: center;
}
```

### Pattern 3: Rounded Window

```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Frameless:      true,
    BackgroundType: application.BackgroundTypeTransparent,
})
```

```css
body {
    background: transparent;
    margin: 8px;
}

.window {
    background: white;
    border-radius: 16px;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
    overflow: hidden;
    height: calc(100vh - 16px);
}

.titlebar {
    --wails-draggable: drag;
    background: #f5f5f5;
    border-bottom: 1px solid #e0e0e0;
}
```

### Pattern 4: Overlay Window

```go
overlay := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Frameless:      true,
    AlwaysOnTop:    true,
    BackgroundType: application.BackgroundTypeTransparent,
})
```

```css
body {
    background: transparent;
}

.overlay {
    background: rgba(0, 0, 0, 0.8);
    backdrop-filter: blur(10px);
    border-radius: 8px;
    padding: 20px;
}
```

## Complete Example

Here's a production-ready frameless window:

**Go:**

```go
package main

import (
    _ "embed"
    "github.com/wailsapp/wails/v3/pkg/application"
)

//go:embed frontend/dist
var assets embed.FS

func main() {
    app := application.New(application.Options{
        Name: "Frameless App",
    })

    window := app.Window.NewWithOptions(application.WebviewWindowOptions{
        Title:     "Frameless Application",
        Width:     1000,
        Height:    700,
        MinWidth:  800,
        MinHeight: 600,
        Frameless: true,
        
        Assets: application.AssetOptions{
            Handler: application.AssetFileServerFS(assets),
        },
        
        Mac: application.MacOptions{
            TitleBarAppearsTransparent: true,
            InvisibleTitleBarHeight:    40,
        },
        
        Windows: application.WindowsOptions{
            DisableFramelessWindowDecorations: false,
        },
    })

    window.Center()
    window.Show()

    app.Run()
}
```

**HTML:**

```html
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="window">
        <div class="titlebar">
            <div class="title">Frameless Application</div>
            <div class="controls">
                <button class="minimize" title="Minimise">−</button>
                <button class="maximize" title="Maximise">□</button>
                <button class="close" title="Close">×</button>
            </div>
        </div>
        <div class="content">
            <h1>Hello from Frameless Window!</h1>
        </div>
    </div>
    <script src="/main.js" type="module"></script>
</body>
</html>
```

**CSS:**

```css
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: #f5f5f5;
}

.window {
    height: 100vh;
    display: flex;
    flex-direction: column;
}

.titlebar {
    --wails-draggable: drag;
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 40px;
    background: #ffffff;
    border-bottom: 1px solid #e0e0e0;
    padding: 0 16px;
}

.title {
    font-size: 13px;
    font-weight: 500;
    color: #333;
    user-select: none;
}

.controls {
    display: flex;
    gap: 8px;
}

.controls button {
    --wails-draggable: no-drag;
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    color: #666;
    font-size: 16px;
    cursor: pointer;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s;
}

.controls button:hover {
    background: #f0f0f0;
    color: #333;
}

.controls .close:hover {
    background: #e81123;
    color: white;
}

.content {
    flex: 1;
    padding: 40px;
    overflow: auto;
}
```

**JavaScript:**

```javascript
import { Window } from '@wailsio/runtime'

// Minimise button
document.querySelector('.minimize').addEventListener('click', () => {
    Window.Minimise()
})

// Maximise/restore button
const maximiseBtn = document.querySelector('.maximize')
maximiseBtn.addEventListener('click', async () => {
    const isMaximised = await Window.IsMaximised()

    if (isMaximised) {
        await Window.Restore()
    } else {
        await Window.Maximise()
    }

    updateMaximiseButton()
})

// Close button
document.querySelector('.close').addEventListener('click', () => {
    Window.Close()
})

// Update maximise button icon
async function updateMaximiseButton() {
    const isMaximised = await Window.IsMaximised()
    maximiseBtn.textContent = isMaximised ? '❐' : '□'
    maximiseBtn.title = isMaximised ? 'Restore' : 'Maximise'
}

// Initial state
updateMaximiseButton()
```

## Best Practices

### ✅ Do

- **Provide draggable area** - Users need to move the window
- **Implement system buttons** - Close, minimise, maximise
- **Set minimum size** - Prevent unusable layouts
- **Test on all platforms** - Behaviour varies
- **Use CSS for drag regions** - Flexible and maintainable
- **Provide visual feedback** - Hover states on buttons

### ❌ Don't

- **Don't forget resize handles** - If window is resizable
- **Don't make entire window draggable** - Prevents interaction
- **Don't forget no-drag on buttons** - They won't work
- **Don't use tiny drag areas** - Hard to grab
- **Don't forget platform differences** - Test thoroughly

## Troubleshooting

### Window Won't Drag

**Cause:** Missing `--wails-draggable: drag`

**Solution:**

```css
.titlebar {
    --wails-draggable: drag;
}
```

### Buttons Don't Work

**Cause:** Buttons are in draggable area

**Solution:**

```css
.titlebar button {
    --wails-draggable: no-drag;
}
```

### Can't Resize Window

**Cause:** Missing resize handles

**Solution:**

```css
body {
    --wails-resize: all;
}
```

## Next Steps

<CardGrid>
  <Card title="Window Basics" icon="laptop">
    Learn the fundamentals of window management.
    
    [Learn More →](/features/windows/basics)
  </Card>

  <Card title="Window Options" icon="seti:config">
    Complete reference for window options.
    
    [Learn More →](/features/windows/options)
  </Card>

  <Card title="Window Events" icon="rocket">
    Handle window lifecycle events.
    
    [Learn More →](/features/windows/events)
  </Card>

  <Card title="Multiple Windows" icon="puzzle">
    Patterns for multi-window applications.
    
    [Learn More →](/features/windows/multiple)
  </Card>
</CardGrid>

---

**Questions?** Ask in [Discord](https://discord.gg/JDdSxwjhGf) or check the [frameless example](https://github.com/wailsapp/wails/tree/v3-alpha/v3/examples/frameless).
