27 KiB
NotebookStory — design e especificação de implementação
O portfólio deixa de mostrar telas como uma faixa que passa diante do visitante e passa a revelar sistemas como uma sessão de trabalho: abrir, navegar, trocar de projeto, fechar.
Status
- Estado: direção aprovada para implementação.
- Escopo: substituir o palco 3D do celular e o
PrintReelhorizontal por uma única experiência de notebook. - Interação: L3 — pin, scrub reversível, objeto 3D, cursor coreografado e tabs acessíveis.
- Implementação: ainda não iniciada.
Decisões fechadas
- O bloco de história terá um único dispositivo e um único Canvas WebGL. A hero atual não ganha outro renderer por causa desta mudança.
- O notebook usará um modelo 3D CC0, com abertura controlada por rig próprio.
- A tela não usará iframe nem dependerá de rede para renderizar a narrativa.
- A interface da tela será determinística, usando screenshots reais dos projetos já presentes no repositório.
- O cursor será coreografado pelo scroll; as tabs e o CTA continuarão sendo controles DOM reais.
- O
PrintReelserá removido depois da migração, sem alias ou caminho paralelo. - O scroll continuará sendo o controle de play e rewind. Não será introduzido Lenis.
Objetivo e não escopo
Objetivo
Criar uma seção de prova visual dos projetos em que o visitante percebe um notebook físico abrindo, vê uma interface de navegador, acompanha um cursor trocando de aba entre projetos reais e observa o dispositivo fechar antes de continuar para a seção About.
A experiência deve comunicar três coisas sem depender de texto longo:
- os projetos são sistemas reais, não apenas imagens de portfólio;
- cada projeto tem uma interface e um contexto próprios;
- a engenharia é apresentada como uma sequência de decisões operacionais.
Não escopo
- Navegar sites reais dentro do notebook.
- Reproduzir autenticação, formulários ou estados internos dos deployments.
- Criar um desktop completo ou um sistema operacional fictício.
- Adicionar áudio, vídeo, partículas pesadas ou um cursor global novo.
- Manter o celular como uma segunda narrativa.
- Redesenhar as seções
About,Skills,Experience,ProjectsouContact.
1. Visual Theme & Atmosphere
Style: Dark Editorial / Systems Workbench.
Keywords: bancada escura, metal fosco, browser chrome, prova operacional, cursor preciso, verde ácido, tipografia técnica, silêncio entre ações.
Tone: cinematográfico e técnico, mas sóbrio — não gaming, não cyberpunk, não demonstração de template.
Feel: uma câmera observa uma máquina de trabalho real sendo preparada, usada e fechada no fim do expediente.
Interaction Tier: L3 — experiência imersiva com uma cena pinada, timeline scrubbed, WebGL e interação DOM.
Dependencies: React/R3F/Three/Drei já existentes + GSAP/ScrollTrigger já existentes. Não adicionar dependências.
Direção de composição
- Fundo:
var(--ink)com grade discreta e halo radial emvar(--slash-dim). - Texto narrativo: lado esquerdo em desktop, alinhado à mesma margem do restante da home.
- Notebook: lado direito em desktop, ocupando o foco visual sem encobrir o texto.
- Tela: navegador escuro, borda fina, barra de abas legível e screenshot com contraste suficiente.
- Acento:
var(--slash)somente em estado ativo, progresso, cursor e CTA. - O modelo não deve parecer um produto anunciado. Deve parecer uma ferramenta em uso.
Estrutura narrativa
- Preparar: notebook fechado repousa sobre a bancada.
- Abrir: a tampa sobe pela dobradiça; o usuário entende imediatamente a mecânica.
- Ligar: o browser chrome aparece e a tela acende.
- Explorar: o cursor entra, clica nas tabs e troca o screenshot do projeto.
- Concluir: a última tela recebe uma pausa para leitura e um CTA real.
- Encerrar: cursor sai, tela reduz a luminosidade, notebook fecha e a seção libera o fluxo normal.
2. Color Palette & Roles
A implementação reutiliza os tokens existentes em app/globals.css. Os nomes abaixo são o contrato visual do novo palco; novos hexadecimais não devem ser introduzidos em componentes.
:root {
/* Backgrounds */
--notebook-bg: var(--ink);
--notebook-surface: #111214;
--notebook-surface-alt: rgba(255, 255, 255, 0.04);
--notebook-surface-hover: rgba(255, 255, 255, 0.08);
/* Borders */
--notebook-border: rgba(255, 255, 255, 0.12);
--notebook-border-hover: rgba(204, 255, 0, 0.62);
/* Text */
--notebook-text: var(--paper);
--notebook-text-secondary: rgba(244, 242, 237, 0.72);
--notebook-text-tertiary: rgba(244, 242, 237, 0.48);
/* Accent */
--notebook-accent: var(--slash);
--notebook-accent-hover: var(--white);
/* RGB variants */
--notebook-bg-rgb: 10 10 10;
--notebook-paper-rgb: 244 242 237;
--notebook-accent-rgb: 204 255 0;
/* Semantic */
--notebook-success: var(--slash);
--notebook-error: #ff6b6b;
--notebook-warning: #f5c451;
}
Regras de cor
- Todo CSS novo usa tokens; nenhum componente recebe hex solto.
- O verde ácido marca apenas ação, estado ativo, foco, progresso ou informação de produção.
- Texto de descrição não usa o verde como cor principal.
- Estados de erro e indisponibilidade não serão simulados dentro da narrativa; projetos não aprovados são removidos da tablist.
- Materiais R3F usam um mapa de cores central que replica os tokens acima; o modelo não cria uma paleta independente.
- Gradientes ficam restritos ao fundo atmosférico e não são aplicados dentro da screenshot para não mascarar a interface real.
3. Typography Rules
O palco mantém as fontes locais e os tokens tipográficos da home; não haverá importação adicional de fonte.
| Papel | Fonte | Tamanho | Peso | Altura | Tracking |
|---|---|---|---|---|---|
| Título da narrativa | var(--font-display) |
clamp(2.4rem, 6vw, 5.2rem) |
800 | 0.98 | -0.03em |
| Título do projeto ativo | var(--font-display) |
clamp(1.25rem, 2.4vw, 2rem) |
800 | 1.0 | -0.025em |
| Corpo | var(--font-mono) |
clamp(0.82rem, 1.2vw, 1rem) |
400 | 1.7 | normal |
| Tab/browser chrome | var(--font-mono) |
10–11px | 600 | 1.2 | 0.06–0.12em |
| CTA | var(--font-mono) |
11px | 700 | 1.0 | 0.10em |
| Metadados | var(--font-mono) |
9–10px | 600 | 1.2 | 0.12–0.16em |
Regras tipográficas
- Títulos permanecem em caixa alta apenas quando já seguem a convenção da home.
- Texto de tab usa nomes reais dos projetos; não abrevia nomes de forma ambígua.
- O screenshot pode conter a tipografia própria do projeto. Não aplicar filtro ou blend que prejudique a leitura.
- Para este estilo editorial, títulos não usam gradiente nem
text-shadow; a profundidade vem do modelo, da luz e do contraste. - Não usar fontes externas, emoji ou ícones improvisados.
4. Component Stylings
NotebookStory shell
Default: fundo var(--notebook-bg), min-height: 100dvh, overflow horizontal bloqueado, conteúdo centrado em max-width: 1240px.
Active: o palco recebe data-phase e data-project-id; apenas o projeto corrente usa o acento visual.
Focus: o foco nunca fica no Canvas; controles DOM recebem outline: 2px solid var(--notebook-accent) e outline-offset: 3px.
Disabled: controles de projeto indisponíveis não aparecem na tablist; o estado não será apenas visualmente desabilitado.
Browser chrome
- Janela com raio discreto (
var(--r-md)), borda de 1px e fundo quase preto. - Barra superior separa tabs, endereço e controles decorativos.
- A barra permanece estável; trocar de projeto muda o conteúdo e o estado ativo, não move o frame inteiro.
- Ícones de janela são decorativos e
aria-hidden; não usar símbolos de texto como controles.
Project tabs
As tabs são buttons reais dentro de role="tablist".
.notebook-tab {
min-height: 44px;
border: 1px solid transparent;
background: transparent;
color: var(--notebook-text-tertiary);
transition:
color 180ms var(--ease-out),
background-color 180ms var(--ease-out),
border-color 180ms var(--ease-out),
transform 180ms var(--ease-out);
}
.notebook-tab:hover {
border-color: var(--notebook-border);
background: var(--notebook-surface-hover);
color: var(--notebook-text);
}
.notebook-tab[aria-selected="true"] {
border-color: var(--notebook-accent);
background: rgb(var(--notebook-accent-rgb) / 0.10);
color: var(--notebook-accent);
}
.notebook-tab:active {
transform: scale(0.98);
}
.notebook-tab:focus-visible {
outline: 2px solid var(--notebook-accent);
outline-offset: 3px;
}
.notebook-tab:disabled {
cursor: not-allowed;
opacity: 0.42;
}
Cursor visual
- Elemento decorativo com
aria-hidden="true"epointer-events: none. - Desenhado em CSS/SVG simples, sem seguir o cursor global do site.
- Estado normal: seta clara pequena.
- Estado de hover: halo de
var(--notebook-accent)com escala limitada. - Estado de click: anel de 1 pulso, sem
filter: blur()em movimento. - O cursor nunca será a única indicação da tab ativa.
CTA Abrir projeto
- Default: bloco preenchido com
var(--notebook-accent)e textovar(--ink). - Hover: fundo
var(--notebook-accent-hover), textovar(--ink). - Active: escala
0.98. - Focus: anel de acento visível.
- Disabled: não renderizar se o projeto não possuir
demoUrlaprovado. - A ação abre o deployment em nova aba com
target="_blank"erel="noopener noreferrer".
5. Layout Principles
Container
- Max width:
1240px, igual às demais seções. - Padding horizontal:
24pxem mobile/tablet e64pxem desktop amplo. - Texto narrativo: máximo de
36chpara preservar hierarquia. - Palco 3D: área mínima de
min(58vw, 760px)em desktop.
Desktop
.notebook-story-grid {
display: grid;
grid-template-columns: minmax(260px, 0.72fr) minmax(520px, 1.28fr);
align-items: center;
gap: clamp(24px, 5vw, 80px);
}
- O notebook ocupa a coluna visual maior.
- O browser chrome não pode tocar as bordas do viewport.
- O texto não deve ser colocado sobre a área mais clara da tela.
- A indicação de progresso fica próxima ao rodapé, seguindo o HUD atual do
ScrollStory.
Scroll runway
- Runway inicial:
520vh, recalculável conforme o número de tabs. - Palco:
position: sticky; top: 0; height: 100dvh. - O sticky libera no final da fase de fechamento; não usar
position: fixedglobal. - O próximo elemento no DOM é
About; não inserir uma faixa horizontal intermediária.
Espaçamento
- Entre kicker e título:
var(--sp-3). - Entre texto e metadata do projeto:
var(--sp-8). - Entre tabs:
4pxa8pxconforme largura. - CTA e tabs devem ter área de toque mínima de
44px.
6. Depth & Elevation
| Nível | Tratamento | Uso |
|---|---|---|
| Flat | var(--ink) + borda sutil |
Fundo e áreas sem foco |
| Surface | plano quase preto + borda var(--notebook-border) |
Browser chrome e tablist |
| Elevated | sombra curta, opacidade baixa | Browser sobre a tela do notebook |
| Device | luz direcional + metalness moderado | Corpo e tampa do GLB |
| Focus | linha/halo de var(--notebook-accent) |
Tab ativa, progresso e cursor |
Regras:
- Não usar sombra gigantesca para esconder recortes do modelo.
- Não animar
box-shadowem todos os frames. - Reflexos do metal devem ser discretos e vir da iluminação R3F.
- O conteúdo da tela deve continuar mais legível que os efeitos de profundidade.
7. Animation & Interaction
Filosofia de movimento
O objeto deve se comportar como um objeto com massa: aproxima, articula na dobradiça, estabiliza, recebe input e fecha. Cada movimento tem função narrativa; não haverá rotação contínua gratuita.
Contrato de progresso
Um único valor progress ∈ [0, 1] dirige:
- transformação do notebook;
- intensidade da tela;
- posição do cursor visual;
- projeto ativo;
- barra de progresso;
- entrada e saída do texto contextual.
O scroll para cima deve recalcular exatamente os mesmos valores em sentido inverso. Não usar timers para alterar o estado narrativo.
Timeline
| Faixa | Estado | Animação principal | Conteúdo |
|---|---|---|---|
0.00–0.10 |
approach |
notebook entra, escala estabiliza, luz de bancada acende | kicker geral |
0.10–0.25 |
open |
tampa gira de fechada para aproximadamente 105° | título da narrativa |
0.25–0.34 |
boot |
tela ganha brilho; browser chrome revela | texto curto de contexto |
0.34–0.44 |
cursor-in |
cursor entra pela lateral e aponta para a primeira tab | tab 01 preparada |
0.44–0.78 |
browse |
quatro ciclos de mover, clicar, trocar screenshot e manter leitura | uma tab ativa por vez |
0.78–0.87 |
hold |
última tela permanece; metadata e CTA ganham foco | projeto corrente |
0.87–1.00 |
close |
cursor sai, brilho reduz, tampa fecha, palco perde opacidade | próximo conteúdo |
Cada ciclo de tab terá uma janela de leitura maior que a janela de clique. A troca não deve parecer um carrossel: a moldura permanece fixa e somente o conteúdo da viewport muda com crossfade curto.
Rig 3D
basepermanece estável e recebe leve deslocamento de entrada.lidé o único elemento que rotaciona na abertura/fechamento.- O plano de tela acompanha a transformação local da tampa.
- A câmera mantém perspectiva 3/4 frontal; mudanças de FOV são proibidas durante a troca de tabs.
- A rotação livre do celular existente será removida; não reaproveitar
motionRigcomo turntable. - O efeito de aproximação da câmera será limitado a uma pequena variação de posição, sem zoom agressivo.
Tela e cursor
A tela será um componente DOM 3D com Html transform do Drei, alinhado ao plano interno do modelo. Isso permite que o browser chrome e as tabs sejam reais, acessíveis e clicáveis.
- Screenshot:
<img>local comobject-fit: covere recorte definido por projeto. - Browser chrome: DOM, não desenhado em Canvas.
- Cursor: elemento DOM dentro do mesmo plano da tela, movido por timeline.
- Clique coreografado: altera
aria-selected,activeProjecte screenshot somente na janela de troca. - O componente deve pré-carregar a imagem atual e a próxima; as demais entram sob demanda.
Interação do visitante
- O visitante pode rolar e rebobinar a sequência normalmente.
- Foco de teclado não altera o scroll automaticamente.
ArrowLeft/ArrowRightmudam a tab selecionada;Home/Endsaltam para a primeira/última.- Clique numa tab usa scroll suave nativo até a janela de progresso daquela tab; depois a timeline continua sendo a fonte de verdade.
- Clique em
Abrir projetoé a única ação que sai do portfólio e abre um deployment. - A coreografia pode ser interrompida pelo input sem deixar dois cursores ou duas tabs ativas.
- O cursor visual não acompanha
pointermoveglobal; o cursor global existente continua com seu contrato atual.
ScrollTrigger
Usar o padrão existente:
const timeline = gsap.timeline({
defaults: { ease: "none" },
scrollTrigger: {
trigger: section,
start: "top top",
end: "bottom bottom",
scrub: 0.85,
invalidateOnRefresh: true,
},
});
- Não criar um segundo listener global de scroll.
- Atualizações de texto e
aria-selecteddevem ser quantizadas por mudança de tab, não por frame. - A animação deve ser destruída via
gsap.context().revert()no unmount. - O Canvas deve pausar quando o palco estiver fora da viewport.
Reduced motion e fallback
@media (prefers-reduced-motion: reduce) {
.notebook-story-stage {
min-height: auto;
}
.notebook-story-sticky {
position: static;
height: auto;
}
.notebook-cursor {
display: none;
}
}
Com prefers-reduced-motion: reduce:
- não inicializar GSAP, ScrollTrigger ou WebGL;
- renderizar notebook aberto como mockup estático ou imagem do modelo;
- mostrar uma tab selecionada e a tablist completa;
- manter título, descrição, screenshot e CTA em fluxo normal;
- permitir troca manual por teclado e clique.
8. Do's and Don'ts
Do
- Usar screenshots reais já versionados em
public/assets/prints. - Fazer o browser chrome permanecer fixo enquanto as tabs trocam.
- Dar tempo suficiente para o visitante entender cada projeto.
- Manter o notebook como protagonista visual e o texto como orientação.
- Usar uma única variável de progresso para toda a cena.
- Parar renderização quando o palco estiver invisível.
- Manter os controles DOM acessíveis mesmo quando o modelo 3D falhar.
- Atualizar
CREDITS.mdcom o modelo e sua licença.
Don't
- Não usar iframe ou rede externa para montar a tela durante o scroll.
- Não transformar screenshots em uma faixa horizontal novamente.
- Não criar um segundo Canvas WebGL para o notebook.
- Não rotacionar o notebook inteiro como se fosse um celular.
- Não animar
filter: blur()em elementos que se movem a cada frame. - Não esconder tabs clicáveis dentro de um Canvas sem equivalente DOM.
- Não usar o cursor global como substituto do estado ativo da tab.
- Não deixar o usuário preso no sticky depois que a tampa fechou.
- Não mostrar projetos fora do snapshot aprovado de saúde.
- Não iniciar timers independentes que quebrem o rewind do scroll.
9. Responsive Behavior
| Breakpoint | Layout | Comportamento |
|---|---|---|
Desktop >= 1025px, hover disponível |
duas colunas | GLB CC0, Html transform, sticky + scrub completo |
Tablet 768–1024px |
uma coluna compacta | 3D somente com hover disponível e espaço suficiente; caso contrário, fallback 2D |
Mobile < 768px ou pointer coarse |
fluxo normal | sem WebGL; mockup 2D aberto, tablist em 2 colunas ou wrap, screenshot única |
| Qualquer largura + reduced motion | fluxo normal | fallback estático acessível, sem pin, cursor automático ou scrub |
Mobile
- Nunca exigir que o visitante acompanhe uma tampa 3D em uma tela pequena.
- O mockup 2D deve manter proporção de notebook, mas priorizar screenshot, tabs e CTA.
- Tabs podem quebrar em duas linhas; não usar
white-space: nowrapem uma faixa que cause overflow. - O screenshot ativo ocupa a largura disponível com
aspect-ratioestável. - O CTA fica depois do screenshot, com largura confortável para toque.
- A seção não deve criar overflow horizontal em viewport de
320px.
Arquitetura e integração com o repositório
Composição da home
CinematicHome.tsx deve passar projects para o novo palco e ficar nesta sequência:
<Hero />
<marquee />
<NotebookStory projects={projects} />
<About />
<SectionDivider />
<Skills />
...
O <PrintReel /> será removido. O conteúdo de contexto atualmente presente no ScrollStory será adaptado para o notebook; não haverá duas introduções concorrentes.
Componentes propostos
app/components/story/NotebookStory.tsx: orquestra runway, ScrollTrigger, capítulos, estado de tab e fallback.app/components/story/NotebookStage3D.tsx: carrega o GLB, monta o rig, câmera, luzes e boundary de falha.app/components/story/NotebookBrowser.tsx: browser chrome, tabs, screenshot, cursor visual e CTA.app/components/story/notebookProjects.ts: mapa entre IDs de projeto e assets de screenshot, sem chamada de rede.app/components/cinematic/CinematicHome.tsx: integração e remoção doPrintReel.public/models/laptop-kenney.glb: asset CC0 aprovado após validação de nós e escala.CREDITS.md: fonte, autor, licença, URL e modificações do modelo.
Depois da migração, remover os caminhos sem consumidores:
PrintReel.tsx;StoryStage3D.tsx;phoneScreenTexture.ts;- testes específicos desses componentes, substituídos por testes do notebook.
ReelDirector.tsx continua responsável pelas animações declarativas das seções restantes.
Dados, assets e licenciamento
Projetos
A fonte continua sendo PortfolioProject[] carregada no servidor. O mapa visual deve usar:
idpara escolher o screenshot local;titleesubtitlepara metadata;technologiespara a linha técnica;demoUrlpara o CTA;- a lista já filtrada pelo snapshot de saúde para decidir quais tabs existem.
A ordem inicial é a já usada no filmstrip atual:
nutriscan—/assets/prints/print-nutriscan.png;sc-plus—/assets/prints/print-scplus.png;sev-modaintima—/assets/prints/print-sev.png;as-servicos—/assets/prints/print-asservicos.png.
Se um projeto live não tiver screenshot mapeado, ele não entra na narrativa até o asset ser incluído; não renderizar imagem quebrada ou placeholder colorido.
Modelo 3D
Candidato aprovado para investigação: Laptop — Kenney / Poly Pizza, listado como OBJ/GLTF e Public Domain (CC0). A licença pública também está referenciada em CC0 1.0.
Antes de integrar:
- baixar uma cópia local, não carregar o GLB por URL remota;
- inspecionar o Outliner e confirmar objetos separados para base, tampa, tela e teclado;
- confirmar origem da tampa próxima à dobradiça;
- corrigir escala, normais, materiais e pivô offline;
- registrar fonte e modificações em
CREDITS.md; - se o arquivo não permitir separar a tampa, escolher outro asset CC0 ou aplicar o fallback procedural; não animar o modelo inteiro como substituto.
Tela
A tela usa os PNGs existentes, sem captura em runtime. Não haverá requisição externa durante renderização, nem dependência de disponibilidade dos deployments para a animação aparecer.
Acessibilidade
- A seção recebe
aria-label="Projetos em produção no notebook". - A tablist usa
role="tablist"; cada tab usarole="tab",aria-selectedearia-controls. - O painel ativo usa
role="tabpanel",aria-labelledbye nome do projeto. - Mudança de projeto atualiza uma região
aria-live="polite"curta, sem narrar cada frame. - O Canvas e o cursor coreografado são decorativos para tecnologia assistiva.
- O CTA real permanece fora da textura e é alcançável por teclado.
- A ordem de tabulação segue texto → tabs → CTA, sem focar controles decorativos.
- O fallback sem JavaScript continua apresentando título, tabs, screenshot e links no fluxo normal.
- Foco visível, contraste e áreas de toque seguem os tokens globais já existentes.
Performance e limites
- Um único Canvas WebGL para o bloco
NotebookStory; não criar um segundo contexto para browser chrome, tabs ou fallback. - Renderers 3D existentes da hero devem ser pausados quando estiverem fora da viewport, evitando duas cenas animando ao mesmo tempo.
- WebGL somente em desktop com hover disponível.
- Canvas pausado fora da viewport por
IntersectionObserverou equivalente do palco. dprlimitado a[1, 1.5]no notebook.- GLB local otimizado para no máximo 2 MB transferidos; texturas externas não são permitidas.
- Uma imagem atual e uma próxima pré-carregadas; demais lazy.
- Screenshot não deve ser redesenhada por frame; apenas a troca de
srce o movimento de cursor acompanham a timeline. - Nenhum
fetchde deployment dentro deNotebookStory. - Nenhuma nova biblioteca de scroll ou de cursor.
- Alvo de verificação: pelo menos 50 FPS em desktop intermediário durante scrub contínuo; se o modelo não atingir isso, reduzir luzes/geometria antes de adicionar efeitos.
- Mobile e reduced motion não criam Canvas WebGL.
Critérios de aceite
Comportamento
- A seção inicia com notebook fechado.
- A tampa abre pela dobradiça, sem girar o corpo inteiro.
- O browser chrome aparece antes da primeira troca de tab.
- O cursor visual move-se até a primeira tab, clica e troca o conteúdo.
- As quatro tabs aprovadas aparecem na ordem definida quando os quatro projetos estão live.
- Cada tab mostra screenshot, nome, metadata e CTA correspondente.
- A tela permanece no mesmo enquadramento; não existe deslocamento horizontal de cards.
- O notebook fecha depois da última leitura.
- Ao terminar o fechamento,
Aboutaparece no fluxo normal sem salto ou seção presa. - Ao rolar para cima, todas as fases rebobinam sem duplicar tabs, cursores ou listeners.
Acessibilidade e fallback
- Tablist, tabs, painel e CTA são encontrados no accessibility tree.
- Teclado consegue trocar tabs sem depender do cursor animado.
prefers-reduced-motiondesativa pin, scrub, cursor automático e WebGL.- Pointer coarse/mobile não recebe Canvas WebGL nem overflow horizontal.
- Falha do GLB mantém screenshot, tabs e CTA no mockup fallback.
- Um projeto sem health check aprovado não aparece na narrativa.
Qualidade técnica
- Não há iframe ou request remoto no componente.
- O bloco
NotebookStorymantém somente um Canvas WebGL; controls e fallback não criam canvases adicionais. - O unmount remove ScrollTrigger, GSAP context e observers.
PrintReel, o rig do celular e a textura antiga são removidos depois de não haver referências.CREDITS.mdcontém atribuição e licença do notebook.- O build de produção continua passando.
Plano de testes e verificação
Testes unitários/componentes
- mapa de projeto: filtra IDs live e ignora screenshot ausente;
- função de timeline: cada faixa de
progressproduz a fase esperada; - seleção de tab:
aria-selected,aria-controlse painel acompanham a mudança; - teclado: setas,
Home,End,EntereSpace; - CTA: URL,
targeterelcorretos; - reduced motion: fallback sem inicializar GSAP/WebGL;
- falha do GLB: boundary renderiza mockup sem perder controles.
Smoke browser
Em Chromium real, verificar:
- carregar a home sem erro de console;
- confirmar notebook fechado no início da seção;
- avançar o scroll até abertura, boot e cada tab;
- voltar o scroll e confirmar rewind visual;
- clicar numa tab e confirmar seek para sua janela;
- usar teclado no tablist;
- chegar ao fim e confirmar que
Aboutsegue normalmente; - medir ausência de overflow horizontal em desktop e
320px; - repetir com reduced motion e confirmar fluxo estático;
- confirmar que o CTA abre somente quando acionado pelo visitante.
Verificações de entrega
pnpm exec jest --runInBand
pnpm exec tsc --noEmit
pnpm exec biome check app/components/story app/components/cinematic/CinematicHome.tsx
pnpm run build
Ordem de implementação
- Validar, otimizar e creditar o GLB CC0.
- Criar o rig
base/lide o fallback procedural no palco existente. - Criar
NotebookBrowsercom DOM 3D, tabs, screenshot e CTA. - Migrar a timeline do
ScrollStorypara o contrato de fases do notebook. - Passar
projectsdeCinematicHomee filtrar a lista visual pelos dados aprovados. - Remover
PrintReel, celular e textura antiga após atualizar todos os consumidores. - Implementar reduced motion, mobile e falhas do modelo.
- Executar testes, smoke browser, build e revisão visual desktop/mobile.
A implementação só começa depois da revisão deste spec; qualquer mudança no número de Canvas, origem dos screenshots, ordem de projetos ou comportamento de fallback deve ser registrada neste documento antes do código.