HTML and CSS course ยท Module 9: Modern CSS
Modern CSS Features - The Future of Styling Egyptian Hieroglyphs
In this lesson12
Welcome to the future of CSS! Just as the ancient Egyptians were pioneers in architecture, we are witnessing a revolution in CSS. Discover modern CSS features from 2020-2024 that are changing the way we style websites!
aspect-ratio - Perfect Proportions
aspect-ratio allows you to easily create elements with maintained proportions without padding hacks!
Before aspect-ratio (the old way)
/* Old hack with padding-bottom */
.video-container {
position: relative;
padding-bottom: 56.25%; /* 16:9 = 9/16 = 0.5625 = 56.25% */
height: 0;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}With aspect-ratio (the new way)
/* Modern way - simple and readable */
.video-container {
aspect-ratio: 16 / 9;
width: 100%;
}
.video-container iframe {
width: 100%;
height: 100%;
}Egyptian analogy: Like the perfect proportions of the Great Pyramid (4:3) - maintained regardless of scale!
Practical aspect-ratio Examples
1. Responsive Video
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.video-wrapper iframe {
width: 100%;
height: 100%;
border: none;
}2. Image Cards
.card-image {
aspect-ratio: 4 / 3;
width: 100%;
object-fit: cover;
border-radius: 8px;
}3. Square Avatars
.avatar {
aspect-ratio: 1; /* 1:1 = square */
width: 100px;
border-radius: 50%;
object-fit: cover;
}4. Golden Ratio
.golden-box {
aspect-ratio: 1.618; /* Golden ratio */
width: 100%;
background: goldenrod;
}5. Instagram Post
.instagram-post {
aspect-ratio: 1 / 1; /* Instagram square */
width: 100%;
max-width: 600px;
}6. Mobile Screens
.phone-mockup {
aspect-ratio: 9 / 19.5; /* iPhone proportions */
width: 375px;
border: 10px solid black;
border-radius: 30px;
}gap for Flexbox and Grid
gap now works in both Grid and Flexbox! (since 2021)
/* Grid - always supported */
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* Row and column gap */
}
/* Flexbox - since 2021! */
.flex-container {
display: flex;
gap: 20px; /* Space between items */
}Practical gap Examples
/* Navigation with gap */
.nav {
display: flex;
gap: 1.5rem;
align-items: center;
}
/* Button group */
.button-group {
display: flex;
gap: 0.5rem;
}
/* Form fields */
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Card grid */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem 1rem; /* 2rem row, 1rem column */
}:is() and :where() - Selector Grouping
New pseudo-classes allow for elegant grouping!
:is() - with specificity weight
/* Instead of: */
h1 a,
h2 a,
h3 a,
h4 a,
h5 a,
h6 a {
color: goldenrod;
}
/* Use :is() */
:is(h1, h2, h3, h4, h5, h6) a {
color: goldenrod;
}Specificity of :is(): Takes the highest specificity from the list!
/* Specificity = ID (100) */
:is(#id, .class, div) {
color: red;
}:where() - without specificity weight
/* Specificity = 0! */
:where(h1, h2, h3) {
margin-top: 0;
}
/* Easy to override */
h2 {
margin-top: 1rem; /* This wins because :where() has 0 specificity */
}Practical Examples
/* Reset for headings */
:where(h1, h2, h3, h4, h5, h6) {
margin: 0;
font-weight: 600;
}
/* Link states */
:is(a:hover, a:focus) {
text-decoration: underline;
color: goldenrod;
}
/* Form inputs */
:is(input, textarea, select):focus {
outline: 2px solid #3498db;
outline-offset: 2px;
}
/* Lists */
:where(ul, ol):where(:not([class])) {
padding-left: 1.5em;
}:has() - The Parent Selector!
:has() is a revolution - it lets you style the parent based on children!
/* Style article that HAS an image */
article:has(img) {
display: grid;
grid-template-columns: 1fr 2fr;
}
/* Style card that HAS a button */
.card:has(.button) {
padding-bottom: 4rem;
}
/* Style form that HAS an error */
form:has(.error) {
border: 2px solid red;
}Practical :has() Examples
1. Layout Adaptation
/* Card with image - different layout */
.card:has(img) {
display: flex;
flex-direction: row;
}
/* Card without image - normal layout */
.card:not(:has(img)) {
text-align: center;
}2. Form Validation
/* Form field with error */
.form-field:has(input:invalid) {
border-color: red;
}
.form-field:has(input:valid) {
border-color: green;
}
/* Label when input has focus */
.form-field:has(input:focus) label {
color: #3498db;
font-weight: bold;
}2. Checkbox Styling
/* Style label when checkbox is checked */
.checkbox-wrapper:has(input:checked) label {
font-weight: bold;
color: green;
}
.checkbox-wrapper:has(input:checked) {
background: #e8f5e9;
}4. Navigation Highlighting
/* Highlight section in navigation */
nav:has(~ main #section-1) .nav-link[href="#section-1"] {
color: goldenrod;
font-weight: bold;
}5. Empty State
/* Style container when empty */
.list:not(:has(li)) {
display: flex;
align-items: center;
justify-content: center;
}
.list:not(:has(li))::before {
content: "No items found";
color: #999;
}subgrid - Nested Grids
subgrid allows children to use the parent's grid!
.parent-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.child-grid {
display: grid;
/* Use columns from parent! */
grid-template-columns: subgrid;
grid-column: span 2;
}Practical Example
<div class="product-grid">
<article class="product">
<h3>Product 1</h3>
<div class="product-details">
<span class="price">$99</span>
<button>Buy</button>
</div>
</article>
</div>.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
}
.product {
display: grid;
/* Align all .product-details on the same line */
grid-template-rows: auto 1fr auto;
}
.product-details {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1;
align-items: center;
}@layer - Cascade Layers
@layer allows you to control the cascade without specificity!
/* Define layer order */
@layer reset, base, components, utilities;
/* Layer reset - lowest priority */
@layer reset {
* {
margin: 0;
padding: 0;
}
}
/* Layer base */
@layer base {
body {
font-family: Arial, sans-serif;
}
}
/* Layer components */
@layer components {
.button {
background: #3498db;
color: white;
}
}
/* Layer utilities - highest priority */
@layer utilities {
.text-center {
text-align: center;
}
}@container - Container Queries
Container Queries allow you to style based on the size of the container, not the viewport!
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
padding: 1rem;
}
/* When container > 400px */
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
.card img {
width: 150px;
}
}
/* When container > 600px */
@container card (min-width: 600px) {
.card {
padding: 2rem;
}
}color-mix() - Color Mixing
.element {
/* Mix 50% blue with 50% red */
background: color-mix(in srgb, blue 50%, red);
/* Lighten a color */
background: color-mix(in srgb, #3498db, white 20%);
/* Darken a color */
background: color-mix(in srgb, #3498db, black 20%);
}accent-color - Coloring Form Controls
/* Change color of checkboxes, radio, range */
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
accent-color: goldenrod;
}
/* Globally */
:root {
accent-color: #3498db;
}Other Modern Properties
overflow: clip
.element {
/* Like overflow: hidden, but doesn't create a scroll context */
overflow: clip;
}inset - Shorthand for top/right/bottom/left
/* Instead of: */
.element {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
/* Use inset */
.element {
inset: 0;
}
/* Or per-side */
.element {
inset: 10px 20px; /* top/bottom left/right */
}object-view-box - Image Cropping
img {
object-view-box: inset(10% 20% 10% 20%);
/* Crops the image from all sides */
}content-visibility - Performance
.long-section {
/* Don't render outside viewport - HUGE performance boost! */
content-visibility: auto;
contain-intrinsic-size: 0 500px;
}Browser Support (2024)
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| aspect-ratio | 88+ (2020) | 89+ (2021) | 15+ (2021) | 88+ |
| gap in Flexbox | 84+ (2020) | 63+ (2018) | 14.1+ (2021) | 84+ |
| :is() | 88+ | 78+ | 14+ | 88+ |
| :where() | 88+ | 78+ | 14+ | 88+ |
| :has() | 105+ (2022) | 121+ (2023) | 15.4+ (2022) | 105+ |
| subgrid | 117+ (2023) | 71+ (2019) | 16+ (2022) | 117+ |
| @container | 106+ (2022) | 110+ (2023) | 16+ (2022) | 106+ |
| @layer | 99+ (2022) | 97+ (2022) | 15.4+ (2022) | 99+ |
Summary
Most important modern features:
- aspect-ratio - perfect proportions without hacks
- gap - spacing in Flexbox and Grid
- :is()/:where() - elegant grouping
- :has() - the parent selector!
- subgrid - nested grids
- @layer - cascade control
- @container - responsiveness at the component level
Why use them:
- Cleaner code
- Fewer hacks
- Better performance
- Easier maintenance
Code for this lesson: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Modern CSS - Project</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header>
<h1>Temple of Modern CSS</h1>
<p class="subtitle">Combine all the techniques in one project</p>
</header>
<main class="grid-layout">
<div class="card-container">
<article class="card">
<div class="card-media">Container Queries</div>
<div class="card-body">
<h2>Adaptive cards</h2>
<p>They react to the size of their container.</p>
</div>
</article>
</div>
<div class="card-container">
<article class="card">
<div class="card-media">:has() + Nesting</div>
<div class="card-body">
<h2>Smart selectors</h2>
<p>A parent styled by its children.</p>
</div>
</article>
</div>
<section class="scroll-section">
<h2>Scroll-snap gallery</h2>
<div class="snap-gallery">
<div class="snap-item" style="background:#d4af37">Scarab</div>
<div class="snap-item" style="background:#8b6914">Ankh</div>
<div class="snap-item" style="background:#1e3a5f">Eye of Horus</div>
<div class="snap-item" style="background:#b87333">Pyramid</div>
</div>
</section>
<section class="form-section">
<h2>Form with accent-color</h2>
<label><input type="checkbox" checked> Option 1</label>
<label><input type="range" min="0" max="100" value="70"></label>
</section>
</main>
</div>
</body>
</html>Remember: Modern CSS is like discovering new hieroglyphs - new possibilities, simpler solutions, more powerful tools!
Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What changed in support for the gap property since 2021?
2. What is the inset: 0 property in CSS?
These are 2 of 3 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Horizontal ordering
Arrange the valid overflow: clip declaration on one line: