Custom Properties: The Secret Ingredients for CSS Magic

A presentation at Smashing Meets – Happy Holidays Edition in December 2020 in by Michelle Barker

Slide 1

Slide 1

Custom Properties The Secret Ingredients for CSS Magic 🎩🐇

Slide 2

Slide 2

:root { —primaryColor: deeppink; }

Slide 3

Slide 3

:root { —primaryColor: deeppink; } .box { background-color: var(—primaryColor); }

Slide 4

Slide 4

:root { —primaryColor: deeppink; } .box { background-color: var(—primaryColor, darkorchid); }

Slide 5

Slide 5

/* :root { —primaryColor: deeppink; } */ .box { background-color: var(—primaryColor, darkorchid); }

Slide 6

Slide 6

IE11 😢 .box { background-color: deeppink; background-color: var(—bg, darkorchid); }

Slide 7

Slide 7

IE11 😢 .box { /* background-color: deeppink; */ background-color: var(—bg, darkorchid); } The square is here, you just can’t see it

Slide 8

Slide 8

Nice lovely browser .box { background-color: deeppink; background-color: var(—bg, 300); } Sorry, it’s still invisible

Slide 9

Slide 9

Sass variable $bgColor: deeppink; !== —primaryColor: deeppink; Custom property

Slide 10

Slide 10

Repeating values Repeating values Repeating values Repeating values Repeating values Repeating values Repeating values

Slide 11

Slide 11

.box { —size: min(90vw, 40rem); } width: var(—size); height: var(—size);

Slide 12

Slide 12

.some-element { padding: 12rem 1rem 1rem; } .another-element { padding: 0 1rem 1rem; }

Slide 13

Slide 13

.some-element { padding: 12rem 1rem 1rem; } .another-element { padding: 0 1rem 1rem; } @media (min-width: 50em) { .some-element { padding: 12rem 3rem 3rem; } } .another-element { padding: 0 3rem 3rem; }

Slide 14

Slide 14

.some-element { padding: 12rem var(—pad) var(—pad); } .another-element { padding: 0 var(—pad) var(—pad); }

Slide 15

Slide 15

:root { —pad: 1rem; } @media (min-width: 50em) { :root { —pad: 3rem; } }

Slide 16

Slide 16

.element { —color: 150, 0, 190; } background: linear-gradient( rgba(var(—color), 0.2), rgba(var(—color), 1));

Slide 17

Slide 17

:root { —clip: polygon(80% 0, 100% 0, 100% 100%, 20% 100%); } .element { -webkit-clip-path: var(—clip); clip-path: var(—clip); }

Slide 18

Slide 18

Global vs local scoping

Slide 19

Slide 19

Dark mode

Slide 20

Slide 20

