button          { background: gray; }
.action         { background: darkBlue; }
[type=“submit”] { background: darkGreen;
                  background: var(--submit, black); }
#send           { background: maroon; }
* including Editor’s Draft proposals
<button> Needs
One Background-Color Color Border-Top-Left-Radius color,border-top-left-radius,removes extra values
fills in missing values
0+ declared values)0|1 cascaded value)1 specified value)1 computed value)1 used value)1 actual value)


(Read it from the specification)
.short { border: thin double; }
.long {
  border-top: thin double;
  border-left: thin double;
  border-bottom: thin double;
  border-right: thin double;
  border-image: none;
}
border-imageBorder is shorthand for setting all of them.short { border: thin double; }
.long {
  /* declared */
  border-top-width: thin;
  border-top-style: double;
  /* reset to 'initial' value */
  border-top-color: currentColor;
  border-image: none;
}
width, style, and color of eachcurrentColorborder-image which is reset-only here


property: value;


in transitions and animations
hotPink
 »
  teal
hotPink
  rgb(93 138 220)
?!
  teal
hotPink and tealbutton {
  background: teal;
  transition: background 1s;
}
button:hover { background: hotPink !important; }
:target { animation: bg-change 3s ease-in both; }
@keyframes bg-change {
  from { background: pink; }
  to { background: cyan; }
}
animation property is in the cascade!important keyframes [present, debug]Just to restate:
<img src="..." width="160px" height="90px" hidden>
<s>strikethrough</s>
width, or height, or hidden[hidden] { display: none !important; }

styling web components
Context in the cascade/* shadow DOM styles */
:host(my-element) { outline: thin solid red; }
[part=title] { outline: thin dashed red; }
::slotted(span) { outline: thin dotted red; }
/* light DOM styles */
my-element { outline: thick solid green; }
my-element::part(title) { outline: thick dashed green; }
my-element > span { outline: thick dotted green; }
similar to user agent origin
!important to protect essential styles,and reverses priority
(demo if needed)
<button style="
  background: mediumVioletRed; /* winner! */
  color: white !important; /* winner! */
  border: thin solid;
">…</button>
button {
  background: white;
  color: teal !important;
  border: thick dashed !important; /* winner! */
}

@layer settings { … }
@layer tools { … }
@layer generic { … }
@layer elements { … }
@layer objects { … }
@layer components { … }
@layer overrides { … }
demo:
@layer { … }
  @layer <name> { … }
  initial, inherit, unset, revert, revert-layer, etc@layer reset {
  audio[controls] { display: block; }
  [hidden] { display: none !important; }
}
@import url(…) layer; @import url(…) layer(<name>);
  @layer reset { /* least powerful */ }
@layer default { /* … */ }
@layer theme { /* … */ }
@layer components { /* more powerful */ }
/* unlayered styles: most powerful */
@layer reset { /* least powerful */ }
@layer components { /* more powerful */ }
@layer reset { /* still least powerful */ }
@layer <name>, <name>, <etc>;
  <style>/* keep this before linked styles */
@layer reset, framework, components, utilities;
</style>
<link rel="stylesheet" href="…">
<link rel="stylesheet" href="…">
!important
  
-> Layers -> Importance
  @layer reset {
  [hidden] { display: none !important; }
}
* { /* universal */ }
p { /* type */ }
.summary { /* attribute */ }
#call-to-action { /* id */ }
* { /* universal */ }

button, div, span {
  /* 'type' (element) */ }
::before, ::after, ::part() {
  /* 'pseudo-element' */ }

.action, .summary {
  /* 'class' */ }
:hover, :user-invalid {
  /* 'pseudo-class' */ }
[type=“submit”] {
  /* 'attribute' */ }

#send { /* 'id' */ }

h1#page-title { /* … */ }

p.summary a:hover { /* descendant */ }
p.summary > a:hover { /* child */ }
p.summary + a:hover { /* next */ }
p.summary ~ a:hover { /* future */ }

h2:has(> button[aria-expanded="false"]) + div {
  display: none;
}
div that comes after an h2button child, that is not expanded)/* 1 IDs, 2 attributes, 2 element type */
/* specificity: 1,2,2 */
form#contact button[type='submit']:active {
  border-color: seaGreen;
}
border-color declaration has a selector1, 2, 2
[1,0,2] vs [0,3,2] vs [0,2,3][0,3,2] vs [0,2,3]Especially “At Scale”
(still meaningful and expressive)
#example { /* [1,0,0] */ }
[id="example"] { /* [0,1,0] */ }
.example { /* [0,1,0] */ }
.example.example.example { /* [0,3,0] */ }
:is() and :where()
Select The Same Things
  :is() and :where() selectors/* (a) AND ALSO (nav .active) */
