Pedro Wiezel
voltar para blog

construindo meu site com ia

como este site tomou forma no SvelteKit, com Claude, Gemini e uma implacável garantia de qualidade humana

Meu portfólio antigo era um clichê constrangedor. Fundo quase preto, fonte monoespaçada verde terminal, cursor piscando no hero e breadcrumbs falsos de terminal (~/showcase/cascade). Sem querer ofender quem faz assim, claro, mas o negócio parecia ter sido gerado em quatro segundos por alguém que nunca desenhou nada na vida.

Eu queria um site que parecesse colagem, já que é um dos estilos de design que mais amo. Como também sou louco por performance e por acomodar diferentes dispositivos e perfis de desempenho, escolhi o SvelteKit devido às suas características de performance, como atualizações cirúrgicas do DOM sem um DOM virtual e sobrecarga de memória insignificante em chips mobile mais fracos. A propósito, este site deve ser super fluido mesmo se você estiver num aparelho mais antigo, como o iPhone 6s que tenho aqui (e usei extensivamente pra testes).

Então abri uma pasta vazia e passei as semanas seguintes construindo e melhorando ele progressivamente com o Claude e o Gemini.

A primeiríssima versão do novo redesign

escapando do conjunto de treino

Se você pede pra um LLM desenhar uma interface sem direções específicas, o resultado cai no meio dos dados de treino, a depender também de vieses da empresa. Pra maioria dos modelos, isso significa gradientes roxos e bento grids, o padrão que dominou a web nos últimos anos. No caso do Claude, o padrão puxa pra um estilo editorial exagerado: títulos enormes em serifada, tipografia de revista dos anos 90, paleta terrosa e toques pretensamente artesanais.

Pra sair desse padrão, pedi pro Claude ir à internet e pesquisar várias escolas de design e arte. Pedi especificamente pra ele olhar o trabalho de artistas e designers de diferentes épocas e regiões. Depois, ele desenhou pranchas avulsas em HTML pra mostrar opções de redesign: o mapa de ônibus de Curitiba, risografia, planos concretistas e algumas outras.

A que decidi manter foi a que o Claude chamou de "Antropofagia", baseada no manifesto de mesmo nome de 1928 de Oswald de Andrade: em linhas gerais, a ideia de pegar o que você aprende de fora e fazer algo próprio com isso em vez de copiar. Achei interessante pois batia com a forma como o trabalho realmente acontece pra mim: Swift aprendido com a Apple, depois design a partir de um cânone majoritariamente europeu, depois psicologia com livros de fora, e no fim eu, um brasileiro, tenho que fazer coisas a partir disso tudo.

Nos componentes do SvelteKit, essa ideia virou literal. Em vez de colocar capturas de tela dentro de molduras falsas de navegador, as letras do título devoram a imagem do projeto mais recente direto na tipografia com background-clip: text. O resto do site fica quieto com seu papel-jornal e tipografia serifada, com a colagem aparecendo só em outros dois lugares: os títulos de seção e as capas rasgadas dos projetos em meio-tom.

Pra grade da vitrine de projetos, também me inspirei no site do estúdio Molleindustria, que coloca todos os jogos em uma grade com legendas que sempre achei muito legais e convidativas.

Uma versão anterior chegou a carimbar a página inicial com Tupi or not Tupi — Andrade, 1928, mas cortamos. Era meio cringe.

IA não tem olhos, mas pode usar uma régua

O título da página inicial foi um dos primeiros lugares em que o Claude me fez dar com a cara no muro. Uma linha de código quebrada é fácil de notar, mas LLMs adoram escrever CSS válido, dar uma explicação convincente de como o negócio funciona e não ter ideia de coisas básicas como o fato de que navegadores frequentemente funcionam de maneiras inesperadas.

Como as letras do hero podem carregar capas de projetos diferentes, elas precisam de um piso de contraste garantido pra continuarem legíveis sobre o fundo de papel-jornal (#efe9e2). O Claude combinou background-color, modo multiply e recorte de texto, me garantindo que o contraste nunca cairia abaixo de 3:1. No Chromium, o background-color é descartado silenciosamente do grupo de mesclagem quando o recorte de texto está ativo. Sobre uma capa escura como a do Cascade, as letras viravam uma mancha ilegível, enquanto as ferramentas de desenvolvedor mostravam estilos computados perfeitamente normais.

Discutir com um LLM no chat sobre bugs visuais não leva a lugar nenhum. Ele não enxerga a tela, então só insiste em por que as contas dele deveriam funcionar. A única saída foi criar uma verificação automatizada. Instruí ele a escrever o floor.js com Playwright e Sharp. Ele abre um navegador headless, tira print do título, apara as bordas com antialiasing de cada letra e afere cada pixel interno. Se qualquer pixel cair abaixo de 3:1 de contraste contra o fundo, o script encerra com erro.

npm run floor verificando o contraste

Assim que o modelo pôde rodar o script e ver números reais no terminal, voilà, o problema foi resolvido em dez minutos. Gradientes lineares dentro de background-image continuam no grupo de mesclagem, então colocar um gradiente sob a capa travou a luminosidade sem mexer em background-color.

botões no papel

Batemos no mesmo problema com as interações dos botões. A maioria dos sites modernos, especialmente aqueles feitos por LLMs, responde ao hover inflando elementos com scale(1.04). Papel de verdade não incha quando o dedo chega perto. Eu queria botões que parecessem recortes de papel sobre papel-jornal: um chanfro duro de 3px embaixo. Ao clicar, o recorte afunda até nivelar com a página.

Quando testamos pela primeira vez, metade dos botões não se mexia. As animações de entrada seguravam os valores de keyframe no lugar, anulando o estado de clique. Mudar o deslocamento do clique pra propriedade CSS translate resolveu na hora, já que translate funciona de forma independente dos keyframes.

Pra garantir que esse comportamento não quebrasse depois, o Claude bolou o scripts/press.js. Ele roda o Playwright, segura o ponteiro pressionado em cada botão em seis rotas nos dois temas e confere se o elemento se move 3px com um clique.

Três estilos de botão

tinta certificada

Os modelos também não têm visão do projeto inteiro. Quando você pede pra IA criar ou mexer num componente isolado, o código funciona bem sozinho, mas ao longo de várias sessões ele vai perdendo o contato com o resto do site.

Neste site, por exemplo, cada seção usa uma tinta própria: carvão na home, azul na vitrine, verde no blog e vermelho no contato. Cada link, estado de hover e chip de filtro deve herdar a cor --accent daquela seção. Mas como uma folha de estilos com quatro cores continua parecendo normal a olho nu, os bugs tendem a não ser percebidos. Por semanas, a página inicial do blog rodou com um link de voltar azul e um chip de filtro preto, até isso entrar na lista de prioridades pra ser corrigido. O modelo nunca fez ideia de que estava errado.

Pra resolver esse desvio, é isso mesmo, você adivinhou, mais scripts foram escritos: colors.js e look.js. Em vez de conferir prints na mão ou reler CSS, os scripts inspecionam o DOM ao vivo num navegador headless. Eles imprimem uma tabela de valores computados, garantem que cada elemento use a tinta certa da seção e medem as larguras das colunas. Se uma rota pega uma cor emprestada de outra seção, o teste falha.

palavras que cabem

O site é bilíngue, o que trouxe um problema de layout. As palavras em português são visivelmente mais compridas do que em inglês. DEVELOPER tem nove letras; DESENVOLVEDOR tem treze. Se você escolher um tamanho fixo de fonte ou chutar com breakpoints, um idioma às vezes vai estourar a coluna ou o outro vai parecer frouxo.

Em vez de adivinhar tamanhos de fonte ou valores de breakpoint, fiz o Claude escrever um pequeno script em Python com fontTools pra calcular a largura exata de avanço de cada palavra do título até a milésima fração de em. Guardamos esse valor numa propriedade customizada (--k) e usamos unidades de container query (cqi) pro título se ajustar sozinho à coluna:

.head { --k: 15.56; font-size: calc(88 / var(--k) * 1cqi); }

Os dois idiomas travam na coluna, evitando qualquer estouro.

cérebro humano, mãos de máquina

Às vezes as pessoas presumem que construir um site com um LLM significa que você não precisa entender o funcionamento interno do navegador. Na prática, você precisa entender ainda melhor. Quando um modelo te dá cinco teorias plausíveis de por que uma animação ou layout quebrou, ainda é você quem tem que saber qual camada do sistema de fato falhou.

Trabalhar assim também muda a forma de fazer prompts. Escrever parágrafos longos tentando convencer um modelo a ter bom gosto ou respeitar restrições físicas não funciona. Em vez de implorar pra ele seguir regras, você usa o próprio modelo pra escrever um script que encerra com código diferente de zero quando ele não segue. O prompt vira lógica básica: o teste falhou, tá aqui a saída, conserta.

No fim das contas, o futuro chegou. Modelos de IA trazem muita velocidade. O Claude refatorou algo como trinta componentes de uma vez quando renomeamos tokens de design, e mais tarde o Gemini descobriu por que o Mobile Safari precisava de um listener vazio de touchstart no +layout.svelte só pra registrar um clique. Mas essas coisas não têm bom gosto visual e não têm resistência alguma a clichês. Cada mudança ainda precisa responder a uma situação real, e precisa ser revisada por uma pessoa com intenção. Eu não acho que isso vai mudar por um bom tempo.