← Biblioteca componentes/prova/marquee-logos/ficha.md CódigoTela cheia
prova/marquee-logos

Esteira infinita de logos

🟢 estavel complexidade baixa prefixo marquee- prova-social logos parceiros clientes loop css-puro sem javascript

Incluir · as linhas que se colam num site novo, depois do core (CLAUDE.md §10)

<link rel="stylesheet" href="componentes/prova/marquee-logos/estilo.css">

markup.html · 41 linhas · 2,2 KB

<!-- ==========================================================================
     MARQUEE — esteira infinita de logos
     Prefixo: marquee-
     Requer: core/tokens.css, core/base.css + este estilo.css
     Sem JavaScript.

     ESTRUTURA OBRIGATÓRIA: o .marquee-rail contém DOIS .marquee-track
     idênticos. A animação anda -50% do trilho, ou seja, exatamente a
     largura de um track — é o que fecha o loop sem salto. A segunda cópia
     é decorativa: aria-hidden e alt vazio, senão o leitor de tela anuncia
     a lista duas vezes.
     ========================================================================== -->

<div class="marquee-strip sec-dark">
  <div class="wrap">
    <p class="marquee-label">Atuamos na instituição financeira de sua preferência</p>
  </div>

  <div class="marquee">
    <div class="marquee-rail">
      <div class="marquee-track">
        <img class="marquee-logo" src="../../../showroom/demo/logo-1.svg" alt="Meridian">
        <img class="marquee-logo" src="../../../showroom/demo/logo-2.svg" alt="Vantor">
        <img class="marquee-logo marquee-logo--tall" src="../../../showroom/demo/logo-selo.svg" alt="Quadrant">
        <img class="marquee-logo" src="../../../showroom/demo/logo-3.svg" alt="Altamira">
        <img class="marquee-logo" src="../../../showroom/demo/logo-4.svg" alt="Kessler">
        <img class="marquee-logo" src="../../../showroom/demo/logo-5.svg" alt="Northbay">
        <img class="marquee-logo" src="../../../showroom/demo/logo-6.svg" alt="Solvane">
      </div>
      <div class="marquee-track" aria-hidden="true">
        <img class="marquee-logo" src="../../../showroom/demo/logo-1.svg" alt="">
        <img class="marquee-logo" src="../../../showroom/demo/logo-2.svg" alt="">
        <img class="marquee-logo marquee-logo--tall" src="../../../showroom/demo/logo-selo.svg" alt="">
        <img class="marquee-logo" src="../../../showroom/demo/logo-3.svg" alt="">
        <img class="marquee-logo" src="../../../showroom/demo/logo-4.svg" alt="">
        <img class="marquee-logo" src="../../../showroom/demo/logo-5.svg" alt="">
        <img class="marquee-logo" src="../../../showroom/demo/logo-6.svg" alt="">
      </div>
    </div>
  </div>
</div>

estilo.css · 181 linhas · 6,7 KB

/* ==========================================================================
   MARQUEE — esteira infinita de logos
   Prefixo: marquee-
   Tokens usados: --bg --fg-muted --sec-gap
   Sem JavaScript. O loop é CSS puro: o trilho anda -50%, que é a largura
   exata de um dos dois tracks idênticos.
   ========================================================================== */

.marquee-strip{
  background:var(--bg);
  overflow:hidden;
  /* 16px a menos embaixo: os logos já têm respiro óptico próprio e a
     seção seguinte encosta bem assim. */
  padding:var(--sec-gap) 0 calc(var(--sec-gap) - 16px);

  --marquee-dur:40s;        /* 7 logos ≈ 40s. Mais logos, mais tempo. */
  --marquee-gap:64px;
  --marquee-h:26px;         /* altura padrão de wordmark */
}

.marquee-label{
  font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--fg-muted);text-align:center;
  margin-bottom:clamp(32px,5vh,48px);
}

.marquee{overflow:hidden}

.marquee-rail{
  display:flex;
  width:max-content;
  animation:marqueeRun var(--marquee-dur) linear infinite;
}

.marquee-track{
  display:flex;align-items:center;
  gap:var(--marquee-gap);
  padding-right:var(--marquee-gap);   /* mantém o gap na emenda entre as cópias */
}

.marquee-logo{
  height:var(--marquee-h);
  width:auto;max-width:140px;
  object-fit:contain;
  opacity:.5;transition:opacity .3s;
}
.marquee-logo:hover{opacity:1}

