Slide 1

Slide 1 text

CSS at scale

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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 */

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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 */ }

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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; }

Slide 12

Slide 12 text

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 …)

Slide 13

Slide 13 text

BEM Block Element Modifier

Slide 14

Slide 14 text

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»

Slide 15

Slide 15 text

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)

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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; }

Slide 23

Slide 23 text

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 Login

Slide 24

Slide 24 text

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; } }

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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 …

Slide 27

Slide 27 text

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),

Slide 28

Slide 28 text

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 (
{text}

Slide 29

Slide 29 text

) } }

Slide 30

Slide 30 text

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),

Slide 31

Slide 31 text

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() {

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Questions ?