When browsers apply styling to a website, they have to determine a single value for every CSS property on every DOM element in the page.
Every individual button,
every span,
and every paragraph
has an accent-color
and align-content,
and a z-index,
and a zoom,
and so on.
Even if we don’t write a line of CSS, every element has all these styling properties attached.

We can see them all in the browser dev tools under computed styles. Every browser has an option to show all the CSS properties –

To filter just the properties that have been declared explicitly. But that doesn’t make them less real!
There’s something like 650 properties – and every one of those properties must have a single value to render the page.
If we inspect a <span> element
on an un-styled page,
we’ll see that
there are no CSS rules applied –
but we can still read the full list of properties,
and see the value used for each one.

We can also load the first website, which was published several years before CSS was even proposed.
The browser loads an HTML page, and creates a DOM tree of the elements. And each element comes with all these properties, already defined before any CSS is applied. And the browser can render that page, without any additional instructions!
(Browser styles are being applied, but there are no document styles)
* { box-sizing: border-box; }
button { background-color: orange; }
article {
background-color: teal;
background-color: deepPink;
background-color: OddBird;
}
When we do write CSS and attach it to the document, or when the browser adds element defaults, or the user changes their preferences – we now have multiple (potentially conflicting and sometimes invalid) values on some of the properties.
So before rendering, the browser has to get us back where we started: filtering down to what’s relevant and valid, cascading to resolve conflicts, defaulting whatever properties are leftover, and then resolving all the values in relation to each other.
Filtering has several parts, and they’re mostly straightforward. The browser is starting from an element in the DOM, and going through each property to find relevant CSS declarations.
To be relevant, a declaration has to be–
In a stylesheet that applies to the document.
Right? That seems obvious.
<link rel="stylesheet" href="print.css" media="print">
That’s mostly just looking at the media attribute.
If a stylesheet is only for print media, and we’re looking at a screen, we can ignore that stylesheet –
But we also have to look at conditional rules inside the stylesheet. Not in a false condition.
/* conditional rules must be 'true' */
body {
+ @media (width >= 0) {
+ background: green;
+ }
- @media (width < 0) {
- background: red;
- }
}
To avoid the double-negative here:
if a declaration is inside a condition
(like at-media or at-supports)
then the condition has to be true,
or we can ignore everything inside.
Similarly, we can ignore anything in a selector that doesn’t match the element.
/* selectors need to match! */
+ * { box-sizing: border-box; }
- button { background-color: orange; }
+ article {
+ background-color: teal;
+ background-color: deepPink;
+ background-color: OddBird;
+ }
0+ declared valuesSo if we’re looking for styles
relevant to an article element,
we can ignore everything in the button selector.
Fair enough!
Styles only apply to the elements we select –
/* conditional rules must be 'true' */
body {
+ @media (width >= 0) {
+ background: green;
+ }
- @media (width < 0) {
- background: red;
- }
}
and under the conditions we specified!
I think that’s what we would expect?
And finally, the syntax must be valid.
example {
color: blue; /* <color> */
opacity: 1; /* <number> or <percentage> */
aspect-ratio: auto; /* auto or <ratio> */
}
CSS is a strongly typed language. Every value has a data type. There are colors, numbers, lengths, percentages, strings, ratios, keywords, and so on.
It’s a long list of types, and many of them are design specific – like colors and lengths. CSS is a design language! Others – like strings and numbers – might be familiar if you write Javascript or a more general purpose language.
opacity syntax…
<number> | <percentage>
And every CSS property has a syntax that only allows specific value types in specific combinations.
Opacity must be either a bare number (without any units) or a percentage.
/* 1em is a <length> (invalid here!) */
example {
- opacity: 1em;
}
If we provide a value that doesn’t match the expected syntax for the property, then the entire declaration will be ignored.
article {
display: grid;
display: grid-lanes;
color: teal;
color: light-dark(teal, powderblue);
}
This can be really useful!
When a new feature comes along
like grid-lanes or the light-dark() function,
we can simply apply both –
/* new syntax filtered out by older browsers */
article {
display: grid;
- display: grid-lanes;
color: teal;
- color: light-dark(teal, powderblue);
}
And let old browsers filter out the new syntax. It’s a great way to handle simple progressive enhancements. But it doesn’t work for everything.
.iacvt {
color: var(--my-variable);
}
We can apply a variable to the color property,
and that’s allowed, right?
I mean,
at this point we don’t know if it will work –
we don’t know if my variable has a value,
or if that value is a color –
but this syntax sure seems valid.
.iacvt {
--my-variable: 2em;
}
And it’s also valid syntax to put anything we want inside a variable. It could contain a length, and that’s still fine.
.option-1 {
--my-variable: 2em; /* length */
}
.option-2 {
--my-variable: hotPink; /* color */
}
And maybe this variable sometimes has a color, but sometimes it has a length. During the filtering step, we’re not sure which of those will apply to the element!
.option-1 {
--my-variable: 2em; /* length */
}
.option-2 {
--my-variable: hotPink; /* color */
}
.iacvt {
color: var(--my-variable); /* 🐈 */
}
We’re not ready to start resolving all the variables yet, we’re just looking at the syntax – so we have to trust for now that the variable could be valid, and leave it alone.
.iacvt {
--my-variable: 2em;
color: var(--my-variable);
}
You might look at this and know it’s going to fail, but the browser knows we’re only getting started. Someone could still come along and override that variable with something better.
So this syntax is entirely valid! And it will not get removed as part of the filtering process.
Because variables are just properties, and go through this process like any other property, browsers won’t know if they are valid where used until we get all the way through filtering, cascading, defaulting, and resolving.
var() function
Are not evaluated at parse time
So declarations that rely on a variable are not evaluated during the filtering process. We just assume they’re valid!
Even though they might become invalid later, at computed value time. People will call this invalid at computed value time behavior.
And it changes how we have to handle fallbacks.
var() function if() function --custom() function
Are not evaluated at parse time
This isn’t just about variables any more.
It applies to anything dynamic in CSS –
like the new if() function,
or custom functions.
(It’s ok if you haven’t used any of that, neither one is supported across browsers yet.)
/* filter out the invalid color */
article {
background-color: teal;
background-color: deepPink;
- background-color: OddBird;
}
0+ declared valuesGoing back to our earlier example,
we can filter out the invalid
OddBird background color –
but that still leaves two remaining background colors,
teal and deepPink.
Both are valid colors in a selector that matches our element. Both are in an applied stylesheet, and neither one is inside a false condition.
/* browser and user styles are merged as well */
* { box-sizing: border-box; }
article {
background-color: teal;
background-color: deepPink;
+ display: block;
}
Any browser and user styles
also get added to the list.
In addition to our background colors,
the browser has also provided display: block on articles.
Filtering is done,
and we’re left with four
“declared values” of the article element,
describing only three properties.
We still have a conflict on the background-color
that needs to be resolved.
Since there’s still a conflict after filtering is complete, those two values are put through the cascade to determine a winner.
We’ll talk about the cascade in depth tomorrow, but the goal is to narrow it down even farther, and get a single winner.
The cascade is the “merge” step of the process, where things like layers, importance, specificity, and source order are used to resolve conflicts.
That might be conflicts between us and the browser defaults, but it might also just be conflicts within our code.
/* the cascade ensures a single winner */
article {
- background-color: teal;
+ background-color: deepPink;
}
0 or 1 cascaded valueIn this case the source-order is all that matters, and the second style wins.
* { box-sizing: border-box; }
button { background-color: orange; }
article {
background-color: teal;
background-color: deepPink;
background-color: OddBird;
}
So we’ve gone from the styles we wrote – four different background colors and a box-sizing value, on three different selectors –
* { box-sizing: border-box; }
article {
background-color: deepPink;
display: block;
}
To a single background color, our box-sizing, and an additional display value that was provided by the browser.
article {
box-sizing: border-box;
background-color: deepPink;
display: block;
/* color? padding? margin-inline-end? */
}
1 specified valueThat’s only three properties with “cascaded” values,
out of 650 total properties.
What about the color, and the padding,
and the margin-inline-end?
And this is where defaulting steps in.
Some properties will inherit a value from the parent element – ~165 inherit by default – and all the rest will reset to their initial value.
Defaulting is only needed on properties where nothing valid was explicitly specified – where nothing made it through the cascade.
I think often we know this intuitively, but I’ve seen it surprise even the most experienced CSS experts. We can get so focused on ideas like specificity and importance, that we forget the cascade is only one part of a larger process.

