HTML and CSS course Β· Module 9: Modern CSS
Modern CSS Functions - Part 2
In this lesson4
A photo carousel that stops exactly on the next image, a form in brand colors, a lighter variant of gold on hover: not long ago each of these tasks meant a JavaScript library or calculating shades by hand. In the second part of our journey through modern CSS, we'll discover more powerful tools: scroll-snap, @supports, accent-color and color-mix(). Each one solves a problem that previously required JavaScript or complicated workarounds, just as archaeological discoveries in Egypt reveal increasingly advanced techniques of ancient builders.
Scroll Snap - Snapping While Scrolling
Scroll Snap is a CSS mechanism that allows you to create a snapping effect to specific points during scrolling. With it, you can create carousels, galleries and full-page sections without using JavaScript.
Imagine a corridor in a pyramid with successive chambers: scroll-snap makes the view automatically stop at each chamber. You need two properties: scroll-snap-type on the container that scrolls, and scroll-snap-align on its children:
1/* Container with scroll-snap */
2.gallery {
3 display: flex;
4 overflow-x: auto;
5 scroll-snap-type: x mandatory;
6 /* mandatory - always snaps to nearest point */
7 /* proximity - snaps only near a point */
8 gap: 1rem;
9}
10
11/* Each element snaps to the start */
12.gallery-item {
13 scroll-snap-align: start;
14 /* Options: start, center, end */
15 flex-shrink: 0;
16 width: 300px;
17}
18
19/* Full-page sections with vertical snap */
20.fullpage-container {
21 height: 100vh;
22 overflow-y: auto;
23 scroll-snap-type: y mandatory;
24}
25
26.fullpage-section {
27 height: 100vh;
28 scroll-snap-align: start;
29 display: flex;
30 align-items: center;
31 justify-content: center;
32}
33
34/* Scroll padding - offset for fixed headers */
35.container {
36 scroll-snap-type: y mandatory;
37 scroll-padding-top: 80px;
38 /* Accounts for header height */
39}scroll-snap-type: x mandatory means horizontal scrolling with a mandatory stop at a snap point, and scroll-snap-align: start puts that point at the beginning of each item. The container itself must scroll, which is why it has overflow-x: auto. I recommend proximity for long content because mandatory always forces a stop at a snap point, and with long or uneven sections it gets in the way of free reading.
@supports - Detecting Browser Capabilities
The @supports rule (also known as feature queries) allows you to check whether the browser supports a given CSS property before applying it. This is a tool for progressive enhancement, building the experience from the basics and adding advanced features where they're available.
It's like testing material strength before building: ancient Egyptians also tested stone before using it to build a pyramid. The syntax is @supports, a condition in parentheses and a block of styles:
1/* Basic @supports usage */
2.layout {
3 display: block; /* Fallback */
4}
5
6@supports (display: grid) {
7 .layout {
8 display: grid;
9 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
10 gap: 1rem;
11 }
12}
13
14/* Checking Container Queries */
15@supports (container-type: inline-size) {
16 .card-wrapper {
17 container-type: inline-size;
18 }
19
20 @container (min-width: 400px) {
21 .card {
22 flex-direction: row;
23 }
24 }
25}
26
27/* Negation - if NOT supported */
28@supports not (aspect-ratio: 1 / 1) {
29 .square {
30 padding-top: 100%; /* Old hack for proportions */
31 height: 0;
32 }
33}
34
35/* Combining conditions */
36@supports (display: grid) and (gap: 1rem) {
37 .modern-grid {
38 display: grid;
39 gap: 1rem;
40 }
41}You combine conditions with the words not, and and or, and with the selector() function you can even check support for a selector, for example @supports selector(:has(a)). The example with aspect-ratio: 1 / 1 recalls the property from part one, which enforces proportions, for example aspect-ratio: 16 / 9. Always roll out new features in the same order: check support on caniuse.com, write a fallback, add the modern version in @supports and test in several browsers.
accent-color - Styling Form Controls
The accent-color property allows you to change the accent color in native form controls such as checkboxes, radio buttons, range sliders and progress bars. Previously, styling these elements required completely overriding their appearance, and now a single line suffices:
1/* Global accent color */
2:root {
3 accent-color: #d4af37; /* Golden like Egyptian ornaments */
4}
5
6/* Specific controls */
7input[type="checkbox"] {
8 accent-color: #c9a66b;
9 width: 20px;
10 height: 20px;
11}
12
13input[type="radio"] {
14 accent-color: #1e3a5f;
15}
16
17input[type="range"] {
18 accent-color: #30d5c8;
19}
20
21progress {
22 accent-color: #d4af37;
23}
24
25/* Different colors for different sections */
26.form-section--gold {
27 accent-color: gold;
28}
29
30.form-section--lapis {
31 accent-color: #1e3a5f;
32}The property is inherited, so set on :root it changes all controls at once, and sections can override it. The browser itself takes care of contrast inside the control, for example it picks a light or dark checkmark in a checkbox. accent-color does not affect text fields and buttons.
So you have a ladder of options, from the hardest to the easiest: a custom checkbox from scratch (a hidden native one plus the ::before and ::after pseudo-elements), appearance: none with manual styling, accent-color, and color-scheme, which with a single word adapts controls to a dark theme:
1/* Dark theme for the whole page, including controls and scrollbars */
2:root {
3 color-scheme: dark;
4}
5
6/* Full control: switch off the native look and draw your own */
7input[type="checkbox"].custom {
8 appearance: none;
9 width: 20px;
10 height: 20px;
11 border: 2px solid #d4af37;
12 border-radius: 4px;
13}After appearance: none the checkbox loses its native look, including the checkmark, so you have to draw the checked state yourself. That is why you should start with accent-color and go lower only when the project really requires it.
color-mix() - Mixing Colors in CSS
The color-mix() function allows you to mix two colors in specified proportions directly in CSS. It's a powerful tool for creating color variants, semi-transparent versions and entire color palettes.
Think of how ancient Egyptians mixed pigments: lapis lazuli with white gave different shades of blue. color-mix() works exactly the same way. You give a color space, the first color with a percentage and the second color:
1/* Basic syntax */
2/* color-mix(in color-space, color1 percentage, color2) */
3
4/* Mixing in proportions */
5.lighter {
6 background: color-mix(in srgb, #d4af37 70%, white);
7 /* 70% gold + 30% white */
8}
9
10.darker {
11 background: color-mix(in srgb, #d4af37 70%, black);
12 /* 70% gold + 30% black */
13}
14
15/* Creating semi-transparent versions */
16.overlay {
17 background: color-mix(in srgb, #1e3a5f 50%, transparent);
18 /* 50% opacity */
19}
20
21/* Generating a palette from one base color */
22:root {
23 --brand: #d4af37;
24 --brand-light: color-mix(in srgb, var(--brand) 30%, white);
25 --brand-lighter: color-mix(in srgb, var(--brand) 15%, white);
26 --brand-dark: color-mix(in srgb, var(--brand) 70%, black);
27 --brand-darker: color-mix(in srgb, var(--brand) 50%, black);
28}
29
30/* Hover effects */
31.button {
32 background: var(--brand);
33}
34
35.button:hover {
36 background: color-mix(in srgb, var(--brand) 85%, black);
37}
38
39/* Mixing in oklch space (better visual results) */
40.gradient-step {
41 background: color-mix(in oklch, blue 60%, green);
42}color-mix(in srgb, blue 70%, white) gives 70% blue and 30% white, a lighter blue, because the missing percentage goes to the second color. Mixing with transparent creates a semi-transparent version, and the oklch space gives transitions that look more natural to the eye. This is how colors in CSS have evolved: from names such as red, through HEX and RGB notation, HSL and CSS variables, all the way to mixing colors in the browser.
The color-mix() function combined with CSS variables creates a powerful theming system where an entire color palette is generated from a single base color. Recall the tools from the previous lessons as well: :is() takes the specificity of the strongest selector in its list, :where() always has zero specificity, and @property registers typed variables that can be animated. In the sandbox below, scroll through the gallery of chambers, change the accent color of the form and add your own shade to the palette.
Remember: modern CSS is the master builder's toolbox, but you test every new chisel with @supports first.
Code for this lesson: index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Modern CSS features - part 2</title>
7 <link rel="stylesheet" href="style.css">
8</head>
9<body>
10 <div class="page">
11 <h1>Modern CSS features - part 2</h1>
12 <p class="subtitle">scroll-snap, @supports, accent-color, color-mix()</p>
13
14 <section>
15 <h2>Scroll Snap - gallery</h2>
16 <div class="snap-gallery">
17 <div class="snap-item" style="background:#d4af37">Chamber 1</div>
18 <div class="snap-item" style="background:#8b6914">Chamber 2</div>
19 <div class="snap-item" style="background:#c9a66b">Chamber 3</div>
20 <div class="snap-item" style="background:#b87333">Chamber 4</div>
21 <div class="snap-item" style="background:#1e3a5f">Chamber 5</div>
22 </div>
23 </section>
24
25 <section>
26 <h2>@supports - Progressive Enhancement</h2>
27 <div class="supports-demo">
28 <div class="box">Element 1</div>
29 <div class="box">Element 2</div>
30 <div class="box">Element 3</div>
31 </div>
32 </section>
33
34 <section>
35 <h2>accent-color - Form</h2>
36 <form class="accent-form">
37 <label><input type="checkbox" checked> Option 1</label>
38 <label><input type="checkbox"> Option 2</label>
39 <label><input type="radio" name="r" checked> Radio A</label>
40 <label><input type="radio" name="r"> Radio B</label>
41 <label>Range: <input type="range" min="0" max="100" value="60"></label>
42 <label>Progress: <progress value="70" max="100">70%</progress></label>
43 </form>
44 </section>
45
46 <section>
47 <h2>color-mix() - Color palette</h2>
48 <div class="color-palette">
49 <div class="color-swatch swatch-lighter">Lighter</div>
50 <div class="color-swatch swatch-light">Light</div>
51 <div class="color-swatch swatch-base">Base</div>
52 <div class="color-swatch swatch-dark">Dark</div>
53 <div class="color-swatch swatch-darker">Darker</div>
54 </div>
55 </section>
56 </div>
57</body>
58</html>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 is the difference between :is() and :where()?
2. What is scroll-snap-type used for in CSS?
These are 2 of 8 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Vertical ordering
Arrange the correct @supports query syntax:
- Click in order
Arrange the syntax for the scroll-snap-type declaration:
- Code editor
Replace a long list of selectors with :is(h1, h2, h3, h4) and use :where() for low-specificity resets.
- Code editor
Use scroll-snap-type: x mandatory and scroll-snap-align: start to create a gallery that snaps to items.
- Code editor
Use color-mix(in srgb, var(--primary) 70%, white) to create color shades by mixing.
- Vertical ordering
Order form control styling approaches from hardest to easiest:
- Code editor
Create fallbacks using @supports (display: grid), @supports (container-type: inline-size) for progressive enhancement.
- Vertical ordering
Order the ways of defining colors in CSS from oldest to newest:
- Code editor
Use accent-color to customize the colors of checkboxes, radio buttons, and range sliders to match your brand colors.
- Vertical ordering
Arrange the correct @property declaration syntax in CSS:
- Horizontal ordering
Arrange the correct color-mix() function syntax:
- Vertical ordering
Arrange CSS selectors from lowest to highest specificity:
- Vertical ordering
Arrange the correct syntax for nested @media in CSS Nesting:
- Click in order
Arrange the correct syntax for setting accent-color for the entire page:
- Click in order
Arrange the syntax for a @container rule:
- Vertical ordering
Arrange the stages of implementing modern CSS features into a project:
- Vertical ordering
Arrange the correct syntax for setting a 16:9 image aspect ratio:
- Vertical ordering
Arrange the stages of progressive enhancement from basic to advanced:
- Code editor
Build a sophisticated layout featuring: Container Queries for responsive components, :has() for conditional styling, CSS Nesting for organized code, @layer for cascade control, logical properties for internationalization, clamp() for fluid sizing, aspect-ratio for media, :is/:where for clean selectors, scroll-snap for galleries, color-mix() for theming, @supports for fallbacks. The layout should represent a state-of-the-art Egyptian complex blending antiquity with cutting-edge CSS!