⚡ Phase 3 · Advanced CSS 🔴 Advanced MODULE 12

Modern CSS & Best Practices

⏱️ 40 min
📖 Theory + Code
🧩 5 Questions
🏗️ 1 Challenge
You've mastered the fundamentals. Now we explore the cutting edge — modern CSS selectors like :is(), :where(), and :has(), container queries that respond to parent width instead of viewport, native CSS nesting, and the performance & accessibility habits that separate pros from beginners. By the end you'll write CSS that is leaner, smarter, and future-proof.
Lesson Progress0%

Modern Selectors: :is(), :where(), :has()

These three pseudo-class functions arrived in browsers around 2021–2022 and have quickly become essential. They let you write shorter, smarter selectors without the combinatorial explosion of the old way.

:is() — Smart Grouping

:is() lets you group selectors in a list. The specificity of :is() is determined by the most specific selector in its argument list. It collapses dozens of repetitive rules into one.

is_selector.css
CSS
/* OLD WAY — repetitive */
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
  color: inherit;
  text-decoration: none;
}

/* MODERN WAY — :is() groups them */
:is(h1, h2, h3, h4, h5, h6) a {
  color: inherit;
  text-decoration: none;
}

/* Combine contexts easily */
:is(.card, .modal, .sidebar) p {
  font-size: 0.9rem;
  color: var(--txt2);
}

:where() — Zero-Specificity Grouping

:where() works exactly like :is() but with zero specificity. This makes it perfect for base/reset styles that you want any author style to override easily.

where_selector.css
CSS
/* :where() has 0 specificity — easy to override */
:where(ul, ol) {
  list-style: none;
  padding: 0;
  margin: 0;
}

/* This single-class rule easily overrides the :where() above */
.my-list {
  list-style: disc;  /* wins because .class > :where() */
}

/* Great for design-system base resets */
:where(button, input, select, textarea) {
  font-family: inherit;
  font-size: 1rem;
}

:has() — The Parent Selector 🎉

For years developers begged for a way to select a parent based on its children. :has() finally delivers. It is the most powerful selector ever added to CSS — it lets you style a parent based on what's inside it.

has_selector.css
CSS
/* Style a card differently if it contains an image */
.card:has(img) {
  padding: 0;           /* remove padding when image present */
}

/* Style a form when any input is invalid */
form:has(:invalid) {
  border: 2px solid red;
}

/* Style a label when its checkbox is checked */
.toggle:has(input:checked) {
  background: var(--acc);
  color: white;
}

/* Layout change when sidebar is present */
body:has(.sidebar) .main {
  margin-left: 280px;    /* only when sidebar exists in DOM */
}
🌐
Browser Support (2025)
:is() and :where() have near-universal support (97%+). :has() is now supported by all major browsers including Chrome 105+, Safari 15.4+, Firefox 121+, and Edge 105+. Safe to use in production.

Container Queries

Media queries respond to the viewport width. But what if a component is used in both a full-width page and a narrow sidebar? Container queries let a component respond to its own parent's size — a game-changer for reusable UI.

container_queries.css
CSS
/* Step 1: declare a container on the PARENT */
.card-wrapper {
  container-type: inline-size;  /* tracks width */
  container-name: card;          /* optional name */
}

/* Step 2: query the container from CHILD styles */
@container card (min-width: 400px) {
  .product-card {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
  .product-card img {
    height: 100%;
    object-fit: cover;
  }
}

/* Without a name, queries apply to nearest containment ancestor */
.sidebar-widget {
  container-type: inline-size;
}

@container (max-width: 250px) {
  .sidebar-widget .icon-label {
    display: none;  /* hide text in tiny sidebars */
  }
}
💡
Container Queries vs Media Queries
Use media queries for page-level layout decisions (single vs multi-column). Use container queries for component-level decisions — cards, widgets, nav items — so they adapt wherever they're placed. Think of it as "intrinsic" responsive design.

Native CSS Nesting

For years, nesting was only available via Sass or PostCSS. Now it's built into native CSS. You can nest selectors inside each other just like you would in a preprocessor — no build step required.

css_nesting.css
CSS
/* Native CSS nesting — no Sass needed! */
.card {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;

  /* Nested child selector */
  & .card-title {
    font-size: 1.2rem;
    font-weight: 700;
  }

  /* Nested hover state */
  &:hover {
    box-shadow: 0 8px 32px rgba(0,0,0,.15);
    transform: translateY(-2px);
  }

  /* Nested media query */
  @media (max-width: 600px) {
    padding: 1rem;
  }

  /* Nested modifier class */
  &.card--featured {
    border: 2px solid gold;
  }
}
⚠️
The & symbol is required
Unlike Sass, native CSS nesting requires the & nesting selector when combining with element selectors or starting with a letter. Omit it only for nested at-rules like @media. Supported in all modern browsers (Chrome 112+, Firefox 117+, Safari 16.5+).

Performance & Accessibility Best Practices

Writing great CSS isn't just about visual results — it's about making sure your styles load fast and work for everyone.

🚀 Performance Tips

performance.css
CSS
/* 1. Use will-change SPARINGLY for animated elements */
.animated-card {
  will-change: transform;  /* hints browser to composite this layer */
}
/* ⚠️ Don't apply to everything — it uses GPU memory */

/* 2. Prefer transform & opacity for animations (GPU-accelerated) */
.slide-in {
  transform: translateX(-100%);  /* ✅ GPU */
  opacity: 0;                    /* ✅ GPU */
}
/* Avoid animating: width, height, top, left, margin (cause reflow) */

/* 3. Use content-visibility for off-screen sections */
.below-fold-section {
  content-visibility: auto;        /* skip rendering until visible */
  contain-intrinsic-size: 0 500px; /* estimated height hint */
}

/* 4. Limit selector depth — shallow is faster */
.nav-item { } /* ✅ fast */
/* header nav ul li a span { } — ❌ slow, avoid */

/* 5. Use logical properties for international support */
.box {
  margin-inline-start: 1rem;  /* left in LTR, right in RTL */
  padding-block: 0.5rem;     /* top + bottom padding */
}

♿ Accessibility in CSS

accessibility.css
CSS
/* 1. Never remove focus outlines — restyle them instead */
:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

/* 2. Respect user motion preferences */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* 3. Respect user color-scheme preference */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #ffffff;
    --txt: #111111;
  }
}

