← Biblioteca componentes/listas/baralho-sticky/ficha.md CódigoTela cheia
listas/baralho-sticky

Baralho de cards que se empilham sobre foto pinada

🟢 estavel complexidade alta prefixo deck- etapas metodo processo sticky scroll vidro pilha

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

<link rel="stylesheet" href="componentes/listas/baralho-sticky/estilo.css">
<script src="componentes/listas/baralho-sticky/script.js"></script>

markup.html · 59 linhas · 3,5 KB

<!-- ==========================================================================
     BARALHO STICKY — cards que se empilham sobre uma foto pinada
     Prefixo: deck-
     Requer: core (tokens, base, motion.css, motion.js) + estilo.css e script.js
     Usa a primitiva .glass do core.

     Duas camadas de sticky ao mesmo tempo:
       .deck-stage  fica pinado por uma tela inteira e segura a foto
       .deck-card   cada um gruda um pouco mais abaixo que o anterior

     A ordem de empilhamento vem do HTML, por variável:
       style="--deck-i:0;--deck-z:1"   (i = posição na pilha, z = z-index)
     Numere sempre a partir de 0 e em sequência.
     ========================================================================== -->

<section class="deck-sec sec-dark sec-pad" id="metodo">
  <div class="deck-stage">
    <div class="deck-bg"><img src="../../../showroom/demo/ph-wide-3.webp" alt="Veleiro solitário em mar aberto sob céu nublado"></div>
    <div class="grain"></div>
  </div>

  <div class="wrap">
    <div class="sec-head deck-head">
      <span class="eyebrow" data-rv>Como atuamos</span>
      <h2 class="sec-title ttl">Trilhe o seu caminho para a <em>liberdade</em></h2>
      <p class="deck-lead" data-rv data-d="1">Sem uma Política de Investimentos clara, alocar recursos é como navegar sem bússola em mar aberto.</p>
    </div>

    <div class="deck" data-deck>
      <div class="deck-list">
        <article class="glass deck-card" style="--deck-i:0;--deck-z:1">
          <span class="pill">01 · Diagnóstico inicial</span>
          <h4 class="deck-title">Mapeamento da estrutura atual</h4>
          <p class="deck-text">Estrutura familiar e patrimonial — composição, relacionamento e objetivos — somada ao seu perfil de investidor: tolerância ao risco, necessidade de liquidez e expectativa de retorno.</p>
        </article>
        <article class="glass deck-card" style="--deck-i:1;--deck-z:2">
          <span class="pill">02 · Objetivos</span>
          <h4 class="deck-title">Mapeamento de objetivos futuros</h4>
          <p class="deck-text">Identificamos seu momento de vida e suas prioridades: aposentadoria, preservação de patrimônio, acúmulo de capital, residência fiscal, sucessão e legado.</p>
        </article>
        <article class="glass deck-card" style="--deck-i:2;--deck-z:3">
          <span class="pill">03 · Estratégia</span>
          <h4 class="deck-title">Diagnóstico e arquitetura financeira</h4>
          <p class="deck-text">Do objetivo à arquitetura financeira personalizada: avaliação do patrimônio consolidado onshore e offshore e definição das metas que orientam cada decisão.</p>
        </article>
        <article class="glass deck-card" style="--deck-i:3;--deck-z:4">
          <span class="pill">04 · Política de Investimentos</span>
          <h4 class="deck-title">Implementação e alocação</h4>
          <p class="deck-text">Documento que detalha a alocação estratégica — sucessão, investimentos onshore e offshore, gestão de liquidez e fluxo. É a base para execução, monitoramento e decisões futuras.</p>
        </article>
        <article class="glass deck-card" style="--deck-i:4;--deck-z:5">
          <span class="pill">05 · Gestão</span>
          <h4 class="deck-title">Acompanhamento contínuo</h4>
          <p class="deck-text">Investimentos alinhados à Política, alocação orientada ao cliente e revisões periódicas do portfólio — o plano acompanha a vida, não o contrário.</p>
        </article>
      </div>
    </div>
  </div>
</section>

estilo.css · 95 linhas · 4,2 KB

/* ==========================================================================
   BARALHO STICKY — cards que se empilham sobre uma foto pinada
   Prefixo: deck-
   Tokens usados: --fg --fg-2 --fg-muted --rule-2 --dark --dark-rgb
                  --display --nav-h --ease
   Primitiva usada: .glass (core/base.css)
   ========================================================================== */

.deck-sec{position:relative}

