What's New with CSS?
What's New with CSS?
CSS has changed!
Native Nesting
.parent {
background: chartreuse;
.child {
background: lemonchiffon;
}
}
/* Renders out the same as */
.parent {
background: chartreuse;
}
.parent .child {
background: lemonchiffon;
}
.parent {
background: chartreuse;
& .child {
background: lemonchiffon;
}
}
.feed {
.card {
background: chartreuse;
h2 {}
ul {
&.tags {}
&.share {
svg {
color: rebeccapurple;
}
}
}
}
}
.card {
background: rebeccapurple;
.feed & {
background: chartreuse;
}
}
/* Is the same as */
.card {
background: rebeccapurple;
}
.feed {
.card {
background: chartreuse;
}
}
Pseudo Selectors
&:has() {}
&:where() {}
&:not() {}
&:is() {}
<div class="block">
<p>This list has <span class="count"></span> item<span class="plural">s</span></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<div class="block">
<p>This list has <span class="count"></span> item<span class="plural">s</span></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</div>
.block {
--items: '1';
.count {
&::before {
content: var(--items);
}
}
.plural {
display: none;
}
&:has(ul li:nth-child(2)) {
--items: '2';
.plural {
display: inline;
}
}
}
.block {
&:has(ul li:nth-child(3)) {
--items: '3';
}
&:has(ul li:nth-child(4)) {
--items: '4';
}
&:has(ul li:nth-child(5)) {
--items: '5';
}
/* And so on */
}
<div class="field">
<p class="error">Error: you must agree to the Terms and Conditions</p>
<input type="checkbox" id="agree" name="agree" />
<label for="agree">I agree to the terms and conditions</label>
</div>
.field {
&:has(input[name="agree"]:checked) {
.error {
visibility: hidden;
}
}
&:has(input[name="agree"]:not(:checked)) {
outline: 5px solid var(--red);
background: var(--red_light);
}
}
<div class="block">
<p>This list has <span class="count"></span> item<span class="plural">s</span></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<div class="block">
<p>This list has <span class="count"></span> item<span class="plural">s</span></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</div>
.block {
--items: '1';
.count {
&::before {
content: var(--items);
}
}
.plural {
display: none;
}
&:has(ul li:nth-child(2)) {
--items: '2';
.plural {
display: inline;
}
}
}
.block {
&:has(ul li:nth-child(3)) {
--items: '3';
}
&:has(ul li:nth-child(4)) {
--items: '4';
}
&:has(ul li:nth-child(5)) {
--items: '5';
}
/* And so on */
}
<div class="field">
<p class="error">Error: you must agree to the Terms and Conditions</p>
<input type="checkbox" id="agree" name="agree" />
<label for="agree">I agree to the terms and conditions</label>
</div>
.field {
&:has(input[name="agree"]:checked) {
.error {
visibility: hidden;
}
}
&:has(input[name="agree"]:not(:checked)) {
outline: 5px solid var(--red);
background: var(--red_light);
}
}
<div class="theme dark">
<p>This section has a dark colour scheme</p>
</div>
<div class="theme light">
<p>This section has a light colour scheme</p>
</div>
<div class="theme">
<p>This section's colour scheme will be based on the system settings</p>
</div>
<div class="theme toggle">
<p>This section's colour scheme will change based on the theme toggle</p>
</div>
.container {
&:has(.toggle input[value="dark"]:checked) {
.theme.toggle {
color-scheme: dark;
}
}
&:has(.toggle input[value="light"]:checked) {
.theme.toggle {
color-scheme: light;
}
}
}
.theme {
background: light-dark(lemonchiffon, var(--navy));
color: light-dark(var(--navy), lemonchiffon);
color-scheme: light dark;
&.dark {
color-scheme: dark;
}
&.light {
color-scheme: light;
}
}
<div class="theme dark">
<p>This section has a dark colour scheme</p>
</div>
<div class="theme light">
<p>This section has a light colour scheme</p>
</div>
<div class="theme">
<p>This section's colour scheme will be based on the system settings</p>
</div>
<div class="theme toggle">
<p>This section's colour scheme will change based on the theme toggle</p>
</div>
.container {
&:has(.toggle input[value="dark"]:checked) {
.theme.toggle {
color-scheme: dark;
}
}
&:has(.toggle input[value="light"]:checked) {
.theme.toggle {
color-scheme: light;
}
}
}
.theme {
background: light-dark(lemonchiffon, var(--navy));
color: light-dark(var(--navy), lemonchiffon);
color-scheme: light dark;
&.dark {
color-scheme: dark;
}
&.light {
color-scheme: light;
}
}
<div class="box old">
I am centred
</div>
<div class="box new">
I am also centred, but with one line of CSS
</div>
.old {
display: flex;
align-items: center;
}
.new {
align-content: center;
}