We start from a tree of un-styled elements,

Then attach styles to those elements, using inline styles or selectors – and resolving conflicts in the cascade. But only conflicts that are on the same element. The cascade doesn’t get involved if one selector is styling the root, and another selector is styling paragraphs.

By the time we get to defaulting and inheritance, the cascade is already over, so things like specificity and importance are no longer relevant.
Only the boxes and properties without a cascaded style will need to inherit a style instead.
So it doesn’t matter how much importance we use when
setting root color to teal.
If we also set the body color to deepPink,
then that’s the color we’ll see.
The body element already has a color applied,
with no cascading conflicts,
and we don’t need inheritance to go find one.
We can see that teal is applied to the root,
it’s just not inheriting
because directly applied styles
will always override defaulting styles.
But even the largest stylesheets on the web can’t set all the properties on all the elements – no one is writing out all that code!
So most properties will use a default value.

Define some constraints. Let the language work out the details.
And that’s a good thing. We want to rely on the defaults anywhere we can. This is one of the most common mistakes people make in CSS – and a mistake that large language models tend to make even worse.
You really want to do as little as possible in CSS, because leaving extra code around – even code that doesn’t seem to do anything – can have surprising consequences down the road!
That’s what the next lesson is all about.
So these steps – filtering, cascading, and defaulting –
find all the relevant declarations in all the relevant stylesheets –
merge them together, resolving any direct conflicts –
and then fill in the blanks, if anything is missing.
Which gets us back where we started –
Every property on every element has a single value.
And then the browser runs another series of steps to apply these styles and determine what it all means.
What’s the actual result of applying this style configuration to the page?
First resolving each value based on its type – lengths can be converted to pixels, and colors convert to RGB. Then formatting the page to see how properties interact, so we can determine things like percentages and grid fractions and flex values in context. And constraining the results to what can actually be rendered, like rounding to the nearest pixel.
What are the suggestions?
How do they fit together?
I think of value processing in two groups. The first set of steps – filtering, cascading, and defaulting – are all about merging and resolving conflicts to get a single value for every property. Once we have that, the second set of steps – resolving, formatting, and constraining – apply those styles to the page.
We have tools that behave differently at every step, but we don’t control the steps involved, or the order that things will happen.
We’re not writing the program in the traditional sense, we’re writing a configuration that will be merged with other configurations, and then used by the program when it runs.
But the program runs the same way, following the same steps, with or without our input.
I don’t mean that CSS isn’t real programming or something. That’s an absurd debate, and a waste of time. “Real” programming isn’t a real thing, it’s only a phrase used for starting fights. So let’s move on.
I think this value resolution process is often where authors get tripped up and confused, especially coming from other programming languages. Because it’s a complex series of steps that our code will have to go through before it applies to the page.
A lot can happen along the way! Styles rarely make it through unchanged.
Even if that declaration survives the cascade, what it means will depend on other properties – and even other elements!
So the meaning of a property – like the meaning of a word – always depends on context.
This is most clear when we’re using
‘relative’ units like percentages,
ems, or line-height (lh) units.
The font-relative units will change
when the font-size changes –
even if that change is inherited from a parent
or ancestor.
And percentages act differently depending where they are used – but always relative to something else, like the inherited font-size, or the width of the parent content-box.
These interactions are also part of the value resolution process – especially the resolving and formatting steps.
At this point the browser knows what styles have been requested.
We have a single specified value for every property, on every element.
So now the browser has to figure out how all those specified styles interact with each other, and how to render the page based on that information.
This is a bit like solving a puzzle, following a provided set of rules –
.feature {
font-size: 200%;
width: 30ch;
}
If the font-size is set to 200%,
a browser needs to find the inherited font-size,
and then multiply it.
If the specified width is 30 characters,
then a browser has to resolve the font-size first,
along with the font-family –
to find out what font is being used –
then check the font metrics to find out the width of a character
at the current font-size,
and multiply that by 30 to get the width.
That’s all part of resolving
the width to a px value,
but the browser still has to consider box-sizing
to determine how the width is applied
when formatting the page.
All those steps have to happen in the correct order, or we would get the wrong answer. And we want every browser to get the same answer, so these interactions – and the order of operations – have to be defined clearly in the specification.
.square-or-widescreen {
/* any two are possible, but not all three */
aspect-ratio: 16/9;
width: 100px;
height: 100px;
}
But our styles don’t just interact, sometimes they come into direct conflict! And the browser has to find a solution – some way of rendering the page – even when we request styles that would be impossible to achieve.
There’s no way to draw a square box, with equal height and width, that also has a 16-over-9 aspect ratio! So if we set the width and height explicitly, and then provide an aspect ratio, the browser has to ignore one of these properties in order to lay out the page.