a:where(nav .active) { color: black; }
a:is(nav .active) { color: black; }
active class inside a nav element)/* (h1 a) AND ALSO (main > *) */
h1 a:where(main > *) { color: black; }
h1 a:is(main > *) { color: black; }


:is() and :where()
Can Group Selectors
  h1 a:hover, h1 a:focus,
h2 a:hover, h2 a:focus,
h3 a:hover, h3 a:focus,
h4 a:hover, h4 a:focus,
h5 a:hover, h5 a:focus {
  text-decoration: underline;
}
:is(h1, h2, h3, h4, h5) a:where(:hover, :focus) {
  text-decoration: underline;
}
:is() and :where()
Also Impact Specificity
  :where()
Removes Specificity
  /* nav a.active { 0,1,2 } */
nav a:where(.active) { /* 0,0,2 */ }
a:where(nav .active) { /* 0,0,1 */ }
*:where(nav a.active) { /* 0,0,0 */ }
:is() takes
Highest Specificity
  :is(a, .b, #c .d) { /* 1,1,0 */ }
a:is(.b, #c .d) { /* 1,1,1 */ }
a:where(#logo, .sponsor .logo) {
  /* specificity: 0,0,1 */
}
a:is(#logo, .sponsor .logo) {
  /* specificity: 1,0,1 */
}
<a class="sponsor logo">Still 1,0,1</a>
a:is(#fakeID, .sponsor .logo) { /* … */ }
Also relies on :is() selector

header {
  h1 { /* header h1 */ }
  > .title  { /* header > .title */ }
  + p { /* header + p */ }
}
header {
  h1 { /* header h1 */ }
  > .title  { /* header > .title */ }
  + p { /* header + p */ }
  body & { /* body header */ }
}
.card {
  @media (width > 20em) {
    display: flex;
  }
}
ol, ul {
  > p { /* :is(ol, ul) > p */ }
  .sidebar & { /* .sidebar :is(ol, ul) */ }
}
:is() under the hood/* :is(button, .btn, #my-btn) */
button, .btn, #my-btn {
  &:focus,
  &:hover,
  &:active { /* 1,1,0 */ }
}
:not() and :has()
  :is() & :not() & :has()
Use Same Specificity
  :not()
For Excluding Elements
  (inside matches are removed from outside matches)
/* (p) UNLESS (.warning)  */
p:not(.warning) { /* … */ }
:has()
For Selecting Context
  (we can move the subject!)
form:has(:focus) { /* form with focus */ }
button:has(svg) { /* button with icon */ }
:has() Selectorform:focus-within { /* form with focus */ }
.card:has(> figure:first-child) { /* image card */ }
.card:not(:has(img)) { /* card without image */ }
input:has(+ .error) { /* input followed by error */ }

article:has(h1, .title) a {
  color: red;
}
article h1 a {
  color: green;
}
Rolling out this year!


(across large teams & projects)
(through lower boundaries & proximity)
.light-theme a { color: purple; }
.dark-theme a { color: plum; }
@scope (<root>) { … }
  @scope (.light-theme) {
  a { /* similar to simple nesting… */ }
}
@scope (.dark-theme) {
  a { /* but the _closer_ scope root wins… */ }
}
.title { /* global */ }
.post .title { /* nested */ }
.post__title { /* BEM */ }


BEM, CSS Modules, Vue, JSX, Stylable, etc
.post__title { /* BEM */ }
.title[data-JKGHJ] { /* Vue */ }
@scope (<root>) to (<boundary>) {…}@scope (.media) to (.content) {
  img { /* only images that are "in scope" */ }
}
<article>
  <style scoped>
    p { color: green; } 
  </style>
  <p>This paragraph will be green.</p>
</article>
<p>This paragraph won't!</p>
<article>
  <style>
    @scope { p { color: green; } }
  </style>
  <p>This paragraph will be green.</p>
</article>
<p>This paragraph won't!</p>
Always child or descendant
@scope (.block-name) to (.block-content) {
  .title { /* only inside the block! */ }
}
button {
  &:hover { background: hotPink; }
  &[aria-pressed=true] { border: thick solid teal; }
  @media (width > 30em) { padding-inline: 1em; }
  & + & { border-inline-start: 1ch; }
}


button {
  background: red;
  background: oklch(0.5 0.2 0);
}
oklch() color syntaxAnd that’s the cascade, in 7 steps…
for each property of each element


