OOCSS, LESS, BEM
et autres pratiques du quartier*

*Le premier film de Pedro Almodovar s'appelait"Pepe, Luci, Bom et autres filles du quartier"

Qui suis-je ?

Ducky Smockton ? Non, mais presque

- Développeur Web autodidacte/ FAMP par curiosité

 (Une amie m'a présentée FreeBSD, et on est dev'nu copains - avec FreeBSD, pas avec mon amie !)

- Enseignant en SVT (3 ans) par nécessité    (il faut manger)

- Développeur Web/Java EE par goût du "luxe"

 (les pâtes au beurre, c'est bien, mais les pâtes alla bolognese, c'est mieux)

- Responsable technique par inadvertance (j'vous expliqu'rai)

- Développeur Front ces derniers temps par choix

(j'suis payé pareil avec moins de responsabilités et plus de fun !)

</ma-vie>

Pourquoi ce sujet ?

- parce que j'aime partager (et pas que les chouquettes)

- parce que je connais bien le sujet (on fait c'qu'on peut)

- parce que j'ai beaucoup travaillé sur du code "historique"

- parce que j'aime me la péter devant un public en délire


(une seule est vraie, je vous laisse deviner)

Rappels

(Pour être sûr de me faire comprendre par la suite)

CSS ?

CSS, qu'est-ce ?

Un langage standard ouvert du web pour le HTML et le XML
(Woah le scoop, eh ! J'suis v'nu pour ça ?)

Pourquoi le CSS ?

Pour séparer le contenu de la forme (Wouhouuu nan mais il ay sayrieux ?)

Par qui ?

Le W3C

Où en est-on ?

CSS2.1 Recommandation en juin 2011

CSS3 découpés en modules, la plupart déjà en recommandation

CSS4 n'existe pas, des modules de niveau 4 sont en brouillon, qui "héritent" de CSS3 (selector, background, etc.), et d'autres de niveau 1 (Flexbox...)

Terminologie

  • Règle
  • Sélecteur
  • Déclaration
  • Propriété
  • Valeurs

/* Règle */

.classe { /* Sélecteur */

  background-color: #555; /* Déclaration */

  /* propriété: valeur(s); */

  background: #555 url(/chemin/de/croix.png) no-repeat center center;
}

Les différents sélecteurs

(Oui, je sais, vous les connaissez)

Les différents sélecteurs

/* Sélecteur universel */
*

/* Sélecteurs de type */
div

/* Sélecteurs descendants */
div span

/* Sélecteurs d'enfant */
ul > li

/* La pseudo-classe */ 
li:first-child

/* Les pseudo-classes de lien */
a:link
a:visited

/* Les pseudo-classes dynamiques */
a:active
a:hover
a:focus

/* La pseudo-classe :lang() */
h1:lang(fr)

/* Les sélecteurs adjacents */
h2 + p
/* Sélecteurs d'attribut */
abbr[title]

/* Sélecteurs d'attribut */
fieldset[foo="warning"]
label[foo~="warning"]
aside[lang|="en"]

/*************************
 *   Seulement en HTML   *
 *************************/

/* Sélecteurs de classe */
/* Identique à DIV[class~="warning"] */
.warning

/* Sélecteurs d'ID */
#myid

/* Les pseudo-éléments */
:first-line
:first-letter
:before
:after 

Spécificité CSS

ou comment savoir quelle déclaration prévaut

/* 1 */
a {
    background-color: yellow;
}
/* 2 */
#content p a {
    background-color: red;
}
/* 3 */
p #wannabee-black {
    background-color: green;
}
/* 4 */
#wannabee-black {
    background-color: black;
}
/* 5 */
a.perfect-color {
    background-color: blue;
}
/* 6 */
div.main p a.perfect-color {
    background-color: orange;
    /* which is the new black */
}
    <body>
        <div class="main" id="content">
            <p>
                This is <a id="wannabee-black" class="perfect-color">fun</a>!
            </p>
        </div>
    </body>

