All Modern CSS & HTML Features (2024–2026) - vdsology

vdsology css 2026 chapter 12

Chapter 12 — All Modern CSS & HTML Features (2024–2026)

CSS Nesting (Native)

Before 2024, nesting required Sass. Now it’s built into every browser.

Old Way — Flat and Repetitive

.card { background: white; }
.card h2 { font-size: 1.25rem; }
.card p { color: #6b7280; }
.card:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.1); }

New Way — Native Nesting

.card {
  background: white;

  h2 { font-size: 1.25rem; }     /* same as .card h2 */
  p  { color: #6b7280; }         /* same as .card p */

  &:hover {                       /* & means .card */
    box-shadow: 0 8px 24px rgba(0,0,0,0.1);
    h2 { color: #2563eb; }        /* .card:hover h2 */
  }
}

& means “the current selector.” Without &, nested selectors become descendants. With &, they attach directly:

.button {
  &:hover  { opacity: 0.9; }       /* .button:hover */
  &:active { transform: scale(0.96); }
  &.large  { padding: 20px 40px; } /* .button.large */
}

Media queries nested inside rules:

.card {
  padding: 16px;

  @media (min-width: 768px) { padding: 24px; }
  @media (min-width: 1024px) { padding: 32px; }
}

:has() — The Parent Selector

For 20 years, CSS had no way to style a parent based on its children. :has() solves this.

/* Select .card IF it contains an img */
.card:has(img) { padding: 0; }

/* Style a label when its checkbox is checked */
.option:has(input:checked) {
  border-color: #2563eb;
  background: rgba(37,99,235,0.05);
}

/* Disable button if any input is invalid */
form:has(input:invalid) .submit-btn {
  opacity: 0.5;
  pointer-events: none;
}

/* Style parent based on child content */
body:has(.hero) .nav {
  background: transparent;
}

Read :has() as “if it has” — like an if statement for styling.


Container Queries (@container)

Media queries check the screen width. Container queries check how much space the parent gives the component.

/* Define the container */
.card-wrapper {
  container-type: inline-size;
  container-name: card;          /* optional name */
}

/* Style based on container size */
@container (max-width: 400px) {
  .card { flex-direction: column; }
}

@container (min-width: 400px) {
  .card { flex-direction: row; }
}

/* Shorthand */
.sidebar { container: sidebar / inline-size; }

@container sidebar (min-width: 300px) {
  .widget { display: grid; }
}

The card adapts to its container’s size, not the screen. Place it anywhere — it figures out the layout automatically.


@layer — Cascade Layers

Explicitly control which group of CSS rules wins — without !important.

/* Declare order — first = lowest priority, last = highest */
@layer reset, base, components, utilities;

@layer reset {
  a { color: blue; }
}

@layer components {
  .nav-link { color: white; } /* beats anything in reset */
}

@layer utilities {
  .text-red { color: red; }  /* always wins */
}

/* Import third-party into a low-priority layer */
@import url('library.css') layer(third-party);
@layer third-party, base, components;
/* Now your components always beat the library */

CSS Subgrid

Children inherit and participate in the parent’s grid tracks.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto 1fr auto;
  gap: 20px;
}

.card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid; /* inherit parent's row tracks */
}

All card titles align, all card footers align — even with different content lengths.


oklch() Colors

Modern color system. Perceptually uniform and supports wider color gamuts.

/* Old way */
--color-primary: #2563eb;
--color-light: #60a5fa;  /* guesswork */

/* New way — oklch(lightness chroma hue) */
--color-primary: oklch(55% 0.2 264);
--color-light:   oklch(75% 0.2 264);  /* same hue, just lighter L */
--color-dark:    oklch(40% 0.2 264);  /* same hue, just darker L */

oklch(L C H):

  • L — Lightness: 0% (black) to 100% (white)
  • C — Chroma (saturation): 0 (gray) to ~0.4 (vivid)
  • H — Hue angle: 0–360 degrees

Same L and C with different H = same lightness and saturation, different color. Predictable and consistent.

/* Build a full palette from one hue */
:root {
  --h: 264;
  --color-100: oklch(93% 0.03 var(--h));
  --color-500: oklch(55% 0.2  var(--h));  /* base */
  --color-900: oklch(25% 0.1  var(--h));
}

Scroll-Driven Animations

Animations controlled by scroll position — no JavaScript.

/* Reading progress bar */
.progress-bar {
  position: fixed;
  top: 0; left: 0;
  height: 4px;
  background: #2563eb;
  transform-origin: left;
  animation: progress-grow linear;
  animation-timeline: scroll();       /* tied to page scroll */
  animation-fill-mode: both;
}

@keyframes progress-grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Reveal elements as they enter the viewport */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  animation: reveal-in linear forwards;
  animation-timeline: view();              /* triggers when element is visible */
  animation-range: entry 0% entry 40%;    /* during first 40% of entry */
}

@keyframes reveal-in {
  to { opacity: 1; transform: translateY(0); }
}

View Transitions API

Smooth animations between page states — no JavaScript animation libraries needed.

/* One rule — cross-fade between pages */
@view-transition {
  navigation: auto;
}

/* Custom transitions */
::view-transition-old(root) {
  animation: slide-out 300ms ease-in forwards;
}

::view-transition-new(root) {
  animation: slide-in 300ms ease-out forwards;
}

