Conserver un affichage Desktop sur mobile avec une version Responsive Web Design

Depuis que le Responsive Web Design a commencé son invasion, beaucoup de site ne possède plus qu'une unique version qui gère :

  • un affichage complet pour écrans larges de type écran d'ordinateur (Desktop) et
  • un affichage léger et partiellement complet pour les écrans de type mobiles/tablettes.

Cela revient souvent à inhiber des fonctionnements jugés peu utiles par les développeurs ou ergonomiquement trop instables pour être présentés sur les petits appareils et empêche alors :

  • d'afficher la version Desktop sur les mobiles/tablettes ou
  • d'afficher la version Desktop dans une fenêtre de navigateur rétréci.

Je vous propose, à travers des exemples CSS-Driven, de vous expliquer comment gérer deux versions d'affichage de vos pages avec comme toujours une seule structure HTML sémantique.

En Desktop First

Le Desktop First consiste à penser en priorité l'affichage maximal pour grand écran d'un site web puis ensuite d'ajouter des instructions pour des versions plus petites. En générale ce n'est pas une approche choisi consciemment mais utilisée sur des sites déjà existant qui n'ont pas initialement fait l'objet d'une version Mobile.

Voici donc un site créer pour un affichage fixe :

CSS du site version Desktop

body {
    /* Le site est taillé pour un affichage 1024px */
    width: 1024px;
}

Une fois la version Mobile ajouté au code existant, le site s'affiche en version Responsive Web Design sur tous les petits périphériques ou dans les fenêtres de navigateurs rétrécis :

CSS addon pour version Mobile

body {
    width: 1024px;
}
@media (max-width: 1023px) {
    /* Cependant, si l'écran est inférieur à 1024px... */
    /* ...alors on applique la propriété suivante */
    body {
        width: auto;
    }
}

Avec cette modification, il n'est plus possible d'afficher la version Desktop sur mobiles/tablettes ou fenêtre rétrécis.

Voici une manière simple d'ajouter un affichage Responsive Web Design (version Mobile) tout en conservant l'affichage, si souhaité, de la version Desktop pour mobiles/tablettes en Desktop First. Il faut pour cela :

  1. Se servir d'une classe conditionnelle .rwd (pour « Responsive Web Design ») que l'on va placer dans <html class="rwd"> pour rendre notre site Responsive Web Design.

  2. Préfixer tous les sélecteurs CSS destinés à habiller des affichages inférieurs à la version Desktop (la plus large) par .rwd :

    Ajout de .rwd

    body {
     /* Le site est taillé pour un affichage 1024px */
     width: 1024px;
    }
    @media (max-width: 1023px) {
     /* Cependant, si l'écran est inférieur à 1024px... */
     /* ...et uniquement si le site est en mode RWD (c'est le cas par defaut)... */
     /* ...alors on applique la propriété suivante */
     .rwd body {
         width: auto;
     }
    }
    
  3. Ajouter un lien Version Desktop qui ne s'affiche que si le site possède la classe .rwd et n'est pas dans sa taille maximale et ajouter un lien Version Mobile qui ne s'affiche que si la classe .rwd n'est pas là.

    Ajout de liens de switch

    /* On force à ne pas proposer l'affichage Desktop... */
    a.ask-for-display-desktop {
     display: none;
    }
    @media (max-width: 1023px) {
     /* ...sauf si le site est en RWD et est... */
     /* ...en version inférieur à la version maximale */
     .rwd a.ask-for-display-desktop {
         display: inline;
     }
    }
    /* Par défaut la demande de passer en version RWD est là... */
    /* ...et si on est déjà en RWD on ne l'affiche pas */
    .rwd a.ask-for-display-responsive {
     display: none;
    }
    

À présent il suffit du côté Back-end :

  • d'injecter la classe .rwd ainsi que la meta name="viewport" pour la version Responsive Web Design dans la page délivrée.

    <!DOCTYPE html>
    <html lang="en" class="rwd">
      <head>
          <!-- Title, Meta, etc. -->
          <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
      </head>
      <!-- Body -->
    </html>
    
  • de ne pas le faire pour la version Desktop

    <!DOCTYPE html>
    <html lang="en">
      <head>
          <!-- Title, Meta, etc. -->
      </head>
      <!-- Body -->
    </html>
    

Exemple live

