Custom Properties: The Secret Ingredients for CSS Magic

A presentation at Bath Digital Festival in October 2020 in by Michelle Barker

Slide 1

Slide 1

Custom Properties: The Secret Ingredients for CSS Magic Michelle Barker (Atomic Smash) Partners #BDF2020 bathdigitalfestival.co.uk @bathdigital

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

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

Slide 12

Slide 12

.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 13

Slide 13

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

Slide 14

Slide 14

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

Slide 15

Slide 15

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

Slide 16

Slide 16

Global vs local scoping

Slide 17

Slide 17

Dark mode

Slide 18

Slide 18

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

Slide 19

Slide 19

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

Slide 20

Slide 20

:root { —color: deeppink; } .purple { —color: darkorchid; }

Slide 21

Slide 21

Vertical rhythm

Slide 22

Slide 22

—vr: 2rem

Slide 23

Slide 23

var(—vr)

Slide 24

Slide 24

calc(2 * var(—vr))

Slide 25

Slide 25

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

Slide 26

Slide 26

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

Slide 27

Slide 27

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

Slide 28

Slide 28

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

Slide 29

Slide 29

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

Slide 30

Slide 30

Color

Slide 31

Slide 31

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

Slide 32

Slide 32

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

Slide 33

Slide 33

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

Slide 34

Slide 34

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

Slide 35

Slide 35

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

Slide 36

Slide 36

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

Slide 37

Slide 37

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

Slide 38

Slide 38

Slide 39

Slide 39

.element { —segment: 30deg; }

Slide 40

Slide 40

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

Slide 41

Slide 41

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

Slide 42

Slide 42

Relative values

Slide 43

Slide 43

codepen.io/michellebarker/pen/yLLGVMQ

Slide 44

Slide 44

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

Slide 45

Slide 45

codepen.io/michellebarker/pen/yLLGVMQ

Slide 46

Slide 46

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

Slide 47

Slide 47

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

Slide 48

Slide 48

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

Slide 49

Slide 49

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

Slide 50

Slide 50

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

Slide 51

Slide 51

Local scoping

Slide 52

Slide 52

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

Slide 53

Slide 53

Slide 54

Slide 54

Slide 55

Slide 55

.grid { display: grid; grid-template-columns: [outer-start] minmax(20px, 1fr) [wrapper-start] repeat(24, minmax(0, 30px)) [wrapper-end] minmax(20px, 1fr) [outer-end]; grid-template-rows: [outer-start] 1fr [text-top-end] 40px [heading-start] auto [heading-end] 40px [text-bottom-start] 1fr [outer-end]; grid-gap: 0 20px; }

Slide 56

Slide 56

.grid { —columnWidth: 30px; —gutter: 20px; } display: grid; …

Slide 57

Slide 57

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

Slide 58

Slide 58

S t a g g e r e d Animations

Slide 59

Slide 59

Slide 60

Slide 60

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

Slide 61

Slide 61

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

Slide 62

Slide 62

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

Slide 63

Slide 63

Progress indicators

Slide 64

Slide 64

Slide 65

Slide 65

<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 66

Slide 66

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

Slide 67

Slide 67

<h1> <span <span <span <span <span <span <span <span <span </h1> style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: 1”>B</span> 2”>r</span> 3”>e</span> 4”>a</span> 5”>t</span> 6”>h</span> 7”>i</span> 8”>n</span> 9”>g</span>

Slide 68

Slide 68

span { —delay: calc((var(—i) + 1) * 400ms); } animation: breathe 4000ms var(—delay) infinite both;

Slide 69

Slide 69

Slide 70

Slide 70

splitting.js.org

Slide 71

Slide 71

Motion Path

Slide 72

Slide 72

⚠ Motion Path

Slide 73

Slide 73

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

Slide 74

Slide 74

.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 75

Slide 75

codepen.io/michellebarker/pen/wvMvRLy

Slide 76

Slide 76

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

Slide 77

Slide 77

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

Slide 78

Slide 78

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index));

Slide 79

Slide 79

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index));

Slide 80

Slide 80

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index));

Slide 81

Slide 81

codepen.io/michellebarker/pen/jOWOdxj

Slide 82

Slide 82

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

Slide 83

Slide 83

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

Slide 84

Slide 84

.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 85

Slide 85

codepen.io/michellebarker/pen/abOKPyg

Slide 86

Slide 86

.char { —delay: calc(var(—char-index) * 30ms); } animation: move 1500ms var(—delay) infinite alternate forwards;

Slide 87

Slide 87

codepen.io/michellebarker/pen/abOKPyg

Slide 88

Slide 88

codepen.io/michellebarker/pen/XWJyydY

Slide 89

Slide 89

codepen.io/michellebarker/pen/VwYOvJG

Slide 90

Slide 90

.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 91

Slide 91

codepen.io/michellebarker/pen/QWbGvWj

Slide 92

Slide 92

State switching

Slide 93

Slide 93

.box { /* Off / —i: 0; / On */ —i: 1; }

Slide 94

Slide 94

.box { —i: 0; border-radius: calc(var(—i) * 50%); transform: rotate(calc(var(—i) * 90deg)) skew(calc(var(—i) * 15deg)); } Off

Slide 95

Slide 95

.box { —i: 1; border-radius: calc(var(—i) * 50%); transform: rotate(calc(var(—i) * 90deg)) skew(calc(var(—i) * 15deg)); } On

Slide 96

Slide 96

—i: 0; —i: 1;

Slide 97

Slide 97

—i: 0; —i: 1;

Slide 98

Slide 98

codepen.io/michellebarker/pen/abdKLLz

Slide 99

Slide 99

✨ Houdini ✨

Slide 100

Slide 100

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

Slide 101

Slide 101

transition: —angle 200ms;

Slide 102

Slide 102

codepen.io/michellebarker/pen/abdKLLz

Slide 103

Slide 103

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

Slide 104

Slide 104

codepen.io/michellebarker/pen/abNZjLB

Slide 105

Slide 105

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

Slide 106

Slide 106

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 107

Slide 107

@MicheBarks @CSSInRealLife

Slide 108

Slide 108

Thanks to our supporters and partners In partnership with the University of Bath Innovation Centre #BDF2020 bathdigitalfestival.co.uk @bathdigital