/* Selo quadrado, não wordmark: a 26px de altura ficaria minúsculo ao lado
   dos logos largos. Precisa de mais altura para pesar o mesmo na fileira.
   Marque na classe, nunca por seletor de alt — o alt é conteúdo, não gancho. */
.marquee-logo--tall{height:calc(var(--marquee-h) * 1.55)}

/* Pausa no hover: quem quer ler um nome consegue parar a esteira. */
.marquee-strip:hover .marquee-rail{animation-play-state:paused}

/* ---- Variação: retratos (.marquee-strip--retratos) ------------------
   A mesma esteira, com gente no lugar de marca: retratos redondos de quem
   atende (ou, com autorização por escrito, de clientes), e o nome numa
   etiqueta que aparece sob o retrato quando o ponteiro para nele. A
   esteira já pausa no hover, então o nome fica parado para ser lido.

   Referência: a fila de retratos do portfólio do Dudes Becker
   (portfolio.dudesbecker.com). O retrato lá CRESCE 5% no hover; aqui não
   (CLAUDE.md §6) — o retorno é a cor voltando à foto e a etiqueta
   acendendo, nada muda de tamanho.

   O NOME É TEXTO NO DOM, não só alt: a etiqueta está sempre na árvore (só
   a opacidade muda), e o leitor de tela lê o nome sem depender de hover.
   Por isso o <img> tem alt vazio — com alt e etiqueta, o nome seria lido
   duas vezes. */
/* Duas classes no seletor: a media query de 640px lá embaixo reescreve
   --marquee-gap em .marquee-strip, e com uma classe só a variação perderia
   para ela no celular — medido, o vão dos retratos voltava a 40px. */
.marquee-strip.marquee-strip--retratos{
  /* Velocidade, não duração, é o que o olho lê: o track de logos anda
     ~36px/s (1430px em 40s). Retrato pede mais devagar — ~18px/s, que com
     seis pessoas (540px) dá 30s por volta. Mais pessoas, mais tempo. */
  --marquee-dur:30s;
  --marquee-gap:14px;
  --marquee-face:76px;
}
/* A JANELA. Na largura da tela, seis retratos (540px por track) deixavam
   metade da faixa vazia no laço de -50% — medido a 1440px. A referência
   resolve com uma janela estreita centrada sob o rótulo (524px lá), e é a
   leitura certa: gente se olha de perto, não passa como logo de patrocínio.
   A REGRA DE CONTA: a largura de UM .marquee-track tem de passar da janela.
   Com retrato de 76px e vão de 14px, são 90px por pessoa: seis pessoas
   fecham uma janela de até 540px.
   As pontas esmaecem por máscara horizontal, como no
   prova/carrossel-depoimentos — o retrato entra e sai em vez de ser
   cortado pela borda da janela. Uma linha por máscara: o #000 é opacidade
   (ver bin/conformidade.py). */
