A presentation at From The Front and the Temple of DOM in in Bologna, Metropolitan City of Bologna, Italy by Gunnar Bittersmann
CSS PREPROCESSORS OF BOTH WORLDS
CSS PREPROCESSORS
OF BOTH WORLDS
COFFEE GRINDERS OUT OF YOUR BEANS Peugeot Coffee grinder 98 € PICK Kalita Coffee grinder 76 € PICK Heyde Coffee grinder 54 € PICK
.product-list { … } .product-item { … } .product-image { … } .product-brand { … } .product-model { … } .product-description { … } .product-price { … } .btn { … } .btn-cta { … }
COFFEE GRINDERS OUT OF YOUR BEANS In your basket: Peugeot Coffee grinder 98 € Kalita Coffee grinder 76 € total amount 174 € BUY
.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 { … }
#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 { … }
#page-products { ⁝ .product-list { ⁝ li { … } .product-image { … } .brand { … } .model { … } .description { … } .price { … } button { … } } } nesting
#page-checkout { ⁝ .product-list { ⁝ li { … } .product-image { … } .brand { … } .model { … } .description { … } .price { … } } } nesting
@import “_page-products”; @import “_page-checkout”; ⁝ modularization
@import “_colors”; @import “_type”; @import “_image-replacement”; @import “_page-layout”; @import “_page-products”; @import “_page-checkout”; ⁝ modularization
Atomic design (Brad Frost)
Atomic design (Brad Frost) ATOMS MOLECULES ORGANISMS TEMPLATES PAGES
Look ma, no classes!
#page-products { ⁝ .product-list { ⁝ li { … } [property=”image”] { … } [property=”brand”] { … } [property=”model”] { … } [property=”description”] { … } [property=”price”] { … } button { … } } }
One more cup of coffee, please! <html lang=”en”> <h1>One more cup of coffee, please!</h1>
ONE MORE CUP OF COFFEE, PLEASE! <html lang=”en”> <h1 class=”uppercase”>One more cup of coffee, please!</h1> .uppercase { text-transform: uppercase }
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 }
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 }
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 }
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 }
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 }
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 } ✘
PRESENTATIONAL MARKUP
Noch ein Tässchen Kaffee, bitte! <html lang=”de”> <h1>Noch ein Tässchen Kaffee, bitte!</h1> h1 { text-transform: lowercase } h1:lang(de) { text-transform: none } ✔
QA’VIN LATLH TU’LUM HINOBNES! <html lang=”tlh”> <h1>qa’vIn latlh tu’lum HInobneS!</h1> h1 { text-transform: uppercase }
qa’vin latlh tu’lum hinobnes! <html lang=”tlh”> <h1>qa’vIn latlh tu’lum HInobneS!</h1> h1 { text-transform: lowercase }
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} ✔
Structure (HTML/DOM) Presentation (CSS) Behavior (JavaScript)
Separation of concerns Structure (HTML/DOM) Presentation (CSS) Behavior (JavaScript)
SEND ORDER CHANGE ADDRESS OOCSS CHANGE BASKET <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>
SEND ORDER CHANGE ADDRESS CHANGE BASKET <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>
Stylesheet .btn { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } .btn-large { padding: 0.375em; font-size: 1.25rem; } OOCSS .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(…); }
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 OOCSS 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
Markup { class attribute btn class selector } <button> “CSS classes” { btn-large } OOCSS <button> { btn-small <button> } { btn-red } { btn-black } Stylesheet 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(…);
Markup { class attribute btn class selector } <button> “CSS classes” { btn-large } OOCSS <button> { btn-small <button> } { btn-red } { btn-black } Stylesheet 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(…);
PRESENTATIONAL MARKUP
OOCSS
“CSS CLASSES”
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 OOCSS 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
Pros • • • • reusable code units smaller CSS file selector specificity selector performance OOCSS Cons • presentational markup • bigger HTML files
Pros Cons • reusable code units • presentational markup • bigger HTML files • selector specificity • selector performance OOCSS
Pros Cons • reusable code units • presentational markup • bigger HTML files OOCSS • selector performance
Pros Cons • reusable code units • presentational markup • bigger HTML files OOCSS
Markup { btn } <button> { btn-large } OOCSS <button> { btn-small <button> } { btn-red } { btn-black } Stylesheet 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(…);
Markup btn Stylesheet { } <button> { btn-large } <button> { btn-small } <button> { 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(…);
Markup btn Stylesheet { } <button> { btn-large } <button> { btn-small } <button> { 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(…);
mixins [type=”submit”] { } @include btn; @include btn-large; @include btn-red; [type=”button”] { } @include btn; @include btn-small; @include btn-black; @mixin btn { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } @mixin btn-large { padding: 0.375em; font-size: 1.25rem; } @mixin btn-small { padding: 0.25em font-size: 1rem; } @mixin btn-red { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } @mixin btn-black { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
mixins [type=”submit”] { } @include btn; @include btn-large; @include btn-red; [type=”button”] { } @include btn; @include btn-small; @include btn-black; <button type=”submit”>send order</button> <button type=”button”>change address</button> <button type=”button”>change basket</button> @mixin btn { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } @mixin btn-large { padding: 0.375em; font-size: 1.25rem; } @mixin btn-small { padding: 0.25em font-size: 1rem; } @mixin btn-red { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } @mixin btn-black { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
[type=”submit”] { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; [type=”submit”] { } @include btn; @include btn-large; @include btn-red; [type=”button”] { } @include btn; @include btn-small; @include btn-black; 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(…);
extends [type=”submit”] { @extend .btn; @extend .btn-large; @extend .btn-red; } [type=”button”] { @extend .btn; @extend .btn-small; @extend .btn-black; } .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(…); }
extends [type=”submit”] { @extend .btn; @extend .btn-large; @extend .btn-red; } [type=”button”] { @extend .btn; @extend .btn-small; @extend .btn-black; } .btn, [type=”submit”], [type=”button”] { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } .btn-large, [type=”submit”] { padding: 0.375em; font-size: 1.25rem; } .btn-small, [type=”button”] { padding: 0.25em font-size: 1rem; } .btn-red, [type=”submit”] { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } .btn-black, [type=”button”] { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
extends [type=”submit”] { @extend .btn; @extend .btn-large; @extend .btn-red; } [type=”button”] { @extend .btn; @extend .btn-small; @extend .btn-black; } <button type=”submit”>send order</button> <button type=”button”>change address</button> <button type=”button”>change basket</button> .btn, [type=”submit”], [type=”button”] { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } .btn-large, [type=”submit”] { padding: 0.375em; font-size: 1.25rem; } .btn-small, [type=”button”] { padding: 0.25em font-size: 1rem; } .btn-red, [type=”submit”] { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } .btn-black, [type=”button”] { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
placeholders [type=”submit”] { @extend %btn; @extend %btn-large; @extend %btn-red; } [type=”button”] { @extend %btn; @extend %btn-small; @extend %btn-black; } %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(…); }
placeholders [type=”submit”] { @extend %btn; @extend %btn-large; @extend %btn-red; } [type=”button”] { @extend %btn; @extend %btn-small; @extend %btn-black; } [type=”submit”], [type=”button”] { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } [type=”submit”] { padding: 0.375em; font-size: 1.25rem; } [type=”button”] { padding: 0.25em font-size: 1rem; } [type=”submit”] { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } [type=”button”] { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
placeholders [type=”submit”] { @extend %btn; @extend %btn-large; @extend %btn-red; } [type=”button”] { @extend %btn; @extend %btn-small; @extend %btn-black; } [type=”submit”], [type=”button”] { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } [type=”submit”] { padding: 0.375em; font-size: 1.25rem; } [type=”button”] { padding: 0.25em font-size: 1rem; } [type=”submit”] { background-color: hsl(2, 42%, 39%); background-image: linear-gradient(…); } [type=”button”] { background-color: hsl(0, 0%, 20%); background-image: linear-gradient(…); }
Markup Sass { %btn } <button> { %btn-large } <button> { %btn-small } <button> { %btn-red } { %btn-black } min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; CSS 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 { min-width: 8rem; font-family: “League Gothic”; text-align: center; border-radius: 0.375em; } #page-checkout [type=”submit”] { @extend %btn; @extend %btn-large; @extend %btn-red; } #page-checkout [type=”button”] { @extend %btn; @extend %btn-small; @extend %btn-black; } #page-other [type=”submit”] { @extend %btn; @extend %btn-small; @extend %btn-red; } %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(…); }
SEND ORDER CHANGE ADDRESS CHANGE BASKET <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>
#ctrl-send-order { @extend %btn; @extend %btn-large; @extend %btn-red; } #ctrl-change-address, #ctrl-change-basket { @extend %btn; @extend %btn-small; @extend %btn-black; } %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(…); }
mixins and extends @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; } ⁝
OOSass nesting modularization mixins extends placeholders
PRESENTATIONAL MARKUP
Separation of concerns Structure (HTML/DOM) Presentation (CSS) Behavior (JavaScript)
Markup btn Stylesheet { } <button> { btn-large } <button> { btn-small } <button> { 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(…);
WHICH SIDE ARE YOU ON?
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.
Here’s what was said about this presentation on social media.
Yay, @g16n sports our t-shirt on stage at #ftf14 #ilike https://t.co/jPfIT0L40n
— UXcamp Europe (@uxcampeurope) October 15, 2014
Awesome conference!!#ftf14 I've met wonderful people :) now,going back home.see you next time pic.twitter.com/bwMfnQu4lc
— Tommaso Sammataro (@tommy82pi) September 20, 2014
Best quote from #ftf14 so far: "Bootstrap is the IKEA of webdev" @sir_pepe quoted by @g16n about disadvantages of #OOCSS vs #SASS
— Roberto Chiaveri (@robertochiaveri) September 18, 2014
@StuRobson Credits: @kapowaz https://t.co/VJ50cSKw2v #ftf14
— Gunnar Bittersmann (@g16n) September 18, 2014
@g16n @StuRobson ha, was this just used in a presentation? Nice :)
— Ben Darlow (@kapowaz) September 18, 2014
I <3 when speakers add slides in the last minute, referring to previous talks @sturobson #ftf14 pic.twitter.com/UC2jU6cos3
— Gunnar Bittersmann (@g16n) September 18, 2014
I hope @brad_frost will forgive me setting his name in Comic Sans. #ftf14
— Gunnar Bittersmann (@g16n) September 18, 2014
OOSass. ¡Yo estaba en lo cierto! #ftf14
— Xaviju (@Xaviju) September 18, 2014
you don't want the back end guys to change their code just to change the appearance of a button
— M. Lissandrini (@Kuzeko) September 18, 2014
- @g16n #ftf14
Bashing OOCSS: "Bootstrap is the IKEA of webdev" @sir_pepe quoted by @g16n #ftf14 (+1)
— Naoise (@naoisegolden) September 18, 2014
Not sure I agree with all of @g16n statements at #ftf14… But I understand the reasoning…
— Pierre Spring (@shvi) September 18, 2014
I'm also using Gunnar's approach to extend bootstrap skins. Was wandering if it were a wrong approach. #ftf14 pic.twitter.com/hWApSV079Z
— Matteo Balocco (@MatteoBalocco) September 18, 2014
Talking about OOCSS: do you really want presentational markup? #ftf14
— Tommaso Baldovino (@tomstardust) September 18, 2014
Single exception to "never use !important' rule: no text transforms in Klingon.
— Estelle Weyl (@estellevw) September 18, 2014
:lang(tlh) {text-transform: none !important;}@g16n #FTF14
A whole new level of nerdary. Featuring Gunnar Bittersmann. #ftf14
— Matteo Balocco (@MatteoBalocco) September 18, 2014
The importance of casing in klingon *_* :) #ftf14
— Carlo 'kj' (@kajyr) September 18, 2014
The perils of text-transform upper and lowercase, for Klingon (and German). #ftf14 pic.twitter.com/6bQrQa1eaB
— Sally Lait (@sallylait) September 18, 2014
CSS preprocessors #ftf14 #fromthefront pic.twitter.com/9JanFdL3AB
— Carlotta Porcelli (@carlotta_93) September 18, 2014
. @g16n explains CSS pre-processors. A coffeegrinder does the trick. #ftf14 pic.twitter.com/7MNfEtiZ3Q
— Holger Bartel (@foobartel) September 18, 2014
Theater getting filled. #ftf14 pic.twitter.com/NId6z9NOvd
— Gunnar Bittersmann (@g16n) September 18, 2014
Topic announcement:@g16n will speak about “CSS preprocessors for the best of both worlds”
— From The Front (@fromthefront) August 20, 2014
Check it out:http://t.co/c5ePmVp4dr#ftf14