Les articles de la catégorie « Intégration »

Faire un lien sur toute une zone en CSS

On m’a récemment rappelé sur Twitter une pratique qui m’exaspère au plus haut point ces derniers temps. Sur certains sites, le clic secondaire sur des liens est rendu inutilisable. C’est le cas par exemple sur Factornews (j’aime beaucoup Factornews, notamment quand ils font des jeux de mots comme à la fin de cet article).

Factornews

Dans la première zone d’actualités du site, chaque encart d’actualité est entièrement cliquable. Mais si j’utilise le clic de la molette de ma souris (pour ouvrir le lien dans un nouvel onglet), il ne se passe strictement rien. Si je maintiens appuyé la touche majuscule de mon clavier (pour ouvrir le lien dans une nouvelle fenêtre), mon raccourci sera ignoré et le lien sera ouvert dans la fenêtre courante.

Si le clic se comporte comme ça, c’est parce que ce n’est pas un vrai lien HTML. Ici, pour chaque actualité, seul le titre de l’actualité est dans une balise <a>. Le clic sur le reste de la zone est géré via JavaScript. La volonté de rendre toute la zone cliquable est fortement louable, mais l’annulation du comportement natif d’un navigateur engendrée nuit fortement à l’utilisabilité. Je rencontre ce genre de problèmes régulièrement sur d’autres sites comme Le Bon Coin ou LEGO Ideas.

Ce problème d’intégration est vieux comme le monde. Prenons par exemple le code HTML suivant.

<article class="item">
	<h1><a href="/faire-un-lien-sur-toute-une-zone-en-css">Faire un lien sur toute une zone en CSS</a></h1>
	<p>On m'a récemment rappelé sur Twitter une pratique qui m'exaspère au plus haut point ces derniers temps…</p>
</article>

En XHTML ou en HTML4, une balise <a> ne pouvait contenir que des éléments inline. Du coup, l’utilisation de JavaScript (voire de jQuery) était fortement recommandée pour résoudre ce problème. Aujourd’hui, les quelques lignes suivantes suffiraient à rendre toute la zone cliquable pour tous les éléments .item sur notre page.

document.addEventListener('DOMContentLoaded', function() {
	var items = document.querySelectorAll('.item');
	for(var i=0; i < items.length; i++) {
		var item = items[i];
		item.addEventListener('click', function() {
			var url = this.getElementsByTagName('a');
			if(url.length > 0)
				url = url[0];
			window.location = url;
		});
	}
});

Mais cette solution est à l’origine des problèmes d’utilisabilité qui m’exaspèrent tant.

La spécification HTML5 a changé la donne, et on peut désormais englober dans un <a> n’importe quel élément. On pourrait alors simplement englober tout notre .item d’une balise <a>. Mais ce n’est pas forcément une bonne idée, en particulier pour le référencement où il serait préférable de conserver un contenu texte court et pertinent.

Heureusement, une solution est possible en CSS. En utilisant un pseudo-élément ::before ou ::after, on peut le positionner en absolu par rapport au conteneur principal parent et faire en sorte qu’il occupe tout l’espace. Il faudra bien s’assurer que le conteneur parent en question (.item) ait lui aussi un positionnement non statique afin de restreindre le pseudo-élément du lien. Le code suivant fait alors l’affaire.

.item {
	position:relative;
}

.item a:before {
	content:'';
	position:absolute;
	left:0;
	right:0;
	top:0;
	bottom:0;
	background-color:rgba(0,0,0,0);
}

Ça fonctionne bien dans Chrome, Firefox, Safari et Opera. Pour Internet Explorer (9 et plus), il est nécessaire d’ajouter un fond transparent afin que la zone soit cliquable même au-dessus des autres éléments.

L’inconvénient de cette solution est qu’elle ne s’applique pas sur IE8. Même si le pseudo-élément est bien créé, celui-ci restera non cliquable sous les autres éléments de la zone. Mais si vous pouvez vous permettre d’avoir un fonctionnement dégradé gracieusement sur IE8, cette solution me semble assez élégante.

L’attribut srcset pour des images responsive

L’attribut srcset est un nouvel attribut pour les balises images décrit dans la spécification de HTML 5.1. Il permet de résoudre en partie l’une des plus grosses problématiques de l’intégration de sites responsive : les images.

Venant en complément de l’attribut src habituel d’une balise <img>, l’attribut srcset permet de spécifier une liste d’images à afficher selon des critères particuliers. L’immense avantage, c’est que c’est le navigateur qui décide quelle image afficher, et que seule cette image sera téléchargée.