If you’ve ever used tools for 3d modeling – I know most of you probably haven’t – there’s an approach called parametric design, which works a lot like CSS. These tools have a helpful way of visualizing what constraints have been added, throwing errors when they come into conflict.

Here’s a polygon made out of four lines with constraints at each corner ensuring they stay attached.

If we want a rectangle, there are several ways we could get there. We could say that the sides across from each other have to be parallel. That’s a reasonable constraint. But it can still squish.

If we make two of the sides perpendicular – adding a right angle – the points are all still free to move, but now the relationships are constrained to ensure we always have a rectangle.

We can also add things like a horizontal constraint to lock the orientation – a height and width to maintain dimensions – and attach one corner to the ‘source’ of the layout.
At this point, our box is fully constrained. There’s only one ‘solution’ possible that achieves everything we explicitly requested, and no lines or points are free to move around. To make any changes, we have to alter or remove those constraints.

If we try adding new constraints, we get an error! Even if we’re asking for a constraint that works with the existing solution, like another perpendicular angle.
The tool is encouraging us to choose what we care about most, and avoid unnecessary constraints.

But there are other ways we could achieve the same result. If we remove the parallel constraint, and add three right angles, we’re back where we started.
There are multiple ways to achieve the same design, depending on what we consider the most important. This isn’t the only way to do 3d modeling, but the advantage is how maintainable these designs are. If we set up the constraints to match our goals, it becomes easy to adjust one dimension or angle, and watch everything else adapt.
CSS is built on the same idea. By documenting all the important relationships and constraints in a design, we make it responsive – not only to the size of the browser, but to changes we might make in the future!
Responsive CSS is maintainable CSS, and responsive components are modular.
When a component is constrained only in the ways we care about, but remains free to adapt in every other way – that component can move from one context to another without issues.
.square-or-widescreen {
aspect-ratio: 16/9;
width: 100px;
height: 100px; /* error: over-constrained! */
}
Each declaration that we write is a constraint on the element. And that’s great when the constraints are expressing our goals. But browsers don’t have good ways to warn us that an element is over-constrained.
Instead, some rules have to get priority over others.
Generally,
rules that are more explicit or direct
(like width and height in this case)
will override rules
that are a bit more conceptual
like the aspect-ratio –
which just describes a relationship
between the width and height.
Even if we change the aspect-ratio to 1/1,
a value that works with the others,
we’re still over-constraining the layout.
Since the width and height are already defined,
the aspect-ratio has no open parameters to fill in –
it’s not doing anything.
At this point it’s not hurting much, but the more explicit we get – the more constraints we add – conflicts between properties can multiply at a much faster rate. By leaving unnecessary constraints in our code, we’re inviting unexpected problems down the road.
.square-or-widescreen {
aspect-ratio: 16/9;
width: 1600px;
height: 900px;
}
So looking at code like this, I would always ask what’s most important to me, and remove any constraints that I can. Our goal is to establish the right priorities to maintain a design across different contexts – including the potential for other styles to apply that might be out of our control.

