CSS PREPROCESSORS OF BOTH WORLDS
A presentation at From The Front and the Temple of DOM in September 2014 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?