Hello everyone and
Thanks the organization for giving me the chance to give this talk here, in the last CSSconf edition in this type or whatever
So it's really a pleasure and an honor to be here in this great conference
So the title of my talk is "Improving Website Performance with CSS Containment"
So quite a long title even doesn't fit on the slide, so I will go over it slowly
So first, let's explain a little bit about me. I'm Manuel Rego
I'm part of Igalia Web Pplatform team. Igalia is an open source consultancy that
has a
Cooperative like structure, so we have people all around the globe
despite the headquarter are in Spain and
we have a large team working on browsers and we having implementing in several CSS
specifications and other things on browsers
I have been working on Grid Layout mostly for several years
And also other specs in Blink and WebKit and now I'm working also in the CSS Containment
And as part of all these work I having interacting more and more with the CSS Working Group people
Putting issues participating in discussions and finally joined the group like two years ago
So
Yeah the spec I mean the talk is about CSS Containment. This is a CSS spec
this spec has a property that is called "contain" and it has four values "layout", "paint", "size" and "style" and that's all
We are almost done
I mean, it's very simple in theory
But I think is going to be tricky to explain
Maybe you watched some of my talks about Grid Layout or read some of my blog post about it.
I mean
Grid has tons of properties, many different values for each of them and all that
But I believe it's easier to understand that maybe CSS Containment
So let's see if I manage to do it properly and you don't get very bored
So the other part of the title was improving website performance
So basically you can improve the performance of a website in many different parts
You can improve how images are loaded or the resources or whatever. In this case is more about the rendering pipeline
These are somehow the main phases from a very nice article by Lin Clark
So you have like where you compute the style you do the layout where you are
Positioning the things then you paint and then you composite the layers
And basically with CSS Containment you're going to be able to get performance benefits in this pipeline
So, let's go to the spec, the spec is now candidate recommendation
So it's quite a stable at this stage
And we will explain how it works and try to explain the different options and all that. So basically the main goal of
CSS Containment is to improve the performance of a website by
identifying the
independent parts of a website which are
parts of the DOM tree that doesn't have to interact with the rest, so somehow it's like isolating
that subtree from the rest of the page
In a way that if you do modifications inside that subtree
they don't get propagated outside so you don't need to do work in the
ancestors and all that
One confusing thing about containment when you first read about it or you hear about it is that
is not that things outside cannot affect the elements inside, is not top to bottom. But is the other way around
Things inside that element are not going to affect anything outside
So this is a sentence
By Tab Atkins, one of the spec editors, that he usually needs to repeat over a over in the CSS Working Group meetings
"contain" property doesn't block things going into the subtree, it blocks things leaking out
So it's quite relevant for all the containment stuff
So yeah, I already say that. This spec only introduces a new property, which name is "contain"
So it's very easy to remember and this property has four values, main values, or types of containments
layout, paint, size, style. You can just put one or put two combine or combine the four, you can do whatever you want there
style is red in the slides because it's market as at risk now in the spec, so we will talk more later about that
So imagine this is a website. You have some content, some text, the header, whatever and
you know that you have a component, maybe in the top left corner
For example that you are showing some live information that you are getting or showing
an animation or whatever, but you know that's totally independent from the rest of the page
So you identify that part, mark it like a contained element
And when the browser does work there, it doesn't need to care about the rest of the pages
The page is very big or very complex DOM tree or whatever
The browser doesn't need to spend any single time in doing other stuff
So basically what web authors will need to do is to identify
which are that independent parts of a website and mark them with the "contain" property and then
The browser engine needs to do
something because it's not just parse the property and
implement the restrictions but also it needs to implement some optimizations and avoid doing extra work. Browser engines usually
Try to do as less work as possible
When you do a change in a website, they have some kind of heuristics in order to achieve that
But still there are many corner cases and many combinations of things
So when you do a change on a single element that you believe is
Not going to affect anything else
The browser still needs to maybe
Check all the siblings just in case your overflowing content and you need to paint something in other parts of whatever
And when you use containment, you have some kind of restrictions or implications of using it
so you cannot just put "contain" to all the elements and you are done you need to
To put it when it makes sense
so there are
Like this four type of containers we are going to review
First one for example layout containment. Basically here that means that the internal layout of the isolated subtree
Is totally isolated from the rest of the tree. So the element where you put "contain: layout"
The layout of that element is totally independent of the ancestor
so these are the effects it takes to element. Any overflow on that element is treated as ink overflow flow
like shadows for example, I'll show an example later
That element becomes a containing block for absolute and fixed position and descendants
it also creates a new stacking context and independent formating context like display flow-root and
Also that the element has no baseline. So why these
Restrictions when you use it. So let's see an example of this ink overflow thing. We have here two
divs that one has
"contain: none" the other "contain: layout", is the magenta dotted border the element that has the "contain: none" or "contain: layout"
So if the size of the text is changing here you see that an
scrollbar is appearing on the parent element. So the browser
Something changes inside and needs to do work outside like painting this scrollbar
But with "contain: layout" it doesn't need to do anything outside
this is
treated as ink overflow, like shadows
You cannot access it actually
But I mean the browser can avoid doing any work in the parents checking if there are overflow and it needs to paint
to show a scrollbar like here
Similar is for baseline. I say that when you use layout containment the element is considered to have no baseline
So basically is the same, here in the "contain: none"
you see that the "foo" is aligned with the "bar" in the other one if use the
synthesized baseline that is the underline in this case, if the size changes inside the browser needs to move foo a
little bit below
however with "contain: layout" something changes inside but the browser
Doesn't care. I mean it can save time and avoid that kind of operations
So another type of containment is paint containment, which is pretty similar to "overflow: hidden," but also has some
Effects so basically the descendants cannot be displayed outside of the boundaries of that element
But again it has some effects, contents are clipped like with "overflow: hidden", but also it becomes the containing block for
positioned descendants/children, it also creates a stacking content and independent formatting context. So let's see another example
So we have "contain: none" again in the dotted magenta
element and "contain: paint" and we have an absolute positioned at the element that has
-100 pixels top and left. It has 200 pixels by 200 pixels size
So it's positioned against the parent, it means the viewport or whatever
And in the other case is positioned against the container element is moved a little bit left
and top and also
the
overflowing things are clipped
So when you change the color
Here the browser needs to paint also things outside that element but here the browser needs to paint
things inside
so again
If you have an element with content paint that is not in the viewport the browser can avoid
doing work on it doing, doing any painting because
the browser is sure that there are not
something overflowing that that is painted in the viewport or whatever, they know that they can save that the time
Size containment is about how the browser computes the size of an element
Basically, if you put "contain: size" is going to ignore the children
so the element is treated as having no contents, only things like padding add space or
column-gap if you have multi column or the tracks in grid something like that
you already defined, but not the contents itself. And also it's monolithic
That means that you cannot split it when you print it or it's in multi column or things like that.
So again, this is the same example again. So the first element
The size depends on the contents is "Hello World!"
and
when the contents change the size changes
but in this case, only is the padding the one that is defining the size of that element, if the
contents change it doesn't care. You see that it's overflowing
The thing is that when you combine this with the other ones, the browser can say, okay
I know for sure that nothing is going to change outside, even the size isn't going to affect anything outside and all that
And then last one is style containment
This is somehow special because it only affects counters and quotes.
I don't know if you use them a lot. I guess no because it's not very common, but
at least is on the spec and
It's currently market as at risk, because the scope is maybe too narrow only counter and quotes
So there are some discussions about if it's worth. Firefox for example is not implementing this so far
So that's another reason why is at risk in the spec? Because they don't see the benefit
And also chroming implementation of style containment is pretty broken so only works for the very simple cases
so if any of you find a use case that
Can get performances benefit with style containment share with the CSS Working Group at the GitHub repository
Because that will be valuable information to know if we need to keep or not this value
so basically the effects are that the counters and quotes are scoped to the subtree and
That when you try to increment a counter you actually are creating a new one instead of incrementing it
So basically, just an example with counters
This is a class that is incrementing this counter "n" and this is just an element that paints the counter
So we are here creating this counter "n" and increment it to 1, then we print it is just 1
then we have a
"contain: style" element, so this counter inside cannot increment the one outside so it creates a new one
so we have here 1.1 and
When we print it after outside of the contained element, this doesn't know anything of what happened inside with the counter
So that's what's isolated, so still 1 again. So that will be the expected output
Then with quotes, quotes are I think even a
less common use case
So you can define
Which symbols are you using when you have
nested quotes. So this is double first and then single or whatever and then we are opening a quote here
We have "foo" then we have the style containment element and we open another quote
we have the text "bar" and then we close
the quote. When we close this, it doesn't know that a new one was opening inside the style containment element
So it just closes the initial opening
We are not closing both in this case, but they mean basically that the changes here doesn't affect anything outside
So, okay
We have aliases in the spec so you can say "contain: content" and that implies layout and paint
Or you can say "contain: strict" and that implies layout, paint and size
This used to include style too, but like style is market at risk and Firefox was planning to ship
CSS Containment, it was removed from the spec at this stage. If in the future style is implemented by all the browsers probably
There will be a new alias like "all" or whatever that will include style too
So let's just check the status of things
This is not a new spec. Maybe it's the first time you've heard about it
But if has been shipping for three years already in Chromium, so it's not an new spec at all
and Google has been using it in their products like YouTube or Gmail, even in the Chrome Dev Tools, things like that
But during past year there has been quite a lot of work in the in the spec
Florian Rivoal one of the spec editors has been working quite a lot
in pushing the spec to candidate recommendation
Also the test suite in Web Platform Tests repository has been completed by Gérard Talbot and with contributors from Mozilla and Chromium
while we were working in the implementations
We at Igalia having fixing the implementation in Chromium. So it's aligned with the spec and we can start to implement optimizations based on that
And that was causing us to be breaking most of the sites
I mean, we broke YouTube for a while
we broke Gmail, we broke Dev Tools several times because maybe they were using "contain: layout" for example in many places and now
the overflow is treated as ink overflow so you can access it, things like that, or now that is a containing block for
the positioned descendants, so everything is positioned wrongly, so you need to or stop using contain layout or
Change how things work inside that element
and
Then Firefox is also working on the implementation
They are targeting Firefox
69 to ship it, but you can already test it behind the runtime flag
And meanwhile Chromium has been implementing some optimizations based on css-contain
So like I say you can just parse the property in the browser implement the restrictions and you are
passing all the tests of CSS Containment so you are totally spec on client
But maybe you don't have any optimization in the browser so is not useful at all at the end
So then when the browser has that information it needs to still implement some
optimizations
and
If we look at "Can I use" like Microsoft is moving to Chromium/Blink
it's getting support for this, all the Chromium browsers has support for this, and Firefox is about to ship in a few releases and
and then regarding Safari/WebKit there are no public signals yet
So let's show some examples to try to understand this a little bit better
This is one example from a blog post I published about
Containment is very simple and not cool at all. But I believe it's the best want to understand how how it works
so imagine that we have a very big DOM tree, like ten thousand elements like this item that has all of them have
one child and
We are modifying the content of that child over and over or whatever. So we have a DOM tree like this
We have many many elements here, but we are only doing changes in this green
div
But still the browser needs to do work in the rest, not a lot of work maybe just
Traversing the whole DOM tree to do some checks or whatever but it's doing work in all of them, so we can say okay
This has "contain: strict". We cannot affect anything outside. We are just changing things inside here, the browser can avoid
Navigating the rest of the things
So this example is, I mean not cool at all, is changing the the text in the first
element
it actually goes from changing is taking six milliseconds without containment to take
0.2 milliseconds with "contain: strict". So it's like a
quite a bit difference
This is another example is the same one, but I'm using grid layout in the items because I know that
grid layout has all these track sizing algorithms. So it's
somehow costly
if we think about performance, so it takes a while and
On the left we have Chromium on the right we have Firefox
that's because I was crafting this example for this talk and
I realize it we were not getting very good results in Chromium
So I was checking Firefox and Firefox was much better and it makes actually sense the results
We are getting in Firefox
For example with these 10,000 items in Chromium is like 12 times faster with "contain: strict"
which is quite a lot but in Firefox is like
2,700 times faster, but that makes sense because if you need to visit
10,000 grid containers and do layout on will on each of them and all that, or whatever you need to do
Instead of just doing it in one. It's suspected that it's very very fast when you just have this
strict containment in that element, that you just do it there
So
We have been working on this at Igalia as part of our ongoing collaboration with Bloomberg in which we implemented
CSS Grid Layout in the past years or we have been implementing things in JavaScript like async/await or BigInt lately
and
Bloomberg has some quite complex UIs and then we're getting benefit of using CSS Containment in some of them
So this is one of the stress cases we had this is like a huge
table like layout or grid like layout. It doesn't have to be a table or grid
It doesn't matter but it has like 10,000 cells or whatever and the text is changing constantly
but we actually know that we are just changing one number by another number
that the number is going to fit in the size we are providing, we know that for sure
So we don't need
We can say to the browser. Okay, this element has
"contain: layout size" and you can avoid doing the layout of the
text you are changing there ,because if you are adding a long sentence or whatever the browser needs to do line breaking, all that and
Calculate some things, so we are saying okay
You can save all that time if we have "contain: layout size"
because we know that we are just changing one number way another and it's going to fit inside and
Then this stress test with 10,000 elements. We were like four times faster using containment. So it's a nice optimization
This is the still to ported to LayoutNG, which is new thing that Chromium is shipping really soon. So
It will be like similar things
This was another one pretty similar again very big table or whatever. Here are some of the
Progress bars are moving are changing size. We're not changing the contents like that. But something is changing size inside each of them
But the thing is that in the use case each of these cells were absolute positioned. So we have like
Each of them has its own layer
So the browser when something was changing inside one. It was traversing the whole layer three
and depending on the size of the DOM, that was taking a while in some phases and then we "contain: strict"
and a very big stress we get like 10% faster. It's not a lot but it is, I mean, everything helps
Probably we were respecting more, but maybe there are room for more things yet
So let's talk about the future of all these
We can expect better browser support Firefox is shipping really soon. We don't know Safari but
We hope that, I mean, we never heard complaints from Apple about this spec. So it seems
Maybe at some point they will have it
Other thing that we can expect is more optimizations based on css-contain
Basically when people are start to use it, they will say okay like this example, I just did in Chromium
Ok, this will be much faster than what we are seeing. I mean I'm changing just one element
It should be almost as fast as having just one element in the DOM despite I have a ton of
10,000 elements or whatever
So you can report a bug and probably the browser will try to
improve that use case with some
optimization because maybe, I mean this is somehow it's not new because it has been there for a while but
There has not being a lot of work
So there are still a lot of room for improvement here in a lot of optimizations to be done in the browser
And also there are some new features
or proposals that some browsers are working on, that are based on this spec
So, I don't know if you have heard about Display Locking proposal from Google. Google is
experimenting with this and have some experimental implementation already in Chromium
Main idea of display locking is that in a DOM tree you can lock an element of the DOM
Then you do manipulation in JavaScript you modify
Whatever you need there without blocking the main thread and then when everything is ready you unlock it and show it in the screen
But for that you need that the element is independent of the rest and that's where containment
comes to the rescue. And I mean you mark that element with "contain" and then you can do that
so things like this
in the future will appear maybe display locking is not the the feature that is finally standardized
But it's one of these ideas
And that's all from my side. I hope you understand a little bit better
Now what this spec is about you can start experimenting with it and report bugs about it
in my blog you can read
I just only have one article about this but maybe in the future
I will have more, I've been writing articles about grid and things we are working on for a while
And I will be around the whole day. Feel free to talk to me about CSS Containment, CSS Grid, what it takes to implement a
CSS spec in a browser or things like that or even ask me about Igalia and the things we do
So, that's all. Thank you