L'ouvrage "Adaptive Web Design" d'Aaron Gustafson, dont nous avons fait le retour de lecture récemment est un excellent condensé du principe de développement par "Amélioration Progressive" (progressive enhancement) dans le Web.
Nous allons tenter de comprendre cette approche et en quoi elle est primordiale à travers un cas concret : la conception d'un menu de navigation responsive.
Amélioration progressive ?
L'amélioration progressive, ou "web design adaptatif", n'est pas une question de compatibilité. Il n'est pas question de savoir "dois-je supporter IE6 ou non?", cela va au-delà de ces basses considérations :)
L'enjeu est plutôt de comprendre :
- de quoi est composé le parc des agents utilisateurs et périphériques susceptibles de tirer information de votre site (navigateurs, moteurs de recherche, synthèses vocales, imprimantes, smartphones)
- le plus petit dénominateur commun à tous les agents utilisateurs afin que le contenu soit toujours restitué
- d'enrichir progressivement l'expérience utilisateur pour les agents utilisateurs qui supportent ces améliorations
Plus schématiquement, et selon Aaron Gustafson, l'amélioration progressive c'est passer d'une expérience minimale vers l'expérience exceptionnelle :
- Le contenu textuel
- La sémantique HTML
- L'enrichissement visuel
- L'interaction avec le visiteur
- L'accessibilité
Note : l'amélioration progressive possède son alter ego, la dégradation gracieuse. Cette dernière part du principe inverse : tous les navigateurs récents doivent être privilégiés, et l'on autorise des alternatives, bidouilles, scripts pour pallier les déficiences des anciens.
Le contenu et l'information, l'essence du Web
Cette façon de percevoir les choses modifie sensiblement l'approche "compatibilité navigateurs". En effet, à partir du moment où le 1er niveau est atteint, le site est forcément "compatible" sur tous les agents utilisateurs. Tout les autres paliers contribuent à enrichir progressivement l'expérience de l'utilisateur.
Bien que semblant basique à mettre en pratique, cette philosophie de conception a l'avantage de bien remettre les choses au point et à leur place.
Et surtout, ça nous rappelle une priorité qui doit surclasser toutes les autres : n'entravez jamais l'accès au contenu.
Et malheureusement, les entraves à l'accès au contenu sont plus nombreuses qu'on ne le croit :
- des liens, ou des navigations, ne fonctionnant qu'avec JavaScript ou Flash
- des contenus masqués par défaut en CSS
- des contenus en display: none alors qu'ils devraient être perceptibles aux lecteurs d'écran
-
des contenus "faussés" (des
alt
outitle
ne servant que de conteneurs de mots-clés) - des images, vidéos sans alternative textuelle ou sous-titrage
- des tailles de contenus minuscules et non agrandissables
- etc.
Exemple concret : une navigation responsive en JavaScript
Passons aux choses sérieuses : mettons tout cela en pratique en prenant comme exemple un menu de navigation responsive.
1er palier : le contenu textuel
Rien de particulier à ce stade, il s'agit de conférer aux liens des contenus explicites :
- Accueil,
- À propos,
- Blog web,
- Alsacréations,
- Contact.
Même hors de son contexte, ce contenu demeure explicite… à condition que tous les visiteurs connaissent ce qu'est un "blog" et savent ce qu'est "Alsacréations".
Profitons-en pour les dépanner un peu et enrichissons l'information lorsqu'il le faut. Un attribut title
sera parfait pour une infobulle :
<a href="http://www.alsacreations.com" title="Alsacréations, communauté d'apprentissage web"><Alsacréations</a>
N'abusez pas, comme on le voit souvent, de l'attribut title
, en le casant sur tous vos liens. Il n'est utile que s'il véhicule une information supplémentaire. Si vous l'appliquez partout, vous ne ferez que parasiter la lecture des synthèses vocales.
2e palier : la sémantique HTML
Le bon choix des éléments
La sémantique commence par le choix judicieux et approprié des éléments HTML (balises) afin que les agents utilisateurs puissent exploiter leurs fonctions.
Il m'a semblé naturel d'opter pour l'élément HTML5 <nav>
, qui remplit parfaitement cette mission à lui seul.
<nav id="navigation">
<a href="#">Accueil</a>
<a href="#">À propos</a>
<a href="#">Blog web</a>
<a href="#">Alsacréations</a>
<a href="#">Contact</a>
</nav>
Une liste de liens ?
Il est de coutume de structurer une navigation sous forme d'une liste de liens (<ul>, <li>
). Sachez que la présence des éléments de liste n'est pas obligatoire, c'est plus par habitude que par réelle nécessité. Bref, à vous de voir !
Un identifiant pour le menu
Les liens d'évitement sont bénéfiques à la navigation clavier et pour les déficients visuels. Dans cette optique, ajoutons un identifiant (id
) à la navigation afin de servir d'ancre aux liens d'évitement; Il nous servira également de marqueur plus tard pour JavaScript :
<nav id="navigation">
Une classe aussi ?
Il n'est pas recommandé de cibler les éléments HTML via leur attribut id
pour des raisons de maintenabilité du code. Nous pouvons donc nous rabattre sur une classe, que nous allons également ajouter :
<nav id="navigation" class="navigation">
Un <button>
pour le bouton, forcément
Le meilleur choix sémantique et en terme d'accessibilité pour représenter un bouton de navigation sera systématiquement l'élément <button>
.
N'oubliez jamais de lui associer un attribut type="button"
afin d'assurer une compatibilité universelle.
<button class="nav-button" type="button">menu</button>
Les microdonnées
Nous pourrions en profiter pour enrichir la sémantique via HTML5 microdata car la documentation Schema.org nous fournit des microdonnées spécifiques à la navigation d'un site web : il s'agit de SiteNavigationElement.
Cependant, à l'heure actuelle, il faut bien avouer que nous ne savons pas très bien si cette microdonnée de navigation est déjà exploitée par les agents utilisateurs ou les moteurs de recherche. Libre à vous d'attendre un peu qu'elle se démocratise (ou pas) avant de l'employer en production.
3e palier : enrichissement visuel (CSS, images, vidéos)
Allez hop ! On passe à l'étape graphique, l'enrichissement le plus perceptible. Mais cela demeure un enrichissement comme un autre, rien de plus.
Nous allons appliquer les styles CSS en gardant à l'esprit que tous les navigateurs ne supportent pas les dernières fioritures de CSS3.
Cette navigation prendra forme en tenant compte des considérations d'ergonomie et d'accessibilité (taille de police, contrastes de couleurs, taille du lien cliquable). Quel que soit l'agent utilisateur, l'information doit être exploitable.
Nous disposons de plusieurs moyens pour cibler le bloc de navigation en CSS :
-
nous pouvons cibler directement l'élément
<nav>
(mais attention, il peut y avoir plusieurs<nav>
dans une page), -
il est possible de se servir du sélecteur d'
id
(en ce qui me concerne, je ne suis pas très fan car il a trop de poids dans le calcul des sélecteurs), -
l'attribut de
class="navigation
" (que nous avons ajouté pour cela), -
nous servir du sélecteur d'attribut via
[id=navigation]
ou encore[role=navigation]
(que l'on aborde dans la dernière étape)
Personnellement, j'ai fait le choix de cibler simplement à l'aide de .navigation.
Les styles de base concerneront tous les devices (mobiles et desktop) et demeureront très basiques :
.navigation {
background: #333;
}
.navigation > a {
display: block;
padding: 10px;
background: rgba(255,255,255,.3);
border-top: 2px solid #fff;
color: #fff;
text-decoration: none;
}
Styles pour le bouton
De nombreuses possibilités vous sont offertes pour styler le bouton de navigation: CSS pur, SVG, font-icons, etc. Je vous propose de piocher des idées en CSS pur sur cette ressource Codepen dont les codes sont récupérables à souhait.
Notez que par défaut, nous allons masquer le bouton, puis l'afficherons uniquement sur petit écran (et lorsque JavaScript est activé) :
.nav-button {
display: none; /* no button by default */
}
Styles pour mobiles
Les styles et positionnements pour mobiles devront s'appliquer uniquement lorsque deux conditions sont réunies :
- la taille de l'écran est suffisamment petite (cette détection se fera en CSS via media queries)
- JavaScript est activé et le navigateur est suffisamment moderne pour le reconnaître (cette détection se fera en JavaScript)
Le principe d'enrichissement progressif intervient de la sorte : tant que les conditions sus-citées ne sont pas remplies, alors la navigation s'affichera de manière brute, dans le flux du document, les liens les uns sous les autres et parfaitement accessibles.
Sans surprise, la partie des CSS3 media queries vient appliquer des styles aux fenêtres de moins de 545 pixels de large et est assez intuitive :
@media (max-width: 545px) {...}
Les media queries ne sont pas reconnus par IE8 et versions inférieures. Dans notre démarche d'amélioration progressive, cela n'est pas du tout un obstacle : les vieux IE disposeront de l'affichage de base.
La détection du support JavaScript se fera de la manière suivante. Le but est d'ajouter la classe .js-enabled
à l'élément HTML si tout va bien :
// old browser or not ?
if ( !('querySelector' in document && 'addEventListener' in window) ) {
return;
}
window.document.documentElement.className += ' js-enabled';
À présent nous disposons de tous les moyens pour cibler nos éléments :
@media (max-width: 545px) {
.js-enabled .nav-button {
display: inline-block;
}
...
}
Aller plus loin
Tant qu'on y est, il pourrait être de bon ton de produire des améliorations visuelles (CSS) selon les périphériques. Par exemple :
- adapter la taille cliquable des liens selon la résolution (taille et DPI) des mobiles,
- modifier les contrastes ou passer la couleur de texte en noir sur imprimante,
- ajouter des fioritures graphiques pour navigateurs récents (coins arrondis, dégradés),
- etc.
4e palier : interactions avec le visiteur (JavaScript)
Lors du clic ou du touch sur le bouton de navigation, la partie principale du site (.main
) doit se décaler sur la droite pour révéler la navigation qui aura été positionnée en absolute derrière.
Pour l'interaction (clic ou touch) avec l'utilisateur, plusieurs techniques sont envisageables. Parmi celles-ci, j'ai choisi JavaScript notamment parce que ce langage se prête parfaitement à cette fonctionnalité. Mais sachez que d'autres langages (CSS) auraient pu faire l'affaire également.
L'un des soucis de JavaScript est qu'il n'y a pas de tolérance à la panne : si JavaScript n'est pas activé ou est bloqué, le menu - qui est un contenu essentiel de la page - se doit de demeurer accessible et fonctionnel.
La mission de JavaScript sera la suivante (quand il est activé) :
-
ajouter la classe
.js-enabled
sur<html>
-
cibler le bouton de navigation (
.nav-button
) ainsi que la partie principale du site (.main
) - détecter le clic et le touch sur le bouton
-
ajouter ou supprimer la classe
.is-opened
sur.main
afin de gérer son déplacement via CSS
var target = document.querySelector('.main');
var button = document.querySelector('.nav-button');
// click-touch event
if ( button ) {
button.addEventListener('click',
function (e) {
target.classList.toggle('is-opened');
e.preventDefault();
}, false );
}
L'opération de déplacement de .main
quand le bouton est cliqué se fera tout naturellement via CSS et des transformations :
/* styling opened nav */
.js-enabled .main {
transition: transform .25s;
will-change: transform;
}
.js-enabled .main.is-opened {
transform: translateX(60vw);
}
Code JavaScript complet
L'usage de JavaScript nécessite souvent d’implémenter des fonctionnalités et détections complémentaires qui complexifient le code initial mais en faciliteront l'emploi dans toutes les circonstances (onresize, timeout, zoom utilisateur, etc.). À vous de voir jusqu'où vous souhaitez parfaire le code minimal actuel.
Voici le code JavaScript complet employé sur notre menu d'exemple :
(function() {
// old browser or not ?
if ( !('querySelector' in document && 'addEventListener' in window) ) {
return;
}
window.document.documentElement.className += ' js-enabled';
function toggleNav() {
// Define targets
var target = document.querySelector('.main');
var button = document.querySelector('.nav-button');
// click-touch event
if ( button ) {
button.addEventListener('click',
function (e) {
target.classList.toggle('is-opened');
e.preventDefault();
}, false );
}
} // end toggleNav()
toggleNav();
}());
5e palier : enrichissement de l'accessibilité
Parvenu à ce dernier palier, profitez-en pour refaire une dernière vérification de routine :
- Le contenu est-il toujours compréhensible hors contexte ?
- Le contraste entre la couleur de texte et la couleur de fond est-il suffisant ?
- Le menu est-il fonctionnel si CSS est indisponible ou non reconnu par les navigateurs ?
- Le menu est-il fonctionnel si JavaScript est indisponible ou non reconnu par les navigateurs ?
- Avez-vous testé l'accessibilité de la page via une synthèse vocale (VoiceOver, Nvda) ?
Navigation au clavier
:focus
des liens de navigation (vous pouvez tester la navigation clavier à l'aide des touches tab ou maj + tab).
Pour nos amis IE6 et IE7, qui ne reconnaissent pas la pseudo-classe :focus
, nous emploierons :active
qui, en raison d'un bug, déclenchera également les styles lors de la navigation au clavier sur ces anciens navigateurs.
La navigation au clavier sera sans doute inutile sur mobile, mais redoutablement efficace sur écran de bureau :
.navigation a:hover, .navigation a:focus, .navigation a:active {
background: #295155;
}
Enrichissement via Landmarks ARIA
Les spécifications W3C d'accessibilité, WAI-ARIA, prévoient des régions nommées (landmarks) que les agents utilisateurs doivent savoir exploiter. Par exemple, la région "navigation", valeur de l'attribut HTML ARIA role
, désigne la navigation prinicipale du document.
Tous les agents utilisateurs récents reconnaissent ce landmark et traitent l'élément comme tel, cela vient en complément du sens apporté par l'élément HTML <nav>
:
<nav role="navigation" id="navigation" class="navigation">
Il sera de bon ton de l'appliquer également au bouton. Pensez également à employer l'attribut aria-label
, qui fait office de texte de remplacement, surtout si vous ne souhaitez pas afficher de texte sur votre bouton :
<button role="button" class="nav-button" type="button" aria-label="navigation">menu</button>
Conclusion
L'exemple de ce menu de navigation vous l'a peut-être fait comprendre : l'amélioration progressive est un travail du quotidien et chaque détail peut avoir son importance.
Ne cherchez pas absolument la perfection, car elle n'existe pas. Cet exemple est d'ailleurs forcément perfectible. Par contre, pensez avant tout à vos utilisateurs en priorité, et non à la technologie qui vous ferait plaisir : l'essentiel est que le contenu et les informations puissent toujours être véhiculées.
Et maintenant c'est à vous de jouer, qu'avez-vous prévu de faire pour faciliter la vie à vos visiteurs ?
Commentaires
Autant pour [role=navigation] je comprend, autant pour un ID, il est préférable d'utiliser la notation standard du CSS pour cibler les id (#nomID) non ?
Et -ms-transition n'a jamais existé, IE a directement pris en compte la version finale.
Merci pour les explications en tout cas. ;)
@Manumanu : Ce n'est pas parce que le sélecteur d'id (#navigation) est plus ancien qu'il est "plus standard" ou préférable.
Ce sélecteur a énormément de poids, et pour ma part (voir le lien explicatif d'ailleurs), je préfère l'éviter au profit du sélecteur d'attribut ([id=navigation]) puisque ce dernier a beaucoup moins de poids (celui d'une classe).
Bien vu pour -ms-transition, c'est le défaut des compilateurs automatiques.
D'accord pour la question du poids, c'est vrai ! En revanche, il me semble que Safari et sa version mobile ont du mal avec ce sélecteur (J'ai fait un test ici : http://lab.infographizm.com/divers/tests-comp...), mais je ne pense pas que ça pose problème sur le cas présent.
Bizarre, je n'ai jamais eu de souci, je n'en ai pas avec ton exemple (sous safari 6 mobile), et n'ai jamais entendu parler d'un tel bug référencé, mais pourquoi pas.
Possible que ça ait été corrigé depuis, j'avais testé sur un iOS 5.
Tout d'abord merci pour cet article.
Je continue à suivre tes "travaux / dossiers" très intéressants et apportant une nouvelle réflexion dans ma manière de développer un site internet mais pas seulement, car tu arrives toujours à m'en apprendre, j'adore :D (le sélecteur [***] sans balise devant, je n'avais jamais essayé, mais je retiens ! :) )
Cette façon de réaliser un menu est tout simplement parfaite (ou presque). Bien adaptée, il est clair qu'elle permet une souplesse déconcertante. Je vais voir pour la mettre en prod dans notre entreprise. :p
Je vois que tu parles de microdata dedans et que Alsacréations n'est pas spécialiste en SEO. Je me permets d'ajouter tout de même qu'il est absolument indispensable d'utiliser les microdata ! Que celà soit sur la navigation ou sur tout autre éléments comme un article, des horaires d'ouvertures, une société, un contact...
Google est friand puisqu'il les utilise d'une part dans les SERP et surtout, c'est conseillé par lui même en lieu et place des microformats ! :) Ajoutons à ça un compte GOOGLE+ et le tour est joué.
Je travaille en tant que Webdesigner / intégrateur / SEO qui développe mes compétences avec mon collègue qui lui est certifié SEO/SMO/SEA donc j'apprends aussi beaucoup. :)
Merci encore pour cet article, je vais voir pour réfléchir au mieux aussi à tes propositions une fois testées (celà me permet davantage de réflexion sur le côté perfectible mais franchement, c'est difficile de faire mieux je pense).
Bonne soirée
@gringo : là tu m'intrigues, tu as un petit exemple en ligne d'un menu qui a des micro-data ? :)
Merci pour cette démonstration.
Le seul truc qui est chiant - pour moi - c'est l’absence de coloration de syntaxe avec le sélecteur [id] quand il n'y a rien devant. Ça reste tout blanc dans mon éditeur préféré. :d
<script type="text/javascript">alert('faille XSS ici')</script>
merci pour cette illustration.