Du responsive sur ce blog

Skelets numériques

Première publication le 02/05/2024
Dernière modification le 12/08/2024
Temps de lecture estimé: 7 m
Post suivant : Support Trans
Post précédent : Hugo, générateur de site statique
Tags liés à cet article: technologie meta

Du responsive sur ce blog

J’utilise parfois des images dans certains de mes billets, par exemple quand je veux mettre des memes ou embarquer du code. L’ennui, c’est que ça rend la lecture difficile sur mobile. Comment faire pour gérer ça proprement et avec le minimum d’effort?

Préliminaires

J’ai beaucoup utilisé la console de debogage du navigateur (sous Firefox, Ctrl+Shift+C) et modifié des éléments de style directement dans le navigateur. Il est possible de basculer sur un affichage qui émule différents types d’appareils mobiles dans cette vue.

Une première approche: des images responsives via Hugo

Une solution: utiliser les capacités de mon générateur de site Hugo pour générer l’image qui correspond à la dimension de l’écran.

Il y a des modules Hugo pour ça. Reste à savoir ce qu’est un module Hugo. Hugo étant écrit en go, il s’agit probablement d’un module go. Il a fallu découvrir comment installer ce truc. Étant sous NixOS depuis peu, j’ai dû installer go dans mon home-manager. Et apprendre le système de build et de module de go. On est là pour apprendre donc pourquoi pas.

La première étape une fois go installé, c’est d’enregistrer son propre site web comme comme un module go (pourquoi? probablement pour qu’on puisse interagir avec avec le reste de la boîte à outil go). Pour se faire, hugo mod init "github.com/handle-github/nom-de-mon-repo". Ensuite, on ajoute le chemin des modules dans le config.toml :

[module]
  [[module.imports]]
    path = 'github.com/future-wd/hugo-responsive-images'

J’ai choisi ce module parce qu’il me permet d’utiliser un shortcode directement dans le markdown, plutôt que de définir des partials. Sauf qu’il ne marche pas :’) (ça vomit des erreurs de conversion en string)

Retour aux bases : les images responsive

Mais en fait, c’est quoi une image responsive?

Ma compréhension initiale, c’est qu’il y a plusieurs composants:

Il y a un processus de transformation d’image qui doit s’opérer à un moment. Est-ce que c’est au navigateur de le faire automatiquement (ce qui m’arrangerait bien), auquel cas il suffirait de spécifier des règles bien choisies sur le CSS? Ou alors est-ce que je dois moi-même transformer les images et rendre chaque variante d’image en fonction de la taille de l’écran (ça a l’air pénible à faire)? En résumé, plutôt que de passer par un module Hugo, est-ce que je ne voudrais pas plutôt apprendre à faire du CSS pour que ça marche bien?

On va se renseigner sur comment faire. Selon le site de développement de mozilla, on peut utiliser une balise media qui conditionne l’emploi de styles CSS en fonction de la taille. Ça semble pas mal! En particulier, il y a l’attribut aspect-ratio que je pourrais tester. Il y a un tutoriel sur W3Schools dont je pourrais m’inspirer. On va faire deux styles simples:

.figure-desktop{
  max-width: 100%;  
  height: auto;
 }
 .figure-mobile{
  max-width: 90%;  
  height: auto;
 }

On va rajouter un shortcode figure dans Hugo pour utiliser ça. Concrètement, je rajoute dans un fichier layouts/shortcodes/figure.html le code original du shortcode.

<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
  {{- if .Get "link" -}}
    <a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
  {{- end -}}

  {{- $u := urls.Parse (.Get "src") -}}
  {{- $src := $u.String -}}
  {{- if not $u.IsAbs -}}
    {{- with or (.Page.Resources.Get $u.Path)
     (resources.Get $u.Path) -}} {{- $src = .RelPermalink -}}
    {{- end -}}
  {{- end -}}

  <img src="{{ $src }}"
    {{- if or (.Get "alt") (.Get "caption") }}
     alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
    {{- end -}}
    {{- with .Get "width" }} width="{{ . }}"{{ end -}}
    {{- with .Get "height" }} height="{{ . }}"{{ end -}}
    {{- with .Get "loading" }} loading="{{ . }}"{{ end -}}
    class="responsive"
  ><!-- Closing img tag -->
  {{- if .Get "link" }}</a>{{ end -}}
  {{- if or (or (.Get "title") (.Get "caption"))
   (.Get "attr") -}}
    <figcaption>
      {{ with (.Get "title") -}}
        <h4>{{ . }}</h4>
      {{- end -}}
      {{- if or (.Get "caption") (.Get "attr") -}}<p>
        {{- .Get "caption" | markdownify -}}
        {{- with .Get "attrlink" }}
          <a href="{{ . }}">
        {{- end -}}
        {{- .Get "attr" | markdownify -}}
        {{- if .Get "attrlink" }}</a>{{ end }}</p>
      {{- end }}
    </figcaption>
  {{- end }}
