Cómo usar las consultas de contenedores ahora

Recientemente, Chris Coyier escribió una entrada de blog en la que plantea la pregunta:

Ahora que las consultas de contenedores son compatibles con todos los motores de navegador, ¿por qué no las utilizan más desarrolladores?

En la publicación de Chris, se enumeran varias razones posibles (por ejemplo, la falta de conocimiento o los hábitos antiguos no son tan importantes), pero hay una razón particular que se destaca.

Algunos desarrolladores afirman que quieren usar consultas de contenedor ahora, pero piensan que no puede porque aún deben admitir navegadores anteriores.

Como habrás adivinado a partir del título, creemos que es posible que la mayoría de los desarrolladores usen ahora las consultas de contenedores, en producción, incluso si tienes que ofrecer compatibilidad con navegadores anteriores. En esta publicación, se explica el enfoque que recomendamos para hacerlo.

Un enfoque pragmático

Si deseas usar consultas de contenedor en tu código ahora, pero quieres que la experiencia se vea igual en todos los navegadores, puedes implementar un resguardo basado en JavaScript para los navegadores que no admitan consultas de contenedores.

Por lo tanto, la pregunta sería la siguiente: ¿qué tan exhaustivo debería ser el resguardo?

Al igual que con cualquier resguardo, el desafío es lograr un buen equilibrio entre utilidad y rendimiento. En el caso de las funciones de CSS, suele ser imposible admitir la API completa (consulta por qué no usar un polyfill). Sin embargo, puedes avanzar bastante identificando el conjunto principal de funcionalidades que la mayoría de los desarrolladores quieren usar y, luego, optimizar el resguardo solo para esas funciones.

Pero ¿cuál es el "conjunto principal de funcionalidades" que la mayoría de los desarrolladores desean para las consultas de contenedores? Para responder esta pregunta, considera cómo la mayoría de los desarrolladores crean sitios responsivos actualmente con consultas de medios.

Casi todos los sistemas de diseño y bibliotecas de componentes modernos están estandarizados en función de los principios mobile first, implementados mediante un conjunto de puntos de interrupción predefinidos (como SM, MD, LG y XL). Los componentes están optimizados para mostrarse bien en pantallas pequeñas de forma predeterminada y, luego, los estilos se superponen de forma condicional para admitir un conjunto fijo de anchos de pantalla más grandes. (Consulta la documentación de arranque y Tailwind para ver ejemplos relacionados).

Este enfoque es tan relevante para los sistemas de diseño basados en contenedores como para los sistemas de diseño basados en viewports, ya que, en la mayoría de los casos, lo que les importa a los diseñadores no es qué tan grande sea la pantalla o el viewport, sino cuánto espacio está disponible para el componente en el contexto en el que se colocó. En otras palabras, en lugar de que los puntos de interrupción sean relativos a todo el viewport (y se apliquen a toda la página), se aplicarían a áreas de contenido específicas, como barras laterales, diálogos modales o cuerpos de publicaciones.

Si puedes trabajar dentro de las limitaciones de un enfoque basado en puntos de interrupción y que prioriza los dispositivos móviles (como lo hacen actualmente la mayoría de los desarrolladores), implementar un resguardo basado en contenedores para ese enfoque es significativamente más fácil que implementar la compatibilidad total para cada función de consulta de contenedor.

En la siguiente sección, se explica exactamente cómo funciona todo esto, junto con una guía paso a paso que te muestra cómo implementarlo en un sitio existente.

Cómo funcionan

Paso 1: Actualiza los estilos de los componentes para usar reglas @container en lugar de reglas @media

En este primer paso, identifica los componentes de tu sitio que crees que se beneficiarían del tamaño basado en contenedores en lugar de basado en viewports.

Es una buena idea comenzar con solo uno o dos componentes para ver cómo funciona esta estrategia, pero si quieres convertir el 100% de tus componentes en un diseño basado en contenedores, también está bien. Lo mejor de esta estrategia es que puedes adoptarla de manera incremental si es necesario.

Cuando hayas identificado los componentes que deseas actualizar, deberás cambiar cada regla de @media en el CSS de esos componentes por una regla de @container. Puedes mantener las condiciones de tamaño.

