← Biblioteca componentes/hero/full-bleed-media/ficha.md CódigoTela cheia
hero/full-bleed-media

Hero full-bleed com mídia, headline em linhas e régua de stats

🟢 estavel complexidade alta prefixo hero- hero above-the-fold imagem parallax stats institucional

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

<link rel="stylesheet" href="componentes/hero/full-bleed-media/estilo.css">
<script src="componentes/hero/full-bleed-media/script.js"></script>

markup.html · 50 linhas · 2,4 KB

<!-- ==========================================================================
     HERO — mídia full-bleed, headline em linhas, régua de stats
     Prefixo: hero-
     Requer: core/tokens.css, core/base.css, core/motion.css, core/motion.js
              + este estilo.css e este script.js
     A régua de stats é opcional: apague o bloco .hero-stats se não houver
     números para sustentar. Quatro itens é o teto — cinco viram lista.
     ========================================================================== -->

<section class="hero sec-dark" data-hero>
  <div class="hero-media">
    <img class="hero-img" data-hero-img src="../../../showroom/demo/ph-wide-1.webp" alt="">
    <div class="hero-scrim"></div>
    <div class="grain"></div>
    <div class="hero-fade"></div>
  </div>

  <div class="wrap hero-body">
    <div class="hero-copy">
      <span class="eyebrow">Consultoria independente</span>

      <!-- Cada linha da headline é uma .hero-ln com um <span> dentro:
           a máscara é o overflow da .hero-ln, o movimento é do span. -->
      <h1 class="hero-title">
        <span class="hero-ln"><span>Segurança no presente,</span></span>
        <span class="hero-ln"><span><em>liberdade</em> no futuro.</span></span>
      </h1>

      <p class="hero-desc">
        Uma consultoria independente à altura do seu legado. Protegemos, multiplicamos
        e organizamos a sucessão do patrimônio de famílias e empresários — com
        excelência técnica, discrição e visão de longo prazo.
      </p>

      <div class="hero-cta">
        <a href="#contato" class="btn btn-primary">
          Agende seu diagnóstico
          <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 7H12M8 3L12 7L8 11"/></svg>
        </a>
      </div>
    </div>

    <div class="hero-stats">
      <div><div class="hero-k">100%</div><div class="hero-v">Independente, sem vínculo<br>com instituições</div></div>
      <div><div class="hero-k">SP · POA</div><div class="hero-v">Sedes em São Paulo<br>e Porto Alegre</div></div>
      <div><div class="hero-k">Global</div><div class="hero-v">Estruturas onshore<br>e offshore</div></div>
      <div><div class="hero-k">CVM</div><div class="hero-v">Consultoria de valores<br>mobiliários autorizada</div></div>
    </div>
  </div>
</section>

estilo.css · 128 linhas · 5,0 KB

/* ==========================================================================
   HERO — mídia full-bleed, headline em linhas, régua de stats
   Prefixo: hero-
   Tokens usados: --bg --fg --fg-2 --fg-muted --rule --accent --dark --dark-rgb
                  --display --nav-h --ease
   ========================================================================== */

.hero{position:relative;overflow:hidden;background:var(--dark);min-height:100vh}

/* ---- mídia ----
   A imagem é maior que a caixa (top:-10% / height:120%) para ter margem de
   deslocamento no parallax sem nunca mostrar borda. */
.hero-media{position:absolute;inset:0;overflow:hidden}
.hero-img{
  position:absolute;top:-10%;left:0;width:100%;height:120%;
  object-fit:cover;object-position:50% 42%;
  will-change:transform;opacity:.46;
}

/* Dois gradientes cruzados: um horizontal que ancora o texto à esquerda,
   um vertical que segura o topo (menu) e o pé (emenda com a próxima seção). */
.hero-scrim{
  position:absolute;inset:0;
  background:
    linear-gradient(100deg,
      rgba(var(--dark-rgb),.92) 0%,
      rgba(var(--dark-rgb),.76) 34%,
      rgba(var(--dark-rgb),.34) 64%,
      rgba(var(--dark-rgb),.18) 100%),
    linear-gradient(to bottom,
      rgba(var(--dark-rgb),.5) 0%,
      rgba(var(--dark-rgb),0) 28%,
      rgba(var(--dark-rgb),0) 58%,
      rgba(var(--dark-rgb),.4) 100%);
}

/* Dissolve a foto no fundo da página. Sem isso aparece um corte reto entre
   o hero e a seção seguinte. */
