CSS preprocessors for the best of both worlds (From the Front 2014)

CSS preprocessors for the best of both worlds (From the Front 2014)

While purists propagate to not mix content structure (HTML), presentation (CSS) and behavior (JavaScript) layers for the reason of maintainability, concepts like OOCSS aim at the same goal, but from a different angle: reusable CSS code, at the expense of bloated, presentational mark-up, violating the separation of concerns. Can’t we have both, clean mark-up and reusable CSS? The benefits of both approaches without the drawbacks?
Yes, we can. This talk shows how to take the OO concept out of the HTML and put it where it belongs to: into the style sheet. Not directly into the CSS though, but into an intermediate layer provided by a CSS preprocessor like Sass.
Like Ogers and onions, concepts have layers. Unlike Ogers and onions, they don’t always stink, but eventually lead to the best of both worlds.

Video: https://www.youtube.com/watch?v=ot-LoU0MGb0

051dbe29a972707cde167602b38c9778?s=128

Gunnar Bittersmann

September 18, 2014
Tweet

Transcript

  1. 4.

    OUT OF YOUR BEANS COFFEE GRINDERS 98 € PICK Peugeot

    Coffee grinder 76 € PICK Kalita Coffee grinder 54 € PICK Heyde Coffee grinder
  2. 5.

    <html id="page-products"> <ul class="product-list"> <li class="product-item"> <img class="product-image" src="…" alt="…"/>

    <span class="product-brand">…</span> <span class="product-model">…</span> <span class="product-description">…</span> <span class="product-price">…</span> <button class="btn btn-cta">Pick me!</button> </li> ộ </ul>
  3. 6.

    .product-list { … } .product-item { … } .product-image {

    … } .product-brand { … } .product-model { … } .product-description { … } .product-price { … } .btn { … } .btn-cta { … }
  4. 7.

    OUT OF YOUR BEANS COFFEE GRINDERS BUY Peugeot Coffee grinder

    Kalita Coffee grinder 98 € 76 € In your basket: total amount 174 €
  5. 8.

    <html id="page-checkout"> <ul class="checkout-list"> <li class="checkout-item"> <img class="checkout-image" src="…" alt="…"/>

    <span class="checkout-brand">…</span> <span class="checkout-model">…</span> <span class="checkout-description">…</span> <span class="checkout-price">…</span> </li> ộ </ul>
  6. 9.

    .product-list { … } .product-item { … } .product-image {

    … } .product-brand { … } .product-model { … } .product-description { … } .product-price { … } .btn { … } .btn-cta { … } .checkout-list { … } .checkout-item { … } .checkout-image { … } .checkout-brand { … } .checkout-model { … } .checkout-description { … } .checkout-price { … }
  7. 10.

    <html id="page-products"> <ul class="product-list"> <li> <img class="product-image" src="…" alt="…"/> <span

    class="brand">…</span> <span class="model">…</span> <span class="description">…</span> <span class="price">…</span> <button>Pick me!</button> </li> ộ </ul>
  8. 11.

    <html id="page-checkout"> <ul class="product-list"> <li> <img class="product-image" src="…" alt="…"/> <span

    class="brand">…</span> <span class="model">…</span> <span class="description">…</span> <span class="price">…</span> </li> ộ </ul>
  9. 12.

    #page-products .product-list { … } #page-products .product-list li { …

    } #page-products .product-list .product-image { … } #page-products .product-list .brand { … } #page-products .product-list .model { … } #page-products .product-list .description { … } #page-products .product-list .price { … } #page-products .product-list button { … } #page-checkout .product-list { … } #page-checkout .product-list li { … } #page-checkout .product-list .product-image { … } #page-checkout .product-list .brand { … } #page-checkout .product-list .model { … } #page-checkout .product-list .description { … } #page-checkout .product-list .price { … }
  10. 13.

    #page-products { ộ .product-list { ộ li { … }

    .product-image { … } .brand { … } .model { … } .description { … } .price { … } button { … } } } nesting
  11. 14.

    #page-checkout { ộ .product-list { ộ li { … }

    .product-image { … } .brand { … } .model { … } .description { … } .price { … } } } nesting
  12. 20.

    <html id="page-products" vocab="http://schema.org/"> <ul class="product-list"> <li typeof="Product"> <img property="image" src="…"

    alt="…"/> <span property="name">…</span> <span property="brand">…</span> <span property="model">…</span> </span> <span property="description">…</span> <span property="offers" typeof="Offer"> <span class="price">…</span> <link property="availability" href="http://schema.org/InStock"/> </span> <button>Pick me!</button> </li> ộ </ul>
  13. 21.

    #page-products { ộ .product-list { ộ li { … }

    [property="image"] { … } [property="brand"] { … } [property="model"] { … } [property="description"] { … } [property="price"] { … } button { … } } }
  14. 22.
  15. 23.
  16. 24.

    ONE MORE CUP OF COFFEE, PLEASE! <html lang="en"> <h1 class="uppercase">One

    more cup of coffee, please!</h1> .uppercase { text-transform: uppercase }
  17. 25.

    one more cup of coffee, please! <html lang="en"> <h1 class="lowercase">One

    more cup of coffee, please!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase }
  18. 26.

    UN’ ALTRA TAZZA DI CAFFÈ, PER FAVORE! <html lang="it"> <h1

    class="uppercase">Un’ altra tazza di caffè, per favore!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase }
  19. 27.

    un’ altra tazza di caffè, per favore! <html lang="it"> <h1

    class="lowercase">Un’ altra tazza di caffè, per favore!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase }
  20. 28.

    NOCH EIN TÄSSCHEN KAFFEE, BITTE! <html lang="de"> <h1 class="uppercase">Noch ein

    Tässchen Kaffee, bitte!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase }
  21. 29.

    noch ein tässchen kaffee, bitte! <html lang="de"> <h1 class="lowercase">Noch ein

    Tässchen Kaffee, bitte!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase }
  22. 30.

    Noch ein Tässchen Kaffee, bitte! <html lang="de"> <h1 class="lowercase">Noch ein

    Tässchen Kaffee, bitte!</h1> .uppercase { text-transform: uppercase } .lowercase { text-transform: lowercase } .lowercase:lang(de) { text-transform: none } ✘  
  23. 32.

    Noch ein Tässchen Kaffee, bitte! <html lang="de"> >Noch ein Tässchen

    Kaffee, bitte!</h1> h1 { text-transform: lowercase } h1:lang(de) { text-transform: none } ✔   <h1
  24. 35.

    qa'vIn latlh tu'lum HInobneS! <html lang="tlh"> <h1>qa'vIn latlh tu'lum HInobneS!</h1>

    h1 { text-transform: lowercase } :lang(tlh) { text-transform: none !important} ✔  
  25. 38.

    <div id="pick-me" class="hidden"> .hidden { display: none !important } $('#pick-me').show();

    document.querySelector('#pick-me').style.display = 'block';
  26. 39.

    <div id="pick-me" class="hidden"> .hidden { display: none !important } $('#pick-me').show();

    document.querySelector('#pick-me').style.display = 'block';
  27. 45.

    OOCSS SEND ORDER CHANGE ADDRESS <button type="submit" class="btn btn-large btn-red">send

    order</button> <button type="button" class="btn btn-small btn-black">change address</button> <button type="button" class="btn btn-small btn-black">change basket</button> CHANGE BASKET
  28. 46.

    SEND ORDER CHANGE ADDRESS <button type="submit" class="btn btn-large btn-red">send order</button>

    <button type="button" class="btn btn-small btn-black">change address</button> <button type="button" class="btn btn-small btn-black">change basket</button> CHANGE BASKET
  29. 47.

    .btn { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius:

    0.375em; } .btn-large { padding: 0.375em; font-size: 1.25rem; } .btn-small { padding: 0.25em font-size: 1rem; } .btn-red { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } .btn-black { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } Stylesheet OOCSS
  30. 48.

    OOCSS Object Oriented CSS CSS “object”: a repeating visual pattern,

    which can be abstracted into an independent snippet of HTML, CSS, and possibly JavaScript. Goal: reusable code, maintainability, performance Principles: 1.  Separate structure and skin 2.  Separate container and content Means: •  class selectors •  no element type selectors •  no ID selectors •  no descendant combinators •  presentational classes in the mark-up
  31. 49.

    OOCSS { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius:

    0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet class selector class attribute “CSS classes”
  32. 50.

    OOCSS { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius:

    0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet class selector class attribute “CSS classes”
  33. 52.
  34. 53.
  35. 54.
  36. 56.
  37. 57.
  38. 58.

    OOCSS Object Oriented CSS CSS “object”: a repeating visual pattern,

    which can be abstracted into an independent snippet of HTML, CSS, and possibly JavaScript. Goal: reusable code, maintainability, performance Principles: 1.  Separate structure and skin 2.  Separate container and content Means: •  class selectors •  no element type selectors •  no ID selectors •  no descendant combinators •  presentational classes in the mark-up
  39. 59.
  40. 60.

    OOCSS Pros •  presentational markup •  bigger HTML files • 

    reusable code units •  smaller CSS file •  selector specificity •  selector performance Cons
  41. 61.
  42. 62.

    OOCSS Pros •  presentational markup •  bigger HTML files • 

    reusable code units •  selector specificity •  selector performance Cons
  43. 63.
  44. 64.

    OOCSS Pros •  presentational markup •  bigger HTML files • 

    reusable code units •  selector performance Cons
  45. 65.
  46. 67.

    OOCSS { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius:

    0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet
  47. 68.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet
  48. 69.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet
  49. 70.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } { @include btn; @include btn-large; @include btn-red; } { @include btn; @include btn-small; @include btn-black; } @mixin btn @mixin btn-large @mixin btn-small @mixin btn-red @mixin btn-black [type="submit"] [type="button"] mixins
  50. 71.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } { @include btn; @include btn-large; @include btn-red; } { @include btn; @include btn-small; @include btn-black; } @mixin btn @mixin btn-large @mixin btn-small @mixin btn-red @mixin btn-black [type="submit"] [type="button"] <button type="submit">send order</button> <button type="button">change address</button> <button type="button">change basket</button> mixins
  51. 72.

    [type="submit"] { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius:

    0.375em; padding: 0.375em; font-size: 1.25rem; background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } [type="button"] { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em; padding: 0.25em; font-size: 1rem; background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } { @include btn; @include btn-large; @include btn-red; } { @include btn; @include btn-small; @include btn-black; } [type="submit"] [type="button"]
  52. 73.

    .btn .btn-large .btn-small .btn-red .btn-black { min-width: 8rem; font-family: "League

    Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="button"] [type="submit"] extends { @extend .btn; @extend .btn-large; @extend .btn-red; } { @extend .btn; @extend .btn-small; @extend .btn-black; }
  53. 74.

    .btn .btn-large .btn-small .btn-red .btn-black { min-width: 8rem; font-family: "League

    Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="button"] [type="submit"] extends { @extend .btn; @extend .btn-large; @extend .btn-red; } { @extend .btn; @extend .btn-small; @extend .btn-black; } , [type="submit"], [type="button"] , [type="submit"] , [type="button"] , [type="submit"] , [type="button"]
  54. 75.

    .btn .btn-large .btn-small .btn-red .btn-black { min-width: 8rem; font-family: "League

    Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="button"] [type="submit"] extends { @extend .btn; @extend .btn-large; @extend .btn-red; } { @extend .btn; @extend .btn-small; @extend .btn-black; } , [type="submit"], [type="button"] , [type="submit"] , [type="button"] , [type="submit"] , [type="button"] <button type="submit">send order</button> <button type="button">change address</button> <button type="button">change basket</button>
  55. 76.

    { @extend %btn; @extend %btn-large; @extend %btn-red; } { @extend

    %btn; @extend %btn-small; @extend %btn-black; } %btn %btn-large %btn-small %btn-red %btn-black { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="button"] [type="submit"] [type="submit"] placeholders
  56. 77.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="submit"], [type="button"] [type="submit"] [type="button"] [type="submit"] [type="button"] { @extend %btn; @extend %btn-large; @extend %btn-red; } { @extend %btn; @extend %btn-small; @extend %btn-black; } [type="button"] [type="submit"] [type="submit"] placeholders
  57. 78.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="submit"], [type="button"] [type="submit"] [type="button"] [type="submit"] [type="button"] { @extend %btn; @extend %btn-large; @extend %btn-red; } { @extend %btn; @extend %btn-small; @extend %btn-black; } [type="button"] [type="submit"] [type="submit"] placeholders
  58. 79.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } %btn %btn-large %btn-small %btn-red %btn-black <button> <button> <button> Markup CSS Sass
  59. 80.

    %btn %btn-large %btn-small %btn-red %btn-black #page-other [type="submit"] { @extend %btn;

    @extend %btn-small; @extend %btn-red; } { @extend %btn; @extend %btn-large; @extend %btn-red; } { @extend %btn; @extend %btn-small; @extend %btn-black; } #page-checkout #page-checkout { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } [type="submit"] [type="button"]
  60. 81.

    SEND ORDER CHANGE ADDRESS <button type="submit" id="ctrl-send-order">send order</button> <button type="button"

    id="ctrl-change-address">change address</button> <button type="button" id="ctrl-change-basket">change basket</button> CHANGE BASKET
  61. 82.

    #ctrl-send-order #ctrl-change-address, #ctrl-change-basket { @extend %btn; @extend %btn-large; @extend %btn-red;

    } { @extend %btn; @extend %btn-small; @extend %btn-black; } { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em; } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } %btn %btn-large %btn-small %btn-red %btn-black
  62. 83.

    @mixin btn { min-width: 8rem; font-family: "League Gothic"; text-align: center;

    border-radius: 0.375em; } %btn { @include btn; } @mixin %btn-large { padding: 0.375em; font-size: 1.25rem; } %btn-large { @include btn-large; } ộ mixins and extends
  63. 84.
  64. 85.
  65. 87.
  66. 90.

    { min-width: 8rem; font-family: "League Gothic"; text-align: center; border-radius: 0.375em;

    } { padding: 0.375em; font-size: 1.25rem; } { padding: 0.25em font-size: 1rem; } { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); } btn btn-large btn-small btn-red btn-black <button> <button> <button> Markup Stylesheet
  67. 91.
  68. 93.