Un petit jeu

pour se mettre dans le bain :

Quelle sera la couleur de fond

du mot "fun" ?

Version simple

Inline

ID

Classe

et pseudo-classe*
et attribut**

Type
(= balise)

Inline

ID

Classe

et pseudo-classe*
et attribut**

Type
(= balise)

header #better #faster .stronger .work-is-never:hover

Inline

ID

Classe

et pseudo-classe*
et attribut**

Type
(= balise)

0

2

3

1

header #better #faster .stronger .work-is-never:hover

Nesting = Evil

Imbrications SASS/Less/Stylus :

La fausse bonne idée

(Saylemal !)

Voyons pourquoi...

Complexité cyclomatique

  • Mesure de la complexité d'un logiciel
  • Dépend entre autres du nombre de chemins que peut prendre le code
  • ==> plus il y a de if, else, elseif et autres while, plus le code est complexe

Complexité cyclomatique

        process.argv.forEach(function (val) {
            if (val === 'prod' || val === 'express:prod') {
                app.set('env', 'production');
            } else if (val.indexOf('port=') > -1) {
                try {
                    var serverPort = parseInt(val.split('=')[1], 10);
                    app.set('serverPort', serverPort);
                    winston.info('Server port = ' + serverPort);
                } catch (e) {
                    winston.warn('Cannot parse server port');
                }
            } else if (val.indexOf('apiHost=') > -1) {
                try {
                    apiHost = val.split('=')[1];
                    winston.info('Api host = ' + apiHost);
                } catch (e) {
                    winston.warn('Cannot parse "--apiHost=X.X.X.X"');
                }
            } else if (val.indexOf('coreApiHost=') > -1) {
                try {
                    coreApiHost = val.split('=')[1];
                    winston.info('Core Api host = ' + coreApiHost);
                } catch (e) {
                    winston.warn('Cannot parse "--coreApiHost=X.X.X.X"');
                }
            } else if (val.indexOf('clientId=') > -1) {
                try {
                    clientId = val.split('=')[1];
                    winston.info('Client id = ' + clientId);
                } catch (e) {
                    winston.warn('Cannot parse "--clientId=XXXX"');
                }
            } else if (val.indexOf('managerId=') > -1) {
                try {
                    managerId = val.split('=')[1];
                    winston.info('Manager id = ' + managerId);
                } catch (e) {
                    winston.warn('Cannot parse "--managerId=XXXX"');
                }
            } else if (val.indexOf('login=') > -1) {
                try {
                    login = val.split('=')[1];
                    winston.info('CCMD login = ' + login);
                } catch (e) {
                    winston.warn('Cannot parse "--login=lorem.ipsum"');
                }
            }
        });

Un exemple

  • le code est plus difficile à comprendre ;
  • augmente le nombre de points d'erreur potentiels ;
  • le code est plus difficile à modifier, à maintenir, et à réutiliser;
  • plus facile d'avoir des effets de bords ;
  • le code est plus difficile à tester.

Complexité cyclomatique

"That's what they say... that's not what they mean"*

Complexité cyclomatique

header #better #faster .stronger .work-is-never:hover {
    (...)
}

"That's what they say... that's not what they mean"*

Ce que ça dit :

Complexité cyclomatique

header #better #faster .stronger .work-is-never:hover {
    (...)
}
@if isMouseOver() and isElementWithClass(work-is-never) {

    @if insideElementWithClass(stronger) {

        @if insideElementWithId(faster) {

            @if insideElementWithId(better) {

                @if inside(header) {

                    (...)
                }
            }
        }
    }
}

"That's what they say... that's not what they mean"*

Ce que ça dit :

Ce que ça veut dire :

Complexité cyclomatique

header #better #faster .stronger .work-is-never:hover {
    (...)
}
@if isMouseOver() and isElementWithClass(work-is-never) {

    @if insideElementWithClass(stronger) {

        @if insideElementWithId(faster) {

            @if insideElementWithId(better) {

                @if inside(header) {

                    (...)
                }
            }
        }
    }
}

