This becomes the mission statement of the web –
web for all, web oneverything.
That includes assistive devices–
And non-visual media –
always with the end-user in control of theoutcome.
There are too many variables toconsider.
–
Keith J Grant
There are too many variables for us to consider,
so we hand some of that control over to the browser,
and to the user, and the device they’reon.
Graphic design of unknown content
on an infinite and unknown canvas,
across operating systems, interfaces, & languages…
–
me, on twitter…
Designing for the web is designing for change,
designing systems that respond and adapt
to unknown content on an infinite and unknown canvas,
across operating systems, interfaces, &languages.
Provide hints that the browser may or may notuse.
–
Håkon Lie
We provide hints and suggestions,
semantic clues,
but only the browser
can put it alltogether.
The Cascade
And the cascade describes that process–
An ordered list (cascade) of style sheets …
can be referenced from the samedocument.
–
Håkon Lie
by accepting style sheets
from everyone involved–
The user/browser specifies initial preferences
and hands the remaining influence over to thedocument.
–
Håkon Lie
Browsers&users establish
global defaults and preferences across the web,
and then we fill in the details
of our particularsite
Cascade Origins
🎨 Author (Document)
👥 User Preferences
🖥 User Agent(Browser)
These are the primary “cascade origins” –
each one representing a
different set of needs andconcerns,
The Cascade
Resolves Merge Conflicts
But these different perspectives,
can sometimes be in conflict.
So the rules of cascade & inheritance
describe how to merge all three origins,
and resolve thoseconflicts.
🎨 Author (Document)
👥 User Preferences
🖥 User Agent(Browser)
In most cases,
the user preferences will override the browser defaults,
and (for better or worse)
we’re allowed to override both ofthem.
If conflicts arise the user should have the last word,
but one should also allow the author to attach stylehints.
–
Håkon Lie
But when things really get heated,
when it really matters,
the user and browser can insist–
❗important
A balance of power
That some styles are
more important than others–
❗🖥 User Agent Important
❗👥 User Important
❗🎨 Author Important
🎨 Author Styles
👥 User Preferences
🖥 User AgentDefaults
Creating important origins
that cascade in reverse order.
Important author styles aren’t that special.
That’s us in the middle,
with our normal and important styles side-by-side.
But users can override us when they need to,
and the browser finally decides
what’s out of bounds,
what’s possible on this device,
what features are supported,
and soon.
2. StyleSheets
Make Styles Reusable
The second goal of CSS
is to make our design objectsreusable…
And Natalie Downe
helped to develop a systemic approach
to all of this:
describing fluid grids,
and re-usablepatterns.
And then browsers implemented Media Queries,
and Ethan Marcotte coined the term
“Responsive WebDesign”
Technical Ingredients…
Fluid grids (%)
Flexible images (%)
Media queries
Based on fluid grids, flexible images, and media queries.
Everything in percentages,
adapting to the size of the browser
at different media-query breakpoints.
It’s a really powerful approach,
and it took over the industry for years.
But now, while the idea of a responsive web,
that core vision,
is just as important as ever,
the technology has changeddramatically.
The web is still a fairly new medium,
and we’re rapidly developing new features
to make it more responsive, and more reusable.
Features like flexbox and grid
(which have been around for years),
and now also intrinsic sizing, aspect-ratios, andmore.
We’re entering a new era of web design,
and web layout,
and we need to update how we work and how we think
to really take advantage of these new features.
You might say it’s an evolution,
not a revolution, but it’s an important shit to understand.
What Jen Simmons calls–
Intrinsic Web Design
Intrinsic Web Design.
Rather than stripping out the
intrinsic size of every image,
and building 12-column percentage-basedgrids,
Responsive & ReusableComponents
we now have the power to make designs
both responsive and reusable at a component level,
building patterns and layouts
that are based on the content itself.
Taking that intrinsic information,
and building systems out ofit.
Fluid&Fixed
Stages of Squishiness
Truly Two-Dimensional Layouts
Nested Contexts
Expand&ContractContent
Media Queries, AsNeeded
These new tools allow us to
create responsive layouts that mix fluid and fixed components.
We can go beyond fluid percentages
to use flex values, grid fractions, minmax, clamp,
and other tools to create stages of intrinsic squishiness –
components growing and shrinking at different rate.
We get truly two-dimensional layouts,
nested contexts can use different layout rules,
and content can expand and contract in various ways.
Rather than designing outside-in with media queries,
we can spend more time designing from the contentout!
Our medium is not done.
Our medium is still
going through radicalchanges.
–
Jen Simmons, Designing withGrid
We’ve come a long way,
but we’re still not done.
CSS is a living language going through radical changes,
and many of the new features build on thisidea.
What’s Painful
About Intrinsic Design?
We’re looking at the pain points that teams run into
when they’re working on intrinsic components,
and asking how we can address those issues.
I’ve been working on
three of these new specifications in particular,
and want to show you what’scoming.
CSSSelectors&Specificity
So let’s jump back to the cascade for a minute,
and selectors specifically.
The cascade is so central to CSS
that it doesn’t get a lot of updates,
but over the years it’s starting to show someage.
Like the cascade origins,
selectors create another potential conflict
for the cascade to resolve.
Since we can use multiple different selectors
to target the same element–
Selector Specificity
The cascade needs to determine a winner,
so it uses a clever heuristic –
an educated guess –
calledspecificity.
*(universal)
type
.class&[attr]
#IDs (single-use)
We assume that each selector type is meant to
represent a different goal or perspective,
based on how narrowly that selector has beentargeted.
universal/type »
Global Defaults
The most generic selectors,
help us paint in broad strokes
to establish low-priority defaults–
attrs/classes »
Common Patterns
Classes and attributes
allow us to describe
higher-priority patterns,
that make up the majority of our styles–
ID/style »
Singular Overrides
Then one-off ID’s are both
the most narrowly targeted,
and the highestpriority.
Unique#IDs
Reusable.classes &[attributes]
Elementtypes
Universal*
We can combine these selectors in various ways,
but their specificity is always compared
one “layer” at a time.
Selectors with an ID will always override
selectors without an ID,
and so-on down thelist.
This is a rough approximation
of the layers in our code –
moving from global abstractions
to narrowly targeted components and overrides.–
Heuristics Can 💥Fail💥
But it’s notperfect.
Especially “At Scale”
As our projects become larger and more complex
with more distributed teams
and third-party integrations,
there are a lot of situations
that don’t fit the rule–
So that brings us to our first new feature:
CascadeLayers.
This will allow us to create our own
custom layers of the cascade,
that more explicitly represent
the different parts of a system –
and potentially different teams on a project,
or even third-party code.
You can think of these as
customizable layers of specificity–
Stacked in Layers
Components?
Themes?
Frameworks?
Resets?
Or like our own custom CSS Origins –
but for things like resets, defaults,
frameworks, themes, components,
utilities –
anything we want,
in whatever order weneed.
❗Important Resets
❗Important Themes
❗Important Components
Components
Themes
Resets
And the important flag works as intended.
Inverting the layers
when it becomes necessary for a lower layer
to insist on something,
and punch above it’sweight.
@importurl(headings.css)layer(default);
We can define a layer,
give it a name,
and add styles to it
using either a layer function on the import rule–
– Or both. Here we’re creating a “default” layer
with the headings.css import,
and using the at-rule
to add a few more styles to the same
“default”layer.
Layers stack in the order they were first defined,
with the first layer at the bottom,
and the last layer at the top.
The highest layer will win conflicts,
no matter what specificity is used
for the selectorsinside.
So this single menu-item class wins over
the combined menu, dropdown, and item classes,
because the override layer is defined
after the framework layer.
This makes it simple to override a tool like bootstrap,
no matter how they write theirselectors.
@layer default{/* … */} @layer theme{/* … */}
/* still a lower layer than "theme" styles */ @layer default{/* … */}
But we don’t need to keep all our styles in that same order.
Once a layer has been established,
we can add to it from anywhere in our code.
The priority is based on when the layer name
firstappears.
@layer default; @layer theme; @layer components;
@layer default{ *{box-sizing: border-box;} }
We can even use the at-layer rule without any styles,
just a name,
to establish our order up front.
After that,
we load the code in any order,
and it will justwork.
That’s especially important if we’re using CSS-in-JS,
where styles might load in anyorder.
@layer default, theme, components;
@layer default{ *{box-sizing: border-box;} }
And there’s a shorthand syntax to make that even easier,
using a comma-separated list of layernames.
One of the goals here
is to make it so that we as authors
get to define exactly where
third-party tools belong
in our layering.
No matter what specificity those tools use internally,
or whatever layers they create,
we can always override them
without resorting to specificityhacks.
Either directly,
or by wrapping those layers into a contained namespace.
We can create or access
“nested” or “name-spaced” layers
using a dot-notation to combine thenames.
Or we can actually nest the layer rules –
it works the sameway.
More Cascade Control
This gives us a lot more control
over our corner of the cascade,
so we’re not totally reliant
on selector specificity
and code-order to determine
what takes precedence.
We have control over thecascade!
Fewer Hacks
Hopefully that allows us to replace
all our specificity & importance hacks
with more clearly definedpatterns.
Of course,
we don’t have to put all our styles into these layers –
and for the sake of progressive enhancement,
we likely want to start adding layersslowly.
Un-layered styles will work the same way they always have,
and belong to an implied “base layer”
below all theothers.
Possible to Polyfill
Do we also need @supports (layers) conditional?
We should be able to polyfill layers
with existing specificity tricks,
to make it work on old browsers if needed.
We could also consider adding an at-supports
feature test if that seemsuseful.
I’m sorry to interrupt you, Miriam,
we have breaking news from the future.
Your future.
But still pre-recorded,
so also still the past,
for anyone watching this.
The more recent past?
My… previous future?
Whatever, time is hard.
It’s a week after I recorded the initial talk,
and there’s some breaking news toreport!
Safari Technology Preview:
» Develop/Experimental Features (menu) » CSS Cascade Layers
» (#133 has unlayered styles reversed, likely fixed in#134)
So:
did I say Chrome & Safari are working on implementations?
That’s still true,
but now Firefox has jumped ahead,
and actually released a public prototype
of cascade layers behind a flag!
We can go to about:config, search for cascade-layers,
and turn that on to start testing itout!
At time of recording,
Chrome has a runtime flag,
which is less “public” a bit harder to use,
but I’ve left a link to instructions here in my slides.
Again, that will probably change by the time you watch this.
So it’s a good thing future-future Miriam is in the chat with you,
and can hopefully provide you with any updated links.
Miriam, do the thing,please.
Since we have a prototype now, we should check it out,right?
Layers are meant to help with overall style architecture,
which makes it hard to capture a real use-case
in a single, concise demo.
But let’s start with something real basic,
to see some layers in action.
In this case we’re just showing that
even low-specificity selectors in our override layer
take precedence over high-specificity styles
that are notlayered.
Demo: We can take that a bit farther
by adding a second layer,
and then using the layer-sorting statement
to re-arrange which one takes precedence.
We can also add !important flags here,
if we want to see how that flips thepriority.
If you saw my talk
from the Spring Summit –
Beyond Variables –
it was all about using custom properties
(CSS variables).
And I showed this demo
with a set of buttons,
where everything comes down to the order of our styles.
But let’s say we want a more robust solution,
so that the order doesn’t matter asmuch?
And I showed a way
that you can stack custom properties
so that the disabled state property
always overrides the button theme property.
Well now we can do that same thing withlayers…
So now that we have all three major browser engines implementing,
this spec is pretty stable.
We’re not expecting any major changes before this comes out –
and it could roll out in all the major browsers prettyquickly.
So I just want to take a second
and highlight that process.
How we got here.
And how every step along the way is public,
and something you can participate in.
It all start on the csswg-drafts github,
and that’s where most of the actionis.
It all started with a proposal.
I had no idea how this feature should look or work,
I just documented the problems with specificity
and suggested:
hey, what if authors had a better way to control this?
That lead to a lot of conversation,
both in the issue thread,
and on CSS Working Group calls
(which are then transcribed and posted back to theissue).
This was all before I joined the group,
so I was relying on those transcripts in order to participate.
There was some excitement, and also some concerns,
so they asked me to work with a few people
and flesh out a more detailed proposal
with actual syntax.
Again, we did that, and reported our progress back to the issue.
Post a draft, get feedback, make adjustments,repeat.
After a people were happy with the direction,
we moved that into an Editor’s Draft specification.
This is like an official working branch for specs,
that you plan to merge once it’s been reviewed,
and everyone agrees on thechanges.
At some point we had enough detail,
and enough agreement,
that the CSS Working Group approved a Working Draft.
The first one is called a First Public WorkingDraft.
Over the last year, we’ve been editing and refining:
make changes in the Editor’s Draft,
then review and publish those changes as a new Working Draft.
Back andforth.
At the bottom of the document
you can often see issues that have been documented,
sometimes with links back to Github.
The whole process is happening inpublic.
Once browsers start implementing the new feature,
they also start to create Web Platform Tests.
These is a test suite shared by all the browsers,
the entire web platform,
to test against.
It’s like caniuse, but fewer browsers and more details.
You can run tests in your current browser
by going to wpt.live,
or see a report of the test status
in major browser engines at wpt.fyi,
or learn more and get involved at web-platform-tests.org.
One of the best ways a web developer can truly help
make websites work the same in every browser
is to help create more tests for WPT.
We all want interoperabilty.
Testing is a way to get there, and not regress.
But we need more tests written to check on morethings.
The next feature is also about how selectors work.
With “scope”,
we’re trying to address two issues
that come up regularly,
and drive people to use tools & conventions
like BEM syntax orCSS-in-JS.
1. AvoidNaming Conflicts
(across large teams&projects)
The first goal is to avoid
naming conflicts as our projectsgrow.
2. By
Expressing Membership
(through lower boundaries&proximity)
Which we can solve
by focusing on our second goal:
expressing “membership” or “ownership”
in ourselectors.
.title{/* global */} .post .title{/* nested */}
While nested selectors
might seem like a way to
express membership –
in this case
a title that is inside a post–
.title{/* global */} .post .title{/* nested */}
.post__title{/* BEM */}
That’s not quite the same thing
as a post-title.
The first one only describes a nested structure,
but the second describes
a more clear membership in a component pattern.
Not all the titles in a post,
just the title that belongs to thepost.
.post__title{/* BEM */} .title[data-JKGHJ]{/* Vue */}
We don’t have a good way to convey that
using our current CSS selectors,
unless we invent a new unique name
for every kind of title,
based on what it belongs to –
either manually using a convention like BEM,
or automated with JavaScriptcompilers.
<h2class="title post__title">
And if we want some global title styles,
we end up using multiple classes –
and hoping the more targeted pattern will override
the globalpattern.
Another way to think about this is
to say that some components
have lower boundaries –
the component itself is a “donut”
with a hole in the middle for content.
We should be able to style a tab component,
or a media-object,
without worrying that we might accidentally
style everything inside it bymistake.
Different from
Shadow-DOMEncapsulation
This might sound similar to shadow-DOM encapsulation,
and there is certainly cross-over
between scope &encapsulation.
But the Shadow-DOM is designed
around highly-isolated widgets.
Boundaries are defined in the DOM,
so that each element has a single scope,
and styles are isolated from getting in or out.
Scopes are never allowed to overlap atall.
Build-tools
Provide Scoped Styles
BEM, CSS Modules, Vue, JSX, Stylable,etc
While that kind of encapsulation is useful sometimes,
it’s very different from the lighter-touch
“scope” that we get from existing
build-tools and conventions–
Where scopes reference the DOM,
but they’re more fluid –
able to overlap,
and integrate more smoothly
with global design systems.
Different scopes can have different boundaries,
and global styles continue to applyglobally.
This provides us with a much lower-impact alternative.
Scopes are defined in CSS,
and can be re-used across components,
or overlap & cascadetogether.
@scope(.media) to (.content){ img{/* only images that are "in scope" */} }
So we’re proposing an at-scope rule,
that accepts both a scope-root selector
(in this case media)
and a lower-boundary selector
(in this case content).
Any selectors inside the at-rule
only apply between the root
and the lower-boundary.
In this case we’re styling images inside media,
unless they are also inside the mediacontent.
We can also talk about this
in terms of proximity.
These two selectors apply to links
inside a light-theme or dark-theme class.
And that works great,
as long as we never nest one theme inside the other.
Since our selectors both have the same specificity,
and ancestor proximity is not part of the cascade–
dark-theme will always override light theme
in nestedsituations.
@scope([data-theme=light]) to ([data-theme]){ a{color: purple;} } @scope([data-theme=dark]) to ([data-theme]){ a{color: plum;} }
We can solve that problem using lower-boundaries,
so that themes never bleed into each other–
But I think it would also make sense for
scope proximity to be added as part of the scope feature.
When specificity is equal,
we would default to using the “closer” scope-root.
This part of the spec is still beingdebated.
A Donut Selector?
There has also been talk about adding
some form of lower-boundary or donut selectorsyntax.
@scope(.media) to (.content){ img{border: red;} }
/* as a selector, without proximity rules? */ img:in(.media / .content){border: red;}
That could be useful for some cases,
but wouldn’t have the same power
to handle proximity relationships
or wrap multipleselectors.
There’s a lot more to the proposal,
which you can look into if your interested.
The CSSWG has expressed interest,
feedback is welcome,
and Chrome plans to prototype this soon,
for moretesting.
Same element in multiple containers,
viewport isn’tuseful
Respond to containersinstead
No matter how those containers arenested.
Layout Loops
CSSContext vs Content
But trying to measure a “container” in CSS,
and then make changes based on that measurement,
poses a bit of aparadox.
One of the coolest responsive features in CSS,
which we don’t talk about nearly enough,
is the way we calculate layout
based on both context and content.
Add more content,
and a container will try to grow,
but it might also be constrained by context,
or explicitsizing.
That’s very cool,
but if you add container queries,
it becomes an infinite loop:
as the container gets larger, we make the content smaller,
which makes the container smaller,
which makes the contentlarger.
2010-2020
🚧 Laying Foundations 🚧
So for a long time,
this seemed impossible to implement.
But behind the scenes,
a lot of people
have been laying the groundwork inbrowsers.
Last year two proposals emerged,
showing different ways we might pull this off.
Both are interesting,
but David Baron’s approach has the most momentum right now,
and I’ve been working on it
to flesh out some of the details,
and start writing aspecification.
Defining Containers
The first thing we need to do
is define our containers–
Any element we want to measure,
query, and respondto.
No Content Sizing
In order to avoid any layout loops,
we need to turn off content-based sizing
on those elements.
Our containers need to be sized
without reference to anything insideit.
.container{ contain: size layout style; }
We already have a property for this!
It’s called contain,
and allows us to “contain” various types ofthings.
Size containment turns off content-based sizing,
layout containment is kinda like a clearfix –
wrapping around floats and margins –
and style containment keeps list-counters
from leakingout.
And we’re going to need all three of these
for our container querieswork.
2D size containment
Is Too Restrictive
But size-containment is…
bad in most cases.
It’s just not possible to build all our layouts
with explicit width andheight!
We need one axis to be fluid,
and respond to content,
so that we don’t create accidentaloverflow.
We needInline Size Containment
And usually we want to contain the width,
or the inline-dimension,
and allow the height to grow or shrink
with the content.
That’s pretty standard web-layout bestpractice.
.container{ contain: inline-size; }
So we’re adding an option to make
single-axis containment possible.
Contain inline-size.
This isn’t easy,
and it’s not totally solved, either.
You can find more details
in the linkedissue.
There are absolutely dragons lurking here,
but we still have hope that it’s possible.
The current prototype side-steps these issues,
in order to show the most common behavior –
it’s a proof of concept, not a finishedproduct.
.container{ contain: block-size; }
We’re not sure if we can also support
a block-size value.
That would raise even more problems,
but we’ll work on it.
Again, this works in the prototype
by side-stepping theissues.
Instead of specifying all the containment required,
we just say what type of container we want –
or what we want to query.
In this case we want to query the inline size.
Browsers can take that,
and apply the right containment
in thebackground.
Warning!
Not a Stable Spec (Yet)
This changed recently,
so some articles and demos
might still use the oldsyntax.
And the spec is still in active development,
so it could changeagain.
Querying Containers
Once we have containers,
we can begin to querythem!
A container-query
looks exactly like a media-query,
but with at-container instead of at-media.
And each element will query
the size of it’s nearest ancestorcontainer.
Container’s can’t query themselves.
That’s ensures there are noloops.
<divclass="container"> <divclass="container"> <divclass="container"> We can nest containers! </div> </div> </div>
In some cases,
like inside flexbox or grid,
there is no outside container
that will tell us the actual space available
for each item.
But we can get around that by adding a container
around each component –
in this case div.card is wrapping each article.
The outer div establishes a container,
and the inner article can queryit.
Max Böck has created this bookstore demo
with self-contained web components.
Each component host element is a container,
and everything inside the component
adjusts based on availablesize.
Una Kravets combines a number of different
named containers to create this responsive card,
with a responsive button,
and a responsive icon.
The named containers allow her to reference
not just the most immediate context,
but look higher up in the DOM
to measure whatever container is mostrelevant.
Of course, we can also get creative!
Jhey Tompkins made these interactive blinds
that get smaller as the container gets bigger.
Because CSS doesn’t have to be practical
to beawesome.
We’re also working on container-relative units,
similar to vw, vh, vmin, vmax,
but a percentage of the container size
rather than theviewport.
Sorry, sorry,
future-past, past-future Miriam here again,
with more breakingnews!
🚨 Try Query Units Today 🚨
Query units are now also supported in the Chromeprototype!
Why CQ?
Because CHAlready Exists
You might also be asking why we went with q instead
of, maybe, c for container.
But we already have a “character unit” called ch,
so we couldn’t use that for “container height”.
Sometimes you’re doing the best you can
working around legacy code.
If you have other ideas,
this isn’t set in stone yet,
feel free to jump in and make suggestions
on the githubissue.
[demo]
… Ok, back to past-past Miriam in three, two…
More to do…
Non-size Queries
And we’re working on queries
that aren’t about the containersize.
@containerstyle(--colors: invert){ … }
We might be able to query the actual value
of a property on the container,
and change internal styles based on thatproperty.
@containerstate(is-stuck){ … }
Or check if our container is position-sticky,
and currently in a “stuck”state.
Both of these should be possible,
but we haven’t worked out all the detailsyet.
Coming Soon
with a polyfill
If we can solve the inline containment issues,
this could start rolling out in browsers
by the end of the year,
and Jonathan Neal is already working on a JavaScript polyfill
to make it backwardscompatible.
@container(width > 30em){/* CQ support */}
@supports not (container-type: inline-size){ @media(width > 40em){/* no CQ support */} }
We can also use the at-supports rule,
to create fallbacks natively in CSS.
This is a great opportunity for progressiveenhancement.
All of these features are designed to worktogether
Building on
Existing CSS
Building on the existing features of CSS–
Building on
The Cascade
And the cascade
that holds it all together–
Already make…
Styles Responsive
But particularly the overlap
between the two main goals of CSS:
to make responsive–
Already make…
Styles Reusable
and reusable styles.
Building components that are inherently responsive–
Intrinsic Web Design
building responsive components inCSS
and intrinsic.
not forcing everything to be an exact percentage
on a 12-column grid –
but allowing for different components
to manage their own intrinsic sizing and layouts and styles.
Sometimes fluid, sometimes fixed,
but always responsive to the overall needs of thepage.
Our medium is not done.
Our medium is still
going through radicalchanges.
–
Jen Simmons, Designing withGrid
We’re not done,
and you, as web designers and developers,
are part of this process –
helping the web achieve it’s potential.
I can’t wait to see what you all build with this new tech,
and I’m always eager to hear
what else we can do
to make the medium even moreresponsive.