Loupe

CSS Feature Queries : L’amélioration progressive efficace avec @supports

Qu’est-ce que l’amélioration progressive ?

Avant de parler des CSS Feature Queries, intéressons-nous à l’amélioration progressive.

L’amélioration progressive est une façon de concevoir et de développer un site ou une application. Elle s’applique en 2 temps :

  1. Implémenter les fonctionnalités essentielles au bon fonctionnement du service sur tous les navigateurs, systèmes et appareils cibles.
  2. Enrichir ces fonctionnalités avec des techniques récentes et modernes tout en admettant qu’elles ne seront peut-être pas disponibles pour tous les utilisateurs.

Il s’agit d’une approche bottom-up.

Dans le cas de CSS, l’amélioration progressive consiste à utiliser des propriétés dont le support navigateur n’est pas total mais qui s’avèrent être pratiques et performantes (ex : faire un dégradé en CSS plutôt qu’avec une image). Généralement, ces nouvelles propriétés sont utilisées à des fins décoratives. Elles améliorent l’aspect graphique mais ne dégradent pas l’Expérience Utilisateur (navigation, recherche, lecture, …).

L’antagoniste de l’amélioration progressive est la dégradation élégante. C’est une approche top-down qui consiste à développer en priorité pour la cible possédant la configuration la plus récente et la plus confortable puis d’assurer, dans un second temps, une rétro-compatibilité pour les autres cibles. Cette dernière permet de proposer une version dégradée mais suffisamment acceptable pour être utilisée. 

Le choix de l’approche dépend du contexte du projet, du temps, de la cible et des fonctionnalités à implémenter.

Comment fait-on d’habitude ?

Ciblage par navigateur

Pour faire de l’amélioration progressive (ou de la dégradation élégante) en CSS, il est possible de faire de la détection de navigateur et / ou de système d’exploitation.

Il existe des scripts comme detect.js ou bowser qui permettent d’obtenir des informations côté client afin de les utiliser, par exemple, pour ajouter des classes sur la balise <html>ou <body>.

<html class="chrome chrome-65 windows windows-10">
    ...
</html>

Ces classes peuvent ensuite être utilisées en CSS comme ceci :

.chrome p
{
    // Styles spécifiques Chrome
}

.windows-10 p
{
    // Styles spécifiques Windows 10
}

Bien que pratique, cette méthode n’est pas fiable puisqu’elle se base sur des informations fournies par l’user agent qui peut facilement être modifié par l’utilisateur et donc biaiser le résultat de la détection.

Ciblage par commentaires conditionnels 

Pour une fois, Internet Explorer est sympa avec nous car il permet d’utiliser des commentaires conditionnels pour faire du ciblage par version et sans JS. Il est très fréquent de voir ce genre d’amorce de document HTML :

<!DOCTYPE html>
<!--[if lte IE 6 ]><html class="ie6 ielt7 ielt8 ielt9 ielt10" ><![endif]-->
<!--[if IE 7 ]><html class="ie7 ielt8 ielt9 ielt10"><![endif]-->
<!--[if IE 8 ]><html class="ie8 ielt9 ielt10"><![endif]-->
<!--[if IE 9 ]><html class="ie9 ielt10"><![endif]-->
<!--[if gt IE 9]><!--><html><!--<![endif]-->
    <head>
        ...
    </head>
    <body>
        ...
    </body>
</html>

Ainsi que ce genre de règles CSS :

.ie8 p
{
    // Styles spécifiques à IE 8
}

.ielt10 p
{
    // Styles spécifiques à tous les IE < 10
}

Malheureusement, il s’agit encore une fois d’une technique à utiliser avec précaution. Car en plus de l’user agent, il est possible de modifier le mode de compatibilité d’Internet Explorer pour obtenir le rendu d’une version différente. Un mode de compatibilité différent est parfois utilisé dans les parcs informatiques de certaines entreprises rendant l’intégration de sites très compliquée (ex : cumul de bugs de versions et de non-compatibilité de fonctionnalités).

Ciblage par Hacks

