Infolettre HTML5Mtl mars 2017 / HTML5Mtl Newsletter March 2017

English version follows

Bonjour à tous,

Lors de cette infolettre, nous allons vous parler d’accessibilité des « Single Page Application », du Women Techmakers Montreal 2017 et du prochain meetup HTML5Mtl.

Lors de la dernière édition de l’infolettre, je voulais continuer de vous parler d’accessibilité. Lors d’un de mes projets, je suis tombé sur la question de l’accessibilité avec les « Single Page Application » ou application à page unique. Ces pages Web obtiennent leurs données d’appels AJAX ou de données préalablement téléchargées. Elles vont rafraichir une portion de l’interface utilisateur à partir de ces données et utiliseront des gabarits chargés dynamiquement eux aussi.

Ce type d’application pose plusieurs problèmes en accessibilité. Nous allons en regarder un en particulier aujourd’hui. Lorsqu’une portion de la page est rafraichie, comment allons-nous en avertir l’utilisateur et qu’elle expérience allons-nous lui donner ?

Je suis loin d’être spécialiste en accessibilité. Je m’y connais juste suffisamment pour achaler mes collègues et leur dire que c’est important J Je ne pourrais dire qu’elle est l’absolue meilleure pratique dans telle ou telle situation. Certains par contre diront que de faire une « Single Page Application » est en soi une mauvaise pratique. Je suis d’accords que cela peut rendre les choses plus difficiles.

Nous allons regarder deux techniques :

  1. Section d’annonce utilisant aria-live.
  2. Amener le focus de l’application au début du composant chargé.

