Repository navigation
Description
Objetivo
Substituir completamente o componente atual de seleção de linguagens (cards) por um carousel horizontal infinito, onde cada linguagem será representada por uma bandeira medieval vertical.
A proposta é transformar a seleção de linguagens em um elemento central da identidade visual do projeto, tornando a navegação muito mais imersiva e memorável.
Contexto
Hoje utilizamos uma lista de cards independentes para selecionar a linguagem.
A nova proposta substitui essa abordagem por um carousel infinito onde cada item representa uma linguagem através de uma bandeira medieval.
Exemplo do conceito visual:
A imagem atual contém diversas bandeiras em uma única composição e deverá ser utilizada apenas como material base.
Asset
A imagem existente contém aproximadamente 10 linguagens em uma única textura.
Ela não deverá ser utilizada inteira.
Será necessário realizar um fatiamento da imagem, gerando um asset individual para cada linguagem.
Exemplo:
typescript.png
bash.png
sql.png
ruby.png
lua.png
dart.png
scala.png
elixir.png
erlang.png
haskell.png
...
Cada banner será um item independente dentro do carousel.
Isso permitirá:
- renderização individual;
- animação independente;
- futura adição de novas linguagens sem alterar o sprite original.
Carousel
O carousel deverá ser:
- infinito
- horizontal
- extremamente fluido
- desaceleração suave
- suporte a mouse e touch
- possibilidade futura de autoplay
O comportamento esperado lembra uma gôndola onde diversas bandeiras ficam penduradas lado a lado.
Animação das bandeiras
O objetivo não é apenas mover os banners junto com o carousel.
Cada bandeira deverá possuir uma animação própria simulando um tecido pesado.
Referência técnica:
https://threejs.org/examples/#physics_ammo_cloth
A simulação deverá ser adaptada para representar:
- tecido espesso
- pouco elástico
- pesado
- amortecimento elevado
- movimentos lentos
- pequena inércia
Não queremos um tecido fino sendo agitado pelo vento.
Também não queremos movimentos exagerados.
A sensação deve lembrar:
- estandartes de castelos
- bandeiras de igrejas
- pendões medievais
- tecidos de veludo
- tapeçarias
Interação com o Carousel
A física deve responder ao movimento do usuário.
Quando o carousel acelerar:
- a parte superior acompanha imediatamente
- o restante da bandeira atrasa levemente
- surge uma pequena curvatura
Quando o carousel desacelerar:
- a bandeira continua seu movimento por inércia
- ocorre uma pequena oscilação
- rapidamente retorna ao equilíbrio
Quando o carousel estiver parado:
- as bandeiras permanecem praticamente imóveis
- apenas um movimento residual extremamente sutil pode permanecer
O efeito esperado é semelhante ao de um tecido pesado preso por sua haste superior.
Diretrizes Visuais
A animação deve transmitir peso.
Evitar:
- tremores
- vibrações constantes
- movimentos rápidos
- ondas excessivas
- comportamento semelhante a seda ou bandeiras leves
Buscar:
- elegância
- estabilidade
- naturalidade
- inércia
- sensação de massa
O usuário deve perceber que existe uma simulação física, mas ela não deve chamar mais atenção que a interface.
Performance
A solução precisa permanecer leve.
Objetivos:
- manter FPS elevado
- reutilizar geometrias sempre que possível
- evitar recriação constante de física
- limitar a complexidade da malha de cada tecido
- considerar pausa ou redução das simulações quando o carousel estiver ocioso
Arquitetura sugerida
Carousel
│
├── Banner
│ ├── Texture
│ ├── Cloth Mesh
│ └── Physics
│
├── Banner
│
├── Banner
│
└── ...
Cada bandeira deverá possuir sua própria simulação, permitindo que todas reajam de forma ligeiramente diferente ao movimento do carousel.
Critérios de Aceitação
- Remover o componente atual baseado em cards.
- Implementar carousel horizontal infinito.
- Fatiar o asset original em imagens individuais por linguagem.
- Cada linguagem deve utilizar sua própria textura.
- Integrar Three.js com a simulação
physics_ammo_clothcomo base para o comportamento do tecido. - Adaptar a física para representar tecidos pesados.
- Movimento do tecido deve responder à velocidade do carousel.
- Oscilações devem ser suaves e amortecidas.
- As bandeiras devem permanecer praticamente estáticas quando o carousel estiver parado.
- Garantir boa performance mesmo com diversas bandeiras simultaneamente.
Resultado Esperado
Ao navegar entre as linguagens, o usuário não terá a sensação de deslizar cartões, mas de movimentar uma sequência de estandartes medievais suspensos.
Cada bandeira reage ao deslocamento do carousel com uma leve inércia, criando um movimento orgânico e elegante, reforçando a identidade visual do projeto sem comprometer a usabilidade ou a performance.