Les browser hacks sont des techniques de ciblage qui se reposent sur le laxisme ou les failles d’implémentation des navigateurs. L’idée est d’écrire des sélecteurs ou des propriétés CSS avec une syntaxe douteuse pour que les styles soient interprétés ou non.

Selon les hacks, il est possible de cibler un ou plusieurs navigateurs à la fois, ou bien une ou plusieurs versions de navigateurs.

Quelques exemples :

/* IE6 */
p { _property: value; }

/* IE6 et IE7 */
p { *property: value; }

/* IE7 et IE8 */
p { property/*\**/: value\9; }

/* Chrome ≤ 28, Safari ≤ 7 et Opera ≥ 14 */
p { [;property: value;]; }

/* Firefox ≥ 6 */
_::-moz-progress-bar, body:last-child p { property: value; }

Vous l’aurez compris : C’est de la bidouille ! Rien ne nous indique si les hacks seront toujours disponibles dans les futures versions des navigateurs ciblés ou pire, si d'anciens hacks seront à nouveau utilisables. L’utilisation des hacks doit rester exceptionnelle voire être proscrite.

Ciblage par fonctionnalités

Comme vu précédemment, les techniques de ciblage par navigateur ne sont pas recommandées car elles reposent sur des informations peu fiables (ex: user agent) ou trop imprécises (nom de navigateur ou n° de version). 

Modernizr est une librairie JS qui permet de faire de la détection précise de fonctionnalités au niveau du terminal de l’utilisateur. Elle ne se limite pas qu’à CSS, puisqu’elle permet également de savoir si des fonctionnalités comme la Géolocalisation ou WebGL peuvent être utilisées par exemple.

Attention : Modernizr n’est pas un pollyfill, il ne comble pas les fonctionnalités manquantes des navigateurs. Son rôle se résume à la détection et aux tests.

Il est possible de choisir les fonctionnalités à détecter et à tester sur un projet. Un fichier JS spécifique est alors téléchargeable à la fin de la sélection. Ce dernier permet, entre autre, d’ajouter des classes sur la balise <html> indiquant le support de chaque fonctionnalité. Voici un exemple :

Exemple de classes ajoutées par Modernizr

Ainsi, si une fonctionnalité est détectée, une classe avec le nom correspondant est ajoutée. Ex : flexbox ou canvas

Au contraire, si la fonctionnalité n’est pas disponible, une classe avec un préfixe no- est ajoutée. Ex : no-csstransforms ou no-touch.

Cette détection et cet ensemble de classes permettent de faciliter et fiabiliser grandement le travail côté CSS. Par exemple :

// Styles génériques
.row
{
    display: flex;
}

// Styles spécifiques aux navigateurs ne supportant pas flexbox
.no-flexbox .row
{
    overflow: hidden;
}

.no-flexbox .row .col
{
    float: left;
}

Comment faire maintenant ?

Il existe aujourd’hui un module CSS pour vérifier le support des propriétés par le navigateur : CSS Feature Queries. La recommandation officielle et définitive du W3C date d’avril 2013 mais son implémentation par les navigateurs s’est faite attendre.

Aujourd’hui le support navigateurs des CSS Feature Queries est excellent. Comme d’habitude, le mauvais élève est Internet Explorer.

Support navigateurs du module CSS Feature Queries

La directive @supports

Les CSS Feature Queries exposent une nouvelle directive : @supports. Elle s’utilise de la manière suivante :

@supports (property: value)
{
    /* Code spécifique pour la propriété testée */
}

Voici un exemple concret :

@supports (display: flex)
{
    .row
    {
        display: flex;
    }
}

Plutôt simple et efficace, non ?

Les opérateurs

A l’image des Media Queries, les Feature Queries possèdent plusieurs opérateurs.

L’opérateur “not”

L’opérateur not permet de détecter le non-support d’une fonctionnalité :

@supports not (display: flex)
{
    .col
    {
        float: left;
    }
}

L’opérateur “and”

L’opérateur and permet de faire le test sur plusieurs fonctionnalités :

@supports (transform: scale(1)) and (transition: 1s)
{
    ...
}