"That's what they say... that's not what they mean"*

Ce que ça dit :

*Jack Shephard dans "Lost" à propos de ses tatouages

Complexité cyclomatique

Ce que ça veut dire :

Tight coupling = Evil

Le couplage fort, saylemal !
(mais ça, vous le savez)







header {

    #better {

        #faster {

            .stronger {

                .work-is-never:hover {

                    (...)
                }
            }
        }
    }
}
<html>
    <head>
    </head>
    <body>

        <header>

            <div id="better">

                <div id="faster">

                    <div class="stronger">

                        <a class="work-is-never">
                            
                        </a>
                    </div>
                </div>
            </div>
        </header>
    </body>
</html>

Tight coupling = Evil

  • Une modification du markup HTML ==> modification du CSS
  • Une modification du CSS ==> modification du markup HTML
  • Styles non réutilisables
  • Augmente les répétitions de déclarations

Le couplage fort, saylemal !
(mais ça, vous le savez)

Efficacité des sélecteurs

  1. ID : #stronger
  2. Class : .promo
  3. Type : header
  4. Adjacent : h2 + p
  5. Enfant : ul > li
  6. Descendant : ul a
  7. Universel : *
  8. Attribut : [type="text"]
  9. Pseudo-classes/-elements, : a:hover

Dites : "Aaargh"

(Criez en voyant la prochaine slide)


#edit-video-popup #video-popup-container #video-settings #embedded-video-menu #video-formats .radio-button.ok:after {

  (...)

}

#toast-container .ng-toast__list .alert.toast-warning .toast-title + .toast-message > div:not(:empty) {

  (...)

}

OOCSS

Penser "objet" en CSS

Le but

OOCSS

  • Séparation de la structure et de l'apparence (skin)
  • Séparation du conteneur et du contenu

Encourager la réutilisation de code, et écrire des feuilles de styles plus efficaces, et plus facile à maintenir et modifier.

Les deux principes

OOCSS

<div class="signupform signup-modal invisible">
    <div id="signup-modal">
        <h2><img src="https://d2ed0w4q03gsmw.cloudfront.net/s3/assets/4b367e1/images/logos/aboutme_logo_black.svg" /></h2>
        <h3 class="action">It’s free and only takes minutes to set up a page. Join now or <a href="https://about.me/">learn more</a>.</h3>
        <div class="signup">
            <form action="/register" id="signup_signupmodal" method="post" onsubmit="return false">
                <input name="_authentication_token" type="hidden" value="140652078138150610996182975593007636835" />
                <fieldset class="first_name name">
                    <label class="label" for="signupmodal_first_name">First name</label>
                    <input type="text" name="first_name" id="signupmodal_first_name" class="input fullwidth large first_name" value="" maxlength="32" />
                </fieldset>
                <fieldset class="last_name name">
                    <label class="label" for="signupmodal_last_name">Last name</label>
                    <input type="text" name="last_name" id="signupmodal_last_name" class="input fullwidth large last_name" value="" maxlength="32" />
                </fieldset>
                <fieldset class="email">
                    <label class="label" for="signupmodal_email">Email address</label>
                    <input type="text" name="email" id="signupmodal_email" class="input fullwidth large email" value="" maxlength="254" />
                </fieldset>
                <input type="hidden" value="signupmodal" name="location" class="signuplocation" />
                <fieldset class="buttons large">
                    <div class="alreadyjoined right invisible button-item"><span class="">Already joined?  </span><a href="https://about.me/login" class="not-deferred">Log In</a></div>
                    <button type="submit" class="button test130106 large dark blue signup-submitbutton not-deferred">
                    <span class="default-text">Join for FREE</span>
                    <span class="loading-text">Join for FREE</span>
                    </button>
                </fieldset>
                <div class="socialsignup">
                    <h4>or continue with…</h4>
                    <div class="buttons">
                        <a href="https://about.me/facebook/login?perm=basic" class="button signupbutton large  facebook glyph-facebook not-deferred">Facebook</a>
                        <a href="https://about.me/twitter/login" class="button signupbutton large  twitter glyph-twitter not-deferred">Twitter</a>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

