and I work at Google.
(Only using standardized stuff, but best viewed in Chrome or most recent Safari.)
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);
}
SASS |
|
---|---|
LESS |
|
My First Attempt |
|
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.
html {
var-main-color: red;
}
.foo {
background: var(main-color);
}
PROS | CONS |
---|---|
|
|
: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));
}
:root {
var-main-color: blue;
}
.widget {
var-main-color: gray;
}
p { color: var(main-color); } /* blue */
.widget h1 { color: var(main-color); } /* gray */
.foo { width: 41.667%; }
.foo { width: calc(5 / 12 * 100%); }
:root { var-p5-12: calc(5 / 12 * 100%); }
.foo { width: var(p5-12); }
:root {
var-phi: 1.618;
var-pi: 3.14159;
}
.foo {
height: 300px;
width: calc(300px * var(phi));
}
.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;
}
.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; }
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.)
Generalizes Media Queries to a more general "condition" concept.
Adds new @supports
condition.
Previously added @document
, but punted it to next level.
Lets you test for whether a property is supported.
Similar to Modernizr and similar libraries.
Contains rules, just like @media
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).
@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%; }
}
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; }
}
.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;
}
}
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
.
@supports (transition-property: color) or
(animation-name: foo) and
(transform: rotate(10deg)) {
// ...
}
@supports ((transition-property: color) or
(animation-name: foo)) and
(transform: rotate(10deg)) {
// ...
}
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.
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)");
Questions and Answers
Current Variables are a bit weak; we'll need more power in the future.
parent-var(foo)
= the value of the variable on the element's parent.
Useful for modifying a variable as you progress down the tree.
Basic version of a nested tree:
Advanced version of a nested tree:
(Note the highlighting on hover.)
.tree {
var-indent: calc(parent-var(indent, 0px) + 1em);
}
.item {
padding-left: var(indent);
}
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. */
}
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);
}
Variables are just the first step in extensible CSS.
@extend
(from SASS)
No pipe-dream - watch these start to arrive in specs this year and the next.
Questions and Answers