L’opérateur “or”

L’opérateur or permet de détecter le support d’au moins une fonctionnalité parmi d’autres. La plupart du temps, il est utilisé pour tester des propriétés avec des vendor prefixes : 

@supports (display: -webkit-flex) or
          (display: -moz-flex) or
          (display: flex)
{
    .row
    {
        display: -webkit-flex;
        display: -moz-flex;
    	display: flex;
    }
}

Combiner les opérateurs

Les différents opérateurs vu précédemment peuvent être utilisés simultanément. 

@supports (property1: value1) or ((property2: value2) and (not (property3: value3))) 
{
    ...
}

Points de vigilance

Les CSS Feature Queries possèdent quelques limitations, notamment au niveau syntaxique.

Valeur obligatoire

Il est n’est pas possible de tester une propriété sans valeur. L’exemple suivant est invalide :

@supports (transform)
{
    ...
}

Espaces

Un espace blanc est requis juste après l’opérateur not et de part et d’autre des opérateurs and et or. Sans quoi le test sera ignoré.

Parenthèses

Les parenthèses sont importantes dans la syntaxe des CSS Feature Queries. L’exemple suivant est invalide :

@supports display: flex
{
    ...
}

De plus, l’utilisation de parenthèses devient obligatoire lorsque l’opérateur not n’est pas utilisé au début du test. Le test suivant est invalide :

@supports (property1: value1) or (property2: value2) and not (property3: value3)
{
    ...
}

Faux-négatifs

Une propriété peut être disponible sur un navigateur mais sa détection peut donner un résultat négatif

Comme vu précédemment, la directive@supports n’est pas supportée par IE. Ainsi, le test du module flexbox via les CSS Feature Queries sera faussé car il sera tout simplement ignoré (même avec les vendor prefixes d’IE).

/* IE ne comprend pas et ignore les lignes suivantes */
@supports (display: -ms-flexbox) or (display: flex)
{
    ...
}

Du coup, il faut s’assurer qu’un test est réalisable avant de l’écrire !

Utilisation avec JavaScript

Il est possible d’utiliser les CSS Feature Queries avec Javascript. L’équivalent de la directive @supports est CSS.supports().

La fonction CSS.supports() peut s’utiliser avec 1 ou 2 paramètres.

Version avec 1 paramètre 

Le paramètre correspond à un test similaire à celui qu’on pourrait écrire en CSS :

if( CSS.supports( '(property: value)' )
{
    ...
}

Même chose avec des opérateurs :

if( CSS.supports( '(property: value) and (property: value)' )
{
    ...
}

Version avec 2 paramètres

Si 2 paramètres sont utilisés alors ils correspondent respectivement à la propriété et à la valeur à tester :

if( CSS.supports( 'property', 'value') )
{
    ...
}

Vigilance

Il est recommandé de mettre l’expression testée entre quotes. La spécification insiste sur le fait qu’il s’agisse d’une évaluation littérale stricte de l’expression saisie. Tout échappement de caractère ou espace blanc inutile sera interprété tel quel et le résultat pourrait être faussé.

Faisabilité

Avant de tester une ou plusieurs propriétés, il faut s’assurer que la fonction CSS.supports() est bien disponible et ne provoque par d’erreur JS. David Walsh propose cette méthode de vérification :

var supportsCSS = !!((window.CSS && window.CSS.supports) || window.supportsCSS || false);

Conclusion

Les CSS Feature Queries apportent un vrai plus dans l’écriture et la maintenabilité du code mais aussi dans la rétro-compatibilité avec les navigateurs.

Ce module permet d’utiliser des fonctionnalités CSS modernes et de fournir des fallbacks proprement sans avoir recours à JavaScript. Cependant, pour assurer une compatibilité maximale et efficace, une utilisation combinée avec Modernizr peut être envisagée.

Utilisée à bon escient, @supports devrait vite devenir une fonctionnalité indispensable dans vos prochaines aventures avec CSS.

❤ Made with CSS & love.

Quelques articles sur le même thème :

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus