Grille CSS Responsive et Sémantique sans Framework

Il y a quelque temps, j'expliquais qu'il était difficile d'obtenir un travail Front-end de qualité avec des Frameworks CSS HTML-Driven. Le gros reproche que je fais à ces Frameworks est de prendre le contre pied de l'approche du W3C qui met tout en œuvre avec HTML5 et CSS3 pour complètement effacer toute trace de design dans l'architecture HTML et la rendre structurellement sémantique.

Un Framework CSS c'est vraiment pratique pour ne pas trop se mouiller et gagner du temps parait-il. On me dit d'ailleurs souvent que les systèmes de Grid c'est bien pratique. Ce que j'en pense pour ma part c'est qu'on ne peut jamais sortir des chemins battu d'un Framework lourd à moins de parfaitement connaître les mécanismes de celui-ci pour le contrer. Et dès lors qu'on est assez aguerrit pour comprendre des mécanismes menant à des « abstractions qui fuient », on est alors assez aguerrit pour s'en passer.

Sachez que l'on peut facilement se passer de Framework pour gérer une grille Responsive Web Design propre et maintenable en évitant ainsi d'horribles codes comme <div class="header col-xs-12 col-sm-6 col-sm-push-6 text-right-sm bg-info"> tout en restant maître de ce que l'on fait. Cette page CodePen en est un exemple.

Suivez-moi, je vais vous montrer !

Création d'une structure HTML

Entrons dans le vif du sujet. Pour commencer, nous allons nous attacher à comprendre comment manager facilement notre grille via une feuille CSS, aussi la structure sera pour le moment plate et non sémantique. Là voici :

HTML

<html>
    <head>
        <title>Responsive and Semantic CSS Grid CSS-Driven</title>
        <link rel="stylesheet" href="common.css">
    </head>
    <body>
        <div class="page">
            <div class="brand"></div>
            <div class="slogan"></div>
            <div class="title"></div>
            <div class="main-nav"></div>
            <div class="overview"></div>
            <div class="anchor-nav"></div>
            <div class="main"></div>
            <div class="call-to-action"></div>
            <div class="call-to-action"></div>
            <div class="call-to-action"></div>
            <div class="call-to-action"></div>
            <div class="call-to-action"></div>
            <div class="call-to-action"></div>
            <div class="secondary-nav"></div>
            <div class="utils"></div>
            <div class="legals"></div>
        </div>
    </body>
</html>

Mise en place de la grille CSS

Comme vous pouvez le constater pour le moment, la structure HTML est vide de contenu. Nous allons l'injecter pendant nos tests unitaires via la CSS.

Tests visuels

Nous allons commencer par habiller l'exemple précédent avec un style de test pour bien vérifier que la construction de la grille se fait sans soucis. C'est une étape non nécessaire mais qui aidera grandement à commenter l'architecture de la page facilement. Injectons ce code voué à être commenté en dernier lieu :

CSS (dans common.css)

.brand:before,
.slogan:before,
.title:before,
.main-nav:before,
.overview:before,
.anchor-nav:before,
.main:before,
.call-to-action:before,
.secondary-nav:before,
.legals:before,
.utils:before {
   width: 100%;
   display: inline-block;
}

.brand:before {
    content: "Brand";
   background-color: rgba(0, 0, 150, 0.5);
}
.slogan:before {
   content: "Slogan";
   background-color: rgba(150, 150, 0, 0.5);
}
.title:before {
   content: "Title";
   background-color: rgba(255, 0, 0, 0.2);
}
.main-nav:before {
   content: "Nav";
   background-color: rgba(0, 255, 0, 0.2);
}
.overview:before {
   content: "Overview";
   background-color: rgba(150, 0, 0, 0.5);
}
.anchor-nav:before {
   content: "Anchor nav";
   background-color: rgba(0, 0, 255, 0.2);
}
.main:before {
   content: "Main";
   background-color: rgba(255, 255, 0, 0.2);
}
.call-to-action:before {
   content: "Call to action";
   background-color: rgba(255, 0, 255, 0.2);
}
.secondary-nav:before {
   content: "Secondary nav";
   background-color: rgba(0, 150, 0, 0.5);
}
.legals:before {
   content: "Legals";
   background-color: rgba(0, 255, 255, 0.2);
}
.utils:before {
   content: "Utils";
   background-color: rgba(150, 0, 150, 0.5);
}

Grille de base

C'est ici que nous allons définir notre ensemble de grilles existantes sur le site web. Pour notre exemple nous n'allons créer qu'une grille de 12 colonnes.

Rappel d'une grille dans un Framework CSS

La grille consiste a diviser l'espace alloué à l'affichage du site en 12 colonnes dans notre exemple. Chaque partie de la grille pourra donc être large de un douzième (1/12) de la taille d'affichage à douze douzième (12/12). Si un composant fait 8 colonnes et que celui qui le suit en fait 4, ils tiendront l'un à côté de l'autre. Par contre, si le second en fais 8 également, il s'affichera à la ligne, car ne pouvant pas rentrer à côté.

À vos calculatrices

Si on estime qu'une colonne faisant 100% de la ligne vaut 12 unités alors :

  • 1 unité = (1 * 100) / 12 = 8.33333333%
  • 2 unités = (2 * 100) / 12 = 16.66666667%
  • 3 unités = (3 * 100) / 12 = 25%
  • 4 unités = (4 * 100) / 12 = 33.33333333%
  • 5 unités = (5 * 100) / 12 = 41.66666667%
  • 6 unités = (6 * 100) / 12 = 50%
  • 7 unités = (7 * 100) / 12 = 58.33333333%
  • 8 unités = (8 * 100) / 12 = 66.66666667%
  • 9 unités = (9 * 100) / 12 = 75%
  • 10 unités = (10 * 100) / 12 = 83.33333333%
  • 11 unités = (11 * 100) / 12 = 91.66666667%
  • 12 unités = (12 * 100) / 12 = 100%

ce qui nous donnerais une grille de référence dans un Framework comme celle-ci :

CSS (dans common.css)

Espace pris par les 12 tailles de colonne possible

/*
.span12-12 { width: 100%; }
.span12-11 { width: 91.66666667%; }
.span12-10 { width: 83.33333333%; }
.span12-9 { width: 75%; }
.span12-8 { width: 66.66666667%; }
.span12-7 { width: 58.33333333%; }
.span12-6 { width: 50%; }
.span12-5 { width: 41.66666667%; }
.span12-4 { width: 33.33333333%; }
.span12-3 { width: 25%; }
.span12-2 { width: 16.66666667%; }
.span12-1 { width: 8.33333333%; }
*/

Espace vers la gauche nécessaire pour générer une colonne vide

/*
.prepend12-12 { margin-left: 100%; }
.prepend12-11 { margin-left: 91.66666667%; }
.prepend12-10 { margin-left: 83.33333333%; }
.prepend12-9 { margin-left: 75%; }
.prepend12-8 { margin-left: 66.66666667%; }
.prepend12-7 { margin-left: 58.33333333%; }
.prepend12-6 { margin-left: 50%; }
.prepend12-5 { margin-left: 41.66666667%; }
.prepend12-4 { margin-left: 33.33333333%; }
.prepend12-3 { margin-left: 25%; }
.prepend12-2 { margin-left: 16.66666667%; }
.prepend12-1 { margin-left: 8.33333333%; }
.prepend12-0 { margin-left: 0; }
*/

Espace vers la droite nécessaire pour générer une colonne vide

