A presentation at Bath Digital Festival by Michelle Barker
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))
: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
li::before { —stop: calc(100% / var(—length) * var(—i)); } background: linear-gradient(to right, var(—c1) var(—stop), var(—c2) var(—stop));
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
Custom Properties are a great way to give our CSS superpowers! Not only can they help us write more concise, maintainable code, they also bestow upon us the ability to get truly creative with CSS. In this talk we’ll walk through some tips and tricks for using custom properties to improve our workflow, as well as some practical (and less practical!) examples of them in use. It’s CSS, but not as we know it.