:root { —primary: deepPink; —headerBg: #1d1d26; —textColor: #0e0f0f; —textColorInverse: #fcfdff; —bg: #fcfdff; —bgTint: #dfeded; —bgGrey: #e6e8e8; —white: #fcfdff; }

Slide 21

Slide 21

@media (prefers-color-scheme: dark) { :root { —bg: #161618; —bgTint: #27272c; —bgGrey: #27272c; —textColor: #dbd7db; —textColorInverse: #0e0f0f; } }

Slide 22

Slide 22

Demo time!

Slide 23

Slide 23

Polyfills: ❌ postcss-custom-properties ✅ ie11CustomProperties

Slide 24

Slide 24

Vertical rhythm

Slide 25

Slide 25

—vr: 2rem

Slide 26

Slide 26

var(—vr)

Slide 27

Slide 27

calc(2 * var(—vr))

Slide 28

Slide 28

      • { margin-top: var(—vr); }

Slide 29

Slide 29

      • { margin-top: var(—vr); } * + h2 { margin-top: calc(2 * var(—vr)); }

Slide 30

Slide 30

:root { —vr: 1rem; } @media (min-width: 50em) { :root { —vr: 2rem; } }

Slide 31

Slide 31

—vr: 1rem; —vr: 2rem;

Slide 32

Slide 32

:root { —vr: 1rem; —2vr: calc(2 * var(—vr)); } @media (min-width: 50em) { :root { —vr: 2rem; } }

Slide 33

Slide 33

Color

Slide 34

Slide 34

Hue Saturation Lightness hsl(3deg, 89%, 61%)

Slide 35

Slide 35

Hue Saturation Lightness hsl(var(—hue), 89%, 61%)

Slide 36

Slide 36

.color-pair { —hue: 3deg; —hue2: calc(var(—hue) + 180deg); }

Slide 37

Slide 37

—hue: 3deg; —hue2: calc(var(—hue) + 180deg);

Slide 38

Slide 38

.color-pair—red { —hue: 3deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL

Slide 39

Slide 39

.color-pair—purple { —hue: 276deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL

Slide 40

Slide 40

.color-pair—yellow { —hue: 65deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL

Slide 41

Slide 41

Slide 42

Slide 42

.element { —segment: 30deg; }

Slide 43

Slide 43

.element { —segment: 30deg; } calc(var(—segment) * 2);

Slide 44

Slide 44

.element { —segment: 30deg; } calc(var(—segment) * 3);

Slide 45

Slide 45

Relative values

Slide 46

Slide 46

codepen.io/michellebarker/pen/yLLGVMQ

Slide 47

Slide 47

Path 1 Path 2 codepen.io/michellebarker/pen/yLLGVMQ

Slide 48

Slide 48

codepen.io/michellebarker/pen/yLLGVMQ

Slide 49

Slide 49

calc(80% - 0.5rem) calc(80% + 0.5rem) codepen.io/michellebarker/pen/yLLGVMQ

Slide 50

Slide 50

calc(20% - 0.5rem) calc(20% + 0.5rem) codepen.io/michellebarker/pen/yLLGVMQ

Slide 51

Slide 51

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } codepen.io/michellebarker/pen/yLLGVMQ

Slide 52

Slide 52

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } calc(var(—top) - var(—offset)) calc(var(—top) + var(—offset)) codepen.io/michellebarker/pen/yLLGVMQ

Slide 53

Slide 53

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } calc(var(—bottom) - var(—offset)) calc(var(—bottom) + var(—offset)) codepen.io/michellebarker/pen/yLLGVMQ

Slide 54

Slide 54

Local scoping

Slide 55

Slide 55

.grid { —cols: 8; } grid-template-columns: repeat(var(—cols), minmax(0, 1fr)); … @media (min-width: 60em) { .grid { —cols: 12; } }

Slide 56

Slide 56

@supports (—cols: 8) { .grid { —cols: 8; } } /* More code here… */

Slide 57

Slide 57

S t a g g e r e d Animations

Slide 58

Slide 58

Slide 59

Slide 59

.some-element:nth-child(2) { —i: 2; } .some-element:nth-child(3) { —i: 3; }

Slide 60

Slide 60

.some-element { —delay: calc(var(—i, 1) * 400ms); }

Slide 61

Slide 61

.some-element { —delay: calc(var(—i, 1) * 400ms); animation: appear 1000ms var(—delay) forwards; }

Slide 62

Slide 62

Progress indicators

Slide 63

Slide 63

Slide 64

Slide 64

<ol style=”—length: 5”> <li style=”—i: 1”>…</li> <li style=”—i: 2”>…</li> <li style=”—i: 3”>…</li> <li style=”—i: 4”>…</li> <li style=”—i: 5”>…</li> </ol>

Slide 65

Slide 65

li::before { —stop: calc(100% / var(—length) * var(—i)); } background: linear-gradient(to right, var(—c1) var(—stop), var(—c2) var(—stop));

Slide 66

Slide 66

Demo time!

Slide 67

Slide 67

splitting.js.org

Slide 68

Slide 68

Motion Path

Slide 69

Slide 69

⚠ Motion Path

Slide 70

Slide 70

.element { offset-path: path(‘M.4 84.1s127.4 188 267.7 0 247.3 0 247.3 0’); offset-distance: 0; }

Slide 71

Slide 71

.element { offset-path: path(‘M.4 84.1s127.4 188 267.7 0 247.3 0 247.3 0’); offset-distance: 0; animation: move 2000ms infinite alternate; } @keyframes move { to { offset-distance: 100%; } }

Slide 72

Slide 72

codepen.io/michellebarker/pen/wvMvRLy

Slide 73

Slide 73

.element { offset-distance: calc(var(—i, 1) * 4rem); } codepen.io/michellebarker/pen/PoZoVPE

Slide 74

Slide 74

.char { offset-distance: calc(var(—i, 1) * 2rem); } codepen.io/michellebarker/pen/abdbXyZ

Slide 75

Slide 75

.char { offset-distance: calc(var(—i, 1) * var(—offset)); } codepen.io/michellebarker/pen/abdbXyZ

Slide 76

Slide 76

codepen.io/michellebarker/pen/jOWOdxj

Slide 77

Slide 77

—offset: calc(100% / (var(—char-total) + 1)); codepen.io/michellebarker/pen/jOWOdxj

Slide 78

Slide 78

offset-distance: calc(var(—offset) * var(—char-index)); codepen.io/michellebarker/pen/jOWOdxj

Slide 79

Slide 79

.char { —position: calc(var(—char-index) * 1em); } offset-distance: var(—position); animation: move 1500ms infinite alternate forwards; @keyframes move { to { offset-distance: calc(var(—position) + 12rem); } }

Slide 80

Slide 80

codepen.io/michellebarker/pen/abOKPyg

Slide 81

Slide 81

—delay: calc(var(—char-index) * 30ms); codepen.io/michellebarker/pen/abOKPyg

Slide 82

Slide 82

codepen.io/michellebarker/pen/XWJyydY

Slide 83

Slide 83

codepen.io/michellebarker/pen/VwYOvJG

Slide 84

Slide 84

.item { —d: 1000ms; —i: calc(var(—item-total) - var(—item-index)); —x: calc((var(—item-index) - var(—i)) * 4deg); —tPerSegment: calc(var(—d) / var(—item-total)); —itemDuration: calc(var(—tPerSegment) * var(—item-index)); —itemDuration2: calc(var(—tPerSegment) * var(—i)); —c: calc(1 / var(—item-total) * 50); —h: calc(var(—item-index) * 15); —color: hsl(var(—h), 50%, 50%); —offset: calc(var(—item-index) * (var(—c) * 1%) + 1.65rem); —offset2: calc(var(—i) * (var(—c) * 1%)); —endPos: calc(var(—item-total) * (var(—c) * 1%)); } codepen.io/michellebarker/pen/VwYOvJG

Slide 85

Slide 85

codepen.io/michellebarker/pen/QWbGvWj

Slide 86

Slide 86

codepen.io/michellebarker/pen/abdKLLz

Slide 87

Slide 87

✨ Houdini ✨

Slide 88

Slide 88

CSS.registerProperty({ name: ‘—angle’, syntax: ‘<angle>’, inherits: true, initialValue: ‘40deg’, })

Slide 89

Slide 89

transition: —angle 200ms;

Slide 90

Slide 90

codepen.io/michellebarker/pen/abdKLLz

Slide 91

Slide 91

@property —angle { syntax: ‘<angle>’; inherits: true; initial-value: 40deg; }

Slide 92

Slide 92

Demo time!

Slide 93

Slide 93

codepen.io/michellebarker/pen/abNZjLB

Slide 94

Slide 94

chars.forEach((el, i) => { el.style.setProperty(‘—s’, getRandom[i]) }) codepen.io/michellebarker/pen/abNZjLB

Slide 95

Slide 95

A user’s guide to CSS variables by Lea Verou bit.ly/3fSZCOS Calculating Color: Dynamic Color Theming with Pure CSS by Una Kravets bit.ly/3akbTKP CSS { In Real Life } css-irl.info

Slide 96

Slide 96

@MicheBarks @CSSInRealLife