I'm Tab Atkins

and I work at Google.

@tabatkins

xanthir.com/+

xanthir.com

xanthir.com/talks/2013-06-14

(Only using standardized stuff, but best viewed in Chrome or most recent Safari.)

This Talk

  1. Variables
  2. Conditional Rules
  3. The Future (if we have time)

Variables History

Variables have been the #1 developer request since 1998.

Many attempts to get them into the language in the past, all failed.

Last big attempt was from Dave Hyatt and Daniel Glazman:

@variables {
  main-color: red;
  column-width: 50px;
}
.foo {
  width: var(column-width);
}

Variables in Preprocessors

SASS
$main-color: red;
.foo {
  background: $main-color;
}
LESS
@main-color: red;
.foo {
  background: @main-color;
}
My First Attempt
@var $main-color red;
.foo {
  background: $main-color;
}

Trouble Selling It

First version was too similar to existing preprocessor-based variables.

Hard to convince CSSWG that it was worth doing.

Had to leverage the power of CSS in a way that preprocessors couldn't.

Modern Variables

html {
  var-main-color: red;
}
.foo {
  background: var(main-color);
}
PROS CONS
  • Variables can now be "scoped" to a subtree of the page.
  • Reading/setting variables is easy - just use el.style
  • Works well for theming Web Components
  • Slightly more verbose.
  • Now limited to use in properties - can't use variables for Media Queries, Selectors, etc.

Lots of uses!

  1. Page theming
  2. Widget theming
  3. Numerical Constants
  4. Fake "shorthands"
  5. "Author prefixes"

Theming

:root {
  var-main-color: #f3601c;
  var-accent-color: #fee65a;
}
.foo { 
  color: var(main-color);
  border-bottom-color: var(accent-color);
}
:root {
  var-column-width: 180px;
  var-gutter-width: 20px;
}
.section {
	width: calc(3 * var(column-width) + 2 * var(gutter-width));
}

Widget Theming

:root {
  var-main-color: blue;
}
.widget {
  var-main-color: gray;
}
p { color: var(main-color); } /* blue */
.widget h1 { color: var(main-color); } /* gray */

Numerical Constants

.foo { width: 41.667%; }
.foo { width: calc(5 / 12 * 100%); }
:root { var-p5-12: calc(5 / 12 * 100%); }
.foo { width: var(p5-12); }

Numerical Constants

:root { 
  var-phi: 1.618;
  var-pi: 3.14159;
}
.foo {
	height: 300px;
	width: calc(300px * var(phi));
}

Fake "shorthands"

.shadowed {
  box-shadow:  2px  2px gray 1px,
               2px -2px gray 1px, 
              -2px  2px gray 1px, 
              -2px -2px gray 1px;
  transition: box-shadow .2s;
}
.shadowed:hover {
  box-shadow:  2px  2px gray 5px,
               2px -2px gray 5px, 
              -2px  2px gray 5px, 
              -2px -2px gray 5px;
}

Fake "shorthands"

.shadowed {
  var-shadow-blur: 1px;
  box-shadow:  2px  2px gray var(shadow-blur),
               2px -2px gray var(shadow-blur), 
              -2px  2px gray var(shadow-blur), 
              -2px -2px gray var(shadow-blur);
  transition: box-shadow .2s;
}
.shadowed:hover { var-shadow-blur: 5px; }

"Author Prefixes"

Unrecognized properties are thrown away. This makes it hard to extend CSS via JS.

"var-" can be thought of as an "author prefix", just like "-moz-" is a vendor prefix.

JS can read the properties and do new things.

Values can be almost anything - it doesn't have to be valid for any real property. (Only limitations come from the basic CSS syntax - check the spec for details.)

Conditional Rules!

Generalizes Media Queries to a more general "condition" concept.

Adds new @supports condition.

Previously added @document, but punted it to next level.

@supports

Lets you test for whether a property is supported.

Similar to Modernizr and similar libraries.

Contains rules, just like @media

Examples

Spec is well written already, with great examples.

