slides.oddbird.net/rws/smashing/vars/

Custom Properties & Functions

@ Smashing Online

Variables In CSS

currentColor

Firefox 2006… Safari 2009IE9 2011

Borrowed from SVG 1

Added to CSS 3

Inherits Keyword Not Resolved Color

It should re-calculate when the color changes!

Custom Properties

Firefox 2014… Chrome/Safari 2016… Edge 2017

--aka: 'css variables';

-<empty>-browser-prefix

--very: few restrictions;

var( --name )

var( --name , fallback)

Only One Fallback

var(--my-font, Baskerville, Georgia, serif)

Nested Fallbacks

var(--my-color, var(--other-color, pink))

Some Overlap With Sass Variables

$my-color: rebeccapurple;
--my-color: rebeccapurple;

Store Data For Later Use

Establish Global Patterns

Keep Code DRY

Don’t Repeat Yourself

With a Single Source of Truth

html {
--gutter: 1.5rem;
--shim: calc(var(--gutter) / 2);
}
html {
--brand-color: hsl(330, 100%, 45%);
--action: var(--brand-color);
}

But there are Important Differences

Sass Variables Scope Without DOM Awareness

.example { 
$columns: 2;
}
.nested-class {
/* $columns == undefined */
}

Properties Inherit With DOM Awareness!

.example { 
--columns: 2;
}
.nested-class {
/* var(--columns) == 2 */
}

Media-Query Changes

@media (min-width: 30em) {
html {
$columns: 6; /* scope only */
--columns: 6; /* 30em+ in browsers! */
}
}

Custom Properties Not Just Variables

Use Both

Will It Change In The Browser?

Will It Change Depending on Context?

Keep NSYNC Properties From Variables

$gutter: 1rem;
html { --gutter: #{$gutter}; }
$brand-colors: (
'brand-blue': hsl(195, 52%, 31%),
'brand-orange': hsl(24, 100%, 62%),
'brand-pink': hsl(330, 100%, 45%),
);
html {
@for $name, $value in $brand-colors {
--#{$name}: #{$value};
}
}

<root-rant>

Only the rootest root

:root {
--brand-color: hsl(330, 100%, 45%);
--action: var(--brand-color);
}

:root selects Any Document Root

Like <svg> or <xml> or <html>

Pseudo-class Has Higher Specificity

Maybe Use Cases? But Be Careful

</root-rant>

Custom Properties Inherit By Default

Reset Inheritance With Explicit Selectors

Declare Locally

[data-button='go'] {
--btn-color: green;
}

Declare Universally

* {
--grid-area: main;
}

Combine Both

[data-grid] * {
--grid-area: main;
}

Declare Nowhere First

[data-grid] * {
grid-area: var(--grid-area, main);
}

Lots of Use Cases

Use (& Manage) The Cascade!

Changes… Based on Context / Theme

button {
background: blue
}

[data-theme='rebecca'] button {
background: rebeccapurple;
}
button {
background: var(--btn-color, blue);
}

[data-theme='rebecca'] {
--btn-color: red;
}

Inheritance Rewards Proximity

Like “scroped styles”

Component Parameters Set By Inheritance

.component {
color: var(--color, black);
}

.context {
--color: rebeccapurple;
}

Changes… Based on State / Type

[data-button] {
background: var(--btn-color, blue);
}

[data-button]:disabled {
--btn-color: gray;
}

[data-button='go'] {
--btn-color: green;
}

Avoids Nesting For Lower Specificity

Component Parameters As Safe Inline Styles

<button style="--color: blue;">

Optionally Use The Variable…

button {
background: var(--color, red);
}

…Or Ignore It

button.green {
background: green;
}

Missing Longhand

(& Defaults)

.box {
box-shadow: var(--shadow-x, 0)
var(--shadow-y, 1px)
var(--shadow-blur, 0)
var(--shadow-color, currentColor);
}

.usage { --shadow-y: -1px; }

Custom Functions & Mixins

CSS Custom Properties in the Cascade by Miriam Suzanne

Color Themes

[data-theme] {
background: var(--background);
color: var(--text);
}

[data-theme='light'] {
--background: #ddf;
--text: #226;
}

Think in Layers

Brand Theme Purpose

html {
--brand-light: #ddf;
--brand-dark: #226;
}

“Soft Patch Between Layers

[data-theme='light'] {
--background: var(--brand-light);
--text: var(--brand-dark);
}

Individual Color Channels

html {
--h: 330;
--s: 100%;
--l: 34%;
--color: hsl(var(--h), var(--s), var(--l));
}

Hue is Radial

* {
--complement: calc(var(--h) - 180);
background: hsl(var(--complement), var(--s), var(--l));
}

Lightness is “Clamped

* {
--threshold: 55;
--contrast: calc((var(--l) - var(--threshold)) * -100%);
color: hsl(var(--h), var(--s), var(--contrast));
}

Custom Cascades

html {
@media (prefers-color-scheme: dark) {
--os-mode: -1;
}

@media (prefers-color-scheme: light) {
--os-mode: 1;
}
}
[data-colors='light'] {
--html-mode: 1;
}

[data-colors='dark'] {
--html-mode: -1;
}
[data-colors] {
--mode: var(
--user-mode, var(
--html-mode, var(
--os-mode, 1
)
)
);
}

Dynamic Layouts

Client Day Planner

Screenshot of schedule grid

Define the Day

<main style="
--day-start: 9,
--day-end: 18
"
>

Define the Events

<section style="
--event-start: 13,
--event-end: 14
"
>
List of code dourses, all titled Learn X Language,
except the JavaScript course titled
Intro To JS

CSS Animation

Set Index Prop To Offset Delay

<div style="--index: {{ loop.index }};">
[style*='--index'] {
animation-delay: calc(var(--index) * 50ms);
}

Create API For HTML Soft Patch

<div style="--ease: var(--in-out-back);">
[style*='--ease'] {
--in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--in-out-back: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
Krystal Campioni sprite animation
Sprite from Monster Slayer animation
<section
class="sprite-demo"
:style="{
'--src': show.sprite.src,
'--columns': show.sprite.columns,
'--rows': show.sprite.rows,
}"
>
...</section>
<div
v-for="action in show.actions"
:key="action.name"
:data-action="action.name"
:style="{
'--row': action.row,
}"

/>

Variable Issues Unknown Value Types

Transition & Animate Target Properties

Not the variable itself…

button {
--color: green;
background: var(--color);
transition: background 0.5s;

&:hover {
--color: red;
}
}

Houdini Fix

Proposed CSS

@property --brand-color {
syntax: "<color>";
initialValue: "pink";
inherits: true;
}

Current JavaScript

CSS.registerProperty({
name: "--brand-color",
syntax: "<color>",
initialValue: "pink",
inherits: true,
});

Variable Issues Content Requires Quoted Values

div::after {
--string: 'hello world';
content: var(--string);
}

No Support in url() and @media

var(--size)em calc( var(--size) * 1em )

@supports ( --css: vars )

Any valid definition will work…

Headshot of Ana Tudor

@anatudor Ana Tudor

CSS Tricks && CodePen