Activity
caioross commentedon Jul 26, 2026
Implementada em PR #98.
Uma AC foi cumprida por outro caminho, e isso precisa do seu aval: não usei Three.js +
physics_ammo_cloth. São ~700 KB (three.js ~170 KB gzip +ammo.wasm~500 KB) para escolher uma linguagem numa home que hoje carrega 212 KB — e dependência de runtime pesada é núcleo irredutível do HANDBOOK §7.1, decisão sua. Além disso, soft-body completo é a ferramenta errada para o efeito pedido: tecido pesado, amortecimento alto e "praticamente imóvel quando parado" é um pêndulo amortecido preso por uma haste, não uma malha de partículas. O caminho entregue custa +4 KB e zero dependências, com a simulação isolada atrás destepCloth/bendProfile— se preferir a rota ammo, troco o backend sem tocar no carousel nem nos assets.Demais ACs atendidas: grade de cards removida dos dois pontos de uso; carousel infinito horizontal (aritmética modular, sem lista duplicada) com arremesso, desaceleração e encaixe; mouse, touch, roda e teclado; as 3 composições fatiadas em 25 estandartes cobrindo as 24 linguagens de
LANGUAGES(284 KB de WebP); pano com curvatura progressiva que só responde a aceleração — reto na rolagem lisa, chicoteia ao frear, assenta em <1,5 s; simulação desligada em repouso (o rAF dorme, custo zero por frame).Um detalhe que a issue não previa: a #72 transformou o seletor do lobby em votação, então lá centralizar não pode votar — o voto sai no clique/Enter (
commitOnSettle={false}), com os selos de contagem preservados.Gate: typecheck ✅ · build ✅ (216 KB) ·
pnpm test120/120 (+32) ✅ · validate-persistence 72/72 ✅ · validate-metrics 45/45 ✅.fix(ui): restaura votação, tira o fundo da gôndola e dá vida ao tecido (
feat(cloth): troca o motor de tecido para soft-body ammo + three.js (#97
caioross commentedon Jul 26, 2026
Backend ammo + Three.js implementado na PR #98 (commit
2072d33), conforme a decisão do dono registrada na PR (núcleo §7.1). Fecha a última AC pendente destas duas issues.O soft-body do Bullet substituiu o pêndulo analítico atrás da fronteira
ClothEngine; carousel, os 25 estandartes,banners.tse a votação seguem intactos. Carregado porimport()dinâmico: first load da home inalterado em 216 kB, com three + ammo em chunks sob demanda (437 kB brotli). Custo medido: 3,22 ms por quadro com 7 panos ativos, contra 16,7 ms de orçamento a 60fps.Medido no navegador dirigindo o laço na mão e lendo o framebuffer: ponta atrasa 12 px no arremesso com o topo cravado na haste, duas oscilações discretas ao soltar, repouso em 2,8 s. Fallback para o motor leve preservado (sem WebGL, import falhando ou reduced-motion).
Detalhes, números e os dois defeitos de calibração que apareceram medindo estão no comentário da PR.
feat(ui): gôndola infinita de estandartes medievais na escolha de lin…