como fiz este site_
Todo portfólio de dev tem o mesmo destino: ninguém lê. A pessoa abre, rola dois segundos por educação e fecha a aba. Quando sentei pra fazer o meu, decidi atacar exatamente isso — e a solução virou uma regra meio boba que guiou o projeto inteiro: o site é um menu gigante.
A home não tem hero, não tem foto minha sorrindo em cima de um parágrafo motivacional. Tem uma página de manhwa: quatro quadros de quadrinhos — projetos, artigos, sobre, contato — com o mascote do canal vivendo uma cena em cada um. Você entra numa seção, rola até o fim, e o site te devolve pra home sozinho. Um loop. Sem footer com sitemap, sem menu hambúrguer, sem decisão difícil pra quem chegou agora.
a home é uma página de manhwa
Cada quadro tem retícula de meio-tom, borda grossa de tinta, uma legenda de “cap. 0N” e o papel entre os quadros fazendo o papel de sarjeta. A regra que me diverti mais em aplicar: nenhum quadro reage ao hover do mesmo jeito. Projetos estoura speed lines com uma onomatopeia; artigos entorta em perspectiva como página virando; sobre inverte pra “quadro noturno” (fundo de tinta, mascote virando adesivo recortado); contato enche de verde de baixo pra cima, tipo maré. O menu inteiro vira uma leitura — e no celular a folha empilha como webtoon.
por que astro, por que estático
O site é Astro 7, 100% estático. Não tem servidor, não tem banco, não tem framework hidratando a página inteira pra renderizar texto parado. O conteúdo mora num arquivo TypeScript e numa content collection de markdown — este artigo é o primeiro dela. O build cospe HTML puro; JavaScript só entra onde tem animação. E mesmo assim, se você navega com prefers-reduced-motion ligado ou o JS falhar no meio do caminho, tudo continua legível. Essa era a regra inegociável: animação é tempero, nunca estrutura.
rolar até o fim te leva de volta
A mecânica que define o site: toda página termina num componente que chamei de EndCap. Quando você chega no fim, a seção trava na tela, um círculo verde cresce do centro conforme você insiste em rolar, um contador sobe de 0% a 100% — e aí a navegação acontece sozinha. Por trás é um ScrollTrigger do GSAP com pin e scrub:
const tween = gsap.to(circulo, {
scale: () => (Math.hypot(innerWidth, innerHeight) / 96) * 1.15,
ease: 'none',
onUpdate() {
const progresso = tween.progress();
pct.textContent = `${Math.round(progresso * 100)}%`;
if (armado && progresso > 0.97 && !navegou) {
navegou = true;
navigate(destino);
}
},
scrollTrigger: {
trigger: cap,
start: 'top top',
end: '+=160%',
scrub: 0.4,
pin: true,
invalidateOnRefresh: true,
onUpdate(g) {
if (g.progress < 0.9) armado = true;
},
},
});
O pin: true segura a seção no lugar enquanto 160% extras de rolagem acontecem, e o scrub: 0.4 amarra o círculo ao scroll com um atraso pequeno que dá peso ao movimento. O navigate é o do próprio Astro, então a saída participa da transição de página como qualquer clique em link. E o flag armado foi a lição que só apareceu na revisão: quando você navega e depois volta pelo histórico, o navegador restaura o scroll já no fim do pin — sem o flag, o ScrollTrigger recém-criado via progresso 100% e navegava de novo sozinho, transformando o botão voltar numa armadilha. Só navega quem rolou de verdade.
o título viaja entre as páginas
Quando você clica em “artigos” na home, o título não some pra reaparecer depois: ele viaja e vira o h1 da página de destino. São as view transitions nativas do Astro — os dois elementos só precisam compartilhar o mesmo nome:
<span transition:name="titulo-artigos">artigos</span>
O navegador faz o morph. Foi a feature com melhor custo-benefício do projeto inteiro: uma linha de cada lado, zero JavaScript meu.
cada biblioteca faz uma coisa só
Já me queimei tentando fazer uma lib de animação resolver tudo. Aqui a divisão é rígida e não abre exceção:
- GSAP + ScrollTrigger pra tudo que é dirigido por scroll: pins, parallax, a barra de progresso no topo, o EndCap.
- Motion pra entrada de blocos com spring — os quadros da home “carimbando” na folha em sequência — e pro traçado dos rabiscos SVG que se desenham quando entram na tela.
- React Spring só nas raras ilhas React, quando eu quero física de verdade: arrastar, soltar, inércia.
Quando cada ferramenta tem um território, o código de animação para de brigar consigo mesmo. E todo script começa com a mesma pergunta: a pessoa pediu menos movimento? Então nada se move.
a fonte estica
A tipografia display é a Archivo variável, e o eixo que eu mais uso nem é o peso — é o font-stretch. Na página de projetos, o título de cada painel estica de 62% a 125% amarrado ao scroll, comprimido quando entra e esparramado quando cruza o centro da tela, com um cubic-bezier que passa do ponto e volta, tipo mola. É o tipo de detalhe que fonte estática não faz, e que custa zero bytes além da fonte que eu já ia carregar de qualquer jeito.
60 / 30 / 10
A paleta vem direto da marca do canal DenisDev: 60% branco de papel, 30% preto de tinta, 10% verde. O verde nunca é protagonista — ele é acento: o underscore piscando, o hover que inunda a linha, o círculo do fim da página. E as regras negativas importam tanto quanto as positivas: nada de gradiente, nada de vidro fosco, nada de sombra difusa e cinza. Sombra aqui é dura e deslocada, borda aparece, radius é zero. No fim das contas, boa parte de “ter estética” é só dizer não pra 90% dos efeitos da moda.
Se você continuar rolando, esta página te leva de volta pros artigos. O site inteiro cabe nessa frase.