Si tu CSS ya usa un conjunto de puntos de interrupción predefinidos, puedes seguir usándolos exactamente como están definidos. Si aún no usas los puntos de interrupción predefinidos, deberás definir nombres para ellos (a los que harás referencia más adelante en JavaScript; consulta el paso 2 para hacerlo).

Este es un ejemplo de estilos para un componente .photo-gallery que, de forma predeterminada, es una sola columna y, luego, actualiza su diseño de modo que se convierta en dos y tres columnas en los puntos de interrupción MD y XL (respectivamente):

.photo-gallery {
  display: grid;
  grid-template-columns: 1fr;
}

/* Styles for the `MD` breakpoint */
@media (min-width: 768px) {
  .photo-gallery {
    grid-template-columns: 1fr 1fr;
  }
}

/* Styles for the `XL` breakpoint */
@media (min-width: 1280px) {
  .photo-gallery {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

Para cambiar estos estilos de componentes y pasar de usar reglas @media a reglas @container, busca y reemplaza en tu código:

/* Before: */
@media (min-width: 768px) { /* ... */ }
@media (min-width: 1280px) { /* ... */ }

/* After: */
@container (min-width: 768px) { /* ... */ }
@container (min-width: 1280px) { /* ... */ }

Una vez que hayas actualizado los estilos de tus componentes de reglas @media a reglas @container basadas en puntos de interrupción, el siguiente paso es configurar los elementos de tu contenedor.

Paso 2: Agrega elementos de contenedor a tu código HTML

En el paso anterior, se definieron estilos de componentes que se basan en el tamaño de un elemento de contenedor. El siguiente paso es definir qué elementos de tu página deben ser aquellos elementos contenedores con los que se relacionará el tamaño de las reglas @container.

Puedes declarar que cualquier elemento es un elemento contenedor en CSS si configuras su propiedad container-type como size o inline-size. Si las reglas de tu contenedor se basan en el ancho, en general, lo que deseas usar es inline-size.

Considera un sitio con la siguiente estructura HTML básica:

<body>
  <div class="sidebar">...</div>
  <div class="content">...</div>
</body>

Para hacer que los elementos .sidebar y .content de este sitio sean containers, agrega esta regla a tu CSS:

.content, .sidebar {
  container-type: inline-size;
}

En el caso de los navegadores que admiten consultas de contenedores, este CSS es todo lo que necesitas para hacer que los estilos de componentes definidos en el paso anterior estén relacionados con el área de contenido principal o la barra lateral, según el elemento en el que se encuentren.

Sin embargo, en el caso de los navegadores que no admiten consultas de contenedores, debes realizar algunas acciones adicionales.

Debes agregar código que detecte cuándo cambia el tamaño de los elementos del contenedor y, luego, actualizar el DOM en función de esos cambios de modo que tu CSS pueda vincularse.

Por suerte, el código necesario para hacerlo es mínimo y se puede resumir por completo en un componente compartido que se puede usar en cualquier sitio y en cualquier área de contenido.

En el siguiente código, se define un elemento <responsive-container> reutilizable que detecta automáticamente los cambios de tamaño y agrega clases de puntos de interrupción a las que tu CSS puede aplicar ajustes de estilo:

// A mapping of default breakpoint class names and min-width sizes.
// Redefine these as needed based on your site's design.
const defaultBreakpoints = {SM: 512, MD: 768, LG: 1024, XL: 1280};

// A resize observer that monitors size changes to all <responsive-container>
// elements and calls their `updateBreakpoints()` method with the updated size.
const ro = new ResizeObserver((entries) => {
  entries.forEach((e) => e.target.updateBreakpoints(e.contentRect));
});

class ResponsiveContainer extends HTMLElement {
  connectedCallback() {
    const bps = this.getAttribute('breakpoints');
    this.breakpoints = bps ? JSON.parse(bps) : defaultBreakpoints;
    this.name = this.getAttribute('name') || '';
    ro.observe(this);
  }
  disconnectedCallback() {
    ro.unobserve(this);
  }
  updateBreakpoints(contentRect) {
    for (const bp of Object.keys(this.breakpoints)) {
      const minWidth = this.breakpoints[bp];
      const className = this.name ? `${this.name}-${bp}` : bp;
      this.classList.toggle(className, contentRect.width >= minWidth);
    }
  }
}

self.customElements.define('responsive-container', ResponsiveContainer);

Este código crea un ResizeObserver que detecta automáticamente los cambios de tamaño en cualquier elemento <responsive-container> del DOM. Si el cambio de tamaño coincide con uno de los tamaños de punto de interrupción definidos, se agrega al elemento una clase con ese nombre de punto de interrupción (y se quita si la condición ya no coincide).

Por ejemplo, si el width del elemento <responsive-container> está entre 768 y 1,024 píxeles (según los valores de punto de interrupción predeterminados establecidos en el código), se agregarán las clases SM y MD de la siguiente manera:

<responsive-container class="SM MD">...</responsive-container>

Estas clases te permiten definir estilos de resguardo para navegadores que no admiten consultas de contenedores (consulta el paso 3: Agrega estilos de resguardo a tu CSS).

Si deseas actualizar el código HTML anterior para usar este elemento de contenedor, cambia la barra lateral y los elementos <div> del contenido principal para que sean elementos <responsive-container>:

<body>
  <responsive-container class="sidebar">...</responsive-container>
  <responsive-container class="content">...</responsive-container>
</body>

En la mayoría de las situaciones, puedes usar el elemento <responsive-container> sin ninguna personalización, pero si necesitas personalizarlo, estarán disponibles las siguientes opciones:

  • Tamaños personalizados de puntos de interrupción: Este código usa un conjunto de nombres de clases de puntos de interrupción predeterminados y tamaños de ancho mínimo, pero puedes cambiar estos valores predeterminados para que sean los que quieras. También puedes anular estos valores por elemento con el atributo breakpoints.
  • Contenedores con nombre: Este código también admite contenedores con nombre pasando un atributo name. Esto puede ser importante si necesitas anidar elementos de contenedor. Consulta la sección de limitaciones para obtener más detalles.

En el siguiente ejemplo, se establecen ambas opciones de configuración:

<responsive-container
  name='sidebar'
  breakpoints='{"bp1":500,"bp2":1000,"bp3":1500}'>
</responsive-container>

Por último, cuando agrupes este código, asegúrate de usar la detección de funciones y el import() dinámico para cargarlo solo si el navegador no admite consultas de contenedor.

if (!CSS.supports('container-type: inline-size')) {
  import('./path/to/responsive-container.js');
}

Paso 3: Agrega estilos de resguardo a tu CSS

El último paso de esta estrategia es agregar estilos de resguardo para los navegadores que no reconocen los estilos definidos en las reglas de @container. Para ello, duplica esas reglas con las clases de puntos de interrupción que se establecen en los elementos <responsive-container>.

Siguiendo con el ejemplo anterior de .photo-gallery, los estilos de resguardo para las dos reglas @container podrían verse de la siguiente manera:

/* Container query styles for the `MD` breakpoint. */
@container (min-width: 768px) {
  .photo-gallery {
    grid-template-columns: 1fr 1fr;
  }
}

/* Fallback styles for the `MD` breakpoint. */
@supports not (container-type: inline-size) {
  :where(responsive-container.MD) .photo-gallery {
    grid-template-columns: 1fr 1fr;
  }
}

/* Container query styles for the `XL` breakpoint. */
@container (min-width: 1280px) {
  .photo-gallery {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

/* Fallback styles for the `XL` breakpoint. */
@supports not (container-type: inline-size) {
  :where(responsive-container.XL) .photo-gallery {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

En este código, para cada regla @container hay una regla equivalente que coincide de forma condicional con el elemento <responsive-container> si la clase de punto de interrupción correspondiente está presente.

La parte del selector que coincide con el elemento <responsive-container> se une en un selector de seudoclase funcional :where(), para mantener la especificidad del selector de resguardo equivalente a la especificidad del selector original dentro de la regla @container.

Cada regla de resguardo también se une a una declaración @supports. Si bien esto no es estrictamente necesario para que el resguardo funcione, significa que el navegador ignora por completo estas reglas si admite consultas de contenedor, lo que puede mejorar el rendimiento de la coincidencia de estilo en general. También permite potencialmente que las herramientas de compilación o CDN eliminen esas declaraciones si saben que el navegador admite consultas de contenedor y no necesita esos estilos de resguardo.

La principal desventaja de esta estrategia de resguardo es que requiere que repitas la declaración de estilo dos veces, lo cual es a la vez tedioso y propenso a errores. Sin embargo, si usas un preprocesador de CSS, puedes abstraerlo en una combinación que genere tanto la regla @container como el código de resguardo por ti. A continuación, se muestra un ejemplo con Sass:

@use 'sass:map';

$breakpoints: (
  'SM': 512px,
  'MD': 576px,
  'LG': 1024px,
  'XL': 1280px,
);

@mixin breakpoint($breakpoint) {
  @container (min-width: #{map.get($breakpoints, $breakpoint)}) {
    @content();
  }
  @supports not (container-type: inline-size) {
    :where(responsive-container.#{$breakpoint}) & {
      @content();
    }
  }
}

Luego, una vez que tengas esta combinación, puedes actualizar los estilos del componente .photo-gallery original a algo como esto, lo que elimina por completo la duplicación:

.photo-gallery {
  display: grid;
  grid-template-columns: 1fr;

  @include breakpoint('MD') {
    grid-template-columns: 1fr 1fr;
  }

  @include breakpoint('XL') {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

Eso es todo.

Resumen

Entonces, para recapitular, a continuación se muestra cómo actualizar tu código para usar las consultas de contenedor ahora con un resguardo entre navegadores.

  1. Identidad los componentes a los que deseas aplicar diseño en relación con su contenedor y actualiza las reglas @media en su CSS para que usen reglas @container. Además, si aún no lo has hecho, estandariza un conjunto de nombres de puntos de interrupción para que coincidan con las condiciones de tamaño de las reglas de tu contenedor.
  2. Agrega el código JavaScript que ejecuta el elemento <responsive-container> personalizado y, luego, agrega el elemento <responsive-container> a cualquier área de contenido de tu página con la que quieras relacionar tus componentes.
  3. Para admitir navegadores más antiguos, agrega estilos de resguardo a tu CSS que coincidan con las clases de puntos de interrupción que se agregan automáticamente a los elementos <responsive-container> de tu HTML. Idealmente, usa una combinación de preprocesador de CSS para evitar tener que escribir los mismos estilos dos veces.

Lo mejor de esta estrategia es que tiene un costo de configuración único, pero después de eso, no se necesita ningún esfuerzo adicional para agregar nuevos componentes y definir los estilos relativos del contenedor para ellos.

Ver cómo funciona

Probablemente, la mejor manera de entender cómo se relacionan todos estos pasos sea ver una demostración de cómo funcionan.

Un video de un usuario interactuando con el sitio de demostración de consultas del contenedor. El usuario está cambiando el tamaño de las áreas de contenido para mostrar cómo se actualizan los estilos de los componentes en función del tamaño del área de contenido contenedor.

Esta demostración es una versión actualizada de un sitio creado en 2019 (antes de que existieran las consultas de contenedores) para ayudar a ilustrar por qué estas consultas son esenciales para crear bibliotecas de componentes verdaderamente responsivas.

Como este sitio ya tenía estilos definidos para varios "componentes adaptables", fue un candidato perfecto para probar la estrategia presentada aquí en un sitio importante. Resulta que fue bastante simple actualizar y casi no requirió cambios en los estilos originales del sitio.

Puedes consultar el código fuente de demostración completo en GitHub y asegúrate de consultar específicamente el CSS del componente de demostración para ver cómo se definen los estilos de resguardo. Si solo deseas probar el comportamiento de resguardo, hay una demostración de solo resguardo que incluye esa variante, incluso en navegadores que admiten consultas de contenedores.

Limitaciones y posibles mejoras

Como se mencionó al comienzo de esta publicación, la estrategia descrita aquí funciona bien para la mayoría de los casos de uso que les interesan a los desarrolladores cuando buscan consultas de contenedores.

Dicho esto, hay algunos casos de uso más avanzados que esta estrategia no intenta admitir de forma intencional, tal como se abordan a continuación:

Unidades de consulta de contenedor

La especificación de consultas de contenedor define la cantidad de unidades nuevas, todas relacionadas con el tamaño del contenedor. Si bien pueden ser útiles en algunos casos, es probable que la mayoría de los diseños adaptables puedan lograrse a través de medios existentes, como porcentajes o el uso de diseños de cuadrícula o flexibles.

Dicho esto, si necesitas usar unidades de consulta de contenedor, puedes agregar compatibilidad con ellas fácilmente mediante las propiedades personalizadas. Específicamente, si defines una propiedad personalizada para cada unidad que se use en el elemento contenedor, de la siguiente manera:

responsive-container {
  --cqw: 1cqw;
  --cqh: 1cqh;
}

Luego, cuando necesites acceder a las unidades de consulta del contenedor, usa esas propiedades, en lugar de usar la unidad:

.photo-gallery {
  font-size: calc(10 * var(--cqw));
}

Luego, para admitir navegadores más antiguos, establece los valores de esas propiedades personalizadas en el elemento de contenedor dentro de la devolución de llamada ResizeObserver.

class ResponsiveContainer extends HTMLElement {
  // ...
  updateBreakpoints(contentRect) {
    this.style.setProperty('--cqw', `${contentRect.width / 100}px`);
    this.style.setProperty('--cqh', `${contentRect.height / 100}px`);

    // ...
  }
}

Esto te permite "pasar" esos valores de JavaScript a CSS de manera efectiva y, luego, tendrás toda la potencia de CSS (por ejemplo, calc(), min(), max(), clamp()) para manipularlos según sea necesario.

Propiedades lógicas y compatibilidad con el modo de escritura

Es posible que hayas notado el uso de inline-size en lugar de width en las declaraciones @container de algunos de estos ejemplos de CSS. Es posible que también hayas notado las nuevas unidades cqi y cqb (para tamaños de intercalado y bloque, respectivamente). Estas nuevas funciones reflejan el cambio de CSS a propiedades y valores lógicos en lugar de propiedades y valores físicos o direccionales.

Lamentablemente, las APIs como Rename Observer aún informan valores en width y height, por lo que si tus diseños necesitan la flexibilidad de las propiedades lógicas, debes averiguarlo tú mismo.

Si bien es posible obtener el modo de escritura con algo como getComputedStyle() pasando el elemento del contenedor, hacerlo tiene un costo, y no hay una buena manera de detectar si cambia el modo de escritura.

Por este motivo, el mejor enfoque es que el elemento <responsive-container> acepte una propiedad de modo de escritura que el propietario del sitio pueda configurar (y actualizar) según sea necesario. Para implementar esto, seguirías el mismo enfoque que se mostró en la sección anterior y, luego, cambiarías width y height según sea necesario.

Contenedores anidados

La propiedad container-name te permite asignar un nombre a un contenedor, al que luego puedes hacer referencia en una regla @container. Los contenedores con nombre son útiles si tienes contenedores anidados dentro de ellos y necesitas que ciertas reglas coincidan solo con ciertos contenedores (no solo con el contenedor principal más cercano).

La estrategia de resguardo que se describe aquí usa la combinadora descendente para aplicar diseño a elementos que coinciden con ciertas clases de puntos de interrupción. Esto puede fallar si tienes contenedores anidados, ya que cualquier cantidad de clases de puntos de interrupción de varios elementos principales del contenedor puede coincidir con un componente determinado al mismo tiempo.

Por ejemplo, aquí hay dos elementos <responsive-container> que unen el componente .photo-gallery, pero como el contenedor externo es más grande que el interno, se agregaron diferentes clases de puntos de interrupción.

<responsive-container class="SM MD LG">
  ...
  <responsive-container class="SM">
    ...
    <div class="photo-gallery">...</div class="photo-gallery">
  </responsive-container>
</responsive-container>

En este ejemplo, las clases MD y LG del contenedor externo afectarían las reglas de estilo que coinciden con el componente .photo-gallery, que no coincide con el comportamiento de las consultas de contenedor (ya que solo coinciden con el contenedor principal más cercano).

Para lidiar con esto, realiza una de las siguientes acciones:

  1. Asegúrate de nombrar siempre los contenedores que anidas y, luego, de que las clases de puntos de interrupción tengan el prefijo de ese nombre de contenedor para evitar conflictos.
  2. Usa la combinadora secundaria en lugar del subordinada en tus selectores de resguardo (lo que es un poco más restrictivo).

En la sección de contenedores anidados del sitio de demostración, se muestra un ejemplo de este trabajo con contenedores con nombre, junto con la combinación de Sass que usa en el código para generar los estilos alternativos de las reglas @container con y sin nombre.

¿Qué sucede con los navegadores que no admiten :where(), Custom Elements ni Rename Observer?

Si bien estas APIs pueden parecer relativamente nuevas, han sido compatibles con todos los navegadores desde hace más de tres años, y todas son parte de la amplia disponibilidad de Baseline.

Por lo tanto, a menos que haya datos que demuestren que una parte significativa de los visitantes de su sitio utilizan navegadores que no admiten una de estas funciones, no hay razón para no usarlos libremente sin un resguardo.

Aun así, para este caso de uso específico, lo peor que podría suceder es que el resguardo no funcione para un porcentaje muy pequeño de usuarios, lo que significa que verán la vista predeterminada en lugar de una optimizada para el tamaño del contenedor.

La funcionalidad del sitio debería seguir funcionando, y eso es lo que realmente importa.

¿Por qué no usar solo un polyfill de consulta de contenedor?

Las funciones de CSS son muy difíciles de usar en polyfill y, en general, requieren que se vuelva a implementar todo el analizador de CSS del navegador y la lógica de cascada en JavaScript. En consecuencia, los autores de polyfills de CSS deben realizar varias compensaciones que casi siempre conllevan numerosas limitaciones de funciones y una sobrecarga significativa de rendimiento.

Por estos motivos, generalmente no recomendamos usar polyfills de CSS en la producción, incluido container-query-polyfill de Google Chrome Labs, que ya no se mantiene (y estaba destinado principalmente a fines de demostración).

La estrategia de resguardo que se analiza aquí tiene menos limitaciones, requiere mucho menos código y tendrá un rendimiento significativamente mejor que cualquier polyfill de consultas de contenedores.

¿Necesitas implementar un resguardo para navegadores anteriores?

Si te preocupa alguna de las limitaciones mencionadas aquí, probablemente valga la pena preguntarte si realmente necesitas implementar un resguardo. Después de todo, la forma más fácil de evitar estas limitaciones es usar la función sin ningún resguardo. Honestamente, en muchos casos, esa puede ser una elección perfectamente razonable.

Según caniuse.com, las consultas de contenedores cuentan con el respaldo del 90% de los usuarios de Internet de todo el mundo, y para muchas de las personas que leen esta publicación, es probable que la cifra sea un poco mayor para su base de usuarios. Por lo tanto, es importante tener en cuenta que la mayoría de tus usuarios verán la versión de consulta de contenedor de tu IU. Y en el caso del 10% de los usuarios que no lo harán, no tendrá una experiencia negativa. Al seguir esta estrategia, en el peor de los casos, estos usuarios verán el diseño predeterminado o "móvil" para algunos componentes, lo que no es el fin del mundo.

A la hora de hacer concesiones, es una buena práctica realizar optimizaciones para la mayoría de los usuarios, en lugar de optar por un enfoque predeterminado con el denominador común más bajo que les brinde a todos los usuarios una experiencia coherente, pero deficiente.

Por lo tanto, antes de suponer que no puedes usar las consultas de contenedor debido a la falta de compatibilidad con los navegadores, tómate el tiempo necesario para considerar cómo sería la experiencia si eligieras adoptarlas. La compensación puede valer la pena, incluso sin ningún resguardo.

Proyecciones para el futuro

Esperamos que esta publicación te haya convencido de que ahora es posible usar consultas de contenedores en producción y de que no tienes que esperar años hasta que desaparezcan por completo todos los navegadores incompatibles.

Si bien la estrategia descrita aquí requiere un poco de trabajo adicional, debe ser lo suficientemente sencilla y directa como para que la mayoría de las personas pueda adoptarla en sus sitios. Dicho esto, sin duda hay lugar para facilitar aún más su adopción. Una idea sería consolidar muchas de las partes diferentes en un solo componente, optimizado para un framework o una pila específicos, que se encargue de todo el trabajo de adhesión por ti. Si creas algo así, infórmanos al respecto para que te ayudemos a promocionarlo.

Por último, más allá de las consultas de contenedores, hay muchas funciones de CSS y de IU increíbles que ahora son interoperables en los principales motores de navegador. Como comunidad, averigüemos cómo podemos usar esas funciones ahora para que nuestros usuarios se beneficien.