@keyframes slide-out { to { transform: translateX(-100%); } }
@keyframes slide-in { from { transform: translateX(100%); } }

/* Named element transitions */
.hero-image { view-transition-name: hero; }
/* Browser morphs this element smoothly between pages */

Anchor Positioning

Position any element relative to any other element — not just its parent.

.button { anchor-name: --my-button; }

.tooltip {
  position: absolute;
  position-anchor: --my-button;
  bottom: anchor(top);      /* tooltip bottom = button top */
  left: anchor(center);     /* align centers */
  transform: translateX(-50%);
}

/* Automatic fallback positions if it overflows */
.tooltip {
  position-try-fallbacks: flip-block, flip-inline;
}

CSS Logical Properties

Direction-agnostic properties that work in RTL and vertical writing modes.

/* Physical — breaks in RTL */
.card { margin-left: 24px; text-align: left; }

/* Logical — works everywhere */
.card { margin-inline-start: 24px; text-align: start; }
Physical Logical
margin-left margin-inline-start
margin-right margin-inline-end
margin-top margin-block-start
margin-bottom margin-block-end
width inline-size
height block-size

Shorthand:

margin-block: 16px;    /* top and bottom */
margin-inline: 24px;   /* left and right */
padding-block: 12px;
padding-inline: 20px;

:is() and :where()

:is() — Group Selectors

/* Old way */
h1 a:hover, h2 a:hover, h3 a:hover { color: blue; }

/* With :is() */
:is(h1, h2, h3) a:hover { color: blue; }

/* Any state */
button:is(:hover, :focus, :active) { background: darkblue; }

Specificity = highest argument in the list.

:where() — Zero Specificity

/* Easy to override — zero specificity */
:where(h1, h2, h3) { font-weight: bold; }

/* This always overrides even with low specificity */
.article h2 { font-weight: 400; }

Modern HTML — <dialog>

Native modal element — handles focus, Escape key, and overlay automatically.

<dialog id="my-modal">
  <h2>Confirm Action</h2>
  <p>Are you sure?</p>
  <button id="cancel">Cancel</button>
  <button id="confirm">Delete</button>
</dialog>

<button id="open">Open Modal</button>
const dialog = document.getElementById('my-modal');
document.getElementById('open').onclick   = () => dialog.showModal();
document.getElementById('cancel').onclick = () => dialog.close();
dialog {
  border: none;
  border-radius: 12px;
  padding: 32px;
  max-width: 480px;
  box-shadow: 0 24px 64px rgba(0,0,0,0.2);
}

dialog::backdrop {
  background: rgba(0,0,0,0.5);
  backdrop-filter: blur(4px);
}

Modern HTML — Popover API

Native tooltips and dropdowns with zero JavaScript for basic use.

<button popovertarget="my-menu">Open Menu</button>

<div id="my-menu" popover>
  <p>Menu item 1</p>
  <p>Menu item 2</p>
</div>

Click button → popover opens. Click anywhere else → closes. No JavaScript.

<div popover="auto">   <!-- closes on outside click (default) -->
<div popover="manual"> <!-- only closes explicitly -->

Modern HTML — <details> and <summary>

Native accordion — no JavaScript.

<details>
  <summary>What is your return policy?</summary>
  <p>You can return any item within 30 days for a full refund.</p>
</details>
details { border: 1px solid #e5e7eb; border-radius: 8px; }
summary { padding: 16px; font-weight: 600; cursor: pointer; }
details[open] summary { color: #2563eb; }

Modern HTML — loading="lazy"

<!-- Loads only when near the viewport -->
<img src="card1.jpg" alt="Card 1" loading="lazy">

<!-- Always pair with width/height to prevent layout shift -->
<img
  src="photo.jpg"
  alt="photo"
  width="800"
  height="600"
  loading="lazy"
>

Other Modern CSS Properties

aspect-ratio

.video { aspect-ratio: 16 / 9; width: 100%; }
.avatar { aspect-ratio: 1; width: 80px; }         /* square */

accent-color

:root { accent-color: #2563eb; }
/* Checkboxes, radios, ranges now use your brand color */

scroll-snap

.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}
.carousel-item {
  scroll-snap-align: start;
  flex-shrink: 0;
  width: 80%;
}

overscroll-behavior

.modal-content {
  overflow-y: auto;
  overscroll-behavior: contain; /* prevent scroll chaining */
}

color-scheme

:root { color-scheme: light dark; }
/* Scrollbars, inputs, native elements match OS mode */

@starting-style — Entry Animation

.popup { opacity: 1; transition: opacity 300ms; }

@starting-style {
  .popup { opacity: 0; } /* what to transition FROM on first render */
}

@scope — Scoped Styles

@scope (.card) {
  p { color: blue; }  /* only affects p inside .card */
}

@scope (.card) to (.card-footer) {
  p { color: blue; }  /* .card p, but not inside .card-footer */
}

@property — Typed Custom Properties

@property --progress {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
/* Enables transitions on this custom property */

light-dark() function

:root { color-scheme: light dark; }

.card {
  background: light-dark(#ffffff, #1a1a1a);
  color: light-dark(#111, #f0f0f0);
}


Tags: #CSS #css #vdsology #webdevelopment #vdsologyCSS #CSS3 #CSSreference #CSSreference2026


Write a comment
No comments yet.