Not just making it work on my device, but communicating clearly what it is we care about in this collaborative context.

Sadly browser tools don’t really show us these conflicts when they come up. Because there’s not a simple on/off switch for any of the properties. It makes this difficult to debug.
But it’s still an important issue to watch for! If one property isn’t applying the way you expect, try turning other properties on and off to see what changes.

I wish browsers would provide more tools like the Firefox inspector for flexbox, which shows exactly how the final layout is determined.
But sometimes we want one property
to constrain another.
Properties like min- and max-width only exist
to put constraints on the width property.
If we define all three,
the min and max values will take priority
whenever there’s a conflict.
And min-width will override max-width
if those two conflict.
Some property interactions are less nuanced.
Many properties in CSS have different ways of working – or sometimes they don’t work at all – depending on what mode we’re in. And the mode might be controlled by another property, or even parent and sibling elements.
display property
Controls Layout Mode
The display property, for example,
often controls what layout mode –
or formatting context –
we’re in, which can change
how other layout properties behave.
.modal-width {
width: 500px;
}
So if we set an element to width: 500px
we might get a box that’s 500px wide –
but that’s not guaranteed!
If that element is inline,
then width doesn’t apply at all.
We also have a flex value
that’s not being applied,
because the parent element is
not a flex container!

In this case,
the browser dev tools
do show us
that some properties are being ignored.
We’d see something similar if we add
grid-template-columns or flex-direction
to an element that is not a grid or flex container.
But this only works when a property is entirely ignored –
width and flex have no way to impact this element.
But most modal changes are more subtle than that.

If the parent is a flex container,
then the flex value will be applied,
and so will the width –
but only as a starting point
that our flex value will override.
This is where Firefox dev tools can help explain exactly what’s going on.

