Introduction to CSS Presentation Part 1

Updated: March 27, 2026

Introduction to CSS Presentation Part 1

TL;DR

Modern CSS (2026) is about Container Queries, the :has() selector, Cascade Layers, and native nesting. Grid and Flexbox remain the layout foundations. CSS custom properties manage design tokens. Tailwind CSS 4 adds native nesting and performance improvements, but vanilla CSS can now do much of what Tailwind does.

If your last deep dive into CSS was a few years ago, prepare for surprises. CSS has evolved dramatically. The language that once felt limited—"just styling"—now handles layout complexity, logic (:has()), design tokens, and responsive adaptation that used to require JavaScript or build tools.

This guide covers modern CSS essentials for 2026: the core layout tools you've always needed (Grid, Flexbox), the revolutionary features (Container Queries), and the syntax improvements (native nesting) that make CSS more enjoyable to write. Whether you're starting fresh or catching up, understanding these fundamentals unlocks the rest.

CSS Grid and Flexbox: The Layout Foundation

Grid and Flexbox are not advanced—they're essential. Every modern layout uses one or both.

Flexbox: One-Dimensional Layout

Flexbox arranges items in a single direction (row or column).

/* Navigation bar: items in a row */
.nav {
  display: flex;
  gap: 1rem;
  justify-content: space-between; /* Space between nav items and logo */
  align-items: center; /* Vertically centered */
}

/* Form: items in a column */
.form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Responsive: row on desktop, column on mobile */
@media (max-width: 768px) {
  .nav {
    flex-direction: column;
  }
}

Key properties:

  • flex-direction: row | column
  • justify-content: Controls spacing along the main axis (row/column direction)
  • align-items: Controls alignment perpendicular to main axis
  • flex-wrap: Wrap items to multiple lines
  • gap: Space between items

CSS Grid: Two-Dimensional Layout

Grid arranges items in rows and columns simultaneously.

/* Dashboard layout */
.dashboard {
  display: grid;
  grid-template-columns: 250px 1fr 300px; /* Sidebar, main, aside */
  grid-template-rows: auto 1fr auto; /* Header, content, footer */
  gap: 1rem;
  height: 100vh;
}

.header {
  grid-column: 1 / -1; /* Span all columns */
}

.sidebar {
  grid-column: 1;
  grid-row: 2;
}

.main {
  grid-column: 2;
  grid-row: 2;
}

.footer {
  grid-column: 1 / -1;
}

The Difference: When to Use Each

Layout Flexbox Grid
Navigation bar
Product list (1-column)
Form inputs
Dashboard (2D)
Card grid with subgrid
Sidebar + main content

Most complex layouts use both:

.dashboard {
  display: grid;
  grid-template-columns: 250px 1fr;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}

.card {
  display: flex;
  flex-direction: column;
}

Grid handles the page structure, Flexbox handles individual component layouts.

CSS Custom Properties (Variables)

Custom properties store reusable values. They're dynamic (can change at runtime) unlike SASS/LESS variables.

Basic Usage

:root {
  --color-primary: #0066ff;
  --color-text: #333;
  --spacing-base: 1rem;
  --radius: 0.5rem;
}

button {
  background: var(--color-primary);
  padding: var(--spacing-base);
  border-radius: var(--radius);
  color: white;
}

Dynamic Theming

/* Light theme (default) */
:root {
  --bg: #ffffff;
  --text: #000000;
}

/* Dark theme */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #ffffff;
  }
}

/* User-selected dark mode */
body.dark-mode {
  --bg: #1a1a1a;
  --text: #ffffff;
}

body {
  background: var(--bg);
  color: var(--text);
}

Scoped Variables

.card {
  --card-bg: #f5f5f5;
  --card-padding: 1.5rem;
}

.card-header {
  background: var(--card-bg);
  padding: var(--card-padding);
}

/* Different card style */
.card.featured {
  --card-bg: #ffd700;
  --card-padding: 2rem;
  /* Header automatically uses new values */
}

Container Queries: Components Adapt to Context

Previously, only media queries existed—they measure the viewport. Container Queries measure the component's container.

/* Define a container context */
.card-container {
  container-type: inline-size;
}

