DroidGlass: Vidro Líquido para qualquer plataforma
Vidro Líquido é o material da Apple que refrata o que está por baixo. Perto da borda, o fundo é puxado em direção ao centro como uma lente grossa, um arco-íris suave aparece na borda, um destaque muito fino corre ao longo da borda e, ao toque, o vidro incha, estica-se em direção ao dedo e volta com elasticidade. O glassmorphism comum apenas desfoca o fundo sob uma névoa branca, é por isso que parece barato e fosco.
A regra principal de bom gosto: o vidro é transparente. Se você não consegue ler o que está por trás dele, é vidro fosco do iOS 7, não Vidro Líquido. Adicione desfoque e uma superfície apenas onde o texto está sobre o vidro.
Diga honestamente ao usuário onde estão os limites. O material real existe apenas no iOS, iPadOS e macOS 26 ou superior; todo o resto é uma imitação. No Android, o Backdrop oferece a imitação completa (a lente precisa do Android 13). Na web, a refração funciona apenas no Chromium (Chrome, Edge, Samsung Internet, Android WebView, Electron). Safari, Firefox e todos os navegadores no iOS recebem um desfoque leve com os mesmos destaques, sombras e molas, mas sem curvatura.
Fluxo de trabalho
- Identifique a plataforma.
- SwiftUI, UIKit, AppKit, WidgetKit no iOS/macOS 26: API do sistema, veja
references/apple.md. Não imite nada. - Jetpack Compose ou Compose Multiplatform:
references/android-compose.md. Não escreva shaders do zero, use a biblioteca. - Web, Electron, Android WebView, Capacitor, Cordova, Tauri no Windows/Android: o motor em
assets/, vejareferences/web.md. No WebView e Electron a refração sempre funciona. - React Native, Flutter: sem equivalente no sistema. Para React Native sugira uma camada WebView ou um módulo nativo no Backdrop; para Flutter,
BackdropFiltercomo fallback e umFragmentProgramcom as fórmulas dereferences/math-and-motion.md. Verifique os pacotes atuais você mesmo, este skill não os lista.
- SwiftUI, UIKit, AppKit, WidgetKit no iOS/macOS 26: API do sistema, veja
- Escolha 1 a 3 elementos da camada de controle: barra de abas, botão flutuante, barra superior, folha, menu, interruptor ou controle deslizante ao ser tocado. Não torne o conteúdo de vidro.
- Escolha uma receita da tabela abaixo. Não invente parâmetros: os valores vêm do catálogo do Kyant e foram verificados na tela.
- Verifique o fundo atrás do vidro. O vidro só é visível sobre conteúdo rico: fotos, cores, gráficos, texto durante a rolagem. Sobre uma página branca simples, o vidro transparente quase desaparece: coloque cor atrás dele, adicione uma camada de escurecimento ou remova o vidro dali.
- Construa a partir da referência correta e verifique com seus próprios olhos usando a lista de verificação no final. Não entregue sem capturas de tela do estado de repouso, pressionado e arrastado.
Regras da Apple
Das Diretrizes de Interface Humana, WWDC25 "Conheça o Vidro Líquido" e habilidades populares da comunidade. Quebrar qualquer uma delas denuncia a falsificação imediatamente.
- Apenas a camada de navegação e controle que flutua sobre o conteúdo. Listas, linhas, cartões e o fundo da página não são de vidro. Exceção: os polegares de interruptores e controles deslizantes tornam-se vidro ao serem tocados.
- Sem vidro sobre vidro. Elementos sobre o vidro são diferenciados por preenchimento e opacidade. Exceção do Backdrop: um polegar de controle deslizante em uma folha de vidro refrata a folha (
exportedBackdrop): uma lente sobre outra, não duas placas foscas. - Use pouco. Apenas os controles mais importantes.
- O vidro é incolor. Cor e brilho vêm do fundo: claro sobre claro, escuro sobre escuro, o destaque é tingido pelo fundo. Nenhuma superfície branca ou cinza própria.
- Dois modos, não os misture. Claro (botões, polegares, lupa, barra de abas agrupada com um botão): desfoque 0 a 4, sem superfície. Regular (folha, diálogo com texto longo): desfoque 8 a 16; adicione uma superfície de 40 a 60% apenas se o texto ficar ilegível sem ela.
- Tingir apenas a ação principal.
- Forma. Cápsula: raio igual à metade da altura. Cantos concêntricos: raio interno igual ao raio do contêiner menos o preenchimento.
- Maior significa mais espesso: elementos grandes recebem uma borda curva mais larga e refração mais profunda.
- Nunca deixe o conteúdo descansando metade sob o vidro.
- Líquido em movimento. Ao pressionar, o vidro cresce alguns pixels e inclina-se em direção ao dedo com amortecimento; um polegar infla 1,5x e estica-se em gota na velocidade. Tudo corre em molas, sem transições lineares.
Receitas (valores do catálogo do Kyant)
| Elemento | Efeitos | Destaque e sombras | Toque |
|---|---|---|---|
| Botão, cápsula de 48 dp | vibração, desfoque 2, lente(12, 24) | Padrão, sombra padrão | brilho; cresce 4 dp em altura; deslocamento min × tanh(0.05 × offset / min); estica até 4 dp em direção ao dedo |
| Botão tingido | mesmo + tom | mesmo | mesmo |
| Botão com superfície | mesmo + branco 30% | mesmo | mesmo |
| Poletar do interruptor 40×24 em trilho 64×28 | em repouso branco 100% e desfoque 8; pressionado p: desfoque 8(1−p), lente(5p, 10p, dispersão), branco (1−p) | Ambiente, largura /1.5, alfa p; sombra raio 4, 5%; sombra interna raio 4p, alfa p | escala para 1.5; o trilho aparece através da lente, encolhido para 0.75 |
| Poletar do controle deslizante 40×24, trilho de 6 dp | como o interruptor, lente(10p, 14p) | como o interruptor | escala para 1.5; tocar no trilho salta para o ponto |
| Barra de abas 64 dp | vibração, desfoque 8, lente(24, 24), superfície #FAFAFA 40% (escuro #121212 40%) | Padrão | ao pressionar, todo o painel cresce 16 dp de largura (rótulos junto) e inclina-se até 4 dp após o dedo |
Cópia de linha oculta 56 dp (tabsBackdrop) | o mesmo painel na cor de destaque, alpha(0): vibração, desfoque 8, lente(24p, 24p), superfície do painel | Padrão com alfa p, InteractiveHighlight | ícones e rótulos ×1.2 ao pressionar (LocalLiquidBottomTabScale é definido apenas nesta linha) |
| Poletar da barra de abas 56 dp | em repouso cinza 10%; pressionado lente(10p, 14p, dispersão) sobre o fundo e a cópia da linha (rememberCombinedBackdrop(backdrop, tabsBackdrop)) | Padrão, sombra e sombra interna 8p com alfa p | escala 78/56; dentro da gota uma "barra sob a barra": seu próprio item em destaque, ampliado, vizinhos visíveis nas bordas |
| Diálogo, raio 48 | brilho 0.2 (claro), saturação 1.5, desfoque 16 (escuro 8), lente(24, 48, profundidade) | Simples | escurece o fundo #29293A 23% (escuro #121212 56%) |
| Bloco da central de controle | vibração, lente(24, 48, profundidade) | Padrão, ângulo da inclinação do dispositivo, queda 2 | entrada: deslocamento de 48 dp e alfa |
| Lupa 128×96 | lente(8, 24, profundidade, dispersão), fundo ampliado 1.5x | sombra interna 16 | arrastar |
Molas, gestos e a matemática compartilhada por trás de cada plataforma estão em references/math-and-motion.md.
Web e Android WebView em um minuto
Copie os três arquivos de assets/ para o projeto inalterados e configure-os com opções. Não reescreva o motor.
<link rel="stylesheet" href="liquid-glass.css">
<button id="buy">Comprar</button>
<div id="wifi" aria-label="Wi-Fi"></div>
<div id="volume" style="width: 280px"></div>
<nav id="tabs" style="position: fixed; left: 16px; right: 16px; bottom: 16px"></nav>
<script src="liquid-glass.js"></script>
<script src="liquid-ui.js"></script>
<script>
LiquidUI.button(document.getElementById('buy'), { tint: '#0088FF', onClick: () => {} });
LiquidUI.toggle(document.getElementById('wifi'), { checked: true, onChange: (on) => {} });
LiquidUI.slider(document.getElementById('volume'), { value: 0.4, onInput: (v) => {}, label: 'Volume' });
const tabs = LiquidUI.tabs(document.getElementById('tabs'), {
items: [{ label: 'Início', icon: '<svg>…</svg>' }, { label: 'Pesquisar', icon: '<svg>…</svg>' }],
index: 0, onChange: (i) => {},
});
// merging: uma barra e um botão próximos a ela se fundem em uma gota; botões elásticos vizinhos fazem isso sozinhos
// LiquidUI.blob([tabs.panel, fab], { blur: 3.5 }); LiquidUI.autoMerge(document.body); após uma mudança de tela LiquidUI.sweep()
// vidro puro para sua própria marcação
const g = LiquidGlass.attach(document.getElementById('panel'), { radius: 28, blur: 8, refractionHeight: 24, refractionAmount: 24, surface: 'rgb(250 250 250 / .4)' });
// g.set({ lens: 0..1, blur, surface, highlightAlpha, angle, shadowAlpha, innerShadow, press: { p, x, y }, tx, ty, sx, sy, sleep })
</script>
| Arquivo | O que é |
|---|---|
assets/liquid-glass.js | motor de vidro: mapas de refração, dispersão, destaque, sombras, grupos de fusão (LiquidGlass) |
assets/liquid-glass.css | estilos base, fallbacks de acessibilidade, estilos de componentes |
assets/liquid-ui.js | molas, gestos e componentes: botão, interruptor, controle deslizante, abas, lupa, blob, autoMerge (LiquidUI) |
assets/demo.html | todos os componentes em uma página; abra-o para ver a aparência correta |
Antes de alterar qualquer coisa ou integrá-lo a um aplicativo, leia references/web.md: ele lista os fatos do Chromium que quebram quase todas as implementações (raízes de backdrop, position: fixed, touch-action, fundos de body) e todas as opções. Quando algo parecer errado, vá para references/troubleshooting.md; quando algo tremer, vá para references/performance.md.
Cinco coisas para nunca errar na web:
- O fundo deve ser um elemento real. Um fundo definido em
bodyque se propaga para a raiz não é visto porbackdrop-filter. - Nunca anime
opacity,filter,maskouclip-pathem um ancestral do vidro (como um fade-in de tela): torna-se uma raiz de backdrop e o vidro perde seu fundo e escurece. Mova telas comtransforme revele-as com uma camada de véu sobre elas. - Não defina seu próprio
transformem um elemento de vidro; escreva o deslocamento base em--lg-base. - Botões elásticos precisam de
touch-action: none(o padrão). Compan-yo navegador rouba o arrasto e enviapointercancel. - Em um Android WebView desabilite o pressionamento longo e os hápticos:
setOnLongClickListener(v -> true)esetHapticFeedbackEnabled(false).
Acessibilidade e legibilidade
- O motor web honra
prefers-reduced-transparency: reduce(vidro desligado, superfície quase opaca; Chrome e Edge 119+, ainda não Safari ou Firefox),prefers-contrast: more(superfície sólida e borda) eprefers-reduced-motion(molas tornam-se instantâneas). - Componentes recebem funções e suporte de teclado: botão (Enter, Espaço),
switch(Enter, Espaço),slider(setas),tablist(setas esquerda e direita). Rotule-os comaria-labelouaria-labelledby. - No Android defina
semantics { role = Role.Switch }e rótulos, como no catálogo do Kyant; no Android antigo verifique se a coronDrawSurfaceé legível sem efeitos. - Verifique o contraste do texto sobre o conteúdo mais claro e mais movimentado que pode acabar sob o painel: 4,5:1 para texto do corpo. Se falhar, adicione desfoque e uma superfície ou mova o conteúdo. Uma sombra no texto não resolve.
Não faça
- Uma superfície translúcida branca mais
blur(10px)em botões: isso é vidro fosco, não Vidro Líquido. A densidade é adequada apenas para barras de abas, folhas e diálogos com texto. - Cartões de conteúdo de vidro e um fundo de página de vidro.
- Dispersão em painéis grandes: caro e chamativo.
- Uma borda branca grossa em vez de um destaque fino.
- O mesmo tom em todos os botões.
- Transições
lineartransitionem vez de molas para pressionar e arrastar. - Animar
widtheheightde vidro na web: o mapa é reconstruído a cada quadro. Anime a escala. - Etapas de filtro pesadas (desfoque, deslocamento, convolução, morfologia) sob um resultado que várias etapas leem: o Chrome as calcula uma vez por ramificação e os telefones congelam.
- Pintar o vidro com sua própria superfície "para aparência". Apenas painéis densos com texto recebem uma superfície.
- Escrever seus próprios shaders do Compose quando o Backdrop se encaixa.
- Símbolos SF e logotipos da Apple fora das plataformas Apple: sua licença os proíbe. Use conjuntos de ícones abertos.
- Chamar o resultado da web ou do Android de "Vidro Líquido oficial". É uma imitação em seu estilo.
Lista de verificação antes de entregar
Capturas de tela na escala 2, olhe para elas você mesmo:
- Repouso sobre uma grade ou texto: linhas são visíveis através do vidro transparente, puxadas em direção ao centro na borda, o destaque é fino. No branco, o vidro é separado da página por sombreamento e sombra da borda; no escuro, por uma linha brilhante no lado iluminado.
- Pressionar: o botão cresceu e inclina-se em direção ao dedo; um polegar está inflado e transparente, o trilho aparece através dele, o arco-íris é suave, sem buracos coloridos.
- Arrastar: um polegar estica em velocidade e desinfla apenas após chegar; a barra de abas inclina e retorna. A gota da barra de abas nas abas de borda e perto da borda inferior da tela: seu próprio item dentro, vizinhos nas bordas, sem áreas pretas, faixas ou peças espelhadas.
- Puxar: um botão segue o dedo em um elástico e quica de volta; uma barra puxada em direção a um botão vizinho funde-se nele, a ponte cresce à medida que se aproximam, sem costura ou borda dupla; em repouso e com um pressionamento simples, eles permanecem separados; um toque normal funciona, um botão puxado para longe e solto não dispara.
- Sobre uma foto e sobre branco: a cor sob o vidro é rica, o texto é legível.
- Tema escuro.
- Fallback: finja o motor (agente de usuário do Safari e
navigator.userAgentDatavazio) e verifique se os elementos não se transformaram em buracos e os ícones sob o polegar da barra de abas não estão desfocados. - Redimensionar e rotação: o mapa é reconstruído, sem degraus na borda.
- Transições de tela: capture vários quadros consecutivos enquanto uma tela entra; botões de vidro nunca estão mais escuros do que em repouso, as camadas de mesclagem permanecem exatamente sob seus botões após a animação.
- Sem erros no console e sem travamentos: após uma rajada de toques, a página responde tão rápido. Um loop de animação, sem
requestAnimationFrameperdidos. - Não mais de 3 elementos de vidro grandes em uma tela de telefone, sem vidro sobre vidro, sem conteúdo de vidro.
Se algo falhar, corrija primeiro, depois mostre ao usuário.
Fontes
- Kyant0/AndroidLiquidGlass, biblioteca Backdrop e catálogo (Apache-2.0): lente, dispersão, destaque e fórmulas de sombra, receitas de componentes, constantes de mola e gesto. O motor web é uma porta desse esquema.
- Apple: Diretrizes de Interface Humana "Materiais", "Adotando Vidro Líquido", WWDC25 "Conheça o Vidro Líquido".
- kube.io, "Vidro Líquido no Navegador": refração através de
feDisplacementMapdentro debackdrop-filter. - iyinchao/liquid-glass-studio (MIT): formas de fusão via
smin, refração de Snell, vidro incolor com um alto