Cómo compilar un componente de aviso

Una descripción general fundamental de cómo compilar un componente de aviso adaptable y accesible

En esta publicación, quiero compartir ideas sobre cómo crear un componente de aviso. Prueba la demostración.

. Demostración

Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:

Descripción general

Los avisos son mensajes cortos no interactivos, pasivos y asíncronos para los usuarios. Suelen usarse como patrón de comentarios de la interfaz para informar al usuario sobre los resultados de una acción.

Interacciones

Los avisos son, a diferencia de las notificaciones, las alertas y mensajes porque no son interactivos; no están diseñados para descartarse o persistir. Las notificaciones son para información más importante, mensajes síncronos que requiere interacción o mensajes a nivel del sistema (a diferencia del nivel de la página). Los avisos son más pasivos que otras estrategias de avisos.

Marca

El <output> es una buena opción para el aviso porque se anuncia lectores. El HTML correcto brinda una base segura para que podamos mejorar con JavaScript y CSS, y habrá mucho JavaScript.

Un brindis

<output class="gui-toast">Item added to cart</output>

Puede ser más inclusivos Para ello, agrega role="status". Esto proporciona un alternativa si el navegador no le da a los elementos <output> el elemento puesto según la especificación.

<output role="status" class="gui-toast">Item added to cart</output>

Un contenedor de avisos

Se puede mostrar más de un aviso a la vez. Para organizar varias los avisos, se usa un contenedor. Este contenedor también controla la posición de la avisos en la pantalla.

<section class="gui-toast-group">
  <output role="status">Wizard Rose added to cart</output>
  <output role="status">Self Watering Pot added to cart</output>
</section>

Diseños

Elegí fijar avisos inset-block-end del viewport y, si se agregan más avisos, se apilan desde el borde de la pantalla.

Contenedor de la GUI

El contenedor de avisos hace todo el trabajo de diseño para presentar avisos. Es fixed al viewport y usa la propiedad lógica inset para especificar bordes para fijar, más un poco de padding del mismo borde de block-end.

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

Captura de pantalla con el tamaño del cuadro y el relleno de Herramientas para desarrolladores superpuestos sobre un elemento .gui-toast-container.

Además de posicionarse dentro del viewport, el contenedor del aviso es un de cuadrícula que puede alinear y distribuir avisos. Los elementos se centran como Grupo con justify-content y centrado individualmente con justify-items Agrega un poco de gap para que los avisos no se toquen.

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

Captura de pantalla con la superposición de la cuadrícula de CSS en el grupo de avisos, esta vez
destacando el espacio y los espacios entre los elementos secundarios de avisos.

Aviso de GUI

Un aviso individual tiene padding, algunas esquinas más suaves, border-radius, y una función min() para ayudan con el tamaño de dispositivos móviles y computadoras de escritorio. El tamaño responsivo del siguiente CSS evita que los avisos aumenten su ancho más del 90% de la viewport o 25ch

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

Captura de pantalla de un solo elemento .gui-toast, con el relleno y el borde
radio de visualización.

Estilos

Con el diseño y el posicionamiento establecidos, agrega CSS que ayude a adaptarse al usuario la configuración y las interacciones.

Contenedor de aviso

Los avisos no son interactivos. Tocar o deslizar el dedo sobre ellos no hace nada, pero actualmente consumen eventos de puntero. Evita que los avisos roben clics con el siguiente CSS.

.gui-toast-group {
  pointer-events: none;
}

Aviso de GUI

Brinda a los avisos un tema adaptable oscuro o claro con propiedades personalizadas, HSL y un consulta de medios de preferencia.

.gui-toast {
  --_bg-lightness: 90%;

  color: black;
  background: hsl(0 0% var(--_bg-lightness) / 90%);
}

@media (prefers-color-scheme: dark) {
  .gui-toast {
    color: white;
    --_bg-lightness: 20%;
  }
}

Animación

Se debe presentar un nuevo aviso con una animación a medida que ingresa a la pantalla. Para adaptarse al movimiento reducido, se deben establecer los valores de translate en 0 predeterminada, pero actualizar el valor de movimiento a una longitud en un medio con preferencia de movimiento consulta . Todos ven algo de animación, pero solo algunos usuarios tienen el viaje de aviso. a distancia.

Estos son los fotogramas clave utilizados para la animación de aviso. CSS controlará el la entrada, la espera y la salida del aviso, todo en una animación.

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

Luego, el elemento de aviso configura las variables y organiza los fotogramas clave.

.gui-toast {
  --_duration: 3s;
  --_travel-distance: 0;

  will-change: transform;
  animation: 
    fade-in .3s ease,
    slide-in .3s ease,
    fade-out .3s ease var(--_duration);
}