/* Query the container's width */
@container (min-width: 400px) {
  .card-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

@container (max-width: 300px) {
  .card-content {
    display: block;
  }
}

A component inside a 250px sidebar displays as a single column. The same component in a 800px main area displays as two columns. Same code, different contexts.

This is revolutionary because components are now truly reusable and context-aware.

The :has() Selector

:has() selects elements based on their children or siblings. It's "parent selection"—something CSS couldn't do before.

/* Select card if it contains an image */
.card:has(img) {
  display: grid;
  grid-template-columns: 200px 1fr;
}

/* Select form if it has an error */
.form:has(.error) {
  border: 2px solid red;
}

/* Select heading followed by text */
h2:has(+ p) {
  margin-bottom: 0.5rem;
}

/* Style based on input state */
form:has(input:invalid) {
  background: #ffe0e0;
}

/* Dark mode: if user prefers dark, select background */
@media (prefers-color-scheme: dark) {
  body:has(.dark-mode-toggle:checked) {
    --bg: #1a1a1a;
  }
}

:has() enables CSS-only responsive behavior without media queries.

Cascade Layers and CSS Nesting

Cascade Layers

Control specificity conflicts with layers:

/* Define layer order */
@layer reset, base, theme, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer base {
  body { font-family: system-ui; }
}

@layer theme {
  --color-primary: blue;
}

@layer components {
  .button { background: var(--color-primary); }
}

@layer utilities {
  .text-center { text-align: center; }
}

/* Utilities override components override theme—determined by layer order */

Layers prevent specificity wars. A utility class in the utilities layer always overrides a component in the components layer, regardless of specificity.

Native CSS Nesting

No more repeating selectors:

/* Before: repetition */
.card { border: 1px solid #ccc; }
.card-header { background: #f5f5f5; }
.card-body { padding: 1rem; }

/* After: native nesting */
.card {
  border: 1px solid #ccc;

  & .header {
    background: #f5f5f5;
  }

  & .body {
    padding: 1rem;
  }

  /* Hover state */
  &:hover {
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  }

  /* Media query inside */
  @media (max-width: 600px) {
    flex-direction: column;
  }
}

The & selector refers to the parent. This syntax is now native CSS (no Sass required).

View Transitions API

Animate between page states:

.modal {
  view-transition-name: modal;
}

@supports (view-transition-name: modal) {
  ::view-transition-new(modal) {
    animation: slideUp 0.5s;
  }

  @keyframes slideUp {
    from {
      opacity: 0;
      transform: translateY(50px);
    }
  }
}

When you transition to a new modal state, the browser automatically animates between the old and new states.

document.startViewTransition(() => {
  updateDOM(); // Change the DOM
  // Browser automatically animates the transition
});

This creates smooth transitions without manual JavaScript animation logic.

Tailwind CSS 4 vs. Vanilla CSS

Tailwind CSS 4 (released January 22, 2025) now supports:

  • Native nesting
  • CSS variables for customization
  • Reduced output size with better tree-shaking

When to use Tailwind:

  • Rapid prototyping (pre-built responsive utilities)
  • Team standardization (consistent spacing, colors)
  • Complex responsive needs (with Container Queries support)

When vanilla CSS is sufficient:

  • Simple, static sites
  • Design systems that rarely change
  • When build tools feel excessive
  • Teams comfortable with CSS

Example: Same component in Tailwind vs. vanilla:

Tailwind:

<div className="grid grid-cols-1 md:grid-cols-2 gap-4 p-4">
  <div className="border rounded-lg p-4 hover:shadow-lg">
    <h3 className="text-lg font-bold mb-2">Title</h3>
    <p>Content</p>
  </div>
</div>

Vanilla CSS with modern features:

<div className="card-grid">
  <article className="card">
    <h3>Title</h3>
    <p>Content</p>
  </article>
</div>

<style>
  .card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1rem;
    padding: 1rem;
  }

  .card {
    border: 1px solid #ccc;
    border-radius: 0.5rem;
    padding: 1rem;

    &:hover {
      box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
  }
</style>

Both are valid. Choose based on project needs, not hype.

@starting-style for Transitions

Animate elements when they first appear:

.toast {
  opacity: 0;
  transform: translateX(-100%);
  transition: all 0.3s;
}

@starting-style {
  .toast {
    opacity: 0;
    transform: translateX(-100%);
  }
}

.toast.show {
  opacity: 1;
  transform: translateX(0);
}

When a toast appears, it transitions from the @starting-style values to the normal values.

Key Takeaways

Modern CSS (2026) handles what used to require JavaScript or build tools:

  1. Grid and Flexbox: Master these for any layout
  2. Custom properties: Dynamic, scoped design tokens
  3. Container Queries: Context-aware component styles
  4. :has() selector: CSS-only logic based on child elements
  5. Native nesting: Cleaner, DRY syntax
  6. Cascade Layers: Managed specificity
  7. View Transitions API: Smooth state transitions
  8. @starting-style: Entry animations

CSS is no longer just styling. It's a powerful language for layout, logic, and interaction. The more you learn modern CSS, the less JavaScript you need for presentation.


FREE WEEKLY NEWSLETTER

Stay on the Nerd Track

One email per week — courses, deep dives, tools, and AI experiments.

No spam. Unsubscribe anytime.