<div class="box old">
I am centred
</div>
<div class="box new">
I am also centred, but with one line of CSS
</div>
.old {
display: flex;
align-items: center;
}
.new {
align-content: center;
}

<div class="container">
<div class="card">
<img src="/img/image.jpg" alt="" />
<h2>Building a Custom Link Shortener</h2>
<time>11 Jul 2024</time>
<ul class="tags">
<li>dev</li>
<li>tools</li>
<li>eleventy</li>
<li>github</li>
<li>netlify</li>
</ul>
<ul class="share">
<li><svg>...</svg></li>
<li><svg>...</svg></li>
</ul>
</div>
<div class="card">
<img src="/img/image.jpg" alt="" />
<h2><a href="/accessibility-for-everyone" target="_blank">Accessibility for Everyone (and by Everyone)</a></h2>
<time>23 Feb 2024</time>
<ul class="tags">
<li>dev</li>
<li>accessibility</li>
</ul>
<ul class="share">
<li><svg>...</svg></li>
<li><svg>...</svg></li>
</ul>
</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 200px auto 1fr auto;
}
.card {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: subgrid;
grid-row: span 4;
gap: 10px;
grid-template-areas:
'image image'
'date .'
'title title'
'tags socials';
}
.card {
img {
grid-area: image;
}
h2 {
grid-area: title;
}
time {
grid-area: date;
}
.tags {
grid-area: tags;
}
.share {
grid-area: socials;
}
}

<div class="container">
<div class="card">
<img src="/img/image.jpg" alt="" />
<h2>Building a Custom Link Shortener</h2>
<time>11 Jul 2024</time>
<ul class="tags">
<li>dev</li>
<li>tools</li>
<li>eleventy</li>
<li>github</li>
<li>netlify</li>
</ul>
<ul class="share">
<li><svg>...</svg></li>
<li><svg>...</svg></li>
</ul>
</div>
<div class="card">
<img src="/img/image.jpg" alt="" />
<h2><a href="/accessibility-for-everyone" target="_blank">Accessibility for Everyone (and by Everyone)</a></h2>
<time>23 Feb 2024</time>
<ul class="tags">
<li>dev</li>
<li>accessibility</li>
</ul>
<ul class="share">
<li><svg>...</svg></li>
<li><svg>...</svg></li>
</ul>
</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: 200px auto 1fr auto;
}
.card {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: subgrid;
grid-row: span 4;
gap: 10px;
grid-template-areas:
'image image'
'date .'
'title title'
'tags socials';
}
.card {
img {
grid-area: image;
}
h2 {
grid-area: title;
}
time {
grid-area: date;
}
.tags {
grid-area: tags;
}
.share {
grid-area: socials;
}
}


<label for="field_status">Lego Set Status</label>
<selectlist id="field_status" name="status">
<button type="selectlist">
<span class="sr-only">Select Tags</span>
<selectedoption></selectedoption>
</button>
<listbox>
<div class="options-container">
<option value="" hidden>
<span>Select Status</span>
</option>
<option value="assembled">
<img src="img/assembled.jpg" alt="" />
<span class="text">Assembled</span>
</option>
/* More options */
<option value="unreleased">
<img src="/img/new.jpg" alt="" />
<span class="text">Unreleased</span>
</option>
</div>
</listbox>
</selectlist>
selectlist {
button {
&[type="selectlist"] {
padding: 0.5em;
border-radius: 10px;
margin: 0;
}
}
.options-container {
display: flex;
flex-wrap: wrap;
}
}
option,
selectedoption {
display: grid;
border-radius: 10px;
cursor: pointer;
gap: 2px;
}
option {
&:hover,
&:focus {
background: var(--purple_bright);
color: var(--white);
}
&[hidden] {
display: none;
}
}