/* 4. Visually hidden but accessible to screen readers */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* 5. Minimum touch target sizes */
.btn {
  min-height: 44px;  /* WCAG 2.5.5 recommended */
  min-width: 44px;
}
Accessibility is not optional
In many countries, web accessibility is a legal requirement. Beyond compliance, it makes your site better for everyone — keyboard users, screen reader users, people with motor impairments, and users on slow connections or bright sunlight. Good CSS defaults cost almost nothing and pay dividends for years.

What's Next in CSS

CSS is evolving faster than ever. Here's a quick radar of what's coming or recently landed:

Anchor Positioning
Attach a tooltip or dropdown to any element without JavaScript. anchor-name + position-anchor. Landing in 2024–2025.
View Transitions API
Smooth page transitions and in-page animations with just a few lines of JS + CSS. Now in Chrome, Safari, Firefox.
Cascade Layers (@layer)
Organize CSS into explicit layers (resets, base, components, utilities) for predictable specificity without !important.
Scroll-Driven Animations
Link CSS animations to scroll position. No JS needed. animation-timeline: scroll().
CSS Scope (@scope)
Scope styles to a subtree of the DOM. Combine with native nesting for true component encapsulation.
color-mix()
Mix two colors natively in CSS. color-mix(in oklch, blue 50%, red) — no preprocessor needed.
cascade_layers.css
CSS
/* @layer — explicit cascade ordering (no !important needed) */
@layer reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
}

@layer base {
  body { font-family: var(--ff-b); }
}

@layer components {
  .btn { padding: 0.5rem 1rem; border-radius: 6px; }
}

@layer utilities {
  /* Utilities always win over components in the same layer order */
  .mt-4 { margin-top: 1rem; }
  .hidden { display: none; }
}
🧩 Knowledge Check
5 questions · Pick the best answer for each
1. What is the key difference between :is() and :where()?
2. You want a .card to display in a two-column grid ONLY when its container is wider than 500px (not when the viewport is). Which approach is correct?
3. What does :has() allow that was previously impossible in pure CSS?
4. Why should you prefer animating transform and opacity over properties like width, top, or margin?
5. In native CSS nesting, when is the & nesting selector required?
🏗️
Coding Challenge
Apply modern CSS techniques from this lesson
Build a Modern Card Component

Create a .card component using the techniques from this lesson:

1. :has() — When the card contains an img, remove its padding and display the image edge-to-edge (border-radius on the image instead).
2. Container Query — When the card's container is wider than 420px, switch the card to a horizontal layout (image left, content right) using display: grid; grid-template-columns: 200px 1fr.
3. Native Nesting — Write all card styles using native CSS nesting (.card { & img { } &:hover { } }).
4. Accessibility — Add :focus-visible styles and respect prefers-reduced-motion for the hover transition.
💡 Show hints
  • Set container-type: inline-size on the wrapper div, not on .card itself
  • Use .card:has(img) { padding: 0; } then .card:has(img) img { border-radius: 12px 12px 0 0; }
  • Inside @container (min-width: 420px), target .card:has(img) for grid layout change
  • Wrap your hover transition in @media (prefers-reduced-motion: no-preference)
🏆
Lesson 12 Complete!
You've completed all Phase 3 lessons — modern selectors, container queries, nesting, and accessibility. Time for the Capstone Projects!
← Course Home
Phase 3 · Advanced CSSLesson 12 of 3