slides.oddbird.net/variables/smashing25/

When Variables Cascade

slides.oddbird.net/variables/smashing25/

@ Smashing Conf NY
/* Sass Variables */
this-slide {
  $my-color: red;
  $my-color: yellow;

  background-color: $my-color;

  $my-color: green;
  $my-color: powderBlue;
}
/* JS Variables */
let myColor = 'red';
myColor = 'yellow';

thisSlide.style.backgroundColor = myColor;

myColor = 'green';
myColor = 'powderBlue';

Imperative languages Follow Steps In Order

this-slide {
  $my-color: red;
  $my-color: yellow; /* winner! */

  background-color: $my-color;

  $my-color: green;
  $my-color: powderBlue;
}
this-slide {
  --my-color: red;
  --my-color: yellow;

  background-color: var(--my-color);

  --my-color: green;
  --my-color: powderBlue;
}

Not a series of steps, but A Set of Declarations

/* property: value */
background-color: yellow;

Every CSS Property
of Every HTML Element
Must Have a Single Value

Every <button> Needs One Background-Color
& One Text Color
& One Padding-Left
& One Margin-Inline-End
& …

on each HTML element For Each Property

  1. Find the relevant/valid declarations
  2. Remove duplicate/conflicting properties
  3. Fill in any missing properties
  4. Resolve any relationships

Need to… Discard Invalid Values

this-slide {
  background-color: red;
  background-color: yellow;
  background-color: green;
  background-color: powderBlue;
  /* background-color: smashingConf; */
}

Need to… Remove Conflicting Values

this-slide {
  /* background-color: red; */
  /* background-color: yellow; */
  /* background-color: green; */
  background-color: powderBlue;
  /* background-color: smashingConf; */
}

Need to… Fill in Missing Values

this-slide {
  /* no text `color` was declared, */
  /* but we can inherit one! */
}

Finally… Resolve Computed Values

this-slide {
  background-color: rgb(176, 224, 230); /* powderBlue */
  color: rgb(0, 0, 0); /* canvasText maybe */
}

Value Resolution Process

  1. Filtering
  2. Cascading
  3. Defaulting (includes inheritance)
  4. Resolving (based on type)
screenshot
this-slide {
  --my-color: red;
  --my-color: yellow;

  background-color: var(--my-color);

  --my-color: green;
  --my-color: powderBlue;
}

Do follow… Value Resolution Process

  1. Filtering
  2. Cascading
  3. Defaulting (includes inheritance)
  4. Resolving
this-slide {
  /* --my-color: red; */
  /* --my-color: yellow; */

  background-color: var(--my-color);

  /* --my-color: green; */
  --my-color: powderBlue;
}

Then resolve relationships

this-slide {
  background-color: powderBlue;
  --my-color: powderBlue;
}
this-slide {
  background-color: var(--my-color);

  --my-color: red;
  --my-color: yellow;
  --my-color: green;
  --my-color: powderBlue; /* winner! */
}
this-slide {
  background-color: var(--my-color);

  --my-color: red;
  --my-color: green;
  --my-color: powderBlue;
  --my-color: yellow; /* winner! */
}
  • Variables
  • Math and color manipulation
  • Nesting
  • if()
  • @function
  • @mixin

Value Resolution Process

  1. Filtering
  2. Cascading
  3. Defaulting (includes inheritance)
  4. Resolving

A Relevant Declaration

(determined one element at a time)

  1. In a stylesheet that applies (see media attr)
  2. Not in a false conditional rule (see at-rules)
  3. In a selector that matches the HTML Element
  4. Is syntactically valid

CSS valid syntax Depends on Type Safety

  • <integer> / <number> / <dimension> etc…
  • <length> / <angle> / <time> etc…
  • <calc-sum>
  • <color> / <hue> / <alpha-value>
  • <image> / <position>
  • <string>
  • <url>
  • <custom-ident> / <dashed-ident>
  • etc.

Not Valid Syntax

html { color: 3em; }

Valid Syntax

html { color: teal; }
html {
  color: teal;
  /* color: 3em; */
}

Value Resolution

  1. Filtering (“parse time validation”)
  2. Cascading
  3. Defaulting
  4. Resolving

Useful Progressive Enhancement

html {
  color: teal;
  color: oklch(55% 0.09 195); /* discarded?? */
}

Some Browsers…

html {
  color: teal;
  /* color: oklch(55% 0.09 195); */
}

Other Browsers…

html {
  color: teal;
  color: oklch(55% 0.09 195);
}

Other Browsers…

html {
  /* color: teal; */
  color: oklch(55% 0.09 195);
}

You can use it and not use it at the same time, because it works and it doesn’t work at the same time. It’s Quantum CSS! It’s Magic!

– Jen Simmons, Intro to Resilient CSS

this-slide {
  background-color: var(--my-color);

  --my-color: powderBlue;
  --my-color: smashingConf; /* 👀👀👀 */
}

But custom properties Have No Defined Type

✅ Valid Syntax

.ಠ_ಠ {
  --(╯°□°)╯: ︵┻━┻;
}

✅ Valid Syntax

html { --my-property: teal; }

✅ Valid Syntax

html { --my-property: 3em; }

✅ Valid Syntax

html { color: var(--my-property); }

✅ Still Valid Syntax

html {
  --my-property: 3em;
  color: var(--my-property);
}

✅ Still Valid Syntax

html {
  --my-property: 3em;
  color: var(--my-property);
}

html {
  --my-property: deepPink;
}

Value Resolution

  1. Filtering (“parse time validation”)
  2. Cascading
  3. Defaulting
  4. Resolving 👀👀👀👀

Declarations containing The var() function

Are not evaluated at parse time

Value Resolution

  1. Filtering (“parse time”)
  2. Cascading
  3. Defaulting
  4. Resolving (“computed value time”)

Might become… Invalid At Computed Value Time

Declarations containing The var() function
or if() function
or any --custom() function

Are not evaluated at parse time

✅ Still Valid Syntax

body {
  --my-property: 3em;
  color: var(--my-property);
}

@property --my-property {
  syntax: "<color>";
  initial-value: teal;
  inherits: true;
}

Value Resolution

  1. Filtering
  2. Cascading
  3. Defaulting
  4. Resolving

Cascade handles… Declaration Conflicts

Tina Turner as Aunty Entity in the Mad Max Thunderdome, with the law 'Two styles enter, one style leaves' in bold text
this-slide {
  background: maroon !important;

  --my-color: deepPink !important;
  --my-color: powderBlue;

  background: var(--my-color);
}
this-slide {
  background: maroon !important;

  --my-color: deepPink !important;
  /* --my-color: powderBlue; */

  /* background: var(--my-color); */
}

Importance Applies to Custom Properties

(not passed along as part of the variable value)

Value Resolution

  1. Filtering
  2. Cascading
  3. Defaulting (filling in empty values)
  4. Resolving
html { color: red; }

body > main { /* inherits… ? */ }
html { color: red; }
/* body inherits from `html` */
body > main { /* inherits from `body` */ }
html { color: red; }
body { color: blue; }
body > main { /* inherits from `body` */ }
html { --brand-color: deepPink; }

form button[type=submit] {
  background: var(--brand-color);
}

Custom properties Are Invisible Until Used

Custom properties can Smuggle Hidden Context

Allowing us to Avoid Inheritance Taxes
Inherit Across Generations

Defaulting process Depends on the Property

Each property has an initial value, defined in the property’s definition table.

– Cascade & Inheritance, § 7.1. Initial Values

/* ultimate reset! */
* { all: initial !important; }
/* initial values */
display: inline;
background: transparent;
color: CanvasText;
font-style: normal;
flex-basis: auto;
/* etc. */
body { display: initial; } /* inline */
section { display: initial; } /* inline */
div { display: initial; } /* inline */
span { display: initial; } /* inline */
head { display: initial; } /* inline */
title { display: initial; } /* inline */
html, body, p, div, article, aside, header,
hgroup, footer, main, nav, section /* etc */ {
  display: block;
}

base, basefont, datalist, head, link, meta,
script, style, template, title /* etc */ {
  display: none;
}

Custom properties… Have No Definition Table?!

Default initial value “The Guaranteed Invalid Value”

like Undefined in JS

Fallbacks… Only When Guaranteed Invalid

p {
	--not-a-color: 3em;
	color: orange;
	color: var(--not-a-color, green);
}

Typed variables Require an Initial-Value

@property --border-size {
  syntax: "<length>";
  initial-value: 1px;
  inherits: false;
}

Typed variables Are Never Guaranteed Invalid

@property --border-size {
  syntax: "<length>";
  initial-value: 12px;
  inherits: false;
}

button {
  border: var(--border-size, thin) solid;
}

Explicit defaulting… “Global Keywords

  • initial (from spec)
  • inherit (from parent element)
  • unset (inherit or initial)
  • revert (from browser)
  • revert-layer (previous layer)

Defaulting keywords Apply To Custom Properties

Use unset or initial For Guaranteed Invalid Value

(on un-typed properties)

Value Resolution

  1. Filtering
  2. Cascading
  3. Defaulting
  4. Resolving

Computed Values Inherit

Defaulting has to happen Before Resolution

(gives us computed value)

But Resolution has to happen Before Inheritance

(at least on the parent)

Move through DOM tree Parent Resolves,
then Children Inherit

<color> » rgb()
<length> » px
<angle> » deg
etc…

Un-typed custom properties Can’t Resolve Until Used

Un-typed custom properties Only Substitute Before Inheriting

On each element For Each Property

  1. Filtering » 0+ ‘declared’ values
  2. Cascading » 0-1 ‘cascaded’ values
  3. Defaulting » 1 ‘specified’ value
  4. Resolving » 1 ‘computed’ value

Properties inherit The Parent Computed Value

var(), if(), & --functions() Become Invalid at Computed Value Time

Fallbacks require The Guaranteed Invalid Value

It still isn’t (But With More Features)

If you just want… To Code Some Styles Good