Surfin' Safari

CSS Selectors Inside Selectors: Discover :matches(), :not() and :nth-child()

Posted by Benjamin Poulain on Monday, August 17th, 2015 at 7:00 am

Since last year, WebKit supports many selectors defined in the draft of CSS Selectors Level 4.

In this post, we will see a special subset of new selectors: new pseudo classes that take other selectors as arguments. Those are :matches(), :not(), :nth-child(), and :nth-last-child().

All the selectors discussed in this post work for styling with CSS, and for querying with JavaScript (querySelector[All](), closest(), etc).

Select Properties A or B with :matches()

The pseudo class :matches() takes a list of selector as argument. It matches an element if any of the selector in the list matches.

For example, if we have:

    :matches(.foo, .bar) {
        background-color: green;

Any element with the class “foo”, “bar”, or both will have a green background.

This new selector is used extensively inside WebKit itself to simplify stylesheets. For example, code like the following used to appear in Web Inspector:

    .syntax-highlighted .css-keyword,
    .syntax-highlighted .css-tag {
        color: rgb(170, 13, 145);

With :matches(), scoped selectors do not required any repetition:

    .syntax-highlighted :matches(.css-keyword, .css-tag) {
        color: rgb(170, 13, 145);

Full Selector Support

The argument inside :matches() does not have to be a list of simple selectors. Any selector is supported, including the use of combinators.

For example, the following rule removes the top margin whenever two title levels follow each other:

    :matches(h1+h2, h2+h3, h3+h4, h4+h5) {
        margin-top: 0;

There has been some confusion about the subject of the selector when using combinators with :matches(). For example, take the following rule:

    .icon:matches(:any-link img)

It matches every image with a class “icon” and that has a link among its ancestors. It does not match links containing image elements. In fact, it is strictly equivalent to:

    :any-link img.icon

If you are unsure what element :matches() would be applied to, you can just think of it as if the selector was invoking Element.matches() on the active element.

Deprecated :-webkit-any

WebKit already had a similar but more limited feature, the pseudo class :-webkit-any(). It is now deprecated.

The old :-webkit-any() has problems that cannot be fixed in a backward compatible way. It is best to avoid it. We removed all of its use inside WebKit.

Make sure to carefully test your pages when replacing :-webkit-any() by :matches() as they are not strictly equivalent. In particular, :-webkit-any() does not handle the specificity correctly.

Rejecting Complete Selectors with :not()

The pseudo class :not() has always been very popular in CSS. It selects an element if the element does not match a simple selector. For example:

    a:not(.internal) {
        color: red;

What is new is the argument you can pass to :not(). Just like :matches(), the new :not() supports any selector list as arguments.

Code that used to chain multiple :not() can now use comma separated selectors to achieve the same. For example:


Can also be written as:

    :not(i, em)

You can use complex selectors, which means you can exclude multiple properties. For example, it is now possible to select any element that does not have simultaneously the classes “.important” and “.dialog”:


And you can use combinators which is becoming one of my favorite features. For example, it is possible to select any image that is not part of a link:

    img:not(:any-link *)

Counting Elements with :nth-child() and :nth-last-child()

The pseudo class :nth-child() and :nth-last-child() are also being augmented with similar capabilities.

Previously, it was only possible to count elements indistinguishably with :nth-child(), or count elements having the same qualified name with :nth-of-type(). With the extended :nth-child(), you can specify what kind of element to count.

For example, if you want to select every other element that is not hidden, you can now do:

    :nth-child(odd of :not([hidden]))

The general syntax is of the form:

    :nth-child(An+B of selector list)

As for the previous selectors, the argument has full support for selectors, including complex selectors, combinators, etc.

Dynamic Specificity

Nesting selectors inside other selectors creates a new interesting question: what is the resulting specificity?

The answer is: it depends. The specificity varies with what is being matched. The final specificity is the largest of all the sub-selectors.

Let’s take a concrete example. If we take the following selector:

    :matches(div, #foo, .bar)

What is the specificity?

When the following element is selected:

    <div id="foo"></div>

The specificity is (1, 0, 0), the same as if we only had the selector “#foo”. The reason is for all the selector that match (“div” and “#foo”), the highest specificity of the two is the one of #foo.

This definition is great because it’s compatible with existing selectors. You can take any selector, wrap it inside a :matches(), and its specificity works exactly the same.

With complicated selectors, finding the specificity is not always this easy. Fortunately, the Web Inspector is there to help us.

In the Inspector, when hovering a selector in the “Rules” panel, the tooltip shows the real specificity of the selector for the current element. If the specificity varies with the element like in the example above, the Inspector also indicates that the specificity is dynamic:

The Web Inspector's tooltip show the dynamic specificity of any selector.

Final Word

There are many new possibilities created by the new selectors. Play with them, invent new combinations, and tell us what you think.

For short questions, you can contact me and Yusuke on Twitter. For longer questions, you can email webkit-help or file bug report.

Introducing Backdrop Filters

Posted by Brent Fulgham on Monday, August 10th, 2015 at 7:00 am

Our recent blog posts have focused on important performance and developer features added to WebKit. But WebKit is about more than just great developer tools; we also build features for authoring amazing web content.

In this post I’m excited to share a great new feature that designers have been demanding for some time: backdrop filters. Let’s start with a few words explaining why this feature is important, then we can delve into how you can start using it. If you are running a recent nightly build of WebKit, you can try out the example yourself!


The User Interface design language for iOS 7 and OS X Yosemite changed to incorporate some beautiful backdrop blur effects. This layering gives a sense of depth, while preventing detail from the content underneath from cluttering the foreground.

The following image shows the WebKit media controls on top of a video. Notice that you can see some of the background content through the frosted glass effect.

View of the media controls in the context of video playback.
It’s even easier to see in this close up, which was captured using a more vivid source video.

Detail view of the media controls, showing frosted glass backdrop effect.
We wanted the WebKit media controls to have this visual style on relevant platforms. However, the controls are implemented in HTML, CSS, and JavaScript.

Designers want to use these kind of beautiful effects in their web designs, but have been unable to because these effects were only available to native applications. This prevented embedded web views from looking as good as native controls. It also prevented these kinds of effects from being used for authoring websites.

Until recently, there was no standards-compliant method for producing these kinds of effects. Many designers were forced to create the illusion of blurred backdrops using pre-rendered background content and carefully clipping and positioning these assets to achieve the desired effect.

Unfortunately, as with most illusions, this approach doesn’t hold up to close scrutiny.

  • New artwork must be generated any time the background image or blur characteristics are changed.
  • The careful alignment and clipping required to maintain this illusion can lead to pixel cracks and other display gitches.
  • Different blurred images are needed for each targeted display resolution.
  • Dynamic layouts or animations require complicated and potentially costly calculations as the user interacts with the page.

In short, instead of focusing on their site design, developers were forced to take heroic measures to achieve the desired effect.

Backdrop Filter

We saw so many instances of these Sisyphean techniques being used that we created a new CSS style and proposed it as part of the CSS Filter Effects Module Level 2.

The backdrop-filter style allows us to style elements with backdrop effects that resemble those in iOS and OS X that motivated this discussion.

This new style allows the browser engine to do the complicated calculations and positioning needed to achieve this effect.

  1. WebKit starts with the content behind the styled element. Note that this is not the background of the element, but rather the content that would be drawn behind the element.
  2. WebKit then applies the blur effect to the content.
  3. Finally, the backdrop is composited with the other elements on the page to yield the final result.

Example: backdrop-filter: blur(10px);

Backdrop Filter Example
Since these blur operations are being done in the browser engine, we can take advantage of hardware support, resulting in very efficient operations. However, be warned! The nature of this backdrop effect forces the engine to perform more rendering passes, which will have an impact on performance. Make sure you only use this feature where it is most necessary.

We wanted to give developers the freedom to use all kinds of filters in their designs, so backdrop-filter supports the full range of effects provided by our CSS Filters implementation. This means we can do all kinds of exciting things with our backdrops:

Example: backdrop-filter: invert();

Inverted Color Backdrop Example

And we can combine multiple filters:

Example: backdrop-filter: blur(10px) grayscale(100%);

Mixed Filters on Backdrop Example
And best of all, this effect is completely dynamic — it can be used on top of HTML5 media, CSS animations, WebGL, and other dynamic content.

This is an amazing advancement of what you can do with your designs. Prior to this style, you could not achieve this kind of effect.


WebKit proposed this feature to the CSS Working Group last year, and it is currently in the Editor’s Draft of the CSS Filters Level 2 specification. We are currently prefixing this property to comply with the W3C requirements for features that have not completed the standardization process. Consequently, you will need to write -webkit-backdrop-filter when using it in your own CSS.


We hope you enjoy this new feature, and share your creations with us! If you find bugs, please report them at

As always, if you have suggestions or feedback on this (or other) Filter Effects, please share them on the mailing list.

For short questions, you can contact me or Dean Jackson on Twitter. For longer questions, you can email webkit-help.


The images and video of the International Space Station were obtained from NASA’s website, and are used under the Public Domain.

Web Inspector Interface Changes

Posted by Timothy Hatcher on Monday, August 3rd, 2015 at 7:00 am

Quickly switching tasks is a common action when developing a web site. You might be debugging JavaScript one minute — the next minute you might be poking around in an XHR to validate server data. A designer might only care about the DOM tree and CSS editing. A backend developer might only need network information and a console. Making sure these task areas are quick to access is key for a tool containing such broad functionality.

Web Inspector Tab Icons

Catering to these disparate tasks, each of Web Inspector’s core functions have been divided out into their own tabs. Like the tabs in Safari, they can be rearranged to fit your workflow and closed if you don’t need them. Quickly switch among tasks for Network, Elements, Timelines, Console, Debugger, Resources, Storage, and Search Results with this flexible tab-based interface.

Web Inspector Tab Bar

The everyday tasks of manipulating a page’s DOM tree and styles are now contained in the dedicated Elements tab. New improvements to our style editing experience also help you to stay productive.

Web Inspector Elements Tab

You can stay on top of resource requests in the Network tab — allowing for continuous network request monitoring, without the overhead of a full timeline recording. It includes filterable access to all resource requests and XHRs, along with a details sidebar for quick-hit information about individual requests and the server responses.

Web Inspector Network Tab

These enhancements are available to use in WebKit Nightly Builds and the Safari 9 beta. Help us make Web Inspector even better by sending us quick feedback on Twitter (@xeenon, @jonathandavis, @JosephPecoraro), file a bug report, or even contributing your own enhancements to Web Inspector.

For more information about other Web Inspector changes, please watch Using Safari to Deliver and Debug a Responsive Web Design from WWDC 2015.

Using the System Font in Web Content

Posted by Myles Maxfield on Monday, July 27th, 2015 at 9:00 am

Web content is sometimes designed to fit in with the overall aesthetic of the underlying platform which it is being rendered on. One of the ways to achieve this is by using the platform’s system font, which is possible on iOS and OS X by using the “-apple-system” CSS value for the “font-family” CSS property. On iOS 9 and OS X 10.11, doing this allows you to use Apple’s new system font, San Francisco. Using “-apple-system” also correctly interacts with the font-weight CSS property to choose the correct font on Apple’s latest operating systems.

Mac OS X 10.11 El Capitan System Font

On platforms which do not support “-apple-system,” the browser will simply fall back to the next item in the font-family fallback list. This provides a great way to make sure all your users get a great experience, regardless of which platform they are using.

There are currently discussions in the w3c regarding standardizing this value so authors could simply specify “system.” However, these discussions have not reached consensus, so WebKit prefixes this value.

Using any other mechanism to specify the system font is not guaranteed to behave as you might expect. This includes using any implementation details such as period-prefixed family names.

Going beyond the system font, iOS has dynamic type behavior, which can provide an additional level of fit and finish to your content. These text styles identify more than simply a particular font family; instead, they represent an entire style, including size and weight. These styles are therefore characterized by values given to the more-general “font” CSS property. The supported values are:

font: -apple-system-body
font: -apple-system-headline
font: -apple-system-subheadline
font: -apple-system-caption1
font: -apple-system-caption2
font: -apple-system-footnote
font: -apple-system-short-body
font: -apple-system-short-headline
font: -apple-system-short-subheadline
font: -apple-system-short-caption1
font: -apple-system-short-footnote
font: -apple-system-tall-body

For more information, please see Antonio Cavedoni’s fantastic WWDC presentation or contact @jonathandavis.

Announcing JetStream 1.1

Posted by Filip Pizlo on Monday, July 13th, 2015 at 3:03 pm

JetStream is the benchmark suite that WebKit developers use to tune JavaScript engine performance. It comprises latency benchmarks, which measure the start-up and worst-case execution times of the JavaScript engine, and throughput benchmarks, which measure average throughput in steady state. We want to make JetStream accurately reflect our performance goals — a change that improves the score should also improve the user experience. Today we are happy to announce JetStream 1.1, which improves the accuracy of the latency component of the suite. JetStream 1.1 fixes bugs in two existing tests and adds a new test to replace the oldest and smallest test in the latency component.

A sound approach to responsiveness scores

The latency component of JetStream is mostly made up of tests that measure the latency incurred when executing code for the first time. But it also includes two tests, splay-latency and mandreel-latency whose scores reflect how close a browser’s worst-case performance is to its average-case. A relatively good score on splay-latency indicates that a browser’s garbage collector is less likely to interfere with a web app’s responsiveness. Mandreel-latency is similar, but due to its larger code base and lower allocation rate, it primarily tests whether a browser’s just-in-time compiler interferes with responsiveness. Both of these tests originate from the Octane version 2 benchmark suite.

Splay-latency and mandreel-latency are intended to reward responsive browsers. A responsive browser will always execute the task quickly, rather than usually executing it quickly but sometimes having a hiccup with very bad performance. But these tests use the wrong metric for computing a responsiveness score: they first compute the root mean squared (RMS) of thousands of samples, and then report a score that is the reciprocal of the RMS. It turns out that this number doesn’t adequately reward browsers that have great responsiveness, and in some cases, it gives a high score to browsers that are less responsive.

For example, consider what would happen if Browser A always ran a task in 20 milliseconds, while Browser B ran the same task in either 10 milliseconds or 5 milliseconds at random with equal probability. Clearly, Browser B would appear to be more responsive – it always completes the task at least twice as fast as browser A. But, using RMS as the latency metric means that Browser A gets a higher score: since its performance is always 20 milliseconds, it will have an RMS of zero, which leads to a latency score of 1/0, i.e. infinity. Browser B will have an RMS around 3.5, leading to a score of 1/3.5, i.e. less than infinity. Setting aside the absurdity of a browser getting an infinite score — that’s also something we want to prevent — it’s clear that Browser A has a higher score on these tests despite always being less responsive. We’d like to be able to use these tests to tune the responsiveness of WebKit: we want to accept changes that improve the score and reject those that degrade it. But we can’t do this if the score rewards bad behavior.

The reason is that the RMS will increase (and the 1/RMS score will decrease) whenever there is an outlier in either direction. Both good outliers (the browser sometimes finishes a task much faster than normal) and bad outliers (the browser sometimes takes a long time) increase the RMS by the same amount. Instead of RMS, we want a one-sided metric, which punishes bad outliers while ignoring the good ones — we are fine with browsers sometimes being much faster than the average so long as the worst case is still great.

The simplest solution would be to compute a score based on the slowest execution out of the thousands of executions that these tests do. It’s tempting to do this, but it carries a cost: the worst case can be a noisy number. We want to reduce the measurement noise, since we want to be able to confidently measure small differences in performance between different versions of our code. An easy way to get most of the benefit of a worst-case measurement while reducing noise is to take the 0.5% worst of all samples and report their average.

Using the average of the worst samples never punishes browsers for sometimes running faster than normal. It zeroes in on exactly what we mean by responsiveness: it’s how fast the browser will run in the worst case. Consider how this affects splay-latency. The samples above the 99.5 percentile — roughly 10 samples in a typical run of splay — are exactly those where WebKit’s garbage collector has to do a full scan of the heap. Browsers that can split the heap scan into smaller chunks of work that never incur one long pause will score better on this test. It’s easy to intuit about the splay-latency and mandreel-latency scores: if one browser scores 2× higher than another, it means that this browser’s occasional performance hiccups will be 2× less severe.

The changes to improve the splay-latency and mandreel-latency tests were tracked by WebKit bugs 145762 and 146378.

Introducing CDjs

JetStream includes many legacy benchmarks from older benchmark suites like SunSpider and V8. Most of those tests are still relevant, since they feature non-trivial workloads that are representative of what we think developers want to be able to do efficiently in JavaScript. But we can further improve the benchmark suite by incrementally replacing the smallest and most trivial tests with larger and more complex ones. We are taking one small step in this direction in JetStream 1.1 by replacing cordic, previously the smallest latency test with just 80 non-comment lines of code, with a new test called CDjs, which is much more sophisticated.

CDjs is our JavaScript implementation of the CDx real-time benchmark. CDx is a collision detection benchmark in which aircraft fly around in a simulated airspace and collisions are reported by interpolating the flight paths between recorded frames. CDx has previously been implemented in Java and C, and was used for worst-case performance comparisons between those languages. CDx is usually run for hundreds, or even thousands, of iterations and performance results are computed based on the worst-case execution. This makes CDx a natural candidate for the latency component of JetStream. CDjs is a direct port of the Java implementation of CDx (also known as CDj), except for two changes:

  1. CDjs uses red-black trees rather than hashtables for its various mappings. This makes CDjs slightly more faithful to the concept of real-time, since hashtables may have random worst-case pathologies due to collisions. Also, while we were tempted to use the new Map API in ES6, we weren’t able to do so in CDjs because it needs to use objects’ values as keys rather than object identity. The red-black tree implementation is a port of WebKit’s existing red-black tree.
  2. CDjs doesn’t attempt to reuse objects quite as aggressively as CDj did. This makes CDjs more faithful to the experimental question that CDx was trying to ask: how much slower do you run when you write a real-time workload in a high-level language? The answer is more meaningful if the high-level program fully utilizes the features of the high-level language, like garbage collection.

We run CDjs as a benchmark by simulating 1,000 aircraft that occasionally collide. We run the benchmark for 200 frames. This makes the benchmark run for around one second on a modern browser and typical notebook computer. The score is the inverse of the average of the 10 worst execution times from those 200 frames. The addition of this benchmark doesn’t change the overall weighting of JetStream between latency and throughput, though it does shift the definition of latency in the direction of worst-case of many samples rather than the cost of cold start.

The addition of CDjs to JetStream was tracked by WebKit bug 146156.


We are happy to introduce this significant update to the JetStream benchmark suite. This update makes JetStream’s latency metrics more accurate than they were before. The new suite is now available to run at As always, file bugs at if you find any issues in this benchmark.