So far in this book I’ve aimed to discuss only those web features that are pretty stable in at least a few browsers, or that should be stable sometime in the near future. But now that we’ve arrived at this last chapter, I can really cut loose and talk about some of the more experimental features on the horizon.
Changes are planned everywhere: A new revision of JavaScript, code-named Harmony, is due for release sometime in 2013 and should make its way into browsers over the coming years; many new APIs are being proposed to the W3C, including one for discovering devices on the same network using Universal Plug and Play (UPnP) and one for measuring ambient light; work on the draft specification for HTML5.1 is well underway; and many CSS modules are already moving to Level 4. I could talk about any number of changes, but I’ll focus on the ones that I think will have the greatest impact on the way we work and that have a good chance of being implemented.
I don’t think I’m exaggerating when I say that the Web Components specification proposes the most radical changes to HTML since its creation some 20+ years ago. Even the much-hyped HTML5 is a small point-version update that doesn’t really add anything genuinely new.
Web Components is a collective title for a group of additions to HTML and the DOM aimed at making rich interfaces for web applications—a kind of reusable widget specification. As I write this, four main components exist: templates, decorators, custom elements, and the Shadow DOM. I’ll explain what each does in turn, but first let me sum up what they do when combined.
One of the principal problems of building application components in HTML today is that the elements used to build them are part of the DOM and, as such, are open to conflicts from CSS or JavaScript. These could be inheritance conflicts, such as rules applied to parent elements cascading into component elements or, inversely, rules applied to component elements leaking or cascading to elements elsewhere in the DOM.
Another problem results from naming conflicts, where the same class or ID is unknowingly used in different pages of a site, meaning rules intentionally declared on one element are also unintentionally applied to others. This problem is commonly found on large sites that lack a clear naming scheme, and it can be made even worse by conflicts in JavaScript when selectors apply unwanted functional behavior to an element.
The best way to avoid conflicts like these is to separate the component from the rest of the DOM to prevent any inheriting or leaking. This technique is known as encapsulation and is fundamental to object-oriented programming languages.
Web Components attempts to bring encapsulation into the HTML DOM by allowing you to create elements that appear only in the rendering of a page, not in the DOM itself. Web Components will offer a way to build widgets that can be reused across many different pages on a site without having to worry about conflicts with existing CSS and JavaScript, since the widget lives in a parallel DOM.
The Web Components spec is still in the draft stage as I write this, so I won’t explore the concepts in great detail, but I will cover the basics since it could be so significant.
Probably the easiest way to grasp Web Components is with an understanding of templates. The idea of developing with reusable blocks of code, or templates, has been a staple of web development for quite some time, although we’ve never seen a native implementation in HTML; server-side languages or JavaScript (such as the Mustache library from Chapter 5) have been required in order to use templates.
Think of a Web Component template as a kind of inert block of DOM. The significance of this is that the contents are parsed, but not rendered, by the browser. This means images and other external elements aren’t loaded and included scripts won’t run, which can be a real performance boost compared to hiding elements with CSS, where assets are still loaded.
A template is declared with the template
element, and any child elements form the content of the template. The following code block shows a template element with the id
#foo, which has two child elements (an h2
and a p
). Outside of the template is a div
with the id
#bar, which contains an h1
element.
<template id="foo"> <h2>Gorilla Beringei</h2> <p>A species of the genus Gorilla…</p> </template> <div id="bar"> <h1>Eastern Gorilla</h1> </div>
If you were to view this page with your browser’s developer tools, you would see the template
element with no content inside it because, essentially, the contents of this element are invisible to the DOM.
You access the template through script using the content
object, which returns the child elements of the template as an HTML fragment. For example, you can see that the next code snippet assigns the template to the variable tpl and logs its content
object to the console:
var tpl = document.getElementById('foo'); console.log(tpl.content);
Once you have the fragment, you can manipulate it as you see fit. The following code uses cloneNode()
to create a clone of the content and appendChild()
to add it inside #bar:
var bar = document.getElementById('bar'), clone = tpl.content.cloneNode(true); bar.appendChild(clone);
At this point, you would see this markup if you inspected the DOM:
<template id="foo"></template> <div id="bar"> <h1>Eastern Gorilla</h1> </div>
But the page would be rendered as if it were using this markup:
<div id="bar"> <h1>Eastern Gorilla</h1> <h2>Gorilla Beringei</h2> <p>A species of the genus Gorilla…</p> </div>
You can see it for yourself in the example file templates.html; the output is shown in Figure 11-1 (see Appendix M for information on current browser support). Note that in order for the contents of the template
element to show in the DOM, I had to enable the Show Shadow DOM option in my developer tools; if that option wasn’t enabled, the element would appear to be empty.
template
element, which exists outside the regular DOM.Code that was inert inside the template
element becomes active once it’s inserted into another DOM element, at which point any external resources will load, scripts will be parsed, and so on.
Decorators extend the utility of templates by allowing you to add custom markup through CSS. Decorators use the decorator
element, which must have a unique id
assigned. Inside the decorator
element, you’ll find a template
element with some custom markup and the content
element, which is where the element that the rule is applied to is rendered. Not clear? It took me a while to get it too.
Let’s break this down into stages. The following code shows an example of a decorator. I gave it the unique id
#foo (for a change). Inside is a template that contains a div
, with the content
element and an h2
inside that.
<decorator id="foo"> <template> <div> <content></content> <h2>A great ape!</h2> </div> </template> </decorator>
Now imagine that in the main document I have an h1
element with the id
#bar, as in the following code:
<h1 id="bar">Gorilla</h1>
I apply the decorator using CSS and the new decorator
property, which has as its value a url()
function containing the decorator’s id
.
h1#bar { decorator: url(#foo); }
Once I’ve done this, the markup in the template #foo is added to the markup of the element #bar, with #bar itself replacing the content
element of #foo. However, this takes effect only at the point of rendering and doesn’t alter the DOM. Although an inspection of the DOM shows only the element #bar, the element will be rendered as though the markup were this:
<div> <h1 id="bar">Gorilla</h1> <h2>A great ape!</h2> </div>
You can do more with templates and decorators, but to show you more, I first need to make a brief digression to talk about scoped styles.
One of CSS’s greatest strengths is its use of inheritance—that is, the way that values can cascade through selectors to apply to multiple elements. That strength can also be a drawback, however, if you’re working on large sites with many stylesheets, where experiencing the naming and inheritance conflicts that I mentioned at the start of this section is not uncommon.
Scoped styles are a way to avoid these conflicts. They’re applied in the document using the style
element with the attribute scoped
, and any rules contained therein are inherited only by the children of the element they’re used in, and won’t be applied anywhere else in the document.
You can see this in action in the following code: A scoped style
tag is used inside a div
element, and the rules applied to the h1
apply only to the h1
within that element (the one with the id
of #foo), and not the one outside the div
(with the id
#bar). The scope of the rule applies only to the children of the div
.
<div> <style scoped> h1 { background-color: #333; color: #FFF; } </style> <h1 id="foo">Scoped</h1> </div> <h1 id="bar">Not Scoped</h1>
Take a look at the example file scoped-style.html. Here, the h1
with the id
#bar follows the one with the id
#foo in DOM order, so you would expect the rules inside the style
element to apply to both. In fact, the scoped
attribute means the rules apply only inside the parent div
. You can see the result in Figure 11-2 and in scoped-style.html.
Having the ability to scope styles in this way is ideal for encapsulation, and it combines especially well with templates and decorators. Case in point, if I return to an earlier example using the markup from the first code block in Decorators, I could create a set of rules to be applied to the original h1
element only when the decorator is applied by using a scoped style
tag inside the template
element:
<decorator id="foo"> <template> <div> <style scoped> h1 { color: red; } </style> <content></content> <h2>A great ape!</h2> </div> </template> </decorator>
In this case, the h1
element is colored red only when the decorator is applied. Even better, that color won’t apply to any subsequent h1
element in the document because of its limited scope—a perfect example of encapsulation.
Although decorators are handy for adding extra presentational markup to an element, when you want to make more substantial changes, use a custom element. The key difference between custom elements and decorators is that the latter are transitory; they can be applied or removed by changing an attribute or selector. Custom elements, on the other hand, are fixed; they are applied when the DOM is parsed and can be changed or removed only with scripting.
A custom element is like an extended template that replaces or enhances a standard element. You create a custom element with the element
element (this paragraph is going for a new record in the number of occurrences of the word “element”), which has some new attributes that I’ll discuss shortly. Inside this element, you can add a template
element with new markup, as well as scoped styles and even a script.
If this sounds a bit confusing, consider this illustration. The following code snippet shows a simple example: an element
containing a template
, which, in turn, contains a div
, which itself contains the content
element I introduced in Decorators. The element
has two attributes: extends
, which takes as a value the name of the element that it will extend (in this case, a button
element), and name
, a user-defined unique identifier value (which must start with x- to avoid conflicting with existing elements).
<element extends="button" name="x-foobutton"> <template> <div id="foo"> <content></content> </div> </template> </element>
Once the custom element has been defined, you can apply it to an existing element with the is
attribute. The is
attribute is applied to the element to be extended and takes as a value the unique identifier from the name
attribute (x-foobutton) defined on the custom element. Actually, this is simpler than it may sound:
<button is="x-foobutton">Go</button>
The resulting effect is the same as that of a decorator: The markup of the custom element extends the markup of the element it’s applied to but only in the rendered view. Although viewing the DOM shows only the button
element, it renders like this:
<div id="foo"> <button>Go</button> </div>
This example is simple, but you can see how the extensibility of this technique would make it easy to build completely tailor-made widgets that could be reused across many documents. As a result, many of the cumbersome widgets we build today (such as carousels, accordions, and date pickers) could be applied to existing elements without filling the DOM with unnecessary markup, with the added benefit of implementing encapsulation to avoid conflicts.
I mentioned earlier that the core difference between a custom element and a decorator is in the permanence of the markup. One advantage of this is that scripts can be included in a custom element that will always be present (a benefit you can’t rely on for the more impermanent decorators). All this means you could even define an imperative API for each custom element, thereby taking interactivity to a whole new level.
The final piece of the Web Components specification is the Shadow DOM. This is not only a cool-sounding name for a supervillain, but it’s also a way to create and access, with script, the elements that exist in the parallel DOM I’ve shown you in this chapter. Just as decorators use CSS to alter elements and custom elements use HTML, Shadow DOM uses script to achieve the same ends.
The Shadow DOM describes the ability of a browser to create a new, fully encapsulated node tree inside the existing DOM. The browser does this by creating a shadow root inside an element, which can be traversed and manipulated like a regular node tree. (A shadow tree won’t show up in the DOM, but it will be rendered.)
Now for an example. The following code snippet contains some simple markup: a div
called #foo, which contains a single h1
element. This is the base markup in the DOM, inside which I’ll add a new shadow root.
<div id="foo"> <h1>Hello, world!</h1> </div>
Now I’ll add a new shadow root inside the div
, and then create and append a new element to the new root. I explain this code point by point in the discussion that follows.
var foo = document.getElementById('foo'), 1 newRoot = foo.createShadowRoot(), 2 newH2 = document.createElement('h2'); newH2.textContent = 'Hello, shadow world!'); 3 newRoot.appendChild(newH1);
The first thing to note 1 is the creation of a new shadow root inside #foo, using the createShadowRoot()
method. In the following two lines 2, I create a new h2
element with the text content 'Hello, shadow world!‘. And finally 3, I append the new h2
element into my new shadow root.
When this code executes, users see an h2
element with the text 'Hello, shadow world!', but if they viewed the DOM, users would see the original content, 'Hello, world!‘. The h1
element has been completely replaced by the new shadow node tree. The DOM remains unaffected.
Figure 11-3 shows how this renders in the Chrome developer tools, with the contents of the shadow root displayed in a new node tree below the identifier #shadow-root.
If you don’t want to replace the content in the element in which you’ve created a new root, you can once again use the content
element (again introduced in Decorators) to include the original elements. I illustrate this in the following code, where I create the content
element and then append it to the new shadow root. As a result, the user sees the new shadow h2
first, followed by the original h1
, although only the h1
appears in the DOM.
var content = document.createElement(content); newRoot.appendChild(content);
You can also use templates with shadow node trees. For example, here’s how to append an HTML fragment of the template #foo content into the Shadow DOM:
var foo = document.getElementById('foo'); newRoot.appendChild(foo.content);
The Shadow DOM goes even further than this simple example and is a very powerful and flexible tool. I can’t spend any more time on it here, but see Appendix L for some links to more detailed articles.
I’ve only brushed the surface of the Web Components specification, but I hope I’ve offered enough to get you excited by the possibilities. Web Components promises to offer fully reusable code components to enhance existing elements. These components will be fully encapsulated from the rest of the document and rendered by the browser but not accessible through the DOM, although a parallel shadow DOM will allow complete manipulation of and access to the elements inside each component.
If the Web Components specification is implemented, it will revolutionize the way we build applications and websites. And if that doesn’t get you excited, you may be in the wrong business!
CSS3 has already revolutionized the Web in some ways, with its introduction of behavioral aspects such as transitions and animations and by seriously addressing the problem of layout mechanisms. But those features barely scratch the surface of what’s to come. Lots of big tech companies are betting their futures on the Web, and their involvement in shaping the future of web standards brings with it some incredible innovation.
For example, Adobe’s wholesale embrace of open standards (a pleasant surprise!) is making it a major player in browser development, and its experience with graphics and publishing is being applied to CSS. The first fruits of this labor are CSS Regions and Exclusions, which open up the possibility of major changes to the way we’ll lay out pages in the future, as dynamic web pages finally begin to catch up with what desktop publishing has been doing for years.
But the web development community is having the biggest effect on the development of CSS. Developer-built JavaScript libraries such as jQuery and Modernizr are directly influencing the language, as you’ve seen with querySelector()
and querySelectorAll()
(back in Chapter 5), and that influence will be felt further with the introduction of feature queries.
Additionally, the rise in popularity of CSS preprocessors such as Sass and LESS means that front-end developers are becoming accustomed to using programming principles, such as variables and functions. The demand for these to be introduced into the language is manifesting itself through cascading variables.
Back in Chapter 4, I discussed CSS columns, where inline content is divided into columns and flows through the first column and then the second, the third, and so on. Imagine that those columns are not immediately adjacent to each other; the first is on the left of the page, the second is on the right, and the third is at the bottom, but the content still flows sequentially through them. That’s the gist of a new CSS concept called Regions.
Regions work like this: An element is declared as a source, and the content of that source is flowed into another element, or series of elements, known as a region chain. What that means in practice is you can have content flowing across multiple elements in a sequence that doesn’t flow in natural DOM order.
Here’s a simple illustration that starts with three div
elements: a #foo and two .bars. The first, #foo, is filled with content, and the others are empty:
<div id="foo"> <p>…</p> </div> <div class="bar"></div> <div class="bar"></div>
The next step is to get the content of #foo and put it into a named flow, kind of like storing it in a variable (or the clipboard on your computer). In CSS Regions, you create this named flow by declaring the flow-into
property on the source element (#foo). The value of the property is a unique name of your choosing, so I’ll name mine myFlow. Having named my flow (or clipboard, if you’re still following the metaphor), I can flow it into other elements, which become known as the titular regions:
#foo { flow-into: myFlow; }
In Internet Explorer 10, the source element must be an iframe
, and the content inside the body
of the linked document will become the content of the flow.
When this property is applied, the source element and all its children are no longer rendered on screen, although they are still visible and accessible in the DOM.
Next I must declare a region chain that the content will be flowed into. Each element that forms part of the region chain should have the flow-from
property declared on it, the value of which is the previously defined named flow. The following code shows how to flow the content of the flow myFlow into all regions called .bar:
.bar { flow-from: myFlow; }
The content in myFlow flows through the region chain in DOM order; it starts by flowing into the first instance of .bar, and then any overflow flows into the second instance of .bar, and so on, until the end of the content. Try this out with the file regions.html, as shown in Figure 11-4.
As I mentioned at the beginning of this section, CSS Regions work like multiple columns, without the columns needing to be immediately adjacent. This new element creates some amazing opportunities for making dynamic, interesting page layouts, inspired by years of experience with the possibilities of print media.
CSS Exclusions can be thought of as a kind of positioned floats—indeed, an earlier concept described them as exactly that. In CSS2.1 you can float elements only to the left, where other content flows around their right side, or vice versa. But the idea of CSS Exclusions is that you can flow content around an element no matter where it’s positioned on the page.
To illustrate, consider the following markup with a container element #foo, some text content in a p
, and a child element #bar:
<div id="foo"> <p>…</p> <div id="bar"></div> </div>
I want to position #bar absolutely over the content in #foo, which will require style rules somewhat like this:
#foo { position: relative; } #bar { left: 20px; position: absolute; }
As written here, #bar sits in a layer stacked above the text content, obscuring what’s behind it, as you can see in Figure 11-5. But I want to make #bar part of the same layer and to float the text content around it. In the parlance of Exclusions, I want #bar to become an exclusion element.
I can accomplish this with the wrap-flow
property, which makes the element it’s applied to an exclusion element. Any sibling content flows around it according to the keyword value of the property.
The following example uses the keyword value both
to make content flow around both sides of the exclusion element:
#bar { wrap-flow: both; }
You can see the difference this makes in Figure 11-6. The elements are in the same positions as before, but because #bar has now been declared an exclusion element, the content in #foo flows around it on both sides.
wrap-flow
property makes the positioned element become part of the document flow, and the content flows around it on both sides.Alternative values for wrap-flow
include:
start
to flow the content on the left side (in left-to-right languages) of the exclusion element, but not the right
end
to do the opposite
maximum
and minimum
to flow content only on the side with the most or least space (respectively) between it and its containing element
clear
to flow content only above and below the exclusion element
Figure 11-7 shows a few of the different values at work. The first example has a value of start
, so the content flows to the left of the exclusion element. The next has the end
value, so the content flows on the opposite side. And in the final example, the clear
value makes content flow on neither side of the element.
To permit more control over the flow of inline elements around the exclusion, you have the wrap-through
property. This property takes one of two keyword values: flow
and none
. The former, the default, makes inline content flow around the exclusion element; the latter doesn’t. This is useful if you want to enable content flow on a per-element basis.
wrap-flow
.For me, one of the most exciting things about CSS Exclusions is the way they interact with the Grid Layout module that I introduced in Chapter 4. Any grid item can be made into an exclusion element, which really expands grid layout possibilities. As a simple example, consider a grid with three columns and two rows:
E {
display: grid;
grid-columns: 1fr 1fr 1fr;
grid-rows: 100px 1fr;
}
On that grid, I’ll place two items that have overlapping cells (they’ll overlap in row two, column two):
F, G { grid-column-span: 2; } F { grid-column: 2; grid-row-span: 2; } G { grid-row: 2; }
Under the usual rules of grid placement, element G
would stack over the top of element F
, as it appears later in the DOM order. But by making element G
the exclusion element, the inline content of element F
will flow around it:
G {
grid-row: 2;
wrap-flow: both;
}
As you can see in the example file grid-exclusion.html (and in Figure 11-8), F
now takes on a kind of inverted upside-down L shape as it flows around element G
. This kind of layout is quite common in print so being able to reproduce it in web pages is really quite exciting. By making possible the kind of art direction that books and magazines have taken for granted for hundreds of years, CSS Exclusions ushers in a whole new era of laying out pages on the Web.
The CSS Exclusions used in the examples in this section so far are based on standard block elements, so they appear boxy. The plan in the future is that you won’t be limited to rectangular exclusions because a pair of new properties will allow you to draw geometrical exclusion shapes.
The shape-inside
and shape-outside
properties will accept as a value either a simple geometric shape such as a circle or ellipse, or a series of coordinates that will create a completely customized polygonal shape. Content could then be flowed around either the inside or outside of this shape (or both), opening up possibilities for the types of rich layout long possible in print but now improved with the dynamism of web content.
As I write this in early 2013, a series of new rules and properties that affect layout are at various degrees of implementation—from barely there to only a proposal. My hope is that they will all be adopted and implemented because they solve different problems. With the CSS specification in a constant state of flux, however, nothing can be taken for granted; these rules and properties may be implemented, partially implemented with a different syntax, or not implemented at all.
Still, I think looking at them is worthwhile for two reasons: First, so you can see the thinking that goes on in trying to find solutions to the problems of web layout; and second, if they are implemented, you may need to use them.
The idea behind the Box Alignment module is to create a syntax that’s common across many different modules, for aligning elements within their parent. Box Alignment takes the Flexbox syntax as its inspiration, using justify-
properties for inline/main axis alignment and align-
properties for stacking/cross-axis alignment. For example, to align an element along its main axis, you’d use the justify-self
property; and to align the child elements of an element along the cross axis, you’d use align-content
.
In addition to the well-known grid formed with rows and columns, typographers also use what’s often known as a line grid, or a vertical rhythm, a secondary grid created from the lines of text and headings on a page. When using a line grid, you try to make the vertical dimensions and alignment of objects harmonious with the text for better readability.
The Line Grid module creates a virtual vertical grid based on the font-size
and line-height
of text elements and lets you better align objects within that grid. It allows you to snap block elements to fixed points in that grid, overriding the default layout position created by the browser’s engine.
Scrolling is the de facto way to work with content that overflows its container, especially the screen, but scrolling isn’t always easy with devices such as television remote controls and liquid paper ebook readers, for example. A better approach for these devices might be to use paginated overflow instead.
You can do this easily with features proposed in the Paged Media module, which introduces the overflow-style
property. A value of paged-x
or paged-y
automatically creates pages horizontally or vertically (respectively), while -controls
(such as paged-x-controls
) adds on-screen controls generated by the browser for interfaces that require them.
A further proposal, Pagination Templates, extends this even further, creating content regions that are fixed to each page for a consistent experience, allowing rich interactive magazine-style layouts.
In Chapter 5 I discussed the JavaScript library Modernizr, which is used for detecting the existence of certain features in a visitor’s browser, and briefly mentioned its native adaptation into CSS through the @supports
at-rule. I want to return to that now and explore it in a little more detail, as it’s extremely useful and fast making its way into browsers.
The @supports
at-rule behaves like a media query: You create a logical query that, if it returns true, will apply the rules contained within the subsequent brackets. But instead of media features, the test conditions are CSS property-value pairs, known as feature queries. For example, to test whether a user’s browser supports the column-count
property so you can serve appropriate styles, you could construct a query like this:
@supports (column-count: 1) { … }
As with media queries, you can build more advanced queries using logical operators. For example, the following query uses the and
operator to serve styles to browsers that support both the column-count
and box-sizing
properties:
@supports (column-count: 1) and (box-sizing: border-box) { … }
You can also use the or
operator to build queries that detect defined features, which is extremely useful when dealing with vendor-prefixed properties. Here, both the hyphens
or -moz-hyphens
properties are tested against, and if either is supported, the rules are applied:
@supports (-moz-hyphens: auto) or (hyphens: auto) { … }
The not
operator allows you to serve styles to browsers that don’t support a given property. (Note that unlike the other operators, this one must be inside parentheses.)
@supports (not (-webkit-hyphens: auto)) { … }
Feature queries include an API that is as simple to use as the at-rule. For example, you can use the CSS.supports()
method to detect a single feature by passing a property-value pair as two arguments. Here, it tests for the flex
value on the display
property:
var supports = CSS.supports('display','flex');
And you can pass in full queries as a single argument, quoted as a string:
var supports = CSS.supports('(column-count: 1) and (display: flex)');
The Modernizr project has already begun implementing this in its library; if native CSS.supports()
implementation is present, the script will use that, and if not, it will fall back to Modernizr’s own tests.
Variables have proven their utility over the years in just about every programming language, but they have never been implemented in CSS, despite regular calls for implementation from the community. But with the surge in popularity of CSS preprocessors, a generation of coders is learning to love variables in their stylesheets, and calls to include them natively in the language can no longer be ignored.
As currently proposed, CSS variables have limited scope. A true variable permits any type of value and could be used at any point in the code—say, to assign a selector to a variable. The proposed CSS variables can be assigned only a valid CSS value and can be used only as the value of a property. For this reason, they’re distinguished with the name Cascading Variables.
Each Cascading Variable is declared using a custom property: a user-defined property name beginning with var-
to which a value is assigned. Here, the color value #F00 is assigned to the custom property var-foo:
:root { var-foo: #F00; }
Notice that I’ve declared this custom property using the :root
selector. (I explain why shortly.)
To use the value of the custom property, you call it using the var()
function, with the user-defined name (the bit after var-
) in parentheses. The value of the custom property is used as the value of the property it’s called on. For example, in the following listing, the h1
element calls the var-foo property using the var(foo)
function twice: once on the border-bottom
property and once on color
. The color value #F00 will be applied appropriately to each property.
h1 { border-bottom: 1px solid var(foo); color: var(foo); }
Cascading Variables are scoped, meaning they apply only to the element on which they are declared and to any child element. My use of the :root
selector to declare a custom property in the example in this section means the variable has global scope: It can be applied on any element on the page. Had I used a different selector, the value of the variable declared in the custom property would apply only to children of the matching element(s).
For example, in the following code the custom property var-foo, with a value of #F00, is declared on the :root
element, but I’ll also add a different value and selector below it. In this case, the value of the variable would be #F00 for the whole document, but it will now be #00F for the .bar element and its children.
:root { var-foo: #F00; } .bar { var-foo: #00F; }
In the longer term, the preprocessor favorite mixins will also be implemented in CSS. A mixin is like an extended variable, allowing blocks of code to be reused across multiple selectors. There’s even been talk of implementing full variables, allowing replacement of property names and selectors.
In this chapter, we’ve looked at some of the more experimental features of the web platform. These are all still in the testing phase and are liable to change, but they’re so powerful and potentially important to the platform’s future that I couldn’t really finish this book without mentioning them.
First up was Web Components, the biggest change to HTML since its invention. Web Components is a suite of features. It makes a parallel DOM that allows reusable code blocks to enhance and extend the standard HTML elements with full encapsulation, protecting them from conflicts with other CSS rules and JavaScript functions.
Next we looked at the future of CSS, which is also undergoing huge changes thanks to the involvement of big tech companies. CSS Regions and Exclusions promise to provide the tools required to create dynamic custom layouts that rival (and exceed?) anything possible in print media.
Finally, I covered new CSS features that are being developed based on innovation from the web development community. These include feature queries that bring native Modernizr-like feature detection to CSS and Cascading Variables that begin the adoption of the best preprocessor features into the language itself.