Son implémentation a déjà commencé depuis février dernier dans la version 34 de Chrome, mais limitée à la sélection d’image selon la densité de pixels de l’écran d’un appareil. Ainsi dans le code suivant, je peux préciser le chemin d’une image optimisée pour des appareils avec une densité de pixels à l’écran d’au moins 2 (aussi vulgairement appelés « écrans rétina »).

<img src="default.jpg" srcset="hidpi.jpg 2x" />

C’est déjà un bon début. Mais ce qui est vraiment bien, c’est que les navigateurs commencent maintenant à implémenter la suite, à savoir la sélection d’images selon le viewport. C’est en cours d’implémentation dans Chrome, Opera, et dans Firefox derrière un flag. Et surtout, ce sera activé par défaut dans Safari 8 sur iOS 8 et OS X Yosemite. Cela signifie que d’ici la fin de l’année, la majorité des navigateurs supporteront srcset. (Pour Internet Explorer, c’est pour l’instant « en considération ».)(On me fait signe que la version beta actuelle de Safari 8 ne supporte que la première partie de la spécification, et pas la sélection d’images selon le viewport).

Il y a donc de quoi être tout excité par cette nouveauté. J’ai fait mumuse avec srcset ces derniers jours, et voici que j’ai appris et compris.

Tout d’abord, pour tester tout ça, il va falloir installer ou configurer les navigateurs compatibles. Pour avoir Safari 8 sur iOS, vous pouvez télécharger la beta de Xcode 6 dans laquelle vous aurez la dernière version du Simulateur iOS. Pour avoir Safari 8 sur OS X, vous pouvez télécharger la bêta publique de OS X Yosemite.
Pour Chrome, vous pouvez télécharger Chrome Canary (actuellement en version 38).
Pour Firefox, vous pouvez télécharger Firefox Nightly (actuellement en version 34). Puis il faudra activer le support de srcset à la main. Pour ça, il faut aller à l’adresse about:config, chercher les préférences dom.image.srcset.enabled et dom.image.picture.enabled, et les passer toutes les deux à la valeur true.

Nous sommes prêts pour tester. Pour commencer, nous allons préciser dans srcset une liste de quatre images : small.png (320×240), medium.png (640×480), large.png (1024×768) et xl.png (1920×1080). Si le navigateur ne supporte pas srcset, c’est l’attribut src qui prendra le relais avec l’image default.png (1024×768). Voici le résultat de ce premier exemple avec le code suivant :

<img src="default.png" srcset="small.png 320w, medium.png 640w, large.png 1024w, xl.png 1920w" alt="Test" />

Comme vous pouvez le constater, chaque image est accompagnée d’un « descripteur w » (dixit la spécification, en opposition au « descripteur x » utilisé lui pour la sélection selon la densité de pixels d’écran). J’ai eu beaucoup de mal à comprendre la signification de ce descripteur, notamment à cause d’article comme celui publié chez Alsacréations (désolé Geoffrey) qui confondent descripteur w et largeur de viewport. À ce stade, il faut bien comprendre qu’il n’y a aucun lien entre les deux. Le descripteur w est une indication laissée au navigateur sur la largeur en pixels de l’image correspondante.

Là où la spécification de srcset est surprenante, c’est dans la façon de décrire comment un navigateur va choisir l’image à afficher :

L’agent utilisateur va calculer la densité de pixel réelle de chaque image à partir du descripteur w spécifié et des tailles de rendus spécifiées dans l’attribut sizes. Il peut ensuite choisir n’importe quelle ressource en fonction de la densité de pixels de l’écran de l’utilisateur, de son niveau de zoom, ou peut-être d’autres facteurs comme les conditions de réseau de l’utilisateur.

En gros : le navigateur fait ce qu’il veut. Cela signifie que Chrome pourrait décider de choisir l’image la plus petite si la connexion est un peu lente, alors que Safari pourrait au contraire décider de systématiquement choisir l’image la plus grande. Bienvenue dans un futur nouvel enfer du responsive !

À noter qu’actuellement, Firefox ne va charger une image qu’au chargement initial de la page, mais ne rafraîchira pas son affichage au redimensionnement de la fenêtre. La dernière version de Chrome Canary gère très bien ça au redimensionnement (et au passage, la nouvelle vue device mode des outils de développement de Chrome est vraiment bien foutue).

