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

:root { —primaryColor: deeppink; }

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

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

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

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

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

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

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

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

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

.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; }

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

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

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

Global vs local scoping

Dark mode

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

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

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

Vertical rhythm

—vr: 2rem

var(—vr)

calc(2 * var(—vr))

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

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

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

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

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

Color

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

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

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

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

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

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

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

.element { —segment: 30deg; }

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

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

Relative values

codepen.io/michellebarker/pen/yLLGVMQ

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

codepen.io/michellebarker/pen/yLLGVMQ

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

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

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

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

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

Local scoping

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

.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; }

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

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

S t a g g e r e d Animations

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

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

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

Progress indicators

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

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

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

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

⚠

splitting.js.org

Motion Path

⚠ Motion Path

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

.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%; } }

codepen.io/michellebarker/pen/wvMvRLy

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

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

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

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

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

codepen.io/michellebarker/pen/jOWOdxj

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

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

.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); } }

codepen.io/michellebarker/pen/abOKPyg

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

codepen.io/michellebarker/pen/abOKPyg

codepen.io/michellebarker/pen/XWJyydY

codepen.io/michellebarker/pen/VwYOvJG

.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

codepen.io/michellebarker/pen/QWbGvWj

State switching

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

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

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

—i: 0; —i: 1;

—i: 0; —i: 1;

codepen.io/michellebarker/pen/abdKLLz

✨ Houdini ✨

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

transition: —angle 200ms;

codepen.io/michellebarker/pen/abdKLLz

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

codepen.io/michellebarker/pen/abNZjLB

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

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

@MicheBarks @CSSInRealLife

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