/* ---- o palco ----
   sticky a top:0 com height:100vh limita a altura VISÍVEL da foto a uma
   tela, por mais alta que a seção fique por causa dos cards.
   O margin-bottom negativo puxa o .wrap de volta para cima, sobrepondo o
   palco. O overflow:hidden mora AQUI e não na seção: na seção ele quebraria
   o sticky dos cards, que ficam fora deste bloco. */
.deck-stage{
  position:sticky;top:0;height:100vh;overflow:hidden;
  margin-bottom:calc(-100vh);z-index:0;
}
.deck-bg{position:absolute;inset:0}
.deck-bg img{width:100%;height:100%;object-fit:cover}

/* Vinheta nas quatro bordas: a foto dissolve no fundo em vez de cortar reto.
   O reforço diagonal garante leitura por trás dos cards de vidro, que ficam
   à esquerda. */
.deck-bg::after{
  content:"";position:absolute;inset:0;
  background:
    radial-gradient(120% 95% at 62% 55%,transparent 40%,rgba(var(--dark-rgb),.6) 86%,var(--dark) 100%),
    linear-gradient(105deg,rgba(var(--dark-rgb),.92) 0%,rgba(var(--dark-rgb),.55) 32%,transparent 56%),
    linear-gradient(to bottom,rgb(var(--dark-rgb)) 0%,rgba(var(--dark-rgb),.7) 14%,transparent 34%,transparent 62%,rgb(var(--dark-rgb)) 100%);
}
.deck-sec > .wrap{position:relative;z-index:2}

/* O sec-pad da seção não segura folga suficiente aqui: o título fica sobre o
   palco pinado e precisa de respiro próprio contra o topo. */
/* Só o respiro do topo: centralização vem de .sec-head do core. */
.deck-head{padding-top:clamp(28px,7vh,80px)}
.deck-head .sec-title{max-width:none;margin-inline:auto}   /* 22ch quebraria a linha */
.deck-head .eyebrow{margin-inline:auto}
.deck-lead{font-size:17px;line-height:1.6;color:var(--fg-2);margin-top:18px;margin-inline:auto}

/* ---- a pilha ----
   Cada card gruda um pouco mais abaixo que o anterior (--deck-i * --deck-peek),
   então eles se empilham deixando só uma faixa do anterior à mostra. O
   z-index cresce junto, então o card mais novo sempre cobre os antigos. */
.deck{
  margin-top:clamp(46px,7vh,80px);
  --deck-top:calc(var(--nav-h) + clamp(16px,4vh,40px));
  --deck-peek:14px;
}
.deck-list{
  display:flex;flex-direction:column;gap:clamp(16px,2.2vh,26px);
  max-width:520px;padding-bottom:clamp(40px,8vh,90px);
}

/* --deck-p (0 a 1) é o quanto este card já foi coberto pelo seguinte.
   Quem calcula é o script; o CSS só o consome para encolher e apagar. */
.deck-card{
  position:sticky;
  top:calc(var(--deck-top) + var(--deck-i) * var(--deck-peek));
  z-index:var(--deck-z);
  border-radius:14px;padding:clamp(24px,2.6vw,34px);
  --glass-bg:rgba(var(--dark-rgb),.55);
  transform-origin:top center;
  transform:scale(calc(1 - .035 * var(--deck-p,0)));
  transition:transform .25s linear;
}
.deck-card > *{transition:opacity .25s linear}
.deck-card.is-stacked > .deck-title,
.deck-card.is-stacked > .deck-text{opacity:calc(1 - .6 * var(--deck-p,0))}


.deck-title{
  font-family:var(--display);font-weight:700;font-size:clamp(18px,2vw,23px);
  letter-spacing:-.025em;line-height:1.25;color:var(--fg);margin-top:16px;
}
.deck-text{font-size:17px;line-height:1.6;color:var(--fg-2);margin-top:12px}

/* ---- mobile ----
   Sem pilha: os cards viram uma lista comum e o palco vira um bloco de
   proporção fixa no fluxo. Empilhar em tela pequena esconderia o conteúdo. */
@media(max-width:900px){
  .deck-stage{position:relative;height:auto;aspect-ratio:4/3.3;margin:0;overflow:hidden}
  .deck-bg img{filter:grayscale(.45) brightness(.62)}
  .deck-bg::after{background:linear-gradient(to bottom,transparent 45%,var(--dark) 96%)}
  .deck-head{padding-top:0;margin-top:-64px;position:relative;z-index:2}
  .deck-list{max-width:none}
  .deck-card{position:static;transform:none}
  .deck-card.is-stacked > .deck-title,
  .deck-card.is-stacked > .deck-text{opacity:1}
}

@media(prefers-reduced-motion:reduce){.deck-card{transform:none}}

script.js · 44 linhas · 1,9 KB

