Saltar al contenido
duxt

ProseImg

Una imagen: consciente del tema, adaptable y ampliable.

Cada imagen de una página Markdown.

Qué añade

Dos variantes. Una captura de un editor oscuro sobre una página blanca — o al revés — es la fealdad más común de una documentación, y CSS no puede arreglarla porque lo que está mal son los píxeles, no el marco. Una página puede nombrar un segundo archivo:

![La barra lateral](/shots/sidebar.png){dark="/shots/sidebar-dark.png"}

Dos peticiones, no un archivo. La imagen de la página y la del diálogo de zoom piden anchos distintos, así que un teléfono ya no descarga el original de 2400 px para dibujarlo en una columna de 48rem. Ambas son srcset generados por @nuxt/image; ninguna es un archivo que alguien deba preparar.

Clic para ampliar, salvo que la imagen pida lo contrario:

![Un diagrama](/diagram.svg){zoom="false"}

Una imagen ampliable lleva un pequeño indicador de zoom en su esquina, de forma permanente y no al pasar el cursor — un cursor no existe en un dispositivo táctil y es invisible en una captura de la página. La imagen es un botón: Tab lo alcanza con un anillo visible, Enter y Espacio abren el diálogo, Escape lo cierra y devuelve el foco. Con zoom="false" no hay indicador, ni anillo, ni nada a lo que tabular.

Un fondo para una imagen transparente. Un diagrama cuyo trazo está pensado para una superficie clara desaparece en una página oscura. Indica un color por tema — cualquier color CSS, var(--card) incluido:

![Un diagrama](/diagram.svg){background="#fafafa" dark-background="#171717"}

Colores y no clases, a propósito: la hoja de estilos de la capa se genera a partir de sus propios componentes, nunca del Markdown, así que una clase dark:bg-… escrita en una página no tendría CSS detrás.

El visor ocupa toda la pantalla sobre la página desenfocada. Sus controles — alejar, el nivel de zoom (que lo restablece), acercar, abrir el original en una pestaña nueva, cerrar — flotan en una fila sobre la imagen y el texto alternativo va debajo, de modo que ninguno la tapa. La rueda amplía alrededor del cursor y el gesto de pinza alrededor de los dedos; un clic en la imagen la amplía al 200 % en ese punto y un segundo clic vuelve; +, - y 0 hacen lo mismo desde el teclado. Una imagen ampliada se arrastra para desplazarla y carga el archivo original para seguir nítida. Un clic en el espacio alrededor cierra el visor.

API

Props

PropTipoPor defectoNotas
srcstring—Tal cual se escribe
altstring—Tal cual se escribe
darkstring—Un segundo archivo, servido bajo el tema oscuro
zoombooleantruefalse deja la imagen inerte
widthnumber—Opcional; véase Dimensiones más abajo
heightnumber—Opcional; véase Dimensiones más abajo
sizesstring—Sustituye la política de la página; véase Tamaños
backgroundstring—Un color CSS detrás de una imagen transparente, en la página y en el visor
dark-backgroundstring—Lo mismo con el tema oscuro; recurre a background

Tamaños

El tema sabe una cosa que @nuxt/image no puede saber: cuál es el ancho de la caja en la que dibuja una imagen. Eso es todo lo que configura.

  • En la página — sm:95vw md:768px. Por debajo de md el artículo es el viewport menos sus márgenes; a partir de md queda limitado a max-w-3xl sea cual sea la pantalla, de modo que el ancho pedido deja de crecer en lugar de seguir subiendo en una pantalla 4K.
  • En el diálogo — sm:96vw md:96vw lg:96vw xl:1264px, siguiendo al viewport hasta el propio límite del diálogo, min(96vw, 80rem).

Ambas nombran solo los breakpoints que @nuxt/image ya trae, así que quien redefina image.screens conserva su definición. La capa no declara screens propios.

Una imagen que no es tan ancha como la columna de texto — un logotipo, una insignia, un diagrama al margen — lo dice:

![La insignia](/badge.png){sizes="sm:50vw md:200px"}

sizes es la sintaxis propia de @nuxt/image y alcanza solo a la imagen de la página. El diálogo conserva su política: una que describe la columna de texto no puede describir un diálogo.

Dimensiones

width y height son opcionales y lo seguirán siendo — la mayoría de las páginas las omiten, y exigirlas rompería todas las ya escritas.

Indícalas de todos modos cuando puedas. Son lo que permite al navegador reservar la caja antes de que llegue el archivo; sin ellas la imagen no tiene proporción intrínseca hasta que carga, y el texto de debajo se mueve cuando lo hace. El componente conserva la proporción del par que recibe y no inventa nada cuando no recibe ninguno: no hay proporción por defecto ni recorte, porque una proporción equivocada es peor que un salto.

Una imagen más pequeña que la columna se deja a su propio tamaño en lugar de estirarse.

Formatos

SVG y GIF animado se pasan sin tocar, en la página y en el diálogo, sin srcset alguno. Un vector no tiene píxeles que remuestrear — pedir uno de 608 px de ancho rasteriza un archivo que era nítido a cualquier tamaño — y un GIF que pasa por una tubería de rasterizado vuelve convertido en su primer fotograma.

Esto es lo que hace el componente, no una afirmación sobre los proveedores: algunos pasan estos formatos sin tocarlos y otros no, y una capa de documentación no puede saber cuál ha configurado su consumidor. Las fuentes adaptables son para los formatos de trama para los que existe el zoom — PNG, JPEG, WebP, AVIF.

Proveedores

La capa no establece ninguna clave image. Qué servicio transforma una imagen es del consumidor, igual que sources: un proveedor elegido por la capa es un proveedor que todo consumidor tiene que deshacer, y decide una cuenta y una factura tanto como una URL. Configura @nuxt/image como en cualquier aplicación Nuxt; el componente solo le entrega dos cadenas sizes.

A su aire, @nuxt/image elige por sí mismo, y los tres casos con los que se encuentra un sitio de documentación son:

  • Un servidor Node. ipx transforma bajo demanda en /_ipx/…. Necesita sharp, un binding nativo, que la salida de Node ya lleva.
  • Un sitio generado (nuxt generate). ipxStatic escribe cada variante en tiempo de compilación. Después no corre nada, y la salida crece por las variantes — el trato que un sitio totalmente estático ya está haciendo.
  • Un entorno de ejecución en el borde — Cloudflare Workers y cualquier otro que no sea Node ni estático. No corre ningún transformador y las imágenes se sirven tal como se subieron. Un sitio prerrenderizado en un entorno así no debe configurarse con ipx: es un binding nativo y allí no puede ejecutarse en absoluto. Recurre a un proveedor CDN si quieres las transformaciones.

Las variantes del diálogo se generan en una compilación estática aunque el diálogo esté cerrado cuando se renderiza la página — el componente las registra él mismo ante el rastreador de prerrenderizado. No se descarga nada hasta que el diálogo se abre.

Una imagen con zoom="false" no recibe ninguna. No hay diálogo que abrir, así que cada archivo de tamaño de diálogo escrito para ella sería salida que nadie puede pedir.

Notas

La variante oscura se nombra explícitamente en lugar de adivinarse por convención. Derivar -dark de un nombre de archivo significa que un archivo que falta se convierte en una imagen rota, y el autor nunca se entera.

Una imagen de una fuente clonada desde otro repositorio no tiene ruta propia: Content descarga el árbol en .data/content/ y nada copia sus recursos a public/. Las rutas de estos ejemplos son absolutas hacia el propio public/ del sitio consumidor, que es donde una imagen de documentación tiene que vivir hoy.

¿Le ha resultado útil esta página?