Les choses sérieuses vont commencer en introduisant l’attribut sizes. Celui-ci permet de spécifier la largeur d’affichage de l’image selon des points de rupture. Dans l’exemple ci-dessus, les images vont toujours par défaut prendre 100% de la largeur du viewport. La spécification explique clairement que la valeur par défaut de l’attribut sizes est de 100vw et que dans ce cas, l’attribut peut-être omis. Malheureusement les développeurs de chez Mozilla ont du s’arrêter de lire la spécification juste avant ce paragraphe car dans l’implémentation actuelle, il faut impérativement préciser l’attribut sizes, même pour une valeur de 100vw. (J’ai remonté le problème, et il a déjà été affecté, donc on peut espérer une correction en moins de douze ans.)

Là où sizes est bien pratique, c’est qu’il va permettre de spécifier différentes largeurs d’affichage pour notre image selon le reste du gabarit de notre page. Si par exemple je suis sur un viewport de 800px de large, mais que j’affiche mon image à seulement 400px de large, je peux le préciser dans l’attribut sizes et le navigateur tentera de charger l’image qu’il juge la plus appropriée. En reprenant le premier exemple et en y appliquant cette condition, ça donnerait ce deuxième exemple et le code suivant :

<img src="default.png" srcset="small.png 320w, medium.png 640w, large.png 1024w, xl.png 1920w" sizes="(width:800px) 400px" alt="Test" />

Ici, l’image est toujours affichée à une largeur correspondant à 100% du viewport, sauf si celui-ci vaut exactement 800px, auquel cas l’image est fixée à une largeur de 400px. Cet exemple est totalement stupide dans la vraie vie.  On pourra alors plutôt écrire ce genre de code un peu verbeux pour ce troisième exemple :

<img src="default.png" srcset="small.png 320w, medium.png 640w, large.png 1024w, xl.png 1920w" sizes="(min-width:20em) and (max-width:50em) 20em, (min-width:50em) and (max-width:80em) 40em, (min-width:40em) 10em" alt="Test" />

J’ai défini ici trois points de rupture précisant des tailles d’images respectivement de 20em, 40em et 10em. Si aucune des conditions des points de rupture n’est remplie, alors le navigateur appliquera à l’image sa valeur par défaut de 100vw.

Là où ça peut devenir un joyeux bazar, c’est qu’on peut combiner les descripteurs w et x. Et tout ça peut être combiné à la balise <picture> qui elle aussi commence à être supportée. Opera a récemment publié un article pour donner des cas pratiques d’utilisation d’images responsive. L’article d’Eric Portis sur « srcset et sizes » est magnifiquement illustré et très complet sur le sujet et m’a aidé à comprendre un tas de trucs.

Les inconnues inconnues

En préparant ma conférence pour Sud Web 2014, je suis tombé sur une excellente citation de Donald Rumsfeld, ancien Secrétaire à la Défense des États-Unis d’Amérique. Il répondait en 2002 aux reproches de journalistes sur le manque de preuves de possession d’armes de destruction massive par le gouvernement Irakien.

Il y a des connaissances connues; ce sont les choses que nous savons que nous savons. Nous savons aussi qu’il y a des méconnaissances connues; c’est à dire que nous savons qu’il y a des choses que nous ne savons pas. Mais il y a aussi des inconnues inconnues, celles que nous ne savons pas que nous ne savons pas.

Ça lui a valu un Foot in Mouth Award. Mais j’ai trouvé que ça collait parfaitement à la description de mon métier d’intégrateur et la façon dont j’aborde ma veille.

Tout d’abord, il y a les choses que je sais que je sais. Par exemple, je connais les positionnements flottants en CSS, et je sais m’en servir. Ensuite, il y a les choses que je sais que ne sais pas. Par exemple, je sais qu’il existe des polices d’icônes, mais je ne sais pas m’en servir car je n’ai jamais eu l’occasion d’en utiliser sur aucun de mes projets. Enfin, il y a les choses que je ne sais même pas que je ne sais pas. Je ne peux pas vous donner d’exemple, puisque par définition, je ne sais pas ce que je ne sais pas.

Le but pour moi dans ma veille quotidienne est de réduire au maximum cette zone « d’inconnues inconnues ». Le métier d’intégrateur devient de plus en plus complexe et diversifié. Il y a deux ans, déjà, j’écrivais qu’une maîtrise complète de l’intégration n’est plus possible. Du coup, l’objectif pour moi est d’avoir une vision suffisamment large pour qu’à chaque choix que je doive faire lors d’une intégration, j’ai un maximum de cartes en main pour m’assurer de faire le bon choix.