Recommend reading http://dev.w3.org/csswg/css-conditional/#at-supports (scroll past all the grammar stuff, until you see Example 5).

Examples

@supports ( display: flex ) {
  body, #navigation, #content { display: flex; }
  #navigation { background: blue; color: white; }
  #article { background: white; color: black; }
}
@supports not ( display: flex ) {
  body { width: 100%; height: 100%; background: white; color: black; }
  #navigation { width: 25%; }
  #article { width: 75%; }
}

Better Fallback

body { width: 100%; height: 100%; background: white; color: black; }
#navigation { width: 25%; }
#article { width: 75%; }
@supports ( display: flex ) {
  body, #navigation, #article {
    display: flex;
    width: auto;
  }
  #navigation { background: blue; color: white; }
  #article { background: white; color: black; }
}

Dealing with Prefixes

.noticebox {
  border: 1px solid black;
  padding: 1px;
}
@supports ( box-shadow: 0 0 2px black inset ) or
          ( -moz-box-shadow: 0 0 2px black inset ) or
          ( -webkit-box-shadow: 0 0 2px black inset ) or
          ( -o-box-shadow: 0 0 2px black inset ) {
  .noticebox {
    -moz-box-shadow: 0 0 2px black inset;
    -webkit-box-shadow: 0 0 2px black inset;
    -o-box-shadow: 0 0 2px black inset;
    box-shadow: 0 0 2px black inset; /* unprefixed last */
    /* override the rule above the @supports rule */
    border: none;
    padding: 2px;
  }
}

Combining Support Queries

Each query must be wrapped in parentheses ()

Can be negated with not, or combined with and and or

To avoid precedence confusion, must use extra parentheses when mixing not/and/or.

Combining Support Queries

@supports (transition-property: color) or
          (animation-name: foo) and
          (transform: rotate(10deg)) {
  // ...
}
@supports ((transition-property: color) or
           (animation-name: foo)) and
          (transform: rotate(10deg)) {
  // ...
}

Repeating History?

Things like @supports have happened before (and failed), like document.implementation.hasFeature("HTML", "1.0").

@supports tries to avoid most of the failings.

Based purely on parsing, not on some arbitrary true/false feature table.

Javascript support!

Like @media and matchMedia(), @supports has CSS.supports().

interface CSS {
  static boolean supports(DOMString property, DOMString value);
  static boolean supports(DOMString conditionText);
}
CSS.supports("display", "flex");
CSS.supports("(display: flex)");

The End!

Questions and Answers

@tabatkins

xanthir.com/+

xanthir.com

xanthir.com/talks/2013-06-14

Bonus Super Fun! Future of Variables

Current Variables are a bit weak; we'll need more power in the future.

parent-var()

parent-var(foo) = the value of the variable on the element's parent.

Useful for modifying a variable as you progress down the tree.

parent-var()

Basic version of a nested tree:

parent-var()

Advanced version of a nested tree:

(Note the highlighting on hover.)

parent-var()

.tree {
  var-indent: calc(parent-var(indent, 0px) + 1em);
}
.item {
  padding-left: var(indent);
}

Default Values

You can declare a fallback value at each variable use right now, but can't declare a "default" value just once.

.widget {
  var-main-color: blue !default;
  /* Only used if nobody else declares
     a var-main-color property. */
  /* Specifically, used if there is no
     cascaded value, and the inherited
     value is missing/invalid. */
}

Type Annotations

Right now, you can't do smooth animations of a custom property, because we don't know what type of value it is until it gets used in a "real" property.

Type annotations let us know how to interpret the custom property early, so we can use it in transitions/animations usefully.

.foo {
  var-highlight-color: #0099cc !type(color);
  text-shadow: 2px 2px var(highlight-color) 1px;
  transition: var-highlight-color .2s;
}
.foo:hover {
  var-highlight-color: blue !type(color);
}

Beyond Variables

Variables are just the first step in extensible CSS.

No pipe-dream - watch these start to arrive in specs this year and the next.

Really The End

Questions and Answers

@tabatkins

xanthir.com/+

xanthir.com

xanthir.com/talks/2013-06-14