Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Images - Amélioration de la fonctionnalité Highlight #8

Open
PhilippeVay opened this issue Apr 1, 2024 · 2 comments
Open

Images - Amélioration de la fonctionnalité Highlight #8

PhilippeVay opened this issue Apr 1, 2024 · 2 comments

Comments

@PhilippeVay
Copy link

Description du problème

La fonctionnalité highlight tente de distinguer les éléments audités par un critère du reste du contenu en ajoutant une opacité de 0.2 au reste du contenu mais pas aux éléments audités (ex: "les images" ou "les tableaux").
Pour les images, certains nœuds texte ne sont pas correctement estompés : les nœuds texte ayant un parent commun avec une image ne deviennent pas translucides et il n'est pas possible de leur ajouter une opacité à moins de les encapsuler dans un span (pas une bonne idée quand on ne connait rien de la page) ou d'également rendre translucide l'image en l'ajoutant sur le parent.
Sur https://www.alsacreations.fr, les 4 images HTML5, etc sont estompées mais pas les p ou li ayant un span avec du texte.

image

Pistes...

Après avoir passé une journée à développer une solution JS, SVG et mask-image, je viens d'en (re)trouver une autre en pur CSS ou presque...
On peut en effet :

  • passer tout le texte en color: rgba(0, 0, 0, 0.2) 👻 (et supprimer fonds, bordures, etc)
  • distinguer les "images" en leur ajoutant outline et bordure (prévoir le cas overflow: hidden)
  • ne pas se préoccuper de la spécificité en dégainant @layer et !important 🌋

Pour la spécificité, j'avais remonté chez ffoodd ce que permettait @layer (snippet d'exemple et lien vers l'article de référence de bram.us) avec 1 seule limitation bien rare.1

Solution proposée (CSS et JS) ⬇️

Placer le at-layer le plus tôt possible (JS)

Placer notre @layer nommé le plus tôt possible dans la page. Pas essentiel à moins de devoir écraser d'autres styles eux aussi dans un @layer et avec !important, ça fait beaucoup de conditions...

(function () {
  let e = document.createElement("style");
  e.prepend("@layer rgaac-hl-img;");
  document.head.querySelector("title").after(e);
})()
Highlight les images d'une page (CSS)
/* Highlight "images" in a page */
@layer rgaac-hl-img {
  * {
    filter: none !important; /* Images may have weird filters */
  }

  /* Everything except images */
  *:not(:where(
    img,
    [role="img"],
    area,
    svg,
    :where(input, object, embed)[type="image"])) {
    color: rgba(0, 0, 0, 0.2) !important;
    background: transparent !important;
    border-color: transparent !important;
    outline-color: transparent !important;
    text-shadow: none !important;
  }

  /* All type of images and then SVG. TODO: <canvas> */
  :where(
    img, /* includes [ismap] */
    [role="img"],
    area,
    :where(input, object, embed)[type="image"]) {
      outline: 2px dashed green !important;
      outline-offset: -2px !important;
      box-shadow:
        4px 0 0 -2px darkred,
        0 4px 0 -2px blue !important;
  }
  
  svg {
    border: 3px solid green !important;
    outline: 3px dashed white !important;
    outline-offset: -3px !important;
  }

  /* Pseudo-elements are not valid in the selector list for :is() or :where() */
  *::before,
  *::after {
    color: rgba(0, 0, 0, 0.2) !important;
    background: transparent !important;
    border-color: transparent !important;
    outline-color: transparent !important;
    text-shadow: none !important;
  }

  /* Detect and flag : inline styles with !important are more specific
      than any @layer :( (for now, all properties are selected even if commented) */
  body:has( [style*="!important"] )::before {
    content: "Page contains inline style with !important" !important;
    position: relative !important;
    z-index: 987654 !important;
    display: block !important;
    margin-bottom: 1rem !important;
    padding: 1rem !important;
    color: white !important;
    background: darkred !important;
  }
}

Tests

URLs de test :

Vu ailleurs

Du côté de la concurrence 🕵️‍♂️ :

  • Entre temps j'ai décompilé le bookmarklet Isolate images d'A11Y-Tools (Lloydi) et il a des différences (code lisible dans un gist).
    En JS, il cible chaque élément qui n'est pas une image et applique el.style.color="transparent";, etc Le texte est réduit à une taille de 1px aussi, pour ne garder que les images.
    Sur les images, pour les distinguer, un outline de 10px (offset de -10px) noir est ajouté.
  • ARC Toolkit : pas testable ce WE

Footnotes

  1. Cela écrase bien aussi les styles inline mais je viens de me rendre compte (ou bien j'avais oublié depuis) qu'un style inline avec !important aura toujours plus de spécificité que n'importe quel @layer. Mmh si j'en ai rencontré une fois, c'était il y a 10 ans dans du GG Maps embedded ou du temps d'IE6 et ça a disparu il y a bien longtemps. Bref c'est plus que rare et ça se détecte en CSS.

@jonathan-vallet
Copy link
Collaborator

Merci beaucoup pour le retour très complet!

Effectivement avec la méthode qu'on a utilisé il y a quelques nodes texte qui peuvent passer au travers.
On n'était pas vraiment dans l'optique de masquer complètement le reste quand on active le "highlight" (plus tard on proposera à chacun de régler l'opacité comme il le souhaite dans des paramètres), mais l'idée est de conserver le contexte autour des éléments.
À terme si les performances ne sont pas trop impactées, l'idée serait d'avoir un canvas par dessus la page avec un masque, et de "faire des trous" dedans aux emplacement de chaque éléments mis en avant. comme ça déjà on n'aurait plus du tout d'adhérence à modifier le style de la page (ce qui peut être problématique puisqu'on peut révéler des éléments qui étaient en "opacity: 0". Et ça peut être très performant puisqu'on peut trouer juste les éléments qui sont visibles dans le viewport plutot que toute la page, et ainsi mettre à jour le masque en temps réel au scroll/resize par exemple.

On a également une feature qui devrait arriver prochainement permettant de trouver un élément spécifique dans la page :)

On va creuser les idées suggérées en tout cas en attendant

@PhilippeVay
Copy link
Author

La version avec un grand masque SVG par dessus la page et d'autres transparents (1 par image, pile par dessus) est ce que j'avais préparé initialement (ou 1 seul masque avec 1 gris foncé et plein de blancs par dessus les images, le résultat est le même).
Code : https://gist.github.com/PhilippeVay/9ed5dc541f7c51f2b30924ee7b292b99

Inconvénient de cette méthode : ça fait chauffer le CPU dans de longues pages, ce que ne fait pas la version rgba(). Cet article fait passer un Ryzen 5 mobile 3xxx de 4% à 100% pendant le scroll au lieu de ~20% sans les masques.

Pour revenir au Highlight actuel, il y a aussi des images qui sont masquées (faux négatifs), cf. capture d'écran ci-dessus, ce qui est plus embêtant que quelques faux positifs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants