Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CSS at scale

CSS at scale

A little reminder about how CSS let's you do shit and how you should embrace the BEM methodology to make your code easy to read and write. (sorry for the crappy PDF export 😬)

Matthias Le Brun

June 24, 2015
Tweet

More Decks by Matthias Le Brun

Other Decks in Programming

Transcript

  1. CSS at scale

    View Slide

  2. Matthias Le Brun
    Développeur front-end
    @bloodyowl
    @reactjsfrance
    Putain de code !

    View Slide

  3. Les problèmes de CSS
    1. Les selecteurs sont des variables globales
    .selector {
    font-size: 1rem;
    }
    =
    window.selector = {
    fontSize: "1rem",
    }

    View Slide

  4. Les problèmes de CSS
    2. La cascade
    .nav {
    font-size: 0;
    }
    .nav .item {
    display: inline-block;
    /* font-size is 0 */
    }

    View Slide

  5. Les problèmes de CSS
    3. La spécificité
    * {} /* specificity = 0,0,0,0 */
    li {} /* specificity = 0,0,0,1 */
    li:first-line {} /* specificity = 0,0,0,2 */
    ul li {} /* specificity = 0,0,0,2 */
    ul ol+li {} /* specificity = 0,0,0,3 */
    h1 + *[rel=up]{} /* specificity = 0,0,1,1 */
    ul ol li.red {} /* specificity = 0,0,1,3 */
    li.red.level {} /* specificity = 0,0,2,1 */
    #x34y {} /* specificity = 0,1,0,0 */
    style="" /* specificity = 1,0,0,0 */
    !important /* specificity = P,T,D,R */

    View Slide

  6. Les problèmes de CSS
    3. La spécificité

    View Slide

  7. Les problèmes de CSS
    4. L'annulation de propriétés
    .item {
    display: block;
    font-size: 1rem;
    color: blue;
    /* un dev vient de rajouter la propriété `border` */
    border: 1px solid red;
    }
    .some-context .item {
    display: inline-block;
    color: red;
    /* regression dans .some-context */
    }

    View Slide

  8. Les problèmes de CSS
    5. La priorisation
    .blue { color: blue; }
    .red { color: red; }

    text
    text

    View Slide

  9. Les problèmes de CSS
    En résumé
    Un langage sans scope
    qui transmet automatiquement toute propriété d'un sélecteur
    à ses descendants
    qui priorise les sélecteurs comme un ivrogne
    qui est régression-friendly™
    qui rend impossible la prédictabilité de priorisation à grande
    échelle

    View Slide

  10. Ce qui n'aide pas non plus
    1. @extend
    .blue {
    color: blue;
    }
    .red {
    color: red;
    }
    .my-selector {
    @extend .red;
    @extend .blue;
    }

    View Slide

  11. Ce qui n'aide pas non plus
    2. Les «CSS atomiques»
    .block { display: block; }
    .inline-block { display: inline-block; }
    .relative { position: relative; }
    .absolute { position: absolute; }
    .static { position: static; }
    .padding-10 { padding: 10px; }

    View Slide

  12. Ce qui n'aide pas non plus
    2. OOCSS, SMACSS …
    Approches compliquées à apprendre à une équipe junior
    Différents niveaux non adaptés à un découpage en
    composants (layout, module, theme …)

    View Slide

  13. BEM
    Block Element Modifier




    Home


    About




    View Slide

  14. BEM
    Block
    le Block représente un composant
    Il peut être utilisé sans contexte particulier
    Il peut avoir son private tree
    Il peut avoir un état local (e.g. .Button--small)
    Cet état est appliqué par un «Modifier»

    View Slide

  15. BEM
    Element
    L'Element est un bloc appartenant au private tree de son
    ancêtre Block
    Il peut avoir un état local (e.g. .Nav-item--active)

    View Slide

  16. BEM
    Block Element Modifier: cas possibles
    Block
    Block--modifier
    Block-element
    Block-element--modifier

    View Slide

  17. BEM
    Block Element Modifier
    Une façon de structurer sainement son app/site
    Des règles minimales
    Un nœud pouvant exister sans dépendre d'un ancêtre est
    un Block .Block
    Un nœud pouvant n'exister qu'en tant que descendant
    d'un ancêtre N est un element du Block N .Block-element
    Un état local pour un Block ou un Element est défini par
    un modifier de ce Block ou Element.Block--modifier ou
    .Block-element--modifier

    View Slide

  18. View Slide

  19. BEM
    Block Element Modifier: aller plus loin
    Dans le cas où un block est enfant d'un autre block, préférer le
    wrapping à la composition










    View Slide

  20. View Slide

  21. BEM
    Block Element Modifier: aller plus loin
    Prefixer les classNames par le nom de l'organisation, afin de
    prévenir les conflits avec le third-party





    View Slide

  22. BEM
    Block Element Modifier: aller plus loin
    Éviter les side effects de la cascade en définissant les
    propriétés au niveau le plus bas possible
    /* bad */
    .Nav { font-size: 2rem; }
    .Nav-item { padding: .5rem; }
    /* good */
    .Nav {}
    .Nav-item { font-size: 2rem; padding: .5rem; }

    View Slide

  23. BEM
    Block Element Modifier: aller plus loin
    Si votre stack vous impose des selecteurs pour les actions JS,
    utilisez des classNames uniquement prévus à cet effet
    class="org-Button org-Button--small org-js-OpenLoginModal">
    Login

    View Slide

  24. BEM
    Block Element Modifier: aller plus loin
    Ne groupez jamais les media queries, gardez les toujours près
    des déclarations qu'elles complètent/override
    .Nav {}
    .Nav-item { font-size: 2rem; }
    @media(--maxM) {
    .Nav-item { font-size: 1.5rem; }
    }

    View Slide

  25. BEM
    Block Element Modifier: les avantages
    Rend triviale la compréhension du markup et du style
    Crée rapidement des automatismes
    Apporte naturellement l'isolation des styles
    Permet d'éliminer facilement le code legacy
    Permet de repérer les différents niveaux de legacy en
    changeant de préfixe

    View Slide

  26. Next steps
    Inline styles
    BEM résoud beaucoup de soucis, mais pas le contrôle de la
    priorisation
    JavaScript + la propriété style nous le permettent
    + variables, conditions, modules …

    View Slide

  27. Next steps
    Inline styles
    import React, {Component} from "react"
    import {lighten, darken} from "color"
    import styleVariables from "stylesVariables"
    const styles = {
    button: {
    display: "block",
    font-weight: 700,
    },
    buttonDefault: {
    backgroundColor: styleVariables.colorBlue,
    },
    buttonHover: {
    backgroundColor: lighten(styleVariables.colorBlue, .1),
    },
    buttonPressed: {
    backgroundColor: darken(styleVariables.colorBlue, .1),

    View Slide

  28. backgroundColor: darken(styleVariables.colorBlue, .1),
    },
    }
    export class extends Component {
    state = {
    hover: false,
    pressed: false,
    }
    render() {
    const {text} = this.props
    const {hover, pressed} = this.state
    return (
    style={{
    ...styles.button,
    ...styles.buttonDefault,
    ...hover && styles.buttonHover,
    ...pressed && styles.buttonPressed,
    }}>
    {text}

    View Slide


  29. )
    }
    }

    View Slide

  30. Next steps
    Inline styles: events
    import React, {Component} from "react"
    import {lighten, darken} from "color"
    import styleVariables from "stylesVariables"
    const styles = {
    button: {
    display: "block",
    font-weight: 700,
    },
    buttonDefault: {
    backgroundColor: styleVariables.colorBlue,
    },
    buttonHover: {
    backgroundColor: lighten(styleVariables.colorBlue, .1),
    },
    buttonPressed: {
    backgroundColor: darken(styleVariables.colorBlue, .1),

    View Slide

  31. backgroundColor: darken(styleVariables.colorBlue, .1),
    },
    }
    export class extends Component {
    state = {
    hover: false,
    pressed: false,
    }
    handleMouseEnter() {
    this.setState({
    hover: true,
    })
    }
    handleMouseLeave() {
    this.setState({
    hover: false,
    })
    }
    handleMouseDown() {

    View Slide

  32. handleMouseDown() {
    this.setState({
    pressed: true,
    })
    }
    handleMouseUp() {
    this.setState({
    pressed: false,
    })
    }
    render() {
    const {text} = this.props
    const {hover, pressed} = this.state
    return (
    onMouseEnter={() => this.handleMouseEnter()}
    onMouseLeave={() => this.handleMouseLeave()}
    onMouseDown={() => this.handleMouseDown()}
    onMouseUp={() => this.handleMouseUp()}
    style={{
    ...styles.button,

    View Slide

  33. ...styles.button,
    ...styles.buttonDefault,
    ...hover && styles.buttonHover,
    ...pressed && styles.buttonPressed,
    }}>
    {text}

    )
    }
    }

    View Slide

  34. Next steps
    Inline styles: bénéfices
    Permet de prioriser les styles
    Possède naturellement des fonctions comme first class
    citizen
    JavaScript dispose déjà d'un outillage intéressant pour
    l'analyse de code
    Rend possible la cohabitation de style & markup dans le
    même fichier (tout comme avec des WebComponents)
    Abolit la spécificité des selecteurs
    Évite au browser de parser la page pour dresser la
    correspondance selector/element

    View Slide

  35. Questions ?

    View Slide