OOCSS

OOCSS

/* BAD! */
#widget-popup button {
	width: 200px;
	height: 50px;
	padding: 10px;
	border: solid 1px #ccc;
	background: linear-gradient(#ccc, #222);
	box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

#widget-popup .box {
	width: 400px;
	overflow: hidden;
	border: solid 1px #ccc;
	background: linear-gradient(#ccc, #222);
	box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

#widget-popup .tooltip {
	width: 500px;
	min-height: 200px;
	overflow: auto;
	border: solid 1px #ccc;
	background: linear-gradient(#ccc, #222);
	box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

OOCSS

/* GOOD! */
.button {
	width: 200px;
	height: 50px;
}

.box {
	width: 400px;
	overflow: hidden;
}

.tooltip {
	width: 500px;
	min-height: 200px;
	overflow: auto;
}

.sf-default-theme {
	border: solid 1px #ccc;
	background: linear-gradient(#ccc, #222);
	box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px;
}

OOCSS

/* BAD! */
#sidebar h3 {
	font-family: Arial, Helvetica, sans-serif;
	font-size: .8em;
	line-height: 1;
	color: #777;
	text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}

OOCSS

/* BAD! */
#sidebar h3 {
	font-family: Arial, Helvetica, sans-serif;
	font-size: .8em;
	line-height: 1;
	color: #777;
	text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}
#sidebar h3, #footer h3 {
	font-family: Arial, Helvetica, sans-serif;
	font-size: 2em;
	line-height: 1;
	color: #777;
	text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}

#footer h3 {
	font-size: 1.5em;
	text-shadow: rgba(0, 0, 0, .3) 2px 2px 4px;
}

OOCSS

/* BAD! */
#sidebar h3 {
	font-family: Arial, Helvetica, sans-serif;
	font-size: .8em;
	line-height: 1;
	color: #777;
	text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}
#sidebar h3, #footer h3 {
	font-family: Arial, Helvetica, sans-serif;
	font-size: 2em;
	line-height: 1;
	color: #777;
	text-shadow: rgba(0, 0, 0, .3) 3px 3px 6px;
}

#footer h3 {
	font-size: 1.5em;
	text-shadow: rgba(0, 0, 0, .3) 2px 2px 4px;
}
#sidebar h3.alternative {
	font-size: .9em;
    	color: #45a;
}

OOCSS

<div class="media">
  <a href="http://twitter.com/sormieres" class="img">
        <img src="sormieres.jpg" alt="me" width="40" />
  </a>
  <div class="bd">
    @smartfocus 9 minutes ago
  </div>
</div>
.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}

The media object

Bootstrap est un peu OOCSS

  • .row et  .col-*-*

  • .btn  et  .btn-primary ,etc.
  • .clearfix
  • .text-left, .text-center, etc.

  • .table, .table-striped

Un mot sur l'ACSS

Atomic CSS

(Beaucoup de) règles avec un sélecteur simple : une classe,
et avec une seule déclaration.

Très réutilisables. Tout ce qui suit n'est pas une bonne idée, mais il y a du bon à prendre (.hidden et .pr et .pa, j'aime bien)

.pt-0 {
    padding-top: 0;
}

.ta-c {
    text-align: center;
}

.pr {
    position: relative;
}

.pa {
    position: absolute;
}

.hidden {
    display: none;
}

BEM

Toujours plus loin

BEM

  • Block
  • Element
  • Modifier
/* Règle concernant le bloc */
.block {}

/* Règle concernant l'élément à l'intérieur du bloc */
.block__element {}

/* Une règle définissant une variante du bloc
   (Ne définit que les propriétés qui ont des valeurs différentes !) */
.block--modifier {}

/* Une règle définissant une variante de l'élément
   (Ne définit que les propriétés qui ont des valeurs différentes !) */
.block__element--modifier {}

Principes :

  • Tout, dans une page, peut être décomposé en blocs avec pour chacun des éléments
  • Les noms de classes expriment la parenté entre bloc et élément

BEM

.person {}

.person__hand {}

.person--female {}

.person--female__hand {}

.person__hand--left {}

Une analogie pour bien comprendre*

BEM

.media {}

.media__img {}

.media__img--rev {}

.media__body {}

Media object version BEM

BEM

Grille CSS version BEM (CSSWizardry)

.grid {
    margin-left:-$base-spacing-unit;
    list-style:none;
    margin-bottom:0;
}

.grid__item {
    display:inline-block;
    width:100%;
    padding-left:$base-spacing-unit;
    vertical-align:top;
}

.one-half {
    width: 50% !important;
}

.one-third {
    width: 33.333% !important;
}

.two-thirds {
    width: 66.666% !important;
}

(...)

.one-tenth {
    width: 10% !important;
}

(...)
<ul class="grid">

    <li class="grid__item">
        Toute la largeur du parent
    </li>

    <li class="grid__item  one-half">
        La moitié de la largeur du parent
    </li>

    <li class="grid__item  one-half">
        La moitié de la largeur du parent
    </li>

    <li class="grid__item  one-third">
        Un tiers de la largeur du parent
    </li>

    <li class="grid__item  two-thirds">
        Deux tiers de la largeur du parent
    </li>

</ul

BEM

#edit-reco-popup {
    (...)
    .parameters-container {
        (...)
        .parameter-list-container {
            (...)
            .scrollable {
                (...)
                .nano > .nano-content {
                    (...)
                    .parameters {
                        (...)
                        .parameter-item {
                            (...)
                            .input-container {
                                (...)
                                input {
                                    display: inline-block;
                                    vertical-align: top;
                                    border: none;
                                    outline: none;
                                    line-height: 30px;
                                    height: 30px;
                                    min-width: 200px;
                                    padding: 0 @gutter;
                                    overflow: hidden;
                                }
                                .personalization-field {
                                    vertical-align: top;
                                    height: 30px;
                                    width: 30px;
                                    cursor: pointer;
                                    background-color: @grey2;
                                    border-left: 1px solid @grey5;
                                    .border-radius(0 2px  2px 0px)
                                }
(...)
#edit-video-popup {
    (...)
    #video-popup-container {
        (...)
        #video-settings {
            (...)
            .input-container {
                (...)
                .input {
                    display: inline-block;
                    vertical-align: middle;
                    border: none;
                    line-height: 28px;
                    height: 28px;
                    outline: none;
                    min-width: 300px;
                }

                .personalization-field {
                    vertical-align: middle;
                    background-color: @grey2;
                    height: 30px;
                    width: 30px;
                    cursor: pointer;
                    border-left: 1px solid @grey5;
                    .border-radius(0 2px  2px 0px)
                }
(...)

BEM

#edit-reco-popup {
    (...)
    .parameters-container {
        (...)
        .parameter-list-container {
            (...)
            .scrollable {
                (...)
                .nano > .nano-content {
                    (...)
                    .parameters {
                        (...)
                        .parameter-item {
                            (...)
                            .input-container {
                                (...)
                                input {
                                    display: inline-block;
                                    vertical-align: top;
                                    border: none;
                                    outline: none;
                                    line-height: 30px;
                                    height: 30px;
                                    min-width: 200px;
                                    padding: 0 @gutter;
                                    overflow: hidden;
                                }
                                .personalization-field {
                                    vertical-align: top;
                                    height: 30px;
                                    width: 30px;
                                    cursor: pointer;
                                    background-color: @grey2;
                                    border-left: 1px solid @grey5;
                                    .border-radius(0 2px  2px 0px)
                                }
(...)
#edit-video-popup {
    (...)
    #video-popup-container {
        (...)
        #video-settings {
            (...)
            .input-container {
                (...)
                .input {
                    display: inline-block;
                    vertical-align: middle;
                    border: none;
                    line-height: 28px;
                    height: 28px;
                    outline: none;
                    min-width: 300px;
                }

                .personalization-field {
                    vertical-align: middle;
                    background-color: @grey2;
                    height: 30px;
                    width: 30px;
                    cursor: pointer;
                    border-left: 1px solid @grey5;
                    .border-radius(0 2px  2px 0px)
                }
(...)

Un bon exemple de mauvaise pratique :

  • répétition de déclarations...
  • ...avec quelques nuances...
  • ...qui empêchent de garantir l'homogénéité de l'UI...
  • ...et qui rendent difficile la maintenance et la correction, les deux codes pourront évoluer séparément si on ne fait pas attention (et on ne fera pas attention, je le sais par expérience)
  • et je ne parle même pas de l'affreux "nesting à l'infini" (et au-délààààà !)

BEM

Nesting... 2.0

On peut utiliser les imbrications LESS/SASS/Stylus intelligemment

BEM

.input-container {
    vertical-align: middle;
    display: inline-block;
    border: 1px solid @grey5;
    margin-left: @halfGutter;
    .border-radius(@uiBorderRadius);

    &--warn {
        border-color: @yellow1;
        color: @yellow1;
    }

    &__input {
        display: inline-block;
        vertical-align: middle;
        border: medium none;
        height: 32px;
        line-height: 32px;
        width: 300px;
        outline: medium none;
        vertical-align: middle;
        padding-left: @halfGutter;
        padding-right: @halfGutter;
        .border-radius(2px);
        
        &[disabled] {
            color: @grey6;
            background-color: @grey3;
        }
        &--left {
            .border-radius(2px 0 0 2px);
        }
        &--right {
            .border-radius(0 2px 2px 0);
        }
        &--middle {
            .border-radius(0);
        }
    }

    &__icon {
        vertical-align: middle;
        background-color: @grey2;
        display: inline-block;
        height: 30px;
        width: 29px;
        border-left: 1px solid @grey5;
        cursor: pointer;
        background-size: @sizeS;
        background-repeat: no-repeat;
        background-position: 50% 50%;
        .border-radius(0 2px  2px 0px);

        &--left {
            border-right: 1px solid @grey5;
            border-left: none;
            .border-radius(2px 0 0 2px);
        }

        &--perso {
            background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNrEl7lLA0EUxmeDiUU84omojaCIVyEGtUllYSkeqIVFIFUaURAs/AssRBQkVQqxFAtFtBGxFIyKqNhY2AhqDOKFR9T1G5iFdZhsZi/z4Mcsb+f4due9t7OKqqpEs1gsRnRWD2ZBL/Dr/ArJYNFolM6h6lwvYAtM496VaIwnw1zt4AAMcIubtQIwDI4grE1WgA+sgQBxzkrAKkR4ZQSMgDrivDWCQRkB/cQ9G+IdeYJOHQLfDdgFaYlFlkE+6AEV3L2gjIBqgS8ELmUeEdEeZhnViuY029z8FvgFom5lF+eEnKFJce6sQegVzJWysed32Tp4JCZJ2xDwyTuwNflGAnzEfSs0ElAsGPBsY7FXgS9gJCAkGHBhQ4BobNefNMSeTLDrShq8ggGbNgRsgwjnW8SaTWjvtTowbzDBoU0B6+ActOh8pWBGJguowjHwY3V11IIvNseTlTSkOZx0IOqvjeqBkYBmELe7Ot5CEjRkuk9jYJJd17Ag1B9A+kA32HewDtC0XmIlnijckWwczQI3YA5MST6taTX8h2cnw/HMtAWDQf3ZkCQSCUUmBh4EfcrcrMu8gDfyz+ZxqI9l44OwCM2jII9rs00Uj8dVMwtrMcE/Ha1YquAYVe7WGxCdCWl+VnF/QnvgGHyDsNsCTjgBhH1MtA+K6wI22P+gKYtEIoq+EFmtA9RWtDKZqzSktXoUfOSyDtCg62S/1u9uCvgVYABLIX8iqQXEMQAAAABJRU5ErkJggg==");
    }

    }


}

Avez-vous noté le & ?

BEM

.input-container {
  vertical-align: middle;
  display: inline-block;
  border: 1px solid #bcbcbc;
  margin-left: 7px;
  border-radius: 3px;
  background-clip: padding-box;
}

.input-container--warn {
  border-color: #f58124;
  color: #f58124;
}

.input-container__input {
  display: inline-block;
  border: medium none;
  height: 32px;
  line-height: 32px;
  width: 300px;
  outline: medium none;
  vertical-align: middle;
  padding-left: 7px;
  padding-right: 7px;
  border-radius: 0;
  background-clip: padding-box;
}

.input-container__input[disabled] {
  color: #969696;
  background-color: #e9e9e9;
}

.input-container__input--left {
  border-radius: 2px 0 0 2px;
  background-clip: padding-box;
}

.input-container__input--right {
  border-radius: 0 2px 2px 0;
  background-clip: padding-box;
}

.input-container__input--middle {
  border-radius: 0;
  background-clip: padding-box;
}

.input-container__icon {
  vertical-align: middle;
  background-color: #f2f2f2;
  display: inline-block;
  height: 30px;
  width: 29px;
  border-left: 1px solid #bcbcbc;
  cursor: pointer;
  background-size: 16px;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  border-radius: 0 2px 2px 0px;
  background-clip: padding-box;
}

.input-container__icon--left {
  border-right: 1px solid #bcbcbc;
  border-left: none;
  border-radius: 2px 0 0 2px;
  background-clip: padding-box;
}

.input-container__icon--perso {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiVJREFUeNrEl7lLA0EUxmeDiUU84omojaCIVyEGtUllYSkeqIVFIFUaURAs/AssRBQkVQqxFAtFtBGxFIyKqNhY2AhqDOKFR9T1G5iFdZhsZi/z4Mcsb+f4due9t7OKqqpEs1gsRnRWD2ZBL/Dr/ArJYNFolM6h6lwvYAtM496VaIwnw1zt4AAMcIubtQIwDI4grE1WgA+sgQBxzkrAKkR4ZQSMgDrivDWCQRkB/cQ9G+IdeYJOHQLfDdgFaYlFlkE+6AEV3L2gjIBqgS8ELmUeEdEeZhnViuY029z8FvgFom5lF+eEnKFJce6sQegVzJWysed32Tp4JCZJ2xDwyTuwNflGAnzEfSs0ElAsGPBsY7FXgS9gJCAkGHBhQ4BobNefNMSeTLDrShq8ggGbNgRsgwjnW8SaTWjvtTowbzDBoU0B6+ActOh8pWBGJguowjHwY3V11IIvNseTlTSkOZx0IOqvjeqBkYBmELe7Ot5CEjRkuk9jYJJd17Ag1B9A+kA32HewDtC0XmIlnijckWwczQI3YA5MST6taTX8h2cnw/HMtAWDQf3ZkCQSCUUmBh4EfcrcrMu8gDfyz+ZxqI9l44OwCM2jII9rs00Uj8dVMwtrMcE/Ha1YquAYVe7WGxCdCWl+VnF/QnvgGHyDsNsCTjgBhH1MtA+K6wI22P+gKYtEIoq+EFmtA9RWtDKZqzSktXoUfOSyDtCg62S/1u9uCvgVYABLIX8iqQXEMQAAAABJRU5ErkJggg==");
}

Voici le CSS produit : du joli BEM, donc lisible, et peu spécifique, donc réutilisable

Note : le code peut encore largement être amélioré, ce code est là surtout pour montrer qu'on peut utiliser les imbrications intelligemment en gardant des sélecteurs peu spécifiques mais en rassemblant dans le code SASS/LESS/Stylus les styles apparentés.

SMACSS

Scalable and Modular Architecture for CSS

SMACSS

  • Base - HTML elements & defaults (reset / normalize + nos styles basiques)
  • Layout - Structure de la page
  • Module - Blocs de code réutilisables (objets)
  • State - Actif/Inactif etc
  • Theme - Polices, couleurs, ombres, bordures...

Principe :

Découper les styles en catégories... et donc en fichiers

ITCSS

ITCSS

  • Settings  : Variables  globales & configuration, couleurs récurrentes (du logo de la marque, notamment)
  • Tools :  Default mixins & functions
  • Generic  Normalize, resets, box-sizing
  • Base  :  HTML elements (sélecteurs de type)
  • Objects : Objets sans skin
  • Components : Composants avec skin, parties d'UI
  • Trumps  :  Helpers & overrides

Principe :

"SMACSS sous extasy anti-dépresseurs pré-processeurs"

Inverted Triangle CSS

ITCSS

/* object.less : Uniquement des classes, en restant peu spécifique */ 

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

.ui-list__item {
    padding: @spacing-unit;
}
/* components.less : Toujours uniquement des classes, mais en étant plus explicite */ 

.carousel {

    height: 32px;
    max-height: 32px;
    vertical-align: middle;

    &__item {
        margin: 0px 7px;
        color: #BCBCBC;
        cursor: pointer;
        transition: color 200ms ease 0s;
    }
}
/* trumps.less : seul fichier où on trouvera des !important */

.one-half {
    width: 50% !important;
}

.hidden {
    display: none !important;
}

ITCSS

ITCSS

ITCSS

Bénéfices

  • Chaque chose est à sa place
  • Les intervenants savent où chercher et ajouter des styles
  • Un ordre des sources évident et sain
  • Réduction de la redondance
  • Augmentation de la scalabilité
  • En finir avec la guerre de la spécificité (des règles)

ITCSS

One more thing...

Be f**king semantic!

Parce qu'il n'y a pas que div et span, dans la vie (et le HTML) !

Des balises à utiliser

  • <header> (plusieurs fois dans la même page !)

  • <main> (un seul par page)

  • <section> (plusieurs fois dans la même page, doit avoir un header)

  • <footer> (plusieurs fois dans la même page !)

  • <nav> (navigation principale uniquement)

  • <aside>

  • <dialog>

S'il y a une saisie de l'utilisateur

  • Utiliser <form>
  • Utiliser <label for="ici_un_id">
  • Utiliser <fieldset> et <legend>

S'il y a des données tabulaires

  • Utiliser <table>, et dedans <caption> (facultatif mais j'aime bien), éventuellement <colgroup> et <col> <thead> (avec ses <th>), <tfooter> et <tbody> (plutôt dans cet ordre)

Références

  • https://github.com/stubbornella/oocss/wiki
  • https://en.bem.info/
  • http://getbem.com/
  • https://smacss.com/book
  • http://cssguidelin.es
  • http://csswizardry.com
  • https://css-tricks.com/
  • https://css-tricks.com/specifics-on-css-specificity/
  • http://csswizardry.com/2015/04/cyclomatic-complexity-logic-in-css/
  • https://speakerdeck.com/dafed/managing-css-projects-with-itcss
  • http://www.smashingmagazine.com/tag/css/
  • http://clubmate.fi/oocss-acss-bem-smacss-what-are-they-what-should-i-use/
  • http://www.alsacreations.com/article/lire/1641-bonnes-pratiques-en-css-bem-et-oocss.html
  • http://designshack.net/category/articles/css/

OOCSS, LESS, BEM et autres bonnes pratiques du quartier (Lecture)

By Stanislas Ormières

OOCSS, LESS, BEM et autres bonnes pratiques du quartier (Lecture)

  • 913