Nothing is greyed out any more, and there are no warnings, but one of these properties is still being ignored!
Flex and grid items can’t be inline.
That value has no meaning here.
But the display property isn’t entirely ignored –
it could still get set to
display: none, display: grid, or display: contents for example.
Since it’s only the inline value being ignored,
and not the whole display property,
we don’t get any warning.
Dev tools aren’t designed with that level of nuance.
width: auto
If a width of 500px
is that context-dependent,
think how much more flexibility is allowed
when the width is auto.
How big is auto?!
That depends on the space available,
and the layout context,
and the size of the contents,
and the margins,
and sibling elements,
as well as the parent layout mode!
We write auto in our CSS file,
and out the other end we get
something like 1234.45px.
Everything flows.
We could be here all day, just talking about properties that change the meaning of another property.
box-sizing changes what width meanswriting-mode changes what direction inline-size refers toWe’ll talk about some of that when we get to layouts.
But for now it’s important to understand: the meaning of a property isn’t isolated or stable. Properties and values get their meaning from context, just like words take on different meanings in different sentences.
And that context can be other properties on the same element, but it can also be the context provided by parent and ancestor elements.
CSS isn’t defined this way just to be confusing. CSS is a design language, and the entire field of design is about managing relationships. Concepts like color and size and shape don’t exist in isolation!
A grid system isn’t a property of a single element, it’s a relationship between elements.
A typographic hierarchy relies on some text being larger or more bold than other text. No matter where we start, it’s the relationship between sizes that matter most – and the relationship those font-sizes have to the overall page.

A and B are both #787878Colors appear different to us based on context –
And the colors around them –

