Je m’amuse depuis quelques temps avec scrollbar-gutter, cela me résout quelques soucis d’alignements et de stabilité d’interfaces dans des applications comme Proton Mail et d’autres.
scrollbar-gutter, c’est quoi t’est-ce ?
Depuis des années et pour des besoins de designs bien léchés si j’ose dire, certains cherchent à éviter les décalages de mise en page provoqués par l’apparition ou la disparition des barres de défilement. La solution historique consistait à forcer :
html {
overflow-y: scroll;
}Cracra, insupportable et disgracieux. Avec scrollbar-gutter, on pouvait enfin écrire :
html {
scrollbar-gutter: stable;
}L’idée semblait simple : réserver l’espace de la barre de défilement afin que la largeur de la page reste constante, notamment si une barre de défilement apparait dans une zone d’une interface.
Enfin… c’est ce que je pensais.
Une découverte étonnante
En utilisant cette propriété, un curieux quiproquoi (= un quiproquo et le dev dit « kewaaaa ? ») m’est arrivé. Tout fier de mon travail d’alignements divers et variés dans l’interface sur laquelle je travaille, j’envoie en test… et on me signale que l’alignement n’y est pas. « Kewaaaa ? »
En creusant un peu et en la faisant courte, j’ai remarqué un comportement étrange sur macOS. Avec une souris branchée, tout fonctionne comme prévu : la page réserve un espace pour la barre de défilement.
Puis je débranche la souris. Instantanément, toute la mise en page s’élargit d’une quinzaine de pixels. Pourtant, le CSS n’a pas changé, scrollbar-gutter est toujours appliqué.
Et pourtant… l’espace réservé a disparu. « Kewaaaa ? »
Le test
En regardant les dimensions du viewport, on obtient ceci.
Avec une souris connectée :
window.innerWidth // 1680
document.documentElement.clientWidth // 1665La différence de 15 pixels correspond à l’espace réservé pour la barre de défilement. Okay.
Après avoir débranché la souris :
window.innerWidth // 1680
document.documentElement.clientWidth // 1680La différence disparaît. Le viewport vient de gagner 15 pixels.
Toute la mise en page est recalculée. « Kewaaaa ? »
Ce n’est pas un bug d’un navigateur
Le plus surprenant est que le phénomène est reproductible dans :
- Safari
- Chrome
- Firefox
Les trois navigateurs se comportent de la même façon. Plus moyen de pester contre l’un ou l’autre, c’est un complot. Plus sérieusement, cela suggère qu’ils suivent tous le comportement fourni par macOS.
Pourquoi ?
Le diable se cache dans les détails, en l’occurence, dans la spécification de scrollbar-gutter.
scrollbar-gutter: stable ne signifie pas :
« Réserve toujours un espace pour la barre de défilement. »
Cela signifie plutôt :
« Réserve un espace lorsque des classic scrollbars existent. »
Or macOS utilise deux types de barres de défilement :
- les classic scrollbars, qui occupent de l’espace dans le layout ;
- les overlay scrollbars, qui sont dessinées par-dessus le contenu.
Et macOS peut passer dynamiquement de l’une à l’autre.
Par exemple lorsque l’utilisateur branche ou débranche une souris, si le réglage système est « Afficher les barres de défilement : automatiquement selon la souris ou le trackpad ».
Lorsque le système passe en mode overlay, le gutter n’a plus de raison d’exister selon la spécification.
Le navigateur le supprime. La page change alors de largeur.
Le problème
Le mot stable est, selon moi, trompeur. En tant que développeur, je comprends naturellement :
« Si j’utilise cette propriété, ma mise en page ne bougera plus à cause des barres de défilement. »
En réalité, ce n’est vrai que tant que le système continue d’utiliser des barres de défilement classiques.
Le moindre changement de mode peut provoquer un recalcul complet de la largeur du viewport. Cela entraîne :
- des layouts qui se décalent horizontalement ;
- des éléments centrés qui changent de position ;
- des calculs basés sur la largeur du viewport qui évoluent ;
- des interfaces qui « sautent » sans que le CSS n’ait changé.
Et surtout du travail supplémentaire pour moi… vous me voyez coder en branchant et débranchant ma souris régulièrement ????
Autrement dit, scrollbar-gutter: stable n’est pas réellement stable dans tous les cas. Et quand on doit gérer des alignements fins entre éléments qui sont dans des zones scrollables ou non… point de salut ainsi, il faut trouver des solutions plus tordues.
Que pourrait-on faire ?
Je comprends pourquoi la spécification a été écrite ainsi. Mais je pense qu’il manque aujourd’hui une façon d’exprimer une intention différente :
« Je souhaite réserver un espace pour la scrollbar, quel que soit le mode utilisé par le système. »
Par exemple avec une valeur comme :
scrollbar-gutter: always;ou tout autre mécanisme permettant de garantir que la largeur du viewport de mise en page ne changera jamais. Aujourd’hui, ce n’est tout simplement pas possible, en tout cas pas avec scrollbar-gutter. Tristitude.
Conclusion et à suivre
Bref, j’ai sacrifié un châton innocent, et ouvert un ticket auprès du CSS Working Group, pour savoir s’il serait pertinent d’introduire une manière de demander un gutter réellement… stable.
CSS is awesome, je me tape des scrollbars de rire.