Devlog · Fase 0 · 6/7
Verre, o design system da Aubia
O Verre substitui o antigo design de aubia.dev e também será usado em aubia.cloud e no aplicativo desktop: dois temas em cinzas neutros, um vidro cinza, Geist e componentes de um registry.
por Mike, fundador da Aubia Publicado em Atualizado em 10 min de leitura
- design-system
- verre
- tailwind
- motion



O fundo escuro de aubia.dev é um cinza quase preto, #0E0F12, sem o tom azulado que tinha na primeira publicação deste artigo. O tema claro, "aurora", parte de um cinza muito pálido, #EDEFF2. Os dois vêm do Verre, a nova identidade visual da Aubia, cujo mockup aprovei em 5 de outubro de 2026.
Em agosto, este artigo descrevia outro site: dois temas Catppuccin completos, um vidro que deixava aparecer as manchas de cor do fundo e a Chakra Petch, uma fonte angulosa reservada aos títulos de aubia.dev. Cada aplicação da Aubia definia suas próprias cores e seus próprios componentes, e o aplicativo desktop previa quatro variantes de tema, enquanto o site oferecia duas. O Verre dá às três aplicações uma identidade única, e o site é o primeiro a aplicá-la.
Do Luma, o estilo de componentes do shadcn/ui, adotei os raios de borda, o espaçamento, a elevação e as animações de estado. O material, as cores, a tipografia e os ícones são próprios do Verre.
Um shadcn registry fornece os componentes
Os componentes do Verre ficam em um shadcn registry privado, servido pelo GitHub. Uma aplicação não os instala como dependência: o comando shadcn add copia o código-fonte deles para o repositório da aplicação, ao lado do resto do código dela. Descartei um pacote npm versionado: ele teria entregue código compilado no lugar desse código-fonte, e eu teria de publicar e proteger mais um registry npm privado.
Instalei 91 itens do registry no site. Um manifesto registra o hash SHA-256 de cada arquivo copiado, e um teste verifica se a cópia continua idêntica ao original. Para que esse controle se sustente, uma cópia nunca é editada diretamente: um ajuste de estilo passa por uma folha de estilo à parte, importada depois das do registry.
Esses componentes são construídos sobre o Base UI 1.9.0, que o shadcn agora recomenda no lugar do Radix. O Base UI cuida do foco, da navegação por teclado e dos papéis ARIA. Um link com aparência de botão passa por um componente ButtonLink à parte, porque a primitiva Button imporia o papel de botão a um link.
Os ícones vêm do Hugeicons, no estilo Stroke Rounded, sob licença MIT. Escolhi esses ícones em vez do Font Awesome Pro: a licença dele é paga, e o mockup aprovado é desenhado com Hugeicons.
Dois temas, e o do sistema por padrão
O Verre define um tema claro, "aurora", e um tema escuro, "Noite". O escuro é feito de cinzas neutros, para as superfícies, as linhas e os textos. Do Catppuccin, a paleta do antigo site, o Verre mantém as cores de destaque: o pêssego do Latte no tema claro, #FE640B, o do Mocha no escuro, #FAB387, e, na página inicial, o verde-azulado e o lavanda dos títulos de seção e o degradê do título principal. O pêssego é usado nos links, nos marcadores e no contorno do foco pelo teclado.
As cores não formam mais uma paleta completa transformada em classes. Elas são nomeadas por função, como fundo, vidro, texto ou destaque, e cada função tem um valor em cada um dos dois temas. Um bloco @theme inline liga esses nomes aos do shadcn e aos utilitários do Tailwind: um componente usa bg-background ou text-muted-foreground, e o valor acompanha o tema exibido.
Mantive as cores do mockup como estão, mesmo onde alguns textos ficam abaixo dos limites de contraste WCAG AA, nos dois temas: o design system sinaliza esses contrastes baixos sem corrigi-los. As demais exigências de acessibilidade continuam valendo: navegação por teclado, foco visível, alvos de toque de pelo menos 44 px em telas com menos de 768 px de largura, link para pular para o conteúdo.
O design anterior abria o site no tema escuro, qualquer que fosse a preferência do visitante. Escolhi o tema do sistema como padrão: você já informou sua preferência ao seu sistema operacional, e a mesma escolha valerá nas três aplicações. O seletor continua oferecendo "Claro", "Escuro" e "Sistema".
O tema é conhecido antes da primeira renderização
Um script colocado no <head>, antes das folhas de estilo, lê a escolha salva na chave aubia-theme e a preferência do sistema. Ele define data-theme para uma escolha explícita e data-scheme para a preferência: o CSS aplica o tema certo sem esperar pelo React. Sem JavaScript, a media query prefers-color-scheme aplica o tema escuro quando o sistema pede. Se o armazenamento local estiver bloqueado, sua escolha vale apenas para a visita atual.
A própria troca de tema é animada. Depois de um clique no seletor, a página espera 200 ms, o tempo de o indicador do seletor deslizar até a nova opção, e então o novo tema é revelado em 650 ms dentro de um círculo que se abre a partir do botão, pela API View Transition. No modo "Sistema", uma mudança na preferência produz um fade da página inteira. Quando o sistema pede movimento reduzido, o tema troca de uma vez.
O CSS do modo "Sistema" lê data-scheme em vez da media query. O Chromium já exibe as cores da nova preferência quando dispara o evento change: se a página seguisse a media query, teria mudado antes de o script capturar o estado inicial da transição.
Os cartões reutilizam o vidro cinza da barra
O efeito de vidro vem da propriedade backdrop-filter: ela desfoca o que passa atrás de um elemento, e um fundo semiopaco tinge o resultado. No design anterior, esse filtro também saturava as cores, e as das manchas do fundo atravessavam os cartões. O vidro comum do design system, o das listas de seleção e das notificações, ainda satura as cores, a 160%, com um desfoque de 24 px. O da barra de navegação desfoca na mesma medida, mas remove toda a cor:
@utility surface-bar {
background: var(--bar-glass);
border: 1px solid var(--glass-line);
backdrop-filter: var(--bar-blur);
}
--bar-blur vale blur(24px) grayscale(100%), e --bar-glass, um branco a 80% no tema claro e um cinza a 70% no escuro: os halos de luz do fundo já não tingem esse vidro. No site, os cartões, as perguntas da FAQ e, nos artigos, a capa e as tabelas o reutilizam, sem reflexo quando o ponteiro passa por cima.
O menu de idiomas é exibido fora da barra
Quando um ancestral já tem um backdrop-filter, ele define uma backdrop root: o filtro de um descendente deixa de ver o que está atrás desse ancestral. O menu de idiomas da barra, um menu do Base UI, é renderizado em um portal: a barra deixa de ser seu ancestral, e o menu aplica seu próprio desfoque. O vidro da barra é definido na raiz do documento para que esse menu também possa usá-lo.
Esse menu só é montado no cliente: seus links não aparecem no HTML inicial. Por isso o rodapé repete esses links em um elemento <details> nativo: eles constam do HTML produzido pela renderização no servidor, e a abertura funciona sem JavaScript. O React acrescenta a navegação pelas setas e o fechamento com Escape ou com um clique fora, e coloca o foco no idioma atual ao abrir.
Halos e uma trama de estrelas sob o conteúdo
O fundo das páginas é feito de duas camadas fixas. A primeira desenha em cada canto uma elipse de luz da cor que o símbolo do logotipo tem nesse mesmo canto: violeta no alto à esquerda, rosa no alto à direita, coral embaixo à esquerda, pêssego-claro embaixo à direita, mais discretas no tema escuro do que no claro. A segunda é uma trama de formas de 7 px, sóis-estrela no tema claro e estrelas no escuro, que a luz dos halos revela. Seu espaçamento, próximo de 32 px, divide exatamente a largura do conteúdo: uma coluna de formas coincide com cada uma de suas bordas.
Em todo o site, a cada 0,3 a 0,9 segundo, uma forma cresce e assume um tom do tema, no máximo seis ao mesmo tempo, e uma lupa ilumina as formas próximas do ponteiro. O loop de animação para quando a aba está oculta.
Geist para o texto e para o código
Removi a Chakra Petch. A Geist agora é usada em todo o texto, incluindo o título principal da página inicial, e a Geist Mono no código, nos caminhos e nos comandos. As duas fontes são variáveis: um único arquivo WOFF2 latino por fonte cobre os pesos de 100 a 900.
Elas são hospedadas no próprio site. O plugin de fontes do laravel-vite-plugin lê os arquivos dos pacotes Fontsource instalados e gera as declarações @font-face durante o build; a diretiva Blade @fonts as insere, com o pré-carregamento, em todas as páginas. Nenhuma requisição sai para um fornecedor de fontes.
Com display: 'swap', o texto aparece primeiro em uma fonte de fallback. Para a Geist, o plugin ajusta as métricas desse fallback, e o texto quase não se desloca quando a fonte chega. A Geist Mono mantém um fallback monospace comum: o plugin procura no arquivo a categoria da fonte, monospace ou sans-serif, não encontra nenhuma e tomaria a Arial como modelo.
O movimento, e uma única regra para reduzi-lo
As durações e as curvas de easing vêm de variáveis CSS, --d-* e --ease-*, lidas tanto pelas folhas de estilo quanto pelos scripts. A curva das microinterações, --ease-out, vale cubic-bezier(.2, .8, .2, 1).
No carregamento, nenhum texto visível parte de opacidade zero, porque o Chromium exclui do LCP um elemento invisível. O título principal da página inicial entra com um deslizamento de 14 px. O fade fica reservado aos textos revelados na rolagem, mais abaixo na página, e aos elementos decorativos.
Antes do Verre, as regras de movimento reduzido atingiam certas animações e deixavam as outras transições acontecerem. O Verre as substitui por uma só, para o site inteiro:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition: none !important;
animation: none !important;
}
}
Cada estado aparece então de imediato. As cores ainda mudam, de uma vez; os deslocamentos no hover e ao pressionar passam pela variante motion-safe: do Tailwind, que deixa de se aplicar.
As animações controladas por script escapam a essa regra, como o slogan da página inicial, cujas frases aparecem caractere por caractere. Elas leem a preferência pelo hook useReducedMotion do registry, construído sobre useSyncExternalStore: ele se inscreve no evento change de matchMedia, e uma mudança dessa configuração durante a visita interrompe na hora a animação em andamento. O servidor não conhece essa preferência. Por isso o hook retorna false na renderização no servidor e durante a hidratação, depois lê o valor real, como descreve a documentação do React sobre useSyncExternalStore. O HTML do servidor já contém a última frase, e o slogan a mantém com movimento reduzido.
O código dos artigos mantém as cores do Catppuccin
Os blocos de código deste blog são realçados no servidor pelo Phiki 2.2, durante o build, com os temas Catppuccin Latte, no claro, e Mocha, no escuro. Uma classe PHP copia esses dois temas e substitui as cores neutras deles, as do fundo, do texto, dos comentários e da pontuação, por variáveis CSS do Verre; ela falha se uma atualização do Phiki remover uma das regras de cor que substitui, em vez de entregar um tema incompleto. Um bloco de código assume, portanto, o fundo do tema exibido, e só as cores de sintaxe ainda vêm do Catppuccin.
A beta da Aubia é gratuita e sem limite de tempo. Inscreva-se para receber o seu convite.
Entrar na lista de espera