.hero-fade{
  position:absolute;left:0;right:0;bottom:0;height:32%;
  background:linear-gradient(to bottom,
    rgba(var(--dark-rgb),0) 0%,
    rgba(var(--dark-rgb),.4) 48%,
    rgba(var(--dark-rgb),.86) 80%,
    rgb(var(--dark-rgb)) 100%);
}

/* ---- corpo ----
   Conteúdo ancorado embaixo: o topo fica para a foto respirar sob o menu. */
.hero-body{
  position:relative;z-index:2;min-height:100vh;
  display:flex;flex-direction:column;justify-content:flex-end;
  padding-top:calc(var(--nav-h) + clamp(48px,9vh,96px));
  padding-bottom:clamp(56px,9vh,104px);
}
.hero-copy{max-width:min(820px,100%)}

/* ---- headline ----
   Reveal por linha: a máscara é o overflow do .hero-ln, quem se move é o
   span de dentro. Não use data-split aqui — a quebra por palavra desmonta
   a força de uma headline curta. */
.hero-title{
  font-family:var(--display);font-weight:700;
  font-size:clamp(32px,5.4vw,62px);line-height:1.08;letter-spacing:-.035em;
  color:var(--fg);margin-top:22px;
}
.hero-ln{display:block;overflow:hidden}
.hero-ln > span{
  display:block;transform:translateY(112%);opacity:0;
  transition:transform .95s var(--ease),opacity .95s;
}
.hero.in .hero-ln > span{transform:none;opacity:1}
.hero .hero-ln:nth-child(2) > span{transition-delay:.09s}
.hero-title em{font-style:italic;font-weight:700;letter-spacing:-.01em;color:var(--accent)}

/* ---- apoio, CTA e stats ----
   Cascata em delays fixos: 0,55s / 0,78s / 0,95s. É o que dá a leitura de
   "a página se monta" em vez de "tudo apareceu junto". */
.hero-desc{
  font-size:17px;line-height:1.6;color:var(--fg-2);max-width:58ch;margin-top:26px;
  opacity:0;transform:translateY(14px);
  transition:opacity .8s ease .55s,transform .8s ease .55s;
}
.hero.in .hero-desc{opacity:1;transform:none}

.hero-cta{
  margin-top:34px;opacity:0;transform:translateY(14px);
  transition:opacity .8s ease .78s,transform .8s ease .78s;
}
.hero.in .hero-cta{opacity:1;transform:none}

.hero-stats{
  margin-top:clamp(44px,7vh,76px);
  display:grid;grid-template-columns:repeat(4,1fr);gap:24px;
  border-top:1px solid var(--rule);padding-top:26px;
  opacity:0;transform:translateY(14px);
  transition:opacity .8s ease .95s,transform .8s ease .95s;
}
.hero.in .hero-stats{opacity:1;transform:none}
.hero-k{font-family:var(--display);font-weight:700;font-size:19px;color:var(--accent);letter-spacing:-.02em}
.hero-v{font-size:12px;line-height:1.7;color:var(--fg-muted);margin-top:8px;letter-spacing:.02em}

/* ---- variação: banner de página interna ----
   Mesma linguagem visual, sem régua de stats nem CTA. */
.hero-compact{min-height:56vh}
.hero-compact .hero-body{min-height:56vh;padding-bottom:clamp(40px,6vh,64px)}
.hero-compact .hero-copy .eyebrow{margin-bottom:4px}

@media(max-width:760px){
  .hero-stats{grid-template-columns:1fr 1fr;row-gap:22px}
}

/* ==========================================================================
   SEM JAVASCRIPT, O CONTEUDO APARECE. §5 do contrato.

   Estas regras revelam por CLASSE PROPRIA (`.hero.in`), e nao pelo `[data-rv]` do
   core — por isso o `@media (scripting: none)` do core/motion.css nao as
   alcanca: ele cobre `[data-rv]`, `.w` e `.ttl`, e mais nada.

   Sem esta excecao, com o JavaScript desligado a HEADLINE e o apoio deste hero nunca apareciam. Medido com
   `node bin/medir.js <preview> --sem-js`: 3 nos com texto proprio em opacidade 0, entre eles as duas linhas do titulo
   ========================================================================== */
@media (scripting: none){
  .hero-ln > span,
  .hero-desc,
  .hero-cta,
  .hero-stats{opacity:1;transform:none;transition:none}
}

script.js · 43 linhas · 1,7 KB

/* ==========================================================================
   HERO — mídia full-bleed
   Faz duas coisas:
     1. dispara a entrada (.in) no primeiro frame após a pintura
     2. desloca a imagem no scroll (parallax), assinando o laço do core

   Não toca no menu. O estado "scrolled" da nav é responsabilidade do
   componente de navegação — acoplar os dois quebra páginas sem hero.
   ========================================================================== */