Issues like text contrast are based on those relationships, and also impacted by differences in font-size. There’s more than one way to make text stand out on a page.
form:has(:invalid) button[type="submit"] {
--invalid: light-dark(#ebe7dc, #242119);
background: var(--invalid);
color: contrast-color(var(--invalid));
}
So writing CSS is a process of showing our work – capturing the goals and relationships behind every design decision. What constraints matter in our desired solution.
So that as other variables change – browser dimensions, user inputs, color-schemes, etc – the browser will hopefully come to the same result that we would, without needing us to write out every single possible iteration by hand.

As a CSS developer, my job translating a design mockup often comes down to a single repeated question: what do we care about most?
I might see a box that is 593px wide.
But why? How did we get there?
Is it related to the size of the container,
or the size of the contents or siblings?
If we’re not the designer, hopefully we can ask them! I find it really useful to write out these questions, and pair-program with a designer – showing them how different approaches move in different situations.

The big difference between CSS and other constraint-based design programs is in the defaults. A page without any CSS attached already has responsive logic built-in.
Every property already has a default value, and those defaults add up to provide –

A baseline design system that might not always be beautiful, but is already extremely flexible – with responsive relationships built-in.
The fonts and font-sizes and colors respond to the user, to the device, to the browser. Text flows in lines, and wraps at the edge of the page – stacking to create blocks.
Everything is fluid. Everything is responsive. Everything can be adjusted by the user. There are just enough constraints to render the page.
The program runs fine without us.

Define some constraints. Let the language work out the details.
Then we come along and add more constraints! That’s not bad, that’s part of design. Adding our voice to the mix is the reason CSS exists.
We’re providing a point of view.
But the more constraints we add, the less flexibility is provided to the browser and the user. Ideally, we only want to say what’s essential, and let the browser work out the details.

Be the browser’s mentor, not its micromanager.
Or as Andy Bell says – “be the browser’s mentor, not its micro-manager.”
Sometimes
the order that properties apply
can make a big difference.
For example,
when we’re moving an element around
using translate, rotate, and scale.
If we’re doing this
inside the transform property,
we can put these functions in any order.
If we rotate and then move the element,
it moves in the rotated direction.
But if we move it first and then rotate,
we get a different result.
The same is true if we scale
before moving an element.
Now the movement is scaled along with the element.
.mover {
/* translate, then scale */
transform: translate(100px) scale(2);
/* scale, then translate */
transform: scale(2) translate(100px);
}
If we use the functions, we’re in control of the order they apply! Because all of this, all these functions in order, act as a single value – which will be maintained when (and if) the property is applied.
But if we switch to the stand-alone
translate, rotate, and scale properties –
the browser will always apply them
in a pre-defined order:
First moving, then rotating, then scaling the element.
And if we define all four properties,
the transform property is always applied last!
That pre-defined order is maintained, no matter what order we write our declarations in. That’s intentional! This order has the most predictable result – without allowing rotation or scale to change the direction or distance of the movement.
This is part of what it means
for CSS to be a declarative language.
We’re not writing a series of steps
to perform in order –
we’re only changing what value is used
for each property on an element.
And then the program runs, using some or all of the values we provided. We define values, but the browser defines how properties interact – how they apply, and in what order.
That’s different from most other programming languages, including JavaScript or pre-processors like Sass.
/* Sass Variables */
this-slide {
$background: red;
$background: yellow;
background-color: $background;
$background: hotPink;
$background: cyan;
}
background-color?Here’s a block of Sass,
that will output a background color
for the slide –
using a variable called $background.
First we set the variable to red,
then yellow,
then we apply it to the background-color CSS property,
and then change the variable to hotPink,
and finally cyan.
/* JS Variables */
let backGround = 'red';
backGround = 'yellow';
thisSlide.style.backgroundColor = backGround;
backGround = 'hotPink';
backGround = 'cyan';
background-color?We can write the same thing in JavaScript,
and follow the same series of steps.
After changing the variable from
red to yellow,
we apply it to the slide backgroundColor property –
the same property that we control from CSS.
What color should we expect the slide background to be?
--- the 'most recent' value is used
this-slide {
- $background: red;
$background: yellow; /* winner! */
background-color: $background;
- $background: hotPink;
- $background: cyan;
}
Both Sass and JavaScript are imperative programming languages, so the code is run in order – a series of steps.
The variable is yellow
at the point in that process
where it gets applied
to the background-color.
Changing the variable later in the program
doesn’t impact what already happened.
--- the 'most recent' value is used
this-slide {
- $background: red;
- $background: yellow;
+ $background: hotPink; /* winner! */
background-color: $background;
- $background: cyan;
}
If we change where the variable gets used, we get a different output.
The variable in this case has different values at different points in time as the program is run. And we control the timing.
But CSS doesn’t work like that.
this-slide {
--background: red;
--background: yellow;
background-color: var(--background);
--background: hotPink;
--background: cyan;
}
background-color in CSS???Let’s try this again with similar code,
but this time we’re just using CSS:
the variable is set to red,
and yellow,
applied to the background-color of the slide,
and set again to hotPink
and cyan.
Remember, a CSS variable is just a custom property, which works like any other CSS property. And, as we’ve discussed, In CSS every property has a single value on every element.
this-slide {
--background: red;
--background: yellow;
background-color: var(--background);
--background: hotPink;
--background: cyan;
}
background-color in CSS???So the value of our variable doesn’t change colors as the code is executed – first one color, and then another…
--- the cascade determines a winner
this-slide {
- --background: red;
- --background: yellow;
background-color: var(--background);
- --background: hotPink;
--background: cyan; /* winner */
}
background-color in CSS???Instead browsers use the cascade to determine a single value for each property, including the variable –
--- the cascade determines a winner
this-slide {
+ background-color: cyan;
- --background: cyan;
}
background-color in CSS???And then resolves any relationships between them.
It doesn’t matter when we apply the variable to the background color – because these are not steps in a program. We can declare the background first – before our variable has been mentioned at all – and it doesn’t change the outcome.
We don’t control the order in which things happen, we only control the value on each property.
How those properties apply is defined by the language, not by our code.
this-slide {
background-color: var(--background);
/* the cascade resolves conflicts! */
--background: red;
--background: yellow;
--background: hotPink;
--background: cyan;
}
background-color in CSS???If you’re thinking order does matter in CSS, you’re right! Code order is an important feature of the cascade. But that’s only used for resolving conflicts when the same property is defined multiple times.
this-slide {
background-color: var(--background);
--background: red;
--background: yellow;
--background: hotPink;
--background: cyan;
}
background-color in CSS???So the order of our code only matters…
this-slide {
background-color: var(--background);
--background: red;
--background: yellow;
--background: cyan;
--background: hotPink;
}
background-color in CSS???When one property has multiple values, and they need to be merged by the cascade to get us back to a single value.
Going back to the initial demo, then…
If I declare
the transform property multiple times,
with different values,
the cascade will prefer
the last one by default.
The order we write our CSS can be very important – we’ll cover that in more detail as part of the cascade – but the order of our code does not describe a timeline of actions. It only helps to resolve conflicts.
The timeline – the series of steps to apply – is already defined by the browser. And that program runs the same way with or without our input. All we can do is customize the settings.