/*
.append12-12 { margin-right: 100%; }
.append12-11 { margin-right: 91.66666667%; }
.append12-10 { margin-right: 83.33333333%; }
.append12-9 { margin-right: 75%; }
.append12-8 { margin-right: 66.66666667%; }
.append12-7 { margin-right: 58.33333333%; }
.append12-6 { margin-right: 50%; }
.append12-5 { margin-right: 41.66666667%; }
.append12-4 { margin-right: 33.33333333%; }
.append12-3 { margin-right: 25%; }
.append12-2 { margin-right: 16.66666667%; }
.append12-1 { margin-right: 8.33333333%; }
.append12-0 { margin-right: 0; }
*/

Décalage vers la gauche nécessaire pour reculer les colonnes dans la grille en vue de les intervertir

/*
.pull12-12 { right: 100%; }
.pull12-11 { right: 91.66666667%; }
.pull12-10 { right: 83.33333333%; }
.pull12-9 { right: 75%; }
.pull12-8 { right: 66.66666667%; }
.pull12-7 { right: 58.33333333%; }
.pull12-6 { right: 50%; }
.pull12-5 { right: 41.66666667%; }
.pull12-4 { right: 33.33333333%; }
.pull12-3 { right: 25%; }
.pull12-2 { right: 16.66666667%; }
.pull12-1 { right: 8.33333333%; }
.pull12-0 { right: 0; }
*/

Décalage vers la droite nécessaire pour avancer les colonnes dans la grille en vue de les intervertir

/*
.push12-12 { left: 100%; }
.push12-11 { left: 91.66666667%; }
.push12-10 { left: 83.33333333%; }
.push12-9 { left: 75%; }
.push12-8 { left: 66.66666667%; }
.push12-7 { left: 58.33333333%; }
.push12-6 { left: 50%; }
.push12-5 { left: 41.66666667%; }
.push12-4 { left: 33.33333333%; }
.push12-3 { left: 25%; }
.push12-2 { left: 16.66666667%; }
.push12-1 { left: 8.33333333%; }
.push12-0 { left: 0; }
*/

Propriété pour permettre le déplacement de colonne et les gouttières inter colonne (arbitrairement placé à 16px)

/*
.pull-push { position: relative; }
.gutter { padding-left: 16px; padding-right: 16px; }
.pull-push-reset { position: static; }
.gutter-reset { padding-left: 0; padding-right: 0; }
*/

La grille et le Responsive Web Design

Pour commencer nous allons initialiser la grille et les colonnes qui la compose.

Notre site sera habillé en Mobile First, nous allons également définir nos gaps de responsivité. Vous pouvez lire cet article pour plus de compréhension sur le Responsive Web Design, le Mobile First et les gaps de responsivité.

CSS (dans common.css)

Propriété pour manager les colonnes

    .page:after,
.grid:after {
    content: "";
    clear: both;
    display: block;
}

    .brand,
    .slogan,
    .title,
    .main-nav,
    .overview,
    .anchor-nav,
    .main,
    .call-to-action,
    .secondary-nav,
    .utils,
    .legals,
.column {
    float: left;
    width: 100%;
}

Pour faire fonctionner ce mécanisme de colonnes côte à côte qui passe à la ligne si elles manquent de place nous utiliserons des balises flottantes. L'idée est de nommer la zone avec la classe « .column » mais ne jamais l'utiliser dans le code HTML. À la place, c'est les éléments HTML de type « column » qui seront empilé au dessus.

De la même manière vous constaterez que l'élément .page:after est référencé comme se comportant comme l'élément .grid:after.

Media Queries pour gérer le comportement de chaque colonne en fonction de la taille d'affichage

/* Mobile / Small tablet */

/* Tablet / Small desktop */
@media (min-width: 768px) {
}

/* Desktop */
@media (min-width: 992px) {
}

/* Large desktop */
@media (min-width: 1200px) {
}

Avant de vous expliquer les étapes pour rapidement manager votre grille voici à quoi doit ressembler votre feuille CSS, et ce qu'elle donne appliquée sur notre structure HTML exemple.

L' Exemple :

Manager ses Grilles