Vous pouvez tester ici un exemple de site inter-changeable entre version Desktop et version Mobile (Responsive) avec la technique Desktop First.

  • Si vous venez d'un grand écran, pensez à rétrécir la fenêtre de votre navigateur pour faire apparaître en pied de page le lien de la version non Responsive.
  • Si vous venez d'un(e) mobile/tablette, cliquez juste sur le lien de pied de page.

Le code source est là

Recharger la meta viewport sans recharger la page

Comme votre structure HTML et CSS est identique quelque soit la version, vous n'êtes pas obligé de rafraîchir le navigateur pour passer d'une version à l'autre. L'astuce pour forcer les appareils à prendre en compte les modifications de la <meta name="viewport"> est, plutôt que de la mettre et la retirer, de la laisser en permanence et simplement remplir (ou non) son attribut content. Voici un exemple de code jQuery :

$("a.ask-for-display-desktop").click(function (e) {
    e.preventDefault();
    $("html")
        .removeClass("rwd");
    $("meta[name=viewport]").attr('content','');
    $.ajax(/* prévenez le Back-end que le site sera dorénavant affiché en Desktop */);
});
$("a.ask-for-display-responsive").click(function (e) {
    e.preventDefault();
    $("html")
        .addClass("rwd");
    $("meta[name=viewport]").attr('content','width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no');
    $.ajax(/* prévenez le Back-end que le site sera dorénavant affiché en Mobile */);
});

avec dans votre fichier html :

<!-- ... -->
<a class="display-desktop" href="desktop-first_desktop-version.html">Afficher version Bureau</a>
<!-- ... -->
<a class="display-responsive" href="desktop-first_mobile-version.html">Afficher version Mobile/Tablette</a>
<!-- ... -->

En Mobile First

Si en Desktop First cela se révèle être un jeu d'enfant, il en est tout autrement pour le Mobile First. Effectivement, cette aproche fait qu'en réalité, notre site ne possède pas « réellement » de version Desktop mais seulement un affichage le plus large possible. Il n'a donc initialement pas été convenu d'une version Desktop dont l'utilité est cette fois plus discutable qu'en Desktop First.

Voyons en quoi cela pause problème avec ces 4 fichiers :

Fichier common-xs.css

/* Fichier pour mobile */
body {
    width: auto;
}

Fichier common-sm.css

/* Fichier pour grand mobile ou petite tablette */
@media (min-width: 768px) {
    body {
        width: 768px;
    }
}

Fichier common-md.css

/* Fichier pour grande tablette ou petit écran */
@media (min-width: 992px) {
    body {
        width: 992px;
    }
}

Fichier common-lg.css

/* Fichier pour grand écrann */
@media (min-width: 1200px) {
    body {
        width: 1200px;
    }
}

Comprenez bien que si en Desktop First, ce qui se trouve dans la partie @media (max-width: 1023px) est interprété sur écran mobiles/tablettes (et inhibé sans .rwd pour afficher la version Desktop) ; ici jamais les instructions du fichier common-lg.css censés s'occuper de l'affichage Desktop ne seront interprétés sur un périphérique plus petit que 1200px. Donc quoi qu'il arrive : il ne peut exister de version Desktop affichable sur mobile/tablette.

Voyons tout de même deux types d'implémentations Mobile First qui pallie à ce problème.

Implémentation en plusieurs fichiers

Les 4 fichiers précédents peuvent être appelés de manière minifiés par 4 balises comme suit :

<!-- ... -->
<head>
    <!-- ... -->
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="common-xs.css" media="screen">
    <link rel="stylesheet" href="common-sm.css" media="screen and (min-width: 768px)">
    <link rel="stylesheet" href="common-md.css" media="screen and (min-width: 992px)">
    <link rel="stylesheet" href="common-lg.css" media="screen and (min-width: 1200px)">
    <!-- ... -->
</head>
<!-- ... -->
  • Avantage : chargement priorisé,

    • si je réclame la page sur grand écran, on arrête le traitement de la page HTML le temps de charger les 4 fichiers mais,
    • si je réclame la page sur mobile, on arrête le traitement de la page HTML le temps de charger le premier fichier, la page est alors rendu complètement, puis les 3 autres fichiers sont chargés à la fin. Si l'écran est un peu plus grand que 768px alors ce seront les 2 premiers fichiers puis les deux autres à la fin, etc.
  • Inconvénient : plus de requêtes HTTP.