.marquee-strip--retratos .marquee{
  max-inline-size:var(--marquee-janela,480px);
  margin-inline:auto;
  -webkit-mask-image:linear-gradient(to right,transparent 0%,#000 14%,#000 86%,transparent 100%);
  mask-image:linear-gradient(to right,transparent 0%,#000 14%,#000 86%,transparent 100%);
}
/* Espaço embaixo do trilho para a etiqueta, que é absoluta e sairia do
   .marquee (overflow:hidden) cortada. */
.marquee-strip--retratos .marquee-rail{padding-block-end:34px}
.marquee-face{
  position:relative;
  /* <figure> chega com margin:1em 40px da folha do navegador: sem zerar, o
     vão entre retratos era 14px de gap mais 80px de margem. */
  margin:0;
  flex:0 0 auto;
  display:grid;
  justify-items:center;
}
.marquee-face-img{
  inline-size:var(--marquee-face);
  block-size:var(--marquee-face);
  border-radius:var(--radius-pill);
  object-fit:cover;
  /* Rosto no terço de cima: o recorte redondo de um retrato 3:4 centrado
     cortaria a testa e mostraria o peito. */
  object-position:50% 22%;
  filter:grayscale(1) brightness(.88);
  transition:filter .3s;
}
.marquee-face:hover .marquee-face-img{filter:none}
.marquee-face-nome{
  position:absolute;
  inset-block-start:calc(100% + 8px);
  white-space:nowrap;
  font-size:10px;
  letter-spacing:.08em;
  line-height:1;
  padding:5px 10px;
  border-radius:var(--radius-pill);
  background:var(--fg);
  color:var(--bg);
  opacity:0;
  transition:opacity .25s;
  pointer-events:none;
}
.marquee-face:hover .marquee-face-nome{opacity:1}

@keyframes marqueeRun{
  from{transform:translateX(0)}
  to{transform:translateX(-50%)}
}

@media(prefers-reduced-motion:reduce){
  .marquee-rail{animation:none}
  .marquee-logo{opacity:.72}
  .marquee-face-img,
  .marquee-face-nome{transition:none}
}

/* Toque não tem hover (MANUAL §5): a etiqueta que só existia sob o ponteiro
   nunca apareceria. No toque o nome desce para o fluxo, embaixo do retrato,
   sem fundo — e o vão cresce para os nomes não encostarem uns nos outros.
   A foto volta em cor: não há hover que a acenda.
   MOVIMENTO REDUZIDO ENTRA NA MESMA REGRA, e foi o bin/varredura.js que
   pegou: com a esteira parada, a etiqueta de hover deixava seis nomes com
   opacidade zero que só um ponteiro revelaria. Faixa parada é fileira de
   retratos, e fileira de retratos mostra os nomes. */
@media(hover:none),(prefers-reduced-motion:reduce){
  .marquee-strip.marquee-strip--retratos{--marquee-gap:22px}
  .marquee-strip--retratos .marquee-rail{padding-block-end:0}
  .marquee-face{gap:8px}
  .marquee-face-img{filter:none}
  .marquee-face-nome{
    position:static;
    opacity:1;
    background:none;
    color:var(--fg-muted);
    padding:0;
    white-space:normal;
    text-align:center;
    max-inline-size:calc(var(--marquee-face) + 18px);
    line-height:1.3;
  }
}

@media(max-width:640px){
  .marquee-strip{--marquee-gap:40px;--marquee-h:22px}
}

Faixa de prova social: rótulo curto centralizado e uma fileira de logos deslizando em loop contínuo, pausando no hover. CSS puro, zero JavaScript.


Quando usar

Quando não usar


Padrão editorial

Slot Regra
.marquee-label 1 linha, 4 a 9 palavras, caixa alta por CSS. Enquadra o que a fileira prova. Não é título de seção — não use h2.
Ordem dos logos Os mais conhecidos primeiro e por volta do meio. O visitante pega a esteira em qualquer ponto.

Exemplos de rótulo que funcionam: "Atuamos na instituição financeira de sua preferência", "Integra com as ferramentas que você já usa", "Presente em". Rótulo que não funciona: "Parceiros" — não diz nada que a fileira já não diga.

Especificação dos logos


Como usar

<link rel="stylesheet" href="componentes/prova/marquee-logos/estilo.css">

Sem script. Cole o markup entre duas seções.

Estrutura obrigatória

O .marquee-rail precisa conter dois .marquee-track idênticos. A animação anda -50% do trilho, que é exatamente a largura de um track — é isso que fecha o loop sem salto. A segunda cópia leva aria-hidden="true" e alt="", senão o leitor de tela lê a lista duas vezes.

Ajustes por variável

Todos no .marquee-strip, sem tocar no resto do CSS:

Variável Padrão Para quê
--marquee-dur 40s Velocidade. Regra prática: ~5s por logo.
--marquee-gap 64px Respiro entre logos.
--marquee-h 26px Altura base do wordmark.

Variações


Gotchas


Histórico

Projeto Onde Data Diferenças
referência: Dudes Becker https://portfolio.dudesbecker.com/portfolio — a fila de retratos sob o título "Portfolio" 2026-10-01 variação retratos: o zoom de 5% no hover saiu (§6), a etiqueta virou <figcaption> sempre no DOM, a janela estreita e a máscara das pontas vieram da referência, e o toque ganhou a etiqueta no fluxo
Liberta Wealth index.html, entre "Wealth planning" e "Valores" 2026-08 Origem. Wrapper .strip, rótulo .strip-label, .marquee fazia o papel de trilho animado, altura do selo por img[alt="Itaú"], sem tratamento de movimento reduzido.

Normalizações aplicadas na extração: prefixo único em tudo; separação de papéis em três níveis (.marquee-strip corta / .marquee recorta / .marquee-rail anda); velocidade, gap e altura viraram variáveis locais; hack de alt virou classe modificadora; adicionado prefers-reduced-motion; adicionado breakpoint mobile.