Pour notre exemple nous n'utilisons qu'une grille. Mais il est tout à fait possible d'en avoir plusieurs (12 colonnes, 16 colonnes, etc.) et d'en changer en fonction du template de page (ou pour les grilles imbriquées différentes).

Étape 1 : Importer les règles

Le rendu ci-dessus étant déjà celui-sur Mobile, nous allons alimenter le rendu pour Tablette et plus.

À partir de 768px, je souhaite que la partie « Brand » occupe 2 colonnes. Je place donc mon curseur dans mon fichier à cette ligne dans mon exemple de grille mise en commentaire :

.span12-2 { width: 16.66666667%; }

et avec le raccourci de duplication rapide de ligne (sur Sublim Text celui-ci est Ctrl + Shift + D) j'obtiens une duplication de celle-ci :

.span12-2 { width: 16.66666667%; }
.span12-2 { width: 16.66666667%; }

avec mon raccourci d’interversion rapide de ligne, je vais aller placer la propriété dupliquée dans la bonne Media Query (sur Sublim Text celui-ci est Ctrl + Shift + Flèche du bas pour descendre une ligne ou Ctrl + Shift + Flèche du haut` pour la monter). Cela nous donne alors :

/* Tablet / Small desktop */
@media (min-width: 768px) {
.span12-2 { width: 16.66666667%; }
}

Étape 2 : Associer la balise

Continuons.

Puisque je souhaite que « Brand » fasse deux colonnes, je place mon curseur sur la ligne ou je l'ai référencé comme étant de type colonne (attaché à « .column »),

    .brand,

je duplique la ligne,

    .brand,
    .brand,

et je place le duplicata sur la règle de deux colonnes que j'ai créé plus tôt :

/* Tablet / Small desktop */
@media (min-width: 768px) {
    .brand,
.span12-2 { width: 16.66666667%; }
}

Étape 3 : Recommencer pour toute la grille Tablette

Il nous reste à présent à faire de même pour les autres colonnes.

Je souhaite que Title fasse également 2 colonnes sur 12 :

/* Tablet / Small desktop */
@media (min-width: 768px) {
  .brand,
  .title,
.span12-2 { width: 16.66666667%; }
}

Je souhaite que Anchor nav et Utils fasse également 3 colonnes sur 12 :

/* Tablet / Small desktop */
@media (min-width: 768px) {
  .brand,
  .title,
.span12-2 { width: 16.66666667%; }
  .anchor-nav,
  .utils,
.span12-3 { width: 25%; }
}

Je souhaite que les Call to Action fassent 6 colonnes sur 12, que Legals et Main en fassent 9 et que Main nav et Slogan en fasse 10 :

/* Tablet / Small desktop */
@media (min-width: 768px) {
  .brand,
  .title,
.span12-2 { width: 16.66666667%; }
  .anchor-nav,
  .utils,
.span12-3 { width: 25%; }
  .call-to-action,
.span12-6 { width: 50%; }
  .legals,
  .main,
.span12-9 { width: 75%; }
  .main-nav,
  .slogan,
.span12-10 { width: 83.33333333%; }
}

Et notre grille commence à prendre forme !

L' Exemple :

N'hésitez pas à retailler la fenêtre de votre navigateur pour constater les différences en fonction de votre périphérique d'affichage.

Étape 4 : Faire de même pour d'autres règles

Sur le même principe, nous allons ajouter des gouttières à nos colonnes pour les espacer les une des autres.

Nous allons donc récupérer toutes les colonnes sur lesquels nous souhaitons appliquer des gouttières. Comme nous les voulons pour toutes les différentes tailles nous allons les attacher dans la partie Mobile :

/* Mobile / Small tablet */
  .brand,
  .slogan,
  .title,
  .main-nav,
  .overview,
  .anchor-nav,
  .main,
  .call-to-action,
  .secondary-nav,
  .utils,
  .legals,
.gutter { padding-left: 16px; padding-right: 16px; }

Ce qui donne :

L' Exemple :

Nous allons à présent intervertir à partir de l'affichage mobile les Brand et Slogan.

Pour cela nous allons poussez Brand de 10 colonnes et reculer Slogan de 2 colonnes. Nous allons passer les colonnes en relative.

Nous allons donc importer les règles,

.pull-push { position: relative; }
.push12-10 { left: 83.33333333%; }
.pull12-2 { right: 16.66666667%; }

et attacher

  .brand,
  .slogan,

dans l'affichage Tablette. Idem pour Legals et Utils avec 9 colonnes en avant et 3 colonnes en arrière :

/* Tablet / Small desktop */
@media (min-width: 768px) {
  .brand,
  .title,
.span12-2 { width: 16.66666667%; }
  .anchor-nav,
  .utils,
.span12-3 { width: 25%; }  
  .call-to-action,
.span12-6 { width: 50%; }
  .legals,
  .main,
.span12-9 { width: 75%; }
  .main-nav,
  .slogan,
.span12-10 { width: 83.33333333%; }
/**/
  .utils,
  .legals,
  .brand,
  .slogan,
.pull-push { position: relative; }
/**/
  .utils,
.push12-9 { left: 75%; }
  .brand,
.push12-10 { left: 83.33333333%; }
  .slogan,
.pull12-2 { right: 16.66666667%; }
  .legals,
.pull12-3 { right: 25%; }
}

Ce qui donne :

L' Exemple :

Étape 5 : Alimenter toutes les tailles de périphérique

Il est à présent temps de faire de même pour Desktop et Large Desktop. Cela va vous faire importer de nouvelles règles ou en écraser des précédentes.

Par exemple avec cette configuration Desktop : Anchor nav, les Call to Action et Main vont changer de comportement :

/* Desktop */
@media (min-width: 992px) {
  .anchor-nav,  
  .call-to-action,
.span12-4 { width: 33.33333333%; }
  .main,
.span12-8 { width: 66.66666667%; }
}

Nous allons en profiter pour introduire les colonnes invisibles avec le Large Desktop :

Nous allons importer les règles :

.prepend12-2 { margin-left: 16.66666667%; }

et

.append12-2 { margin-right: 16.66666667%; }

Pour créer des colonnes inexistantes de part et d'autres de nos Call to Action. Nous allons les cibler avec la propriété CSS3 :nth-child() :

/* Large desktop */
@media (min-width: 1200px) {
  .anchor-nav,
.span12-3 { width: 25%; }
  .call-to-action
.span12-4 { width: 33.33333333%; }
  .main,
.span12-9 { width: 75%; }
/**/
  .call-to-action:nth-child(8),
  .call-to-action:nth-child(10),
  .call-to-action:nth-child(12),
.prepend12-2 { margin-left: 16.66666667%; }
/**/
  .call-to-action:nth-child(9),
  .call-to-action:nth-child(11),
  .call-to-action:nth-child(13),
.append12-2 { margin-right: 16.66666667%; }
}

Ce qui donne :

L' Exemple :

Grille CSS Sémantique

Maintenant que nous avons vu que la grille fonctionne avec un empilement de div tout ce qu'il y a de plus classique, offrons à notre structure un petit côté sémantique et observons que cela n'affecte en rien le fonctionnement de la grille. C'est même bien plus pratique dans le cas de composant en liste comme pour les Call to Action !

Nouvelle structure

Voici une structure HTML fonctionnelle avec nos grilles.

HTML

<html>
    <head>
        <title>Responsive and Semantic CSS Grid CSS-Driven</title>
        <link rel="stylesheet" href="common.css">      
    </head>
    <body>
        <header>
            <div class="brand"></div>
            <div class="slogan"></div>
            <div class="title"></div>
            <nav class="main-nav"></nav>
        </header>
        <div class="content">
            <section class="overview"></section>
            <nav class="anchor-nav"></nav>
            <article class="main"></article>
            <ul class="call-to-action-list">
                <li class="call-to-action"></li>
                <li class="call-to-action"></li>
                <li class="call-to-action"></li>
                <li class="call-to-action"></li>
                <li class="call-to-action"></li>
                <li class="call-to-action"></li>
            </ul>
        </div>
        <footer>
            <nav class="secondary-nav"></nav>
            <div class="utils"></div>
            <div class="legals"></div>
        </footer>
    </body>
</html>

Ajustement CSS

Comme la structure a légèrement changé, nous allons également légèrement changer nos associations.

    .page:after,
.grid:after {
    content: "";
    clear: both;
    display: block;
}

devient

  body > header:after,
  body > footer:after,
  .content:after,
.grid:after {
  content: "";
  clear: both;
  display: block;
}

Création d'un annulateur de liste dans la grille pour les balises comme .call-to-action-list.

.list-reset { margin: 0;padding: 0;list-style-type: none; }

Association dans la partie Mobile et plus :

  .call-to-action-list,
.list-reset { margin: 0;padding: 0;list-style-type: none; }

Et facilitation du ciblage des Call to Action devenu des listes :

  .call-to-action:nth-child(8),
  .call-to-action:nth-child(10),
  .call-to-action:nth-child(12),
.prepend12-2 { margin-left: 16.66666667%; }
/**/
  .call-to-action:nth-child(9),
  .call-to-action:nth-child(11),
  .call-to-action:nth-child(13),
.append12-2 { margin-right: 16.66666667%; }

devient

  .call-to-action:nth-child(odd),
.prepend12-2 { margin-left: 16.66666667%; }
/**/
  .call-to-action:nth-child(even),
.append12-2 { margin-right: 16.66666667%; }

Ce qui donne :

L' Exemple :

Grille imbriquée

Le système de grille peut ensuite s'appliquer à l'intérieur même des colonnes. En fonction de ce que vous souhaitez faire, il peut être intéressant de retirer les gouttières d'un élément parent pour les appliquer à la sous grille.

Voici la zone Main rempli avec une sous grille. Main fait alors office de .grid, Article et Steps office de .column

HTML

<!-- ... header ... -->

<div class="content">
  <section class="overview"></section>
  <nav class="anchor-nav"></nav>


  <article class="main">
    <div class="article"></div>
    <ol class="step-list">
      <li class="step"></li>
      <li class="step"></li>
      <li class="step"></li>
    </ol>
  </article>


  <ul class="call-to-action-list">
    <li class="call-to-action"></li>
    <li class="call-to-action"></li>
    <li class="call-to-action"></li>
    <li class="call-to-action"></li>
    <li class="call-to-action"></li>
    <li class="call-to-action"></li>
  </ul>
</div>

<!-- ... footer ... -->

Nous allons donc améliorer nos CSS de test en rajoutant,

.main:before {
  content: none;
}
.article:before {
  content: "Article";
  background-color: rgba(255, 255, 0, 0.2);
  width: 100%;
  display: inline-block;
}
.step:before {
  content: "Steps";
  background-color: rgba(0, 255, 0, 0.2);
  width: 100%;
  display: inline-block;
}

en rajoutant les listes ordonnées sans style pour la grille,

    .step-list,
    .call-to-action-list,
.list-reset { margin: 0;padding: 0;list-style-type: none; }

en retirant les gouttières des « .column » faisant office de « .grid » et

/* Mobile / Small tablet */
    .brand,
    .slogan,
    .title,
    .main-nav,
    .overview,
    .anchor-nav,
    .call-to-action,
    .secondary-nav,
    .utils,
    .legals,
.gutter { padding-left: 16px; padding-right: 16px; }

en abonnant .step-list et .step aux .grid, .column et gutter

    .brand,
    .slogan,
    .title,
    .main-nav,
    .overview,
    .anchor-nav,
    .main,
    .article,
    .step,
    .call-to-action,
    .secondary-nav,
    .utils,
    .legals,
.column {
    float: left;
    width: 100%;
}

/* Mobile / Small tablet */
    .brand,
    .slogan,
    .title,
    .main-nav,
    .overview,
    .anchor-nav,
    .article,
    .step,
    .call-to-action,
    .secondary-nav,
    .utils,
    .legals,
.gutter { padding-left: 16px; padding-right: 16px; }

Il ne nous reste plus qu'à manager le comportement des colonnes en fonction des périphériques :

/* Tablet / Small desktop */
@media (min-width: 768px) {
    .brand,
    .title,
.span12-2 { width: 16.66666667%; }
    .anchor-nav,
    .utils,
.span12-3 { width: 25%; }
    .step,
.span12-4 { width: 33.33333333%; }    
    .call-to-action,
.span12-6 { width: 50%; }
    .legals,
    .main,
.span12-9 { width: 75%; }
    .main-nav,
    .slogan,
.span12-10 { width: 83.33333333%; }

    .utils,
    .legals,
    .brand,
    .slogan,
.pull-push { position: relative; }

    .utils,
.push12-9 { left: 75%; }
    .brand,
.push12-10 { left: 83.33333333%; }
    .slogan,
.pull12-2 { right: 16.66666667%; }
    .legals,
.pull12-3 { right: 25%; }
}

L' Exemple :

Équivalent d'un clearfix

En remplissant notre architecture avec du contenu on s'aperçoit qu'il y a des problèmes de chevauchement.

C'est le cas avec la zone Title dans l'exemple ci-après :

L' Exemple :

Pour résoudre cela, il suffit de créer une règle CSS clearfix et d'associer la zone Title.

    .title,
.clearfix { clear: both; }

Note : J'en profite également pour empêcher le site d'être plus large que 1200px

L' Exemple :

Pour aller plus loin

Alignement

Il est tout à fait possible de reproduire ce type de fonctionnement pour autre chose que la grille. Par exemple pour l'alignement des textes, images, etc.

Créons par exemple les règles exemples suivantes :

/*------------------------------------*\
    $ALIGMENT
\*------------------------------------*/

/*
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-justify { text-align: justify; }
*/

et appliquons la CSS sur les zones de la manière suivante :

/* Mobile / Small tablet */
    .utils,
.text-center { text-align: center; }

/* Tablet / Small desktop */
@media (min-width: 768px) {
     .brand,
     .utils,
.text-right { text-align: right; }
}

L' Exemple :

Afficher / Masquer

De la même manière il est possible d'afficher ou de masquer certaines zones en fonction de la résolution d'affichage.

/*------------------------------------*\
    $VISTIBLE / HIDDEN
\*------------------------------------*/

/*
.display-block, visible { display: block; }
.display-inline { display: inline-block; }
.display-inline-block { display: inline-block; }
...
.display-none, hidden { display: none; }
*/

N'affichons que 2 des 3 Call to Action en Tablette et en Large Desktop et les Step seulement à partir de Tablette :

/* Mobile / Small tablet */
    .step,
.display-none, hidden { display: none; }

/* Tablet / Small desktop */
@media (min-width: 768px) {
    .step,
.display-block, visible { display: block; }
    .call-to-action:nth-child(n + 3),
.display-none, hidden { display: none; }
}

/* Desktop */
@media (min-width: 992px) {
    .call-to-action:nth-child(n + 3),
.display-block, visible { display: block; }
}

/* Large desktop */
@media (min-width: 1200px) {
    .call-to-action:nth-child(n + 3),
.display-none, hidden { display: none; }
}

L' Exemple :

Pour finir

La rapidité d’adressage et la maintenance du code CSS est tout aussi aisé sinon plus rapide avec un bon éditeur de texte. La perte de temps réside dans la préparation des classes possibles avant affectation mais rien ne vous empêche d'aller les piocher dans les divers Frameworks CSS existant et de faire vos mélanges !