Saltar al contenido principal

Altura de escena e indicadores de progreso

Estos dos patrones amplían la escena anclada básica: uno hace que la altura de una escena anclada siga la cantidad de elementos que realmente contiene, el otro construye una barra fina que se llena con el progreso de cualquier escena o rango de scroll.

Altura de escena que sigue el número de elementos​

Con 3 pasos o con 12, cada uno debería recibir la misma cantidad de scroll para reproducir su propia animación. En lugar de fijar una altura de escena a mano, esta receta mide cuántos elementos hay realmente presentes y deriva de ahí la altura correcta — y recalcula el progreso de la ventana anclada para exactamente esa altura.

Estructura:

section .scene-section reinicio de overflow-x, padding 0
└─ div .scene su propia fila Custom, sin Start/End (sin "Get parent")
└─ div .scene__stage sticky (ver Escenas ancladas)
└─ … .scene__item ×n los elementos que se cuentan, a cualquier profundidad

Los hijos leen el --fa-scroll--custom de la escena por herencia; no necesitan su propia fila.

CSS:

.scene {
position: relative;
width: 100%;
--sc-x: 4;
--sc-h: calc(var(--fa-scroll--custom) * 0vh + var(--sc-x) * 100vh);
height: var(--sc-h, auto);
}
.scene:has(.scene__item:nth-child(2)) { --sc-x: 2.2; }
.scene:has(.scene__item:nth-child(3)) { --sc-x: 2.8; }
.scene:has(.scene__item:nth-child(4)) { --sc-x: 3.4; }
.scene:has(.scene__item:nth-child(5)) { --sc-x: 4; }
.scene:has(.scene__item:nth-child(6)) { --sc-x: 4.6; }
/* … X = 1 + .6 × k, continuando hasta k = 12 (X = 8.2); mantenlas en orden ascendente, gana la última coincidencia */

.scene__stage {
--sc-q: clamp(0, (var(--fa-scroll--custom) * (var(--sc-x) + 1) - 1) / (var(--sc-x) - 1), 1);
}
  • Una escena que mide X pantallas de alto se ancla desde el progreso 1/(X+1) hasta X/(X+1). --sc-q reasigna exactamente ese tramo a un limpio 0 → 1, sea cual sea el X resultante.
  • :has(.scene__item:nth-child(k)) coincide siempre que haya al menos k elementos. Sin soporte de :has(), X simplemente se queda en su valor por defecto de 4.
  • Sin la animación en marcha, --sc-h se vuelve inválida y la escena recurre a su altura natural — el escenario simplemente no queda anclado, y su contenido fluye como una sección normal.
  • El elemento i (desde 0) de n en total está "actual" mientras --sc-q × n se sitúa entre i y i + 1. Con "Index children" en el propio padre de los elementos, --fa-i y --fa-n te dan i y n directamente.

Valores que puedes cambiar: el multiplicador .6 (pantallas de scroll por elemento), y el --sc-x por defecto (4, usado como respaldo en navegadores sin soporte de :has()).

  • Escena: añade una fila Custom sin Start/End. Si sus hijos necesitan suavizado del scroll, ajústalo aquí también, ya que heredan el valor suavizado.
  • Las reglas :has() van en el CSS personalizado propio de la escena, referenciando el nombre de clase real del elemento.

Problemas comunes:

  • El progreso ajustado a ojo se descuadra en cuanto se añade o quita un elemento. Las ventanas se fijaron a mano en lugar de derivarlas de --sc-q y del índice del elemento — deriva todo de esos valores en su lugar.
  • El escenario se corta en pantallas bajas dentro del constructor. Se usó una altura fija en lugar del patrón de respaldo var(--sc-h, auto).
  • --sc-q da un valor que no cuadra. El propio elemento de la escena estaba transformado, o se está midiendo a través de "Get parent" desde otro sitio — debe medirse a sí mismo, y permanecer sin transformar.

Indicador de progreso​

Una barra fina que se llena de 0% a 100% a medida que una escena se reproduce, o a medida que una lista larga pasa de largo junto a una columna lateral fija.

Estructura (horizontal, dentro de una escena anclada):

div .scroll-progress centra la barra
└─ div .scroll-progress__bar el carril: tamaño fijo, overflow hidden
└─ div .scroll-progress__fill "Get parent" + una fila Custom — la misma ventana que la animación que reporta

Estructura (vertical, junto a una lista que hace scroll normal):

section .list-progress "I am parent", reinicio de overflow-x
└─ div .list-progress__row fila flex
├─ div .list-progress__content la lista larga
└─ div .list-progress__side columna fija (sticky), una pantalla de alto
└─ .scroll-progress__bar > .scroll-progress__fill "Get parent" + Custom, Start 20, End 80

CSS:

.scroll-progress { display: flex; justify-content: center; }
.scroll-progress__bar {
width: 300px; height: 3px;
background: #d4d4d8; border-radius: 10em; overflow: hidden;
}
.scroll-progress__fill {
height: 100%; min-width: 0;
background: var(--primary, #2563eb);
width: calc(var(--fa-scroll--custom, 0) * 100%);
}

/* variante vertical */
.list-progress__row { display: flex; justify-content: space-between; }
.list-progress__content { width: calc(100% - 4rem); }
.list-progress__side {
position: sticky; top: 0; height: 100vh; width: 1rem;
display: flex; align-items: center; justify-content: center;
}
.list-progress__side .scroll-progress__bar { width: 4px; height: 180px; }
.list-progress__side .scroll-progress__fill { width: 100%; min-height: 0; height: calc(var(--fa-scroll--custom, 0) * 100%); }

El elemento de relleno también puede prescindir de su propia fila y simplemente heredar --fa-scroll--custom de un ancestro que ya tenga una — el valor es el mismo de cualquier forma.

Valores que puedes cambiar: usa el mismo Start/End (y el mismo valor de suavizado del scroll) que la animación que la barra está reportando, o ambas se irán desincronizando visiblemente. Para la pista horizontal anclada en concreto, usa en su lugar la barra de progreso incorporada en esa receta — la altura de su escena sigue las tarjetas directamente, así que una ventana fija aquí se desincronizaría de ella.

  • Fill: "Get parent" + una fila Custom con el Start/End que estás reportando.
  • La barra y el fill son elementos vacíos — dale a sus clases un ancho/alto mínimo explícito de 0, o el lienzo del constructor los muestra con su tamaño de marcador de posición por defecto de 90px.
  • Variante vertical: la sección necesita "I am parent" y el reinicio de overflow-x; las reglas descendientes .list-progress__side … van en el CSS personalizado propio de la columna lateral, escritas completas.

Problemas comunes:

  • La barra está llena desde el principio, o nunca se mueve. Falta "Get parent" (así que se mide a sí misma, lo cual dentro de un escenario anclado nunca cambia), o falta "I am parent" por encima.
  • Va por delante, o por detrás, de la animación que debería seguir. Una ventana distinta, o un valor de suavizado del scroll distinto, al de lo que está reportando.
  • La columna lateral no se queda fija. Falta el reinicio de overflow-x en la sección, o la columna no mide una pantalla completa de alto.
  • El fill muestra una franja visible incluso al 0%. Un ancho/alto mínimo por defecto del constructor en un elemento vacío — ajústalo explícitamente a 0.