Pour que cette configuration fonctionne, il faut dans un premier temps retirer les Media Queries des fichiers CSS eux-mêmes. Cela n'impacte en rien le rendu puisque les Media Queries sont gérées au niveau de la page HTML dans la propriété media :

/* Fichier pour mobile */
body {
    width: auto;
}

Fichier common-sm.css

/* Fichier pour grand mobile ou petite tablette */
body {
    width: 768px;
}

Fichier common-md.css

/* Fichier pour grande tablette ou petit écran */
body {
    width: 992px;
}

Fichier common-lg.css

/* Fichier pour grand écrann */
body {
    width: 1200px;
}

À présent quand le site est réclamé en version Desktop via un lien spécifique il faut :

  • ne pas injecter les Media Queries dans l'attribut media des balises link de la page délivrée et de retirer la <meta name="viewport">

    <!-- ... -->
    <head>
      <!-- ... -->
      <link rel="stylesheet" href="common-xs.css" media="screen">
      <link rel="stylesheet" href="common-sm.css" media="screen">
      <link rel="stylesheet" href="common-md.css" media="screen">
      <link rel="stylesheet" href="common-lg.css" media="screen">
      <!-- ... -->
    </head>
    <!-- ... -->
    

Implémentation en un seul fichier

Pour reproduire une équivalence du Mobile First en respectant l'ordre d'appel des propriétés CSS (de l'affichage le plus petit au plus grand) tout en permettant un fonctionnement comme dans la partie Desktop First de cet article il va falloir :

  1. Rassembler les éléments en un seul fichier.

    /* Fichier pour mobile */
    body {
     width: auto;
    }
    /* Fichier pour grand mobile ou petite tablette */
    @media (min-width: 768px) {
     body {
         width: 768px;
     }
    }
    /* Fichier pour grande tablette ou petit écran */
    @media (min-width: 992px) {
     body {
         width: 992px;
     }
    }
    /* Fichier pour grand écrann */
    @media (min-width: 1200px) {
     body {
         width: 1200px;
     }
    }
    
  2. Empiler les sections par pallier maximal avec Media Queries et traiter les propriétés de la partie large Desktop sans Media Queries :

    /* Fichier pour mobile */
    @media (max-width: 767px) {
     body {
         width: auto;
     }
    }
    /* Fichier pour grand mobile ou petite tablette */
    @media (max-width: 991px) {
     body {
         width: 768px;
     }
    }
    /* Fichier pour grande tablette ou petit écran */
    @media (max-width: 1199px) {
     body {
         width: 992px;
     }
    }
    /* Fichier pour grand écrann */
    body {
     width: 1200px;
    }
    
  3. Et intégrer une classe .rwd que l'on va placer dans <html class="rwd"> sur chacune des propriétés dans les Media Queries :

    /* Fichier pour mobile */
    @media (max-width: 767px) {
     .rwd body {
         width: auto;
     }
    }
    /* Fichier pour grand mobile ou petite tablette */
    @media (max-width: 991px) {
     .rwd body {
         width: 768px;
     }
    }
    /* Fichier pour grande tablette ou petit écran */
    @media (max-width: 1199px) {
     .rwd body {
         width: 992px;
     }
    }
    /* Fichier pour grand écrann */
    body {
     width: 1200px;
    }
    

À présent il suffit du côté Back-end :

  • d'injecter la classe .rwd ainsi que la meta name="viewport" pour la version Responsive Web Design dans la page délivrée.

    <!DOCTYPE html>
    <html lang="en" class="rwd">
      <head>
          <!-- Title, Meta, etc. -->
          <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
      </head>
      <!-- Body -->
    </html>
    
  • de ne pas le faire pour la version Desktop

    <!DOCTYPE html>
    <html lang="en">
      <head>
          <!-- Title, Meta, etc. -->
      </head>
      <!-- Body -->
    </html>
    

Scénario de cas d'utilisation

Dans cet exemple, l'utilisateur n'a pas accès aux trois zones vertes « Step » en pied de la zone « Article » s'il visite le site sur mobile.

C'est un utilisateur qui va quasiment toujours sur le site sur son PC de bureau. Cependant, quand il se rend sur son site préféré depuis son mobile afin justement d'aller consulter les informations dans ces zones « Step », il ne les trouve pas. Il aurait juré qu'elles étaient là sur son PC, mais là, il ne voit rien. De plus, avant la version mobile n'existait pas.

C'est ennuyant car sont mobile à un très bon écran et gère le zoom à merveille. Alors il va cocher le paramètre proposé par son navigateur mobile qui s'appelle « Voir version ordinateur » mais rien... toujours pas de « Step », pourquoi diable ne peut-il pas afficher cette @!#$? de version Desktop !

Le problème de notre utilisateur est résolu grâce a cet article car, comme bon nombre de sites qui on une version Desktop et une version Mobile dédiée, il y a heureusement un lien « Afficher version bureau » ! Ce que notre utilisateur ne sait pas, c'est que ça version Desktop est au « .rwd » prêt la même page !

Et côté serveur ?

Nous avons vu comment gérer côté client l'affichage « Responsive Web Design » ou « Desktop ». Nous allons voir un exemple permettant côté serveur de maintenir la version que le client réclame. Pour cela nous allons faire appel à un cookie de session. En PHP par exemple, cela reviendrait à stocker la version d'affichage dans la variable $_SESSION afin de connaître la version d'affichage demandé par l'utilisateur. Il nous reste ensuite a :

  • Tester la valeur de la session pour savoir s'il faut mettre la classe rwd sur la balise html et
  • Créer une méthode AJAX permettant de mettre à jour la valeur de la session auprès du serveur si l'utilisateur demande un changement.

Exemple Node.js avec NodeAtlas

Ici, nous traitons exclusivement du HTML, CSS ou JavaScript, aussi je vous propose un petit exemple en Node.js avec le framework NodeAtlas.

Nous allons donc utiliser la valeur request.session qui représente le cookie de session et faire des échanges client-serveur avec des Websockets.

La source de cette exemple est disponible sur ce dépôt Github.

Installer NodeAtlas

npm install -g node-atlas

Créer l'arborescence

├─ controllers/
│ └─ index.htm
├─ views/
│ └─ index.htm
└─ webconfig.json

Contenu des fichiers

views/index.htm

<!DOCTYPE html>
    <html lang="en"<?- (designVersion) ? ' class="' + designVersion + '"': "" ?>>
    <head>
        <meta charset="utf-8">
        <title>Version Mobile/Desktop</title>
        <style>
            /* Je suis la version desktop */
            body {
                background-color: red;
            }
            .responsive {
                display: none;
            }
            .desktop {
                display: block;
            }

            /* Je suis la version responsive web design */
            .rwd body {
                background-color: blue;
            }
            .rwd .responsive {
                display: block;
            }
            .rwd .desktop {
                display: none;
            }
        </style>
    </head>
    <body>
        <p>Je suis la version 
        <span class="responsive">Responsive Web Design</span>
        <span class="desktop">Desktop</span></p>
        <button>Changer de version</button>
    </body>
    <script src="socket.io/socket.io.js"></script>
    <script src="node-atlas/socket.io.js"></script>
    <script>
        /* Si on clique sur le bouton. */
        document.getElementsByTagName("button")[0].addEventListener("click", function () {
            var html = document.getElementsByTagName("html")[0];

            /* on prévient le serveur pour « maintenir » cet état pour toutes les pages. */
            NA.socket.emit("change-version", {
                designVersion: html.getAttribute("class")
            });

            /* et on change la version de la page courante. */
            html.classList.toggle("rwd");
        });
    </script>
</html>

controllers/index.htm

exports.changeVariations = function (next, locals, request) {

    // On affecte au valeur utilisable dans la view `designVersion`.
    // Par défaut on est en RWD.
    locals.designVersion = (request.session.designVersion !== undefined) ? request.session.designVersion : "rwd";

    next();
};

exports.setSockets = function () {
    var NA = this,
        io = NA.io;

    // Dès qu'on a un lien valide entre le client et notre serveur...
    io.sockets.on("connection", function (socket) {

        // ...rester à l'écoute de la demande « change-version »...
        socket.on("change-version", function (data) {
            var session = socket.request.session;

            // Si data vaut "rwd" (existe)...
            if (data.designVersion) {
                // on le passe à rien
                session.designVersion = "";
            } else {
                // sinon on le passe à "rwd"
                session.designVersion = "rwd";
            }

            // On sauvegarde le nouvel état de session.
            session.touch().save();
        });
    });
};

webconfig.json

{
    "routes": {
        "/": {
            "view": "index.htm",
            "controller": "index.js"
        }
    }
}

Lancer le site

node-atlas --browse