</figure>

Miracle, il y a un champ class déjà existant dans le code. J’ai seulement à le mettre par défaut par la classe résultant de la media-query. En modifiant la figure, ça ne change pas la taille. Pas grave, je vais mettre la classe sur toutes les images embarquées dans la classe figure. Ça marche sur desktop. J’essaie de faire une media-query avec le CSS suivant:

/* Responsive figures */
@media (aspect-ratio: 16/9) {
    .responsive {
            max-width: 60%;
            height: auto;
    }
}
@media (aspect-ratio: 9/16) {
    .responsive {
            max-width: 80%;
            height: auto;
    }
}

et ça ne marche plus sur mon PC. Un tour dans les options développeurs de mon navigateur (Ctrl+Shift+C) m’indique que les images ne sont plus concernées par la règle définie par la classe reponsive.

J’ai probablement mal écrit ma requête media. Peut-être que aspect-ratio n’est pas la bonne clef, ou que mon écran n’est pas en 16/9 (c’est un 1366*768 donc en effet, ça ne tombe pas pile sur 16/9). Pour éviter de tomber dans des travers d’arrondi d’entier hasardeux, on va basculer sur l’attribut CSS orientation, qui n’a que deux clefs: landscape et portrait qui correspondent respectivement à l’orientation de l’écran: paysage ou portrait. J’en profite aussi pour ajouter un centrage des figures et un style différent pour les légendes. Le résultat final est le suivant:

/* Responsive figures */
figure{
  align: center;
}
figcaption {
  text-align: center;
  font-style: italic;
}
@media (orientation: portrait) {
    .responsive {
	    display: block;
	    margin-left: auto;
	    margin-right: auto;
        max-width: 90%;
        height: auto;
    }
}
@media (orientation: landscape) {
    .responsive {
            max-width: 100%;
            height: auto;
    }
}

C’est satisfaisant comme résultat sur mon blog, les images prennent la bonne taille et ne déforment pas le texte. En revanche, une limite qu’on aura rapidement, c’est que les images au format paysage seront potentiellement beaucoup moins lisibles sur mobile, car potentiellement écrasées. C’est là que produire des images en fonction de différentes caractéristiques du média est nécessaire.

Aparté: configurer son CSS avec des variables

Une question que je me pose: est-ce que je peux définir des variables css via des requêtes media? Ce qui me permettrait de changer la largeur maximale et les marges pour mon blog suivant l’orientation? hé bien oui, il suffit d’embarquer les requêtes media dans la directive :root du CSS. Ce qui me permet d’écrire:

/* Some basic variables instanciated in the root pseudo-class, as specified in https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties */
:root {
--main-bg-color: #031b00;
--main-color: #fff;
--box-bg-color: #2e130c;
--box-blue-bg-color: #000033;
--font-size: 1em;
--link-color: #FFB871;
--link-visited-color: #D16300;
@media (orientation: portrait){
  --max-width: 40em;
};
/* Slightly larger width when in landscape */
@media (orientation: landscape){
  --max-width: 50em;
};
[...]
}

Comment un billet sur le responsive fini par ne pas l’être

En mettant en ligne une première version de ce billet, je me suis rendu compte que le rendu des codes contenus dans les boîtes précédentes était foireux: il débordait vers la droite, ce qui le rendait illisible sur mobile 🥲

En passant à la ligne le commentaire, on retrouve le résultat voulu. Il n’empêche que j’aimerais bien avoir un rendu de code où les lignes de texte passent à la ligne toute seules. En observant le HTML généré, un affichage de code inline une succession d’objets span encapsulés dans un objet code, lui-même dans un objet pre. On aimerait donc spécifier comment devrait se comporter un pre code (j’apprends qu’en CSS, on peut spécifier le style d’un objet B inclu dans un objet A avec A B { /*style*/ } . Est-ce que B doit être directement inclu, ou alors y’a-t-il une règle d’indirection?).

CSS est bien fait et on trouve la propriété overflow. Je voulais ajouter un ascenseur horizontal sur mobile à l’aide de l’attribut overflow-x: scroll, mais je n’arrive pas à avoir un ascenseur horizontal sur toute la boîte de contenu. Frustré, j’ai cherché directement “Hugo wrap lines inline code” sur un navigateur de recherche, et un Jésus sur le github de Hugo a répondu à ma question. Avec l’attribut white-space

pre code {
    white-space:pre-wrap;
}

on a les espaces conservés (c’est mieux pour respecter l’indentation), et le code passe à la ligne quand nécessaire.

Retour en haut de page Tous mes posts sont en licence CC-BY-NC-SA 4.0