ProseImg
Une image : consciente du thème, adaptative et agrandissable.
Chaque image d'une page Markdown.
Chaque exemple ci-dessous est un bloc de code. Les images de test sont ce même balisage réellement dessiné : un jumeau sombre, un original plus étroit que la colonne, un vecteur, une animation et une image qui refuse de s'agrandir.
Ce qu'elle ajoute
Deux variantes. Une capture d'un éditeur sombre sur une page blanche — ou l'inverse — est la laideur la plus courante d'une documentation, et CSS ne peut pas la corriger parce que ce sont les pixels qui sont faux, pas le cadre. Une page peut nommer un second fichier :
{dark="/shots/sidebar-dark.png"}
Deux requêtes, pas un fichier. L'image dans la page et celle du dialogue
d'agrandissement demandent des largeurs différentes : un téléphone ne télécharge
donc plus l'original de 2400 px pour le dessiner dans une colonne de 48rem. Ce
sont deux srcset produits par @nuxt/image ; ni l'un ni l'autre n'est un
fichier que quelqu'un doit préparer.
Cliquer pour agrandir, sauf si l'image demande le contraire :
{zoom="false"}
Une image agrandissable porte un petit indicateur dans son coin, en permanence
plutôt qu'au survol — un curseur n'existe pas sur un appareil tactile et reste
invisible sur une capture de la page. L'image est un bouton : Tab l'atteint
avec un anneau visible, Entrée et Espace ouvrent le dialogue, Échap le
ferme et rend le focus. Avec zoom="false", pas d'indicateur, pas d'anneau et
rien où tabuler.
Un fond pour une image transparente. Un schéma dont le trait est pensé pour
une surface claire disparaît sur une page sombre. Indiquez une couleur par thème —
n'importe quelle couleur CSS, var(--card) compris :
{background="#fafafa" dark-background="#171717"}
Des couleurs plutôt que des classes, volontairement : la feuille de style de la
couche est construite à partir de ses propres composants, jamais du Markdown, si
bien qu'une classe dark:bg-… écrite dans une page n'aurait aucun CSS derrière
elle.
La visionneuse occupe tout l'écran au-dessus de la page floutée. Ses commandes —
zoom arrière, le niveau de zoom (qui le réinitialise), zoom avant, ouvrir
l'original dans un nouvel onglet, fermer — flottent sur une ligne au-dessus de
l'image et le texte alternatif se place en dessous, si bien qu'aucun ne la
masque. La molette zoome autour du curseur et le pincement autour des doigts ;
un clic sur l'image zoome à 200 % à cet endroit et un second revient ; +, -
et 0 font de même au clavier. Une image zoomée se fait glisser pour la
déplacer et charge le fichier original pour rester nette. Un clic autour ferme
la visionneuse.
API
Props
| Prop | Type | Défaut | Notes |
|---|---|---|---|
src | string | — | Tel qu'écrit |
alt | string | — | Tel qu'écrit |
dark | string | — | Un second fichier, servi sous le thème sombre |
zoom | boolean | true | false laisse l'image inerte |
width | number | — | Facultatif ; voir Dimensions plus bas |
height | number | — | Facultatif ; voir Dimensions plus bas |
sizes | string | — | Remplace la politique de la page ; voir Tailles |
background | string | — | Une couleur CSS derrière une image transparente, dans la page et la visionneuse |
dark-background | string | — | La même sous le thème sombre ; se replie sur background |
Tailles
Le thème sait une chose que @nuxt/image ne peut pas savoir : la largeur de la
boîte dans laquelle il dessine une image. C'est tout ce qu'il configure.
- Dans la page —
sm:95vw md:768px. Sousmd, l'article vaut la fenêtre moins ses marges ; à partir demd, il est plafonné àmax-w-3xlquel que soit l'écran, si bien que la largeur demandée cesse de croître au lieu de grimper sur un écran 4K. - Dans le dialogue —
sm:96vw md:96vw lg:96vw xl:1264px, suivant la fenêtre jusqu'au plafond propre au dialogue,min(96vw, 80rem).
Les deux ne nomment que les points de rupture que @nuxt/image fournit déjà :
qui redéfinit image.screens garde sa définition. La couche ne déclare aucun
screens à elle.
Une image qui n'est pas aussi large que la colonne de texte — un logo, un badge, un schéma en marge — le dit :
{sizes="sm:50vw md:200px"}
sizes est la syntaxe propre de @nuxt/image et n'atteint que l'image de la
page. Le dialogue garde sa politique : une politique qui décrit la colonne de
texte ne peut pas décrire un dialogue.
Dimensions
width et height sont facultatifs et le restent — la plupart des pages les
omettent, et les exiger casserait toutes celles déjà écrites.
Donnez-les tout de même quand vous le pouvez. Ce sont elles qui permettent au navigateur de réserver la boîte avant l'arrivée du fichier ; sans elles, l'image n'a pas de rapport intrinsèque tant qu'elle n'est pas chargée, et le texte en dessous bouge quand elle l'est. Le composant conserve le rapport de la paire qu'on lui donne et n'invente rien quand on ne lui en donne aucune : il n'y a ni rapport par défaut ni recadrage, car un mauvais rapport est pire qu'un saut.
Une image plus petite que la colonne est laissée à sa propre taille plutôt qu'étirée.
Formats
SVG et GIF animé passent sans être touchés, dans la page comme dans le
dialogue, sans aucun srcset. Un vecteur n'a pas de pixels à rééchantillonner —
en demander un de 608 px de large matricialise un fichier qui était net à toutes
les tailles — et un GIF passé dans une chaîne de matricialisation revient réduit
à sa première image.
C'est ce que fait le composant, pas une affirmation sur les fournisseurs : certains laissent ces formats passer, d'autres non, et une couche de documentation ne peut pas savoir lequel son consommateur a configuré. Les sources adaptatives concernent les formats matriciels pour lesquels l'agrandissement existe — PNG, JPEG, WebP, AVIF.
Fournisseurs
La couche ne définit aucune clé image. Quel service transforme une image
appartient au consommateur, comme sources : un fournisseur choisi par la couche
est un fournisseur que chaque consommateur doit défaire, et il décide d'un compte
et d'une facture autant que d'une URL. Configurez
@nuxt/image comme dans
n'importe quelle application Nuxt ; le composant ne lui remet que deux chaînes
sizes.
Laissé à lui-même, @nuxt/image choisit seul, et les trois cas que rencontre un
site de documentation sont :
- Un serveur Node.
ipxtransforme à la demande sous/_ipx/…. Il lui fautsharp, une liaison native, que la sortie Node embarque déjà. - Un site généré (
nuxt generate).ipxStaticécrit chaque variante au moment de la construction. Plus rien ne tourne ensuite, et la sortie grossit des variantes — le marché qu'un site entièrement statique passe de toute façon. - Un runtime en périphérie — Cloudflare Workers, et tout ce qui n'est ni Node
ni statique. Aucun transformateur ne tourne et les images sont servies telles
qu'elles ont été versionnées. Un site prérendu sur un tel runtime ne doit pas
être configuré sur
ipx: c'est une liaison native et elle ne peut pas y tourner du tout. Tournez-vous vers un fournisseur CDN si les transformations sont souhaitées.
Les variantes du dialogue sont générées lors d'une construction statique alors même que le dialogue est fermé au rendu de la page — le composant les déclare lui-même au crawler de prérendu. Rien n'est téléchargé tant que le dialogue n'est pas ouvert.
Une image avec zoom="false" n'en reçoit aucune. Il n'y a pas de dialogue à
ouvrir, donc chaque fichier à la taille du dialogue écrit pour elle serait une
sortie que personne ne peut jamais demander.
Notes
La variante sombre est nommée explicitement plutôt que devinée par convention.
Dériver -dark d'un nom de fichier signifie qu'un fichier manquant devient une
image cassée, et l'auteur ne l'apprend jamais.
Une image issue d'une source clonée depuis un autre dépôt n'a pas de route à
elle : Content télécharge l'arbre dans .data/content/, et rien ne copie ses
ressources dans public/. Les chemins de ces exemples sont absolus vers le
public/ du site consommateur, qui est l'endroit où une image de documentation
doit vivre aujourd'hui.