Un casse-tête en intégration à base de grille

J’aime bien les casse-tête. Et quand je dis j’aime bien, je veux en fait dire que ça a le don de m’énerver et de me rendre complètement fou. Il y a quelques temps j’avais posté un joli casse-tête en intégration. Raphaël Goetter avait aussi publié sur son blog trois défi « T’es pas cap ! » (un, deux, trois). Hier soir, je suis tombé sur un nouveau problème bien casse-tête en intégration. Je l’ai posté sur Twitter afin de m’assurer que je n’étais pas passé à côté d’une solution évidente. En voyant ce matin le titre d’une solution inachevée proposée par Kaelig ou certains tweets nocturnes, j’ai ma confirmation que c’est bien un nouveau joli casse-tête.

Voici le casse-tête en question :

Une grille casse-tête en CSS

  1. Les éléments (ici en orange) ont une largeur fixe de 200px.
  2. La grille est fluide et contient 4 éléments par ligne.
  3. Les « cellules » de la grille (ici en gris) ont une marge de 10px à gauche et à droite, sauf :
    • La première cellule de chaque ligne, qui n’a pas de marge à gauche
    • La dernière cellule de chaque ligne, qui n’a pas de marge à droite
  4. Les éléments sont centrés horizontalement dans leur cellule, sauf :
    • Le premier élément de chaque ligne, qui est aligné à gauche
    • Le dernier élément de chaque ligne, qui est aligné à droite
  5. Les éléments sont répartis équitablement sur chaque ligne. Les rectangles gris sont de la même largeur partout.
  6. Le premier élément et le dernier élément de chaque ligne sont collés respectivement à gauche et à droite de la grille.
  7. Toutes les cellules ont le même parent direct. Le code HTML doit donc ressembler à quelque chose comme : .grille > .cellule > .element. Vous pouvez ajouter des classes spécifiques en plus si besoin.
  8. Pas de JavaScript, que du HTML et CSS.
  9. La solution doit fonctionner à partir d’IE9 (et se dégrader gracieusement pour les plus vieux navigateurs).

Pour vous aider à démarrer, j’ai mis à votre disposition un exemple de code HTML et CSS sur CodePen. Vous êtes libre de modifier le code HTML et CSS, tant que vous respectez les règles ci-dessus.

Je vous invite à partager vos réalisations dans les commentaires. Je publierais la solution que j’ai trouvé dans le courant de la semaine. Amusez-vous bien !

Comment Apple adapte des pages pour mobile sur un site pas responsive

La semaine dernière, Apple fêtait les 30 ans du Mac avec une page dédiée. Je suis tombé sur cette page via John Gruber, qui s’extasiait qu’elle soit responsive. Ça m’a tout de suite surpris, car j’ai tendance à penser qu’Apple n’aime pas trop le responsive design (même s’ils ont fait quelques tentatives sur certains e-mails).

Aussitôt les 11,3 Mo de la page chargée sur mon ordinateur (sans commentaire), je me suis empressé d’assouvir mon TOC d’intégrateur et de redimensionner la page. Et là, surprise, il se passe bien quelque chose (les textes sont redimensionnés), mais la page n’est clairement pas responsive.

Je vais donc visiter la page sur mon iPhone, curieux de voir le rendu de cette page. Après tout, le gabarit du site d’Apple n’est pas responsive. Alors comment est-ce qu’Apple aurait pu rendre une page responsive sur un site non responsive ?

La réponse est simple : en trichant.

apple-30-years-mobile

Comme le dirait Cyril Lignac, « c’est très astucieux ». Plutôt que de rendre leur gabarit responsive (avec l’enfer que ça peut engendrer), les intégrateurs d’Apple ont préféré simplement mettre à l’échelle le header et le footer du site. Pour ça, ils utilisent simplement la propriété transform:scale(0.3125); (où 0.3125 est une valeur calculée en JavaScript le ratio calculé entre la taille du site, 1024px, et la largeur de l’écran d’un iPhone, 320px).

La page d'Apple vue dans l'inspecteur web de Safari Mobile

La propriété transform étant ce qu’elle est, Cyril Lignac s’exclamerait surement aussi « c’est très gourmand ».

Si ce n’est clairement pas une solution optimale (la navigation reste difficilement utilisable dans cet état), je trouve l’idée quand même très originale. À noter que la même chose avait été utilisé par Apple pour la page du Mac Pro.