Um filete de 3px colado no topo da janela que cresce da esquerda para a direita conforme a página desce, e fecha a largura inteira quando ela acaba. Não é uma dobra: é um elemento de página, que vive uma vez, como primeiro filho do <body>, e não pertence a nenhuma seção.
Ele resolve um problema de página longa: a barra de rolagem do navegador desapareceu do celular e encolheu no desktop, e sem ela a pessoa não sabe se falta um parágrafo ou dez. O filete devolve essa informação sem ocupar espaço de layout.
Quando usar
- Página longa de leitura corrida — artigo, carta ao investidor, manifesto, página de método com muitas etapas.
- Landing de campanha com muitas dobras, onde a pessoa precisa sentir que está chegando a algum lugar.
- Junto do
navegacao/nav-fixa-vidro: os dois se encaixam com a variação abaixo do menu fixo.
Quando não usar
- Página curta. Se não há o que rolar, o componente se esconde sozinho — mas então ele não deveria estar ali.
- Página de aplicação com painéis que rolam por dentro. O filete lê a rolagem da página, não a de um trilho com
overflow. Num layout onde o conteúdo rola dentro de uma caixa, ele fica parado em zero e mente. - Quando já existe outro indicador de posição — um sumário lateral com item ativo, por exemplo. Dois indicadores da mesma coisa competem.
Padrão editorial
Não há conteúdo a escrever: o componente não tem texto. O que existe é uma decisão de cor, e ela é editorial.
O filete lê --accent. Numa página em que o acento já é o botão, o olho e o realce, ele vira o quarto uso da mesma cor — e aí a cor perde a função de apontar. Em página assim, ancore o filete em --fg-muted ou --rule-2 e deixe o acento para o que converte.
Especificação dos assets
Nenhum. Não há imagem, ícone nem SVG.
Como usar
Cole o markup logo depois da abertura do <body>, antes de qualquer seção:
<body>
<div class="prog-rail" data-prog aria-hidden="true">
<span class="prog-fill"></span>
</div>
<!-- o resto da página -->
A posição no DOM não muda o desenho — é position:fixed —, mas muda a ordem de leitura, e um ornamento tem de vir antes do conteúdo, nunca no meio dele.
Ganchos
| Gancho | O quê |
|---|---|
[data-prog] |
a raiz que o script procura. Um por página |
--prog-k |
0 a 1, escrito pelo script no próprio nó. O CSS lê no scaleX |
--prog-top |
distância do topo. 0px por padrão; var(--nav-h) desce para baixo do menu |
--prog-h |
espessura. 3px |
--prog-z |
camada. 120, acima do menu fixo, que para no 100 |
Variações
| Variação | Como |
|---|---|
| Abaixo do menu fixo | --prog-top: var(--nav-h). O filete encosta na borda de baixo do header em vez de cobrir o topo dele |
| Sobre seção clara | o trilho lê --rule e --accent do :root, que serve o escuro. Em página de topo claro, declare o escopo no próprio filete (class="prog-rail sec-paper") ou fixe a cor no tema |
| Mais grosso | --prog-h: 5px. Acima disso deixa de ser filete e vira barra, competindo com o header |
Gotchas
-
Fora de qualquer
[data-rv]— isto é medido, não estilo. O core termina todo reveal emfilter:blur(0), eblur(0)continua sendo um filtro: elemento com filtro vira bloco de contenção para descendenteposition:fixed. Se o filete nascesse dentro de uma dobra revelada, grudaria no topo da dobra em vez do topo da janela — e só depois que aquela seção entrasse na tela. É o pior tipo de bug: o que não aparece na primeira olhada. Ver MANUAL §5. -
O denominador é o curso, não a altura.
scrollHeight - innerHeighté o quanto a página tem para rolar. Em página curta isso dá zero, e zero no divisor devolveNaN— que o CSS descarta em silêncio, deixando o filete cheio para sempre. Por isso a página curta esconde o indicador em vez de mostrar um errado. -
.prog-rail[hidden]{display:none}é obrigatório (§6.5 do contrato). Sem essa linha ohiddendo parágrafo acima não esconderia nada: o componente não dádisplayao trilho, mas basta uma regra de autor em qualquer lugar da folha para vencer odisplay:nonedo navegador. -
O valor é grampeado entre 0 e 1. Rolagem elástica — o "puxão" do iOS e do trackpad — devolve número negativo no topo e maior que um no fim. Sem o grampo, o filete encolhe para dentro da esquerda ou vaza pela direita durante o repique.
-
scaleX, nãowidth. Transform e opacidade são as duas únicas propriedades que o navegador anima sem refazer layout. Comwidth, cada quadro de rolagem remontaria a caixa — em página longa isso aparece como filete tremido no celular. -
pointer-events:nonenão é detalhe. É uma faixa da largura da tela colada no topo, exatamente onde mora o primeiro item de menu de muito site. -
Ele lê a rolagem da página, não a de um elemento. Num layout onde o conteúdo rola dentro de uma caixa com
overflow,LIB.onScrollnão dispara e o filete fica em zero. Não é bug do componente — é o caso de uso errado.
Movimento reduzido / toque
Com prefers-reduced-motion: reduce, o filete continua funcionando: a posição é dado, não enfeite. O que sai é só a suavização de 90ms entre um valor e o seguinte, e a barra passa a saltar direto para o valor certo.
No toque não há nada a adaptar — não depende de ponteiro. A barra de endereço do navegador móvel entrando e saindo muda innerHeight, e com ele o denominador; LIB.onScroll já dispara no resize, então a conta se refaz sozinha.
Histórico
| Origem | Quando | O quê |
|---|---|---|
| autoral | 2026-09-04 | Construído do zero. Padrão de domínio comum, sem referência única |
Origem. É o filete do Medium, dos blogs de produto e de quase toda página longa premiada no Awwwards — um padrão tão difundido que não tem dono. Nada foi copiado: reescrito do zero dentro do contrato desta biblioteca. O registro consolidado vai em projetos/autorais.md.
Decisões da tradução para o contrato. Cresce por transform:scaleX e não por width, porque width remonta a caixa a cada quadro. O valor entra por --prog-k, custom property do próprio componente, o que deixa a suavização inteira no CSS e o script sem uma linha de animação — por isso ele não consulta LIB.reduce: não há movimento em JS para desligar, e o @media do próprio estilo.css já trata. A página curta esconde o indicador em vez de mostrar um cheio. O valor é grampeado por causa da rolagem elástica. E o filete é aria-hidden de propósito: posição no documento é justamente o que a tecnologia assistiva já reporta melhor que qualquer barra — um role="progressbar" aqui só acrescentaria um anúncio a cada scroll.
Calibragem no primeiro uso. Espere decidir a cor: --accent é o padrão, mas em página onde o acento já é botão, olho e realce, o filete vira o quarto uso da mesma cor. Ver o padrão editorial acima.