Je vais utiliser vue.js et vue-router.js (https://vuejs.org/ ) pour exprimer les techniques. Les concepts sont récupérables en utilisant les autres librairies / framework populaires comme React ou Angular. Les tests ont été effectués avec Voice Over et Chrome sous Mac OS Sierra et NVDA avec Firefox sous Windows 10. J’ai pris les dernières versions des logiciels en date du 26 mars 2017.

J’utilise vue.js parce que c’est probablement la libraire de « Single Page Application » la plus simple à apprendre (je suis vieux et je trouve Angular 2 compliqué).

Généralement, nous aurons un composant de la page qui représente le contenu principal. Ce composant sera celui qui sera remplacé ou rafraichi lors d’un changement d’état de l’application. Les composants d’applications page unique ont un « cycle de vie ». Lors de ce cycle, le composant sera créé, et le DOM associé remplacé ou modifié. Les librairies comme vue.js ou React.js utiliseront des techniques pour rendre les modifications du DOM les plus performantes possibles. Ce ne sera pas simplement un « main ».innerHTML = « nouveau HTML ». Peu importe ce que la librairie fera, les outils d’adaptation ne seront pas au courant, et tant mieux, car je n’imagine pas ce que cela donnerait. Au moment où le DOM est prêt, nous pouvons demander à la librairie d’effectuer certaines activités. C’est ici que nous pouvons amener le focus de la page à un endroit qui fait du sens ou modifier une autre portion de l’interface utilisateur qui aura un attribut « aria-live ».

Notre application vue.js sera décomposé en deux éléments : index.html et main.js.

Comme pré-requis, nous devrons installer vue.js et vue-router.js. Pour cela, vous pouvez directement utiliser les fichiers js téléchargé ou utiliser node.js et npm. Il existe plusieurs tutoriels disponibles pour faire ceci. Pour simplifier, vue.js gère les composants de la page et vue-router gère le changement de l’état de la page. Nous pouvons voir une route comme étant une configuration pour charger l’équivalent d’une nouvelle page.

Voici un exemple pour index.html

<!doctype html>
 <html>
 <head>
    <title>Test d'Accessibilit&eacute; avec vue.js</title>
    <meta charset="utf-8">
    <script src="node_modules/vue/dist/vue.min.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
 </head>
 <body>
 <header>
    <h1>Application page unique</h1>
    <div id="announce" aria-live="polite"></div>
 </header>
 <main role="main">
    <div id="app">
        <h1>Allo application page unique !</h1>
        <nav>
            <ul>
                <li><router-link to="/toto">Allons vers toto</router-link></li>
                <li><router-link to="/tata">Allons vers tata</router-link></li>
            </ul>
        </nav>
    <router-view></router-view>
    </div>
 </main>
 </body>
 <script src="js/main.js"></script>
 </html>

Nous avons un menu de navigation et une section router-view qui est la portion qui est rafraichie. Une section pour annoncer les changements est définie dans le header.

Nous avons aussi des liens vers quelques routes. Ces liens vont changer l’état de la page et chargeront le composant associé avec cette route.

Le fichier main.js contiendra notre code Javascript d’initialisation de notre application. Nous aurons trois routes : toto, tata et tutu. Chacune d’entre elle modifieront la portion router-view du HTML. À chaque route sera associé un composant pour lesquels nous aurons un gabarit.

var Toto = {
    template: '<div id="toFocus">Texte informatif sur toto</div>'
 };
 var Tata = {
    template: '<div id="toFocus">Texte informatif sur tata <br>' +
                '<router-link to="/tutu">Allons vers tutu</router-link>' +
                '</div>'
 };
 var Tutu = {
    template: '<div id="toFocus">Texte informatif sur tutu</div>'
 };
 var routes = [
    {path: '/toto', component: Toto, name: 'toto'},
    {path: '/tata', component: Tata, name: 'tata'},
    {path: '/tutu', component: Tutu, name: 'tutu'}
 ];

Nous allons ensuite initialiser Vue et Vue-router :

var router = new VueRouter(
    {
        routes:routes
    }
 );
var app = new Vue({
   router:router
 }).$mount('#app');

Vue contrôlera tout ce qui est après le id « app » dans le HTML.

Si vous exécutez cette page dans votre navigateur avec un outil d’adaptation, lorsque vous cliquerai un lien, il ne se passera pas grand chose. Pas très accessible.

Essayons au moins d’annoncer que la page a été rafraichie.

Nous pouvons modifier une section avec aria-live qui spécifiera que la page à été rechargé. Nous pourrions aussi ajouter une navigation simple pour suggérer des actions à l’utilisateur. Cet élément peut aussi contenir une liste d’erreur si un formulaire est disponible sur la page.

Nous allons utiliser le « hook » after router pour ce faire. Après que le router ait fini son travail, nous pouvons l’annoncer à l’utilisateur et pourquoi pas, changer le titre de la page en même temps.

router.afterEach(function(to, from)  {
  if (to.path!=="/") {
      document.getElementById("announce").innerHTML = "La page " + to.name + " s'est chargée";
      document.title = to.name;
  }
});

Veuillez insérer ce code juste après l’initialisation de router. NVDA avec Firefox sous Windows ou Voice Over avec Chrome annoncera maintenant la nouvelle page.

On peut amener l’utilisateur au début du composant en ajoutant un attribut tabindex = -1 à un titre par exemple et amener le focus à cet élément. Pour ce faire, nous allons utiliser un mixin pour ajouter cette fonction à chacun des composants. Lorsque le rendu du composant est terminé, nous allons exécuter une fonction qui amènera le focus à un élément. Définissez le mixin au début de main.js. Sans entrer dans les détails, un mixin est une façon d’ajouter une même fonctionnalité à plusieurs objets différents.

var focusMixin = {
  mounted: function () {
      this.$nextTick(function () {
          document.getElementById("toFocus").setAttribute('tabindex', '-1');
          document.getElementById("toFocus").focus();
      })
  }
};

Notez que nous ajoutons une fonction à être exécutée lorsque le composant est en état « mounted ». this.$nextTick est utilisé pour nous assurer que le DOM est bien présent. Nous ajoutons ensuite le mixin à chacun des composants.

var Toto = {
    template: '<div id="toFocus">Texte informatif sur toto</div>',
    mixins: [focusMixin]
};

Si vous réessayer maintenant, vous aller entendre l’élément sur lequel le focus est placé en plus de l’annonce du chargement. Dans certains cas, l’un est entendu avant l’autre et dans certains cas le focus est placé et seul l’annonce est entendue. Ce n’est pas nécessairement la meilleure expérience utilisateur, mais cela donne une idée de ce que nous pouvons faire avec les outils que les librairies de « Single Page Application » peuvent nous offrir.

D’autres « hook » dans le cycle de vie des composants ou du « routage » peuvent être utilisés pour prévenir l’utilisateur d’un changement. Par exemple, au moment d’un changement dans le routage de l’application, nous pourrions avertir la page est en train d’être chargé. Nous pourrions aussi rendre inactifs certaines portions de la page. Les concepts de cycle de vie des composants et du routage existent dans la plupart des librairies « Single Page Application ». Vous pouvez les utiliser pour rendre votre « Single Page Application » un peu plus accessible.

Les meilleures pratiques lors de ces situations ne sont pas encore très précises. Je vous recommanderais toutefois d’imaginer quelle serait l’expérience utilisateur lors d’un changement d’état de votre application. Si l’utilisateur active la soumission du formulaire, mais ne peut voir que le bouton « submit ». Est-ce qu’il sait que la page est supposée se recharger ? Est-ce qu’il peut voir ou entendre la liste des erreurs sur le formulaire s’il ne voit que le bouton et que les erreurs apparaissent ailleurs ?

Vous devez réfléchir à l’expérience utilisateur pour trouver les meilleures alternatives.

Évènements de la communauté.

Nous avons un évènement super intéressant à vous proposer pour les 1ier et 2 avril prochains. Le « Women Techmakers Montreal 2017 » http://www.wtmmontreal.com/

Le programme Women Techmakers est une initiative de Google qui célèbre la Journée internationale de la femme et qui souligne l’apport des femmes en technologie.

Plus de 200 personnes ont assisté à l’évènement d’un jour de 2016, qui comportait 17 conférencières et 7 commanditaires. Après un tel succès, l’expérience sera recréée cette année, durera deux jours et se tiendra à L’École de technologie supérieur (ETS). Nous y aurons la chance de voir 19 conférencières de renommée sous le thème « Telling Our Story » (Raconter notre histoire). Le billet inclus les conférences, le déjeuner, le « swag », la garderie pour les enfants et plus.

Vous pouvez acheter vos billets ici : http://www.wtmmontreal.com/

D’autres informations sont disponibles ici :

Articles sur l’évènement :

Articles et blogues sur l’événement de l’année dernière :

Pour terminer, n’oubliez pas le prochain HTML5Mtl qui se tiendra le lundi 27 mars (aujourd’hui !!) sur Keep it Simple Aurelia avec David Avenante chez Shopify Montréal (merci à Shopify) à partir de 18 :00. Inscrivez-vous ici : https://www.meetup.com/fr-FR/html5mtl/ Nous n’avons pas eu de meetup en février, mais je vais essayer d’en organiser deux pour avril :-)

Merci beaucoup de lire !

-Benoit

— — — -

English version

Hello to everyone,

For this newsletter, we will talk to you about Single Page Application accessibility, Women Techmakers Montreal 2017 and our next HTML5Mtl meetup.

Last time on this newsletter, I wanted to talk a bit more on accessibility. On one of my project, we are looking at accessibility in the context of “Single Page Applications”. These pages get their data from Ajax calls and other pre-downloaded data. They will refresh parts or all the page contents dynamically by using dynamically loaded templates. These type of application pose problems for accessibility. When a portion of the page is refreshed, how are we going to be able to tell the user and what experience we are going to give?

I’m not a specialist on accessibility, but I know enough to annoy my coworkers, at least to tell them that it is important. I could not tell what is the ideal best practice in a specific circumstance, but I know some techniques that can be useful. Some will tell us that just making a “Single Page Application” is bad practice. I know it can make things a bit more difficult, but there are definite techniques that can help us.

Today, we will look at two of these techniques that can work within a “Single Page Application” (SPA).

  1. An announce section using aria-live
  2. Focusing on a specific part of the page after a component is loaded

I’m going to use vue.js and vue-router.js (https://vuejs.org/ ) to experiment with these techniques. Concepts are reusable with any other SPA libraries like React.js or Angular. I have tested with Voice Over with Chrome on a Mac and NVDA with Firefox on Windows 10. (All latest versions as of 26th of March 2017). I used vue.js because I think it’s the simplest SPA library and I’m too old to understand Angular 2.

Let’s have a component that represents the main content of a page. This component will be replaced by another component when the page changes state. Components in SPA have a lifecycle. The SPA also has state. Within this lifecycle, the DOM gets updated. Libraries like React.js and Vue.js will make updating the DOM as efficient as possible. This won’t be just a “main”.innerHTML = “new HTML”. Whatever the library does, screen readers won’t know in what order they do it (and that’s probably for the best). When the DOM is ready, or when the SPA state changes is where we can ask the library to do certain things like announcing the change in the page or to change the focus of the page to the most relevant part from a user’s perspective.

Let’s try to experiment with this. Our page will have two files: index.html and main.js. We must install vue-js and vue-router.js. You can simply download the distribution and make them available using scripts on the page, or use node.ja + npm. There are a lot of tutorials for doing this. As a simplification on our we use these two libraries, vue.js manages visual components and vue-router the state of the SPA. We can see a route as a configuration for a specific state of the SPA as if we would follow a link and load a new page.

Here is an example of index.html:

<!doctype html>
 <html>
 <head>
    <title>Accessibility test with vue.js</title>
    <meta charset="utf-8">
    <script src="node_modules/vue/dist/vue.min.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
 </head>
 <body>
 <header>
    <h1>Single page application</h1>
    <div id="announce" aria-live="polite"></div>
 </header>
 <main role="main">
    <div id="app">
        <h1>Hello single page application !</h1>
        <nav>
            <ul>
                <li><router-link to="/foo">Let’s go to foo </router-link></li>
                <li><router-link to="/bar">Let’s go to bar </router-link></li>
            </ul>
        </nav>
    <router-view></router-view>
    </div>
 </main>
 </body>
 <script src="js/main.js"></script>
 </html>

We have a navigation menu and a router-view section which is where the page content is loaded. We have our announce element in the header. We also have a couple of links to other routes. These are like links. They will change the state of the page.

Our main.js will contain our Javascript initialization code. We will are three routes: foo, bar and foobar. Each of these will modify the router-view portion of the HTML. To each route, we associate a component which has a template with static HTML in it.

var Toto = {
    template: '<div id="toFocus">Texte informatif sur toto</div>',
    mixins: [focusMixin]
};

We initialize Vue & Vue-router:

var Foo = {
    template: '<div id="toFocus">Informative text on Foo</div>'
 };
 var Bar = {
    template: '<div id="toFocus"> Informative text on Bar <br>' +
                '<router-link to="/tutu">Let’s go to Foobar</router-link>' +
                '</div>'
 };
 var FooBar = {
    template: '<div id="toFocus">Informative text on FooBar. Silence is foo!</div>'
 };
 var routes = [
    {path: '/foo', component: Foo, name: 'foo'},
    {path: '/bar', component: Bar, name: 'bar'},
    {path: '/foobar', component: FooBar, name: 'foobar'}
 ];

var router = new VueRouter(
    {
        routes:routes
    }
 );
var app = new Vue({
   router:router
 }).$mount('#app');

Vue will control everything within the app id div. If you run this in your browser with either NVDA or Voice Over, not much will happen. This is very much inaccessible (though we all saw worse in our career).

Let’s try to at least announce the change in the page.

We can modify a section that has an aria-live attribute, which will tell the speech reader that the page changed. We could also add more navigation to make the page more useful. We could also use this section to tell if errors are present in a form and where to look for them.

We will use the change in the SPA state as a hook to make this change. After the router has finished its job, we can ask it to announce the change and also (why not) change the title of the page.

router.afterEach(function(to, from)  {
  if (to.path!=="/") {
      document.getElementById("announce").innerHTML = "The page " + to.name + " has loaded";
      document.title = to.name;
  }
});

Insert this code just after the router initialization. Voice Over and NVDA will now announce a change to the page.

We can also change the focus of the page at the beginning of the loaded component by adding a tabindex=-1 attribute and focus our browser to it. We will use a mixin to add this function to all our components. When rendering of the component is finished we can use a function that will add the tabindex and focus on the element. Let’s define the mixin at the beginning of main.js. Without entering into too many details, we can see mixins as a way to add functionality to more than one component without using copy paste.

var focusMixin = {
  mounted: function () {
      this.$nextTick(function () {
          document.getElementById("toFocus").setAttribute('tabindex', '-1');
          document.getElementById("toFocus").focus();
      })
  }
};

Note that we are adding a function that will be executed when the component is in the state “mounted”. This.$nextTick is used to make sure that all the DOM of the component is ready. We next add the mixin to all our components like this:

var Foo = {
    template: '<div id="toFocus">Informative text on Foo</div>',
    mixins: [focusMixin]
};

If you try this now, you will hear the element on which focus was placed and the announce. In certain case, you may or not hear all of it depending on which combination of software you use. The focus will be on the right element. This is not the best user experience, but this gives us a good idea of what can be done to make our SPA a bit more accessible and what kind of tools we have at our disposition. You can use other hooks in component lifecycle or SPA states. You could tell the user that a new page is about to be downloaded which she/he activates a submit button. These concepts exist in all SPA libraries.

Best practices for using these techniques are not really precise. What I would recommend is to try to imagine how it would be to use the SPA with different limitations. What if when a user submits a form, she/he can only see the submit button? How can she/he knows if the page is supposed to change when errors appear elsewhere on the page? That person will only see the submit button with no change to the page. Here, an announce that says the page has errors is important. You have to think about the user experience to find what are the best alternatives.

Community Events

There is a very interesting event coming soon: Women Techmakers Montreal 2017!

http://www.wtmmontreal.com/

Here is the official press release:

We are pleased to invite you to the Women Techmakers Montreal 2017 event, a program created by Google to celebrate International Women’s Day and to highlight the talent of women in technology.

Last year’s one-day only event had more than 200 attendees, 17 speakers, and 7 sponsors. After such a huge success, we want to recreate this experience. This year’s event will span over the course of two days, on April 1st & 2nd 2017 at École de technologie supérieure in Montreal.

This year theme is “Telling Our Story”. With 19 speakers chosen from 130 submissions, this edition will be amazing. The ticket includes access to all sessions; swag; breakfast, lunch, snacks and coffee during both days; dinner on the first day; coat check; party; daycare for children if needed. Inside parking is available at the venue for $15/day.

This event is open to all (women and men) of 18+.

For more information and to buy tickets, please visit our website: http://www.wtmmontreal.com/

Information

Articles from this year event:

Blog posts and articles about last year’s event:

To finish this newsletter, let’s not forget our next HTML5Mtl meetup which will be held at Shopify the 27th of March (today!). The subject if Keep it Simple with Aurelia (in French). The venue is Shopify Montreal (thanks Shopify). You can subscribe here: https://www.meetup.com/HTML5mtl/events/238462676/

We did not have a meetup in February, but I’m trying to have 2 meetups on April J

Thanks for reading!

-Benoit

Like what you read? Give Benoit Piette a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.