Landing usando Display Grid y Position Sticky

Landing ficticia tipo Single Page usando Display grid para contenedores y posicionamiento Sticky
Landing usando display grid y position sticky

En esta ocasión he construido una landing con la idea de practicar con display grid y position sticky Al final he he ido metiendo más cositas, como menú lateral tipo navigation drawer, customización de Scroll, animación de scroll en los enlaces internos y animación de texto "spliteado".

Para la animación de menú me he basado en algo muy chulo que está publicado en Codrops y para la customización de Scroll este Codepen

Aquí puede verse la Landing

Comentaré brevemente:

  • CSS grid y sticky
  • Animación de textos

CSS grid y sticky

He creado cuatro secciones diferentes en las que iba colocando cajas con texto y cajas para imágenes.

La creación de filas/columnas la hice mediante grid-template-columns y grid-template-rows y cada caja dentro tomaba los tamaños en función de la cuadrícula en cada caso

El posicionamiento sticky lo he añadido mediante dos clases txt--sticky-top y txt--sticky-bottom que serán válidas según el tamaño de pantalla mínimo que me interese. La primera tiene el valor top: 0 y la segunda bottom: 0 para que se alineen dentro en la cara que interese dentro de su contenedor al hacer scroll. Existe un caso, que por defecto la caja de texto está centrado verticalmente respecto de su elemento padre, para este caso he usado ambas clases conjuntamente

El CSS del primer ejemplo sería:

.container-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, minmax(300px, auto));
  &--schema-1 {
    .div1 { grid-area: 1 / 1 / 3 / 2; }
    .div2 { grid-area: 1 / 2 / 3 / 3; }
    .div3 { grid-area: 1 / 3 / 2 / 4; }
    .div4 { grid-area: 1 / 4 / 2 / 5; }
    .div5 { grid-area: 2 / 3 / 3 / 4; }
    .div6 { grid-area: 2 / 4 / 3 / 5; }
  }
}
.txt {
  &--sticky-top {
    position: sticky;
    top: 0;
  }
  &--sticky-bottom {
    position: sticky;
    bottom: 0;
  }
  //Resto de código
}

Para la parte de HTML he añadido las imágenes como background

El HTML del primer ejemplo

<section class="container-grid container-grid--schema-1">
  <article class="div1 txt txt--right">
    <h4 class="title-box">Título de la caja principal</h4>
    <p class="txt-box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ea explicabo laboriosam nulla inventore quasi accusamus, obcaecati impedit natus nisi est, voluptatibus aliquid porro exercitationem maxime modi, sequi distinctio at? Totam!</p>
  </article>
  <div class="div2 bg" style="background-image: url(img/underwater.jpg);"></div>
  <div class="div3 bg" style="background-image: url(img/marigold.jpg);"></div>
  <article class="div4 txt txt--scnd txt--sticky-top">
    <h4 class="title-box">Título</h4>
    <h5 class="txt-box">Subtítulo, breve descripción con Heading 3</h5>
  </article>
  <article class="div5 txt txt--scnd txt--sticky-bottom">
    <h4 class="title-box">Titulo</h4>
    <h5 class="txt-box">Subtítulo, breve descripción con Heading 3</h5>
  </article>
  <div class="div6 bg" style="background-image: url(img/kingfisher.jpg);"></div>
</section>

Animación de textos

La animación de entrada de textos en el "Slider" lo ha realizado:

  1. Aplicando con JS split de cada letra
  2. Animación de entradas y salidas de textos con CSS keyframes
  3. Cada una de las letras tendrá un "delay", tanto en entrada como en salida

Las funciones JS para realizar el Split y añadir clases, atributos y estilos son las siguientes:

const elFactory = (type, attributes, ...children) => {
  const el = document.createElement(type)

  for (key in attributes) {
    el.setAttribute(key, attributes[key])
  }

  children.forEach(child => {
    if (typeof child === 'string') el.appendChild(document.createTextNode(child))
    else el.appendChild(child)
  })

  return el
}

const sliptWords = words => {
  const fragment = new DocumentFragment();
  let globalIndex = 0;

  words.split(' ').forEach((word, iWord) => {
    const fragmentLetter = new DocumentFragment();

    word.split('').forEach((letter, iLetter) => {
      globalIndex++;
      const el = elFactory(
        'span',
        {
          'data-letter': `${letter}`,
          class: `letter`,
          style: `--letter-index:${iLetter+1}; --global-index: ${globalIndex};`
        },
        `${letter}`
      )
      fragmentLetter.appendChild(el);
    })

    const space = elFactory(
      'span',
      {
        'data-space': true,
        class: `space`
      },
      ` `
    )
    fragmentLetter.appendChild(space);

    const el = elFactory(
      'span',
      {
        'data-word': `${word}`,
        class: `word`,
        style: `--word-index:${iWord+1}`
      },
      fragmentLetter
    )
    fragment.appendChild(el);
  })

  return fragment;
}

La animación se producirá a aquellos elementos HTML que tengan el atributo [data-split-word]

let splits = document.querySelectorAll('[data-split-word]');

splits.forEach(split => {
  let splitTextContent = split.textContent;

  split.innerHTML = '';
  split.appendChild(sliptWords(splitTextContent))
})

Esta landing tiene más cosas como por ejemplo:

  • Animación de scroll usando scrollIntoView
  • Menú lateral con animación de SVG
  • Uso de mix-blend-mode bajo el slider
  • Customización de ScrollBar

Puede verse todo el código en mi Git

Código en mi GitHub