(function () {
  var hero = document.querySelector('[data-hero]');
  if (!hero) return;

  var img = hero.querySelector('[data-hero-img]');

  /* ---- entrada ----
     Dois rAF encadeados garantem que o estado inicial já pintou antes de
     ligar a transição — sem isso o texto às vezes aparece pronto.
     O setTimeout é a rede de segurança para aba aberta em background,
     onde o rAF não roda. */
  requestAnimationFrame(function () {
    requestAnimationFrame(function () { hero.classList.add('in'); });
  });
  setTimeout(function () { hero.classList.add('in'); }, 450);

  /* ---- parallax ----
     p = progresso de saída do hero, de 0 (topo) a 1 (todo rolado).
     0.28 do próprio height de deslocamento e 6% de escala: acima disso a
     imagem descola do texto e a leitura fica enjoativa. */
  if (!img || window.LIB.reduce) return;

  window.LIB.onScroll(function () {
    var r = hero.getBoundingClientRect();
    var h = hero.offsetHeight || 1;
    if (r.bottom < 0 || r.top > innerHeight) return;   // fora da tela: não calcula

    var p = Math.min(Math.max(-r.top / h, 0), 1.2);
    img.style.transform =
      'translate3d(0,' + (p * h * 0.28).toFixed(2) + 'px,0) ' +
      'scale(' + (1 + p * 0.06).toFixed(4) + ')';
  });
})();

Abertura de página inteira: foto sangrando na tela toda, escurecida por dois gradientes cruzados, com o conteúdo ancorado na base. A headline entra linha por linha, subindo por dentro de uma máscara; texto de apoio, CTA e régua de números vêm em cascata; a foto desloca em parallax enquanto a página rola.


Quando usar

Quando não usar


Padrão editorial

Slot Regra Referência
Olho (.eyebrow) 2 a 4 palavras. Categoria, não benefício. Opcional. "Consultoria independente"
Headline 2 linhas, uma .hero-ln cada. 3 a 6 palavras por linha. Uma palavra em <em> — só uma. "Segurança no presente, / liberdade no futuro."
Apoio (.hero-desc) 1 parágrafo, 25 a 45 palavras, teto de 58ch. Diz o que a empresa faz e para quem. —
CTA 1 botão. Verbo no imperativo + objeto. Nunca "Saiba mais". "Agende seu diagnóstico"
Stats 4 itens, ou nenhum. Chave curta (até 8 caracteres) + descrição em 2 linhas de até 30 caracteres. "100%" / "Independente, sem vínculo com instituições"

Sobre a régua de stats: ela existe para responder "por que confiar", não "o que vocês fazem". Números, praças, certificações, tempo de casa. Se os quatro itens não passarem nesse teste, apague o bloco — hero sem régua continua íntegro.

Sobre a headline: duas linhas é estrutural, não estilístico. A terceira linha quebra o ritmo do reveal (o nth-child(2) já carrega o delay) e empurra o apoio para fora da dobra em notebook.

Especificação da imagem


Como usar

<link rel="stylesheet" href="core/tokens.css">
<link rel="stylesheet" href="core/temas/<projeto>.css">
<link rel="stylesheet" href="core/base.css">
<link rel="stylesheet" href="core/motion.css">
<link rel="stylesheet" href="componentes/hero/full-bleed-media/estilo.css">
...
<script src="core/motion.js"></script>
<script src="componentes/hero/full-bleed-media/script.js"></script>

Cole o markup.html como primeiro filho do <body>, troque o src da imagem e o escopo de tom se o projeto não for escuro.

Ganchos

Gancho Papel
[data-hero] raiz — o script procura por ele, não por id
[data-hero-img] a imagem que recebe o parallax
.hero.in classe de entrada, adicionada pelo script

Variações


Gotchas


Histórico

Projeto Onde Data Diferenças
Liberta Wealth index.html #hero 2026-08 Origem. Classes .ln/.desc/.k/.v sem prefixo, ids hero/heroImg, parallax acoplado ao estado da nav, cores cruas rgba(6,6,6,…) no scrim.
Liberta Wealth quem-somos.html #hero 2026-08 Variação .hero-sm → virou .hero-compact aqui.

Normalizações aplicadas na extração: prefixo em todas as classes internas; ids trocados por data-*; scrim e fade passaram a ler --dark-rgb; textos e linhas passaram a ler --fg / --fg-2 / --fg-muted / --rule; sage cru virou --accent; parallax passou a assinar LIB.onScroll em vez de registrar listener próprio; desacoplado da nav.