@media (prefers-reduced-motion: no-preference) {
  .gui-toast {
    --_travel-distance: 5vh;
  }
}

JavaScript

Como los estilos y el lector de pantalla están listos para usar con el formato HTML, se necesita JavaScript para organizar la creación, adición y destrucción de avisos basados en eventos. La experiencia del desarrollador con el componente de aviso debe ser mínima y para empezar, como se muestra a continuación:

import Toast from './toast.js'

Toast('My first toast')

Crea el grupo de avisos y los avisos

Cuando el módulo de aviso se carga desde JavaScript, debe crear un contenedor de aviso. y agregarla a la página. Elegí agregar el elemento antes de body, esto hará Es poco probable que haya problemas de apilado de z-index, ya que el contenedor se encuentra por encima del contenedor para todos los elementos del cuerpo.

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

Captura de pantalla del grupo de avisos entre las etiquetas de la cabeza y el cuerpo.

La función init() se llama de forma interna al módulo y se guarda el elemento. como Toaster:

const Toaster = init()

La creación de elementos HTML de aviso se realiza con la función createToast(). El requiere texto para el aviso, crea un elemento <output> y adorna con algunas clases y atributos, configura el texto y muestra el nodo.

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

Cómo administrar uno o más avisos

JavaScript ahora agrega un contenedor al documento para incluir avisos Listo para agregar avisos creados. La función addToast() organiza el manejo de uno o muchos avisos. Primero verificando la cantidad de avisos y si el movimiento está bien, luego, usar esta información para agregar el aviso animación para que los otros avisos "hagan espacio" por el nuevo aviso.

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

Cuando se agrega el primer aviso, Toaster.appendChild(toast) lo hace a la página que activa las animaciones de CSS: animar para entrar, esperar 3s, animar fuera. Se llama a flipToast() cuando hay avisos existentes, mediante el uso de una técnica. llamado FLIP por Paul Lewis. La idea es calcular la diferencia en posiciones del contenedor, antes y después de que se haya agregado el nuevo aviso. Piensa en esto como marcar dónde está la tostadora, dónde estará animando desde donde estaba hasta donde está.

const flipToast = toast => {
  // FIRST
  const first = Toaster.offsetHeight

  // add new child to change container size
  Toaster.appendChild(toast)

  // LAST
  const last = Toaster.offsetHeight

  // INVERT
  const invert = last - first

  // PLAY
  const animation = Toaster.animate([
    { transform: `translateY(${invert}px)` },
    { transform: 'translateY(0)' }
  ], {
    duration: 150,
    easing: 'ease-out',
  })
}

La cuadrícula de CSS levanta el diseño. Cuando se agrega un nuevo aviso, la cuadrícula lo coloca al comienzo y los separa a los demás. Mientras tanto, una página web animación es que se usa para animar el contenedor desde la posición anterior.

Juntar todo el código JavaScript

Cuando se llama a Toast('my first toast'), se crea un aviso que se agrega a la página. (quizás incluso el contenedor esté animado para adaptarse al nuevo aviso), una promesa y el aviso que se creó se mirado durante Finalización de animación CSS (las tres animaciones de fotogramas clave) para la resolución de promesa.

const Toast = text => {
  let toast = createToast(text)
  addToast(toast)

  return new Promise(async (resolve, reject) => {
    await Promise.allSettled(
      toast.getAnimations().map(animation => 
        animation.finished
      )
    )
    Toaster.removeChild(toast)
    resolve() 
  })
}

Me pareció que la parte confusa de este código está en la función Promise.allSettled(). y toast.getAnimations(). Ya que usé varias animaciones de fotogramas clave para el brindis, para saber con seguridad que todos terminaron, cada uno debe estar solicitadas desde JavaScript y cada uno de sus finished y promesas observadas para su finalización. allSettled ¿eso funciona para nosotros? Se resuelve como completa una vez que se completaron. Usar await Promise.allSettled() significa que la siguiente línea de código puede quitar con confianza el elemento y suponer que el aviso ha completado su en el ciclo de vida del AA. Por último, llamar a resolve() cumple con la promesa de aviso de alto nivel, por lo que los desarrolladores pueden limpiar o hacer otro trabajo una vez que se haya mostrado el aviso.

export default Toast

Por último, la función Toast se exporta del módulo para otras secuencias de comandos importar y usar.

Cómo usar el componente de aviso

El uso del aviso, o de la experiencia del desarrollador, se hace importando función Toast y llamarla con una cadena de mensaje.

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

Si el desarrollador quiere realizar una limpieza o cualquier otro elemento, luego de que se haya enviado el aviso pueden usar las funciones asíncronas esperar.

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías?‽ 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crear una demostración, twittearme vínculos y la agregaré. a la sección de remixes de la comunidad.

Remixes de la comunidad