Executando verificação de segurança...
1

Pitch: Bely Launcher — Como implementei Glassmorphism com Acrylic Blur no Tauri 2 (Windows)

Implementar acrylic blur no Tauri 2 não foi nada trivial. Envolve configuração de janela, chamadas diretas à DWM API do Windows, e um workaround para quando a janela perde o foco. Vou mostrar o passo a passo com código.

O resultado

A janela da Bely tem um fundo translúcido que borra o que está atrás dela — o famoso acrylic blur do Windows. Quando a janela perde o foco, o fundo vira opaco escuro. E tudo isso sem barra de título, com bordas arredondadas nativas.

Passo 1: Configurar a janela no Tauri

No tauri.conf.json, a janela precisa ser transparente e sem decoração:

{
  "label": "main",
  "width": 660,
  "height": 480,
  "transparent": true,
  "decorations": false,
  "shadow": false,
  "alwaysOnTop": true,
  "skipTaskbar": true
}

O transparent: true é obrigatório — sem isso o acrylic não funciona. O decorations: false remove a barra de título nativa do Windows.

Passo 2: Aplicar o Acrylic via Rust

No Cargo.toml, adicionei a crate window-vibrancy:

window-vibrancy = "0.7"

No setup do app, aplico o efeito acrylic com uma cor escura semitransparente:

use tauri::webview::Color;

if let Some(win) = app.get_webview_window("main") {
    // Fundo totalmente transparente
    let _ = win.set_background_color(Some(Color(0, 0, 0, 0)));

    // Acrylic com tint escuro: RGB(9, 9, 11) com alpha 180/255
    let _ = window_vibrancy::apply_acrylic(&win, Some((9, 9, 11, 180)));
}

O apply_acrylic recebe uma tupla (r, g, b, a) onde o alpha controla quanto do blur você vê. Valores mais baixos = mais transparente e mais blur visível. Eu uso 180 que dá um escuro com blur sutil.

Passo 3: Bordas arredondadas via DWM

O Tauri não expõe uma API para bordas arredondadas quando decorations: false. Precisei chamar a DWM API do Windows diretamente:

use winapi::shared::minwindef::DWORD;
use winapi::shared::windef::HWND;

extern "system" {
    fn DwmSetWindowAttribute(
        hwnd: HWND,
        dwAttribute: DWORD,
        pvAttribute: *const std::ffi::c_void,
        cbAttribute: DWORD,
    ) -> i32;
}

const DWMWA_WINDOW_CORNER_PREFERENCE: DWORD = 33;
const DWMWCP_ROUND: DWORD = 2;
const DWMWA_BORDER_COLOR: DWORD = 34;
const DWMWA_COLOR_NONE: u32 = 0xFFFFFFFE;

if let Ok(hwnd) = win.hwnd() {
    unsafe {
        // Bordas arredondadas
        DwmSetWindowAttribute(
            hwnd.0 as HWND,
            DWMWA_WINDOW_CORNER_PREFERENCE,
            &DWMWCP_ROUND as *const DWORD as *const std::ffi::c_void,
            std::mem::size_of::<DWORD>() as DWORD,
        );
        // Remove borda nativa
        let none_color: u32 = DWMWA_COLOR_NONE;
        DwmSetWindowAttribute(
            hwnd.0 as HWND,
            DWMWA_BORDER_COLOR,
            &none_color as *const u32 as *const std::ffi::c_void,
            std::mem::size_of::<u32>() as DWORD,
        );
    }
}

O DWMWA_WINDOW_CORNER_PREFERENCE = 33 com valor DWMWCP_ROUND = 2 força cantos arredondados no Windows 11. O DWMWA_BORDER_COLOR com COLOR_NONE remove aquela borda fina que o Windows coloca por padrão.

Importante: Isso só funciona no Windows 11 22H2+. No Windows 10 os cantos ficam retos.

Passo 4: O problema do foco

Esse foi o detalhe mais chato de resolver. O Windows desabilita o acrylic blur quando a janela perde o foco. É uma limitação do sistema operacional — não tem como contornar via API.

O resultado é horrível: quando você clica fora da janela, o fundo transparente vira um cinza feio sem blur nenhum.

A solução foi detectar foco/desfoco no frontend e trocar o background dinamicamente:

const [focused, setFocused] = useState(true);

useEffect(() => {
  const onFocus = () => setFocused(true);
  const onBlur = () => setFocused(false);
  window.addEventListener("focus", onFocus);
  window.addEventListener("blur", onBlur);
  return () => {
    window.removeEventListener("focus", onFocus);
    window.removeEventListener("blur", onBlur);
  };
}, []);

E no JSX, aplico o background condicional:

<div style={{
  background: focused
    ? "rgba(9, 9, 11, 0.5)"   // translúcido — mostra o blur
    : "#09090B",                // opaco escuro — esconde o cinza feio
}}>

Quando focado: fundo com 50% de opacidade, você vê o blur atrás.
Quando desfocado: fundo sólido escuro, transição suave, sem aquele cinza horroroso.

Passo 5: CSS Glass System

Para manter consistência visual, criei um sistema de variáveis CSS para o tema glass:

:root {
  --glass-bg: rgba(9, 9, 11, 0.5);
  --glass-bg-solid: #09090B;
  --glass-border: rgba(255,255,255,0.08);
  --glass-divider: rgba(255,255,255,0.06);
  --glass-text: rgba(255,255,255,0.95);
  --glass-text-dim: rgba(255,255,255,0.45);
  --glass-text-muted: rgba(255,255,255,0.3);
  --glass-btn-bg: rgba(255,255,255,0.08);
  --glass-btn-border: rgba(255,255,255,0.1);
  --glass-selected: rgba(255,255,255,0.1);
}

A regra principal: nunca usar cores sólidas em elementos internos. Todos os fundos, bordas, textos e divisores usam rgba com branco em diferentes opacidades. Isso garante que o blur do fundo continue visível através dos componentes.

Se você colocar um background: #18181B sólido em um painel filho, ele cobre o blur e mata o efeito. Use rgba(255,255,255,0.08) no lugar.

Gotchas que aprendi

  1. Acrylic vs Mica: O acrylic borra o que está atrás da janela. O Mica (Windows 11) borra o wallpaper. São efeitos diferentes — eu uso acrylic porque quero que o conteúdo da tela atrás fique visível.

  2. Windows 10 vs 11: No Windows 10 o blur é mais sutil e não tem cantos arredondados nativos. A crate window-vibrancy lida com isso internamente, mas o resultado visual é diferente.

  3. Performance: O acrylic tem um custo de GPU. Em máquinas fracas, o Windows pode desabilitar automaticamente. Sempre tenha um fallback opaco.

  4. Alpha do acrylic vs alpha do CSS: São duas camadas de transparência. O acrylic com alpha 180 já é escuro. O CSS com rgba(9,9,11,0.5) adiciona mais escurecimento por cima. Ajuste os dois juntos até achar o balanço certo.

Resultado

A stack completa: Tauri 2 + window-vibrancy + DWM API + estado de foco no React + variáveis CSS com rgba.

Se quiserem ver o efeito funcionando, a Bely está em fase de whitelist. É só se cadastrar no site e eu aprovo rápido: https://bely.my

Carregando publicação patrocinada...