O que Washing Line Hero faz?
A primeira tela de um site: a barra e navegação da marca no topo, um grande título com uma frase colorida, uma linha de introdução, um ou dois botões e uma pequena nota, e ao lado deles um varal estendido pelo céu. No varal, pendura um pano de algodão preso por pregadores de madeira, impresso com o pôster da própria marca: um nome, uma pequena linha, duas palavras grandes, uma coluna de especificações e um pequeno gráfico de barras. Uma brisa o levanta de vez em quando e as nuvens passam ao fundo.
Ele foi feito para ser tocado. Mova o ponteiro sobre o pano e a ponta do dedo o amassa. Pressione e arraste, e o pano acompanha sua mão: puxe uma ponta para cima, dobre sobre o varal, estique em sua direção. Solte e ele cai, balança e se acomoda. Em celulares, um dedo faz o mesmo, e a página não rola enquanto o pano é segurado. O pano é simulado (uma grade de partículas unidas por restrições de esticar, cisalhar e dobrar, processadas a 120 Hz fixas), então ele se comporta como tecido onde quer que seja pego, em vez de reproduzir uma animação pré-definida.
Você descreve o herói e a impressão em hero.json. A construção verifica tudo e escreve out/index.html, uma página inteira em um arquivo, e out/embed.html, a mesma seção para colar em um site que você já tem. Como a simulação é executada em etapas fixas a partir de um estado inicial em repouso, o record.mjs pode avançar quadro a quadro: no vídeo, um ponteiro desenhado puxa uma ponta, solta e depois passa a ponta do dedo pelo pano, para que o clipe mostre o que os visitantes podem fazer, o mesmo em qualquer máquina.
O que não é: o pano não rasga, não se enrola em si mesmo nem voa do varal (não há colisão própria, então se você o dobrar com força através de si mesmo, ele passa e cai de volta), e a impressão é desenhada pela página a partir das suas palavras, não de uma foto que você envia.
O que você precisa
- Node.js 18 ou mais recente. Nada para instalar.
- Chrome, Chromium ou Edge, para as verificações e a gravação (os scripts o encontram; defina
CHROME_PATHse não encontrarem). - ffmpeg, para o vídeo (
brew install ffmpeg,winget install ffmpeg,apt install ffmpeg; ou definaFFMPEG_PATH). - Acesso à internet quando a página for aberta: three.js e as fontes carregam do jsDelivr em versões fixas com hashes de integridade. Sem ele, os visitantes veem a imagem estática (o mesmo varal e pano, desenhados planos) e fontes do sistema; a página ainda funciona.
A pasta desta skill é <skill> abaixo: a pasta onde este SKILL.md está.
Passos
1. Colete a marca e as palavras
Pergunte à pessoa ou encontre no site atual dela:
- Nome, e o logotipo como um arquivo SVG (quadrado ou quase; simples, sem scripts). Sem um, até três letras formam um monograma.
- Cores: o fundo, que também é o céu no horizonte (a cor do texto deve atingir 7:1 sobre ele), e um acento. Um azul claro dá um céu do meio-dia com texto escuro; um azul marinho profundo ou ameixa dá um céu crepuscular com texto claro e um sol baixo e quente.
- Fontes: uma face decorativa para o título e as palavras grandes no pano, e uma para o resto. Elas carregam fixas e verificadas: Space Grotesk, Inter, Instrument Sans, Instrument Serif, DM Sans, Manrope, Fraunces, Sora, Outfit, Bricolage Grotesque, IBM Plex Sans, JetBrains Mono, Plus Jakarta Sans, Newsreader, Playfair Display. Qualquer outra família de fontes do Google Fonts também funciona, sem fixação.
- As palavras na página: um título de algumas palavras com a frase para colorir, uma ou duas frases sobre o que é e para quem, o botão principal e um segundo opcional com para onde vão, até cinco links de navegação e o botão na barra. Um pequeno selo acima do título e uma nota abaixo dos botões são opcionais.
- As palavras no pano: o nome a ser impresso no topo (geralmente o produto e a versão), uma pequena linha abaixo, duas a quatro palavras grandes (duas linhas são melhores), até seis pares curtos de especificação (rótulo e valor), uma legenda para o gráfico e um endereço web no canto.
Todo nome, número, alegação e data devem vir da pessoa. Não invente especificações ou figuras; deixe uma especificação de fora. As barras do gráfico são decoração desenhadas a partir da semente, não dados: dê a ele uma legenda que não alega números, ou desligue-o.
2. Escreva hero.json
Copie a pasta de exemplo mais próxima e substitua tudo: examples/paperwind/ (um app de anotações, céu claro do meio-dia, quatro pregadores) ou examples/halden/ (uma marca de linho, céu crepuscular, três pregadores, um pano mais quadrado).
| Campo | O que é | Limite |
|---|---|---|
brand.name, brand.href | O nome no canto superior esquerdo e para onde ele linka (padrão /) | 28 |
brand.logo | Um arquivo .svg ao lado de hero.json, incorporado na página; ou até 3 letras para um monograma | – |
nav[] | Até 5 { "label", "href" } em uma pílula (oculto em celulares) | 18 |
action | O botão no canto superior direito, { "label", "href" }; false para nenhum | 22 |
hero.badge | Uma pequena linha em caixa alta acima do título | 40 |
hero.title | O título. *frase* define em cor de acento; \n quebra a linha (até 3 linhas) | 72 |
hero.lede | O que é e para quem. Markdown inline: código, links, negrito, itálico | 220 |
hero.actions[] | 1 ou 2 { "label", "href" }: o primeiro é o botão principal sólido com uma seta, o segundo um contornado | 26 |
hero.note | Uma pequena linha abaixo dos botões | 90 |
hero.align | left (varal e pano à direita, padrão) ou right (espelhado). center coloca o pano atrás do texto; não recomendado | – |
theme.background, theme.ink, theme.accent | Cores hex. ink deve atingir 7:1 sobre background | – |
theme.mode | light ou dark; calculado a partir do fundo quando omitido | – |
theme.fonts | { "display", "body", "weight" }: nomes das famílias (false para a fonte do sistema) e o peso do título, 300–800 (padrão 500) | – |
theme.scrim | 0–1: quão fortemente o céu atrás do texto é lavado com a cor de fundo. Padrão 0.3 | – |
effect | O céu, o varal e o pano, abaixo | – |
motion.speed | 0.25–2, padrão 1: quão rápido tudo se move | – |
motion.pointer | true (padrão) | – |
meta | title (70) e description (170) para pesquisa e pré-visualizações de link; url e image (endereços https completos), lang (padrão en) | – |
A impressão (effect): ao menos printTitle ou printHeadline.
| Configuração | Limite | Padrão | O que é |
|---|---|---|---|
printTitle | 28 caracteres | nenhum | O nome no topo da impressão |
printLine | 64 caracteres | nenhum | Uma pequena linha abaixo do nome |
printHeadline | 48 caracteres | nenhum | As palavras grandes; \n quebra a linha. Elas encolhem para caber na coluna |
printSpecs | 0 a 6 { "label", "value" }, 12 e 14 caracteres | nenhum | A coluna de especificações à direita; rótulos imprimem em caixa alta |
printChart | true / false | true | Um pequeno gráfico de barras abaixo das especificações, sua barra mais alta em theme.accent |
printChartLabel | 32 caracteres | nenhum | Legenda abaixo do gráfico |
printFoot | 40 caracteres | nenhum | Texto pequeno no canto inferior, por exemplo, um endereço web |
cloth | cor | #ecebe6 | O algodão |
printInk | cor | #15181d | A impressão. Deve atingir 7:1 sobre cloth |
A cena (effect), toda configuração opcional:
| Configuração | Faixa | Padrão | O que faz |
|---|---|---|---|
seed | 1–9999 | 7 | Semeia as nuvens e as barras do gráfico |
skyTop | cor | #5f9be0 claro, o fundo escurecido quando escuro | O céu no topo do herói |
skyBottom | cor | theme.background | O céu no horizonte |
clouds | 0–1 | 0.6 | Quanto do céu é nuvem; 0 é claro |
cloudColor | cor | branco (claro), um violeta acinzentado suave (escuro) | As nuvens |
drift | 0–1 | 0.35 | Quão rápido as nuvens derivam |
light | day, dusk | day para claro, dusk para escuro | Sol do meio-dia de cima, ou um sol baixo e quente e uma luz de céu fria |
aspect | 0.8–1.7 | 1.35 | Largura do pano sobre sua altura |
pegs | 2–6 | 4 | Pregadores de madeira ao longo da borda superior |
peg | cor | #c39a64 | Os pregadores |
rope | cor | #6f6a62 claro, #cfc6b8 escuro | O varal |
breeze | 0–1 | 0.45 | Quão forte a brisa levanta o pano |
weave | 0–1 | 0.5 | Quão fortemente a trama simples aparece de perto |
poke | 0–1 | 1 | Quão profundo o amassado sob o ponteiro; 0 desliga |
drag | true / false | true | Deixar visitantes pegarem o pano e puxá-lo |
size | 0.6–1 | 0.76 | Quanto do espaço ao lado do texto o pano preenche (deixa espaço para ser puxado) |
hint | 40 caracteres | "Pressione · puxe · solte" | Uma pequena legenda no canto inferior distante em telas largas dizendo aos visitantes que podem tocá-lo; " · " separa suas partes; vazio para nenhum |
Dicas de escrita: o título na página e as palavras grandes no pano devem dizer coisas diferentes; o pano parece um pôster, então mantenha suas palavras poucas e grandes. Dois ou três pares de especificações são melhores que seis. Se a marca tem uma linha divertida sobre ser leve, voar, secar ou pendurar, o pano é o lugar para ela.
3. Construir
node <skill>/scripts/build.mjs hero.json out
Ele lista cada problema em hero.json de uma vez (campos faltando, texto acima do limite, configuração fora do intervalo, cores que não contrastam, tinta de impressão muito fraca no pano) e não escreve nada até que sejam corrigidos. Em seguida, imprime as configurações usadas e qualquer coisa que mereça atenção, como um acento que escureceu para que o título leia em 4.5:1.
Ele escreve out/index.html (CSS, script, logotipo e a imagem estática inline), out/embed.html e out/assets/logo.svg (o ícone da aba).
4. Verificar
node <skill>/scripts/check.mjs out/index.html
Ele carrega a página em 390, 768, 1024 e 1440 px e deve dizer ok em cada: nada mais largo que a tela ou cortado, contraste legível para cada linha de texto contra o céu atrás, nomes em cada link e botão, um h1. Linhas marcadas com · merecem uma olhada, mas não falham.
5. Olhar e tocar
node <skill>/scripts/record.mjs out/index.html --still 1.6 --size 1440x900
node <skill>/scripts/record.mjs out/index.html --still 3.5 --size 1440x900
node <skill>/scripts/record.mjs out/index.html --still 1.6 --size 390x844 --scale 2
node <skill>/scripts/screenshot.mjs out/index.html
O primeiro é o pano em repouso, o segundo no meio da puxada (a mão programada tem uma ponta levantada), o terceiro o celular. Olhe para eles: o pano inteiro está na tela com espaço ao redor, a impressão se lê, os pregadores estão no varal? screenshot.mjs executa o Chrome sem GPU, então mostra a imagem estática que os visitantes obtêm sem WebGL ou quando a rede falha (em out/screens/); verifique se também se lê.
Em seguida, abra out/index.html em um navegador e brinque com ele: passe o mouse, pressione, puxe uma ponta sobre o varal, solte. Em um celular, arraste com o dedo e verifique se a página ainda rola quando você desliza ao lado do pano.
6. Gravar o vídeo
node <skill>/scripts/record.mjs out/index.html --seconds 10
Ele avança o herói por 10 segundos a 30 fps em 1920×1080 e escreve out/preview.mp4 e out/poster.png (o quadro em 3.5 s, no meio da puxada). No vídeo, um ponteiro desenhado pega a ponta inferior em 2.2 s, puxa-a para cima em sua direção, solta em 4.4 s e, a partir de 6 s, passa a ponta do dedo pelo pano. Opções: --size 1080x1350 (retrato), --size 1080x1920, --fps 60, --poster 1.6. Os quadros são os mesmos em qualquer máquina, desde que a gravação comece em 0 (sempre começa).
7. Publicar
- Como página: envie
out/index.htmleout/assets/para qualquer host estático (Cloudflare Pages, Netlify, Vercel, GitHub Pages, S3). Useout/poster.png(ou um corte 1200×630 dele) comometa.image, enviado com o site. - Em um site existente: cole
out/embed.htmlonde o herói vai. Tudo está sob#mh-washing-line; mova os links de fontes<link>para o<head>se desejar. Se o site já tem um<script type="importmap">, mescle as entradasimportseintegritydo embed nele. A seção tem a altura total da viewport; defina--mh-heightnela (ex.:style="--mh-height:760px") para menos. Em React ou outras estruturas, coloque a marcação no componente e as tags<script>na página, ou renderize em um iframe. - Para alterar a impressão, edite
hero.jsone construa novamente.
Solução de problemas
build: fix hero.json first: cada linha nomeia o campo e o que mudar; problemas de cor sugerem uma cor que funciona.- O pano está cortado na borda: diminua
size, ou torne o pano mais quadrado (aspectperto de 1). - As palavras grandes no pano são pequenas: elas encolhem para caber ao lado da coluna de especificações. Use menos palavras, um
\n, ou menos especificações e nenhum gráfico (as palavras então ocupam a largura total). - O pano não se acomoda, ou balança demais: diminua
breeze. - O amassado está muito forte ou muito fraco:
poke(0 desliga; arrastar ainda funciona). record: the effect didn't start: a página caiu para a imagem estática: o Chrome não pôde carregar three.js do jsDelivr (verifique a conexão ou um proxy) ou não tem WebGL.ffmpeg not found: instale-o ou definaFFMPEG_PATH.- Visitantes veem um pano estático: o navegador deles tem WebGL desligado ou a CDN está bloqueada (a imagem plana), ou eles pediram movimento reduzido (um quadro do pano vivo em repouso, que não se move). Isso é proposital.
- Em um celular a página não rola: ela só para de rolar enquanto um dedo segura o pano; deslize ao lado dele. Se o pano preenche a tela do celular, diminua
size.
Arquivos
scripts/build.mjs,scripts/record.mjs: o que você executa. Eles chamam o kit Motion Heroes.scripts/kit/: o kit compartilhado por todas as skills Motion Heroes: a construção e verificações (build.mjs), o CSS da seção (shell.css), o runtime do navegador (runtime.js: relógio, pausa, proporção de pixels, movimento reduzido, fallback), o gravador (record.mjs,cdp.mjs), os three.js e fontes fixos (three.json,fonts.json) eKIT.md, que os explica.scripts/effect/: o varal em si:effect.mjs(suas configurações, verificações e imagem estática),effect.js(sombreador do céu, o
...
[truncated for translation]