/* ==========================================================================
   BARALHO STICKY — profundidade da pilha
   O empilhamento em si é CSS (position:sticky com top escalonado). Aqui só
   medimos o quanto cada card já foi coberto pelo seguinte, para encolher e
   apagar na medida certa — é isso que dá a leitura de profundidade.
   ========================================================================== */

(function () {
  document.querySelectorAll('[data-deck]').forEach(function (deck) {
    var lista = deck.querySelector('.deck-list');
    if (!lista) return;
    var cards = [].slice.call(lista.querySelectorAll('.deck-card'));
    if (cards.length < 2) return;

    /* Movimento reduzido: a pilha continua empilhando (isso é layout, e o
       position:sticky não é animação), mas o encolher-e-apagar sai de cena.
       É ele que produz a sensação de profundidade em movimento contínuo,
       exatamente o que a preferência pede para evitar. */
    if (window.LIB.reduce) return;

    window.LIB.onScroll(function () {
      /* No mobile os cards são estáticos: não há pilha para medir. */
      if (innerWidth <= 900) return;

      var peek = parseFloat(getComputedStyle(deck).getPropertyValue('--deck-peek')) || 14;

      for (var i = 0; i < cards.length - 1; i++) {
        var card = cards[i], proximo = cards[i + 1];

        /* Distância entre o topo dos dois. Quando nada está empilhado ela
           vale a altura do card; conforme o próximo sobe, encolhe até a
           faixa visível (peek). */
        var vao = proximo.getBoundingClientRect().top - card.getBoundingClientRect().top;
        var altura = card.offsetHeight;

        var p = (altura - vao) / Math.max(altura - peek, 1);
        p = p < 0 ? 0 : p > 1 ? 1 : p;

        card.style.setProperty('--deck-p', p.toFixed(3));
        card.classList.toggle('is-stacked', p > 0.02);
      }
    });
  });
})();

Etapas de um método apresentadas como cards de vidro que sobem com a página e grudam no topo em alturas escalonadas — cada um deixando aparecer uma faixa de 14px do anterior. Conforme é coberto, o card de baixo encolhe e apaga, o que dá profundidade real à pilha. Tudo isso sobre uma foto que fica pinada por exatamente uma tela.

É o componente mais elaborado da biblioteca. Duas camadas de sticky operando ao mesmo tempo.


Quando usar

Quando não usar


Padrão editorial

Slot Regra
Título da seção 1 linha, uma palavra em <em>. Centralizado.
.deck-lead 1 frase, 15 a 25 palavras. Costuma ser a metáfora que a foto ilustra.
.deck-tag 01 · Nome da fase. O número e o nome curto do estágio, 1 a 3 palavras.
.deck-title 3 a 6 palavras. O que acontece na etapa: "Mapeamento da estrutura atual".
.deck-text 1 frase, 25 a 40 palavras. Todos os cards com comprimento parecido.

Comprimento parecido é estrutural. O --deck-peek é fixo em 14px: se um card tem o dobro da altura do outro, a faixa que sobra fica desproporcional e a pilha desanda.

Especificação da imagem


Como usar

<link rel="stylesheet" href="componentes/listas/baralho-sticky/estilo.css">
<script src="componentes/listas/baralho-sticky/script.js"></script>

Cada card declara sua posição na pilha por variável, a partir de 0 e em sequência:

<article class="glass deck-card" style="--deck-i:0;--deck-z:1">
<article class="glass deck-card" style="--deck-i:1;--deck-z:2">

Variações


Gotchas


Histórico

Projeto Onde Data Diferenças
Liberta Wealth index.html #metodo 2026-08 Origem, com 5 etapas. Prefixos misturados: .jrn-sec, .jrn-stage, .jrn-bg, .jrn-head, .jrn-lead, .jrn, .jrn-deck e .jcard (sem hífen), .jtag, variáveis --i/--z/--p/--peek/--jtop, título por h4, vidro escrito inline no componente, script com id #jrnDeck e listener próprio de scroll.

Normalizações aplicadas na extração: prefixo único deck- (a origem usava jrn- e j- misturados); variáveis genéricas --i/--z/--p → --deck-i/--deck-z/--deck-p, que colidiriam com qualquer outro componente que usasse nomes curtos; h4 → .deck-title; o vidro virou a primitiva .glass, com --glass-bg local para o preto translúcido; id → [data-deck]; o script passou a assinar LIB.onScroll em vez de registrar listener próprio.

No catálogo original esta seção aparecia duas vezes — como listas/trilha-vertical e como listas/baralho-empilhado. São a mesma coisa: o comentário do CSS chamava de "baralho que se recolhe" e o do HTML, de "trilha vertical". Ficou um componente só.