DevToolBoxGRATIS
Blog

CSS Nesting Native 2026

11 menitoleh DevToolBox

Native CSS Nesting in 2026: No Preprocessor Needed

Native CSS nesting is now supported in all major browsers. You can nest selectors directly in your stylesheets without Sass, Less, or any build tool. The syntax is slightly different from preprocessor nesting but achieves the same goal: scoped, readable, maintainable styles. This guide covers the full specification, real-world patterns, migration tips, and edge cases.

Browser Support

BrowserVersionRelease
Chrome120+ (132 latest)December 2023
Firefox117+ (135 latest)August 2023
Safari17.2+ (18.3 latest)December 2023
Edge120+ (132 latest)December 2023

As of March 2026, native CSS nesting has over 97% global browser support (Can I Use data). You can safely use it in production without a preprocessor for any project that does not need to support very old browsers.

Basic Nesting Syntax

/* Native CSS nesting */
.card {
  padding: 1.5rem;
  border-radius: 0.75rem;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

  /* Nested child selector — & is optional for element and class selectors */
  .card-header {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 1rem;
    border-bottom: 1px solid #e5e7eb;
    padding-bottom: 0.75rem;
  }

  .card-body {
    line-height: 1.6;
    color: #374151;
  }

  .card-footer {
    margin-top: 1rem;
    padding-top: 0.75rem;
    border-top: 1px solid #e5e7eb;
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
  }
}

/* Equivalent flat CSS */
/*
.card { ... }
.card .card-header { ... }
.card .card-body { ... }
.card .card-footer { ... }
*/

The & Nesting Selector

The & represents the parent selector. It is required when combining selectors (like pseudo-classes, attribute selectors, or chaining class names) and optional for descendant selectors.

/* & for pseudo-classes and pseudo-elements */
.button {
  padding: 0.5rem 1rem;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: all 0.15s ease;

  /* Pseudo-classes require & */
  &:hover {
    background: #2563eb;
    transform: translateY(-1px);
  }

  &:active {
    transform: translateY(0);
  }

  &:focus-visible {
    outline: 2px solid #3b82f6;
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
  }

  /* Pseudo-elements */
  &::before {
    content: "";
    display: inline-block;
    width: 1em;
    height: 1em;
  }

  /* Chaining classes (no space — same element) */
  &.primary {
    background: #3b82f6;
  }

  &.danger {
    background: #ef4444;
  }

  &.outline {
    background: transparent;
    border: 2px solid #3b82f6;
    color: #3b82f6;

    &:hover {
      background: #3b82f6;
      color: white;
    }
  }
}

Deep Nesting

/* You can nest multiple levels deep */
.nav {
  display: flex;
  align-items: center;

  .nav-list {
    display: flex;
    list-style: none;
    gap: 0.25rem;

    .nav-item {
      position: relative;

      .nav-link {
        display: block;
        padding: 0.5rem 1rem;
        color: #4b5563;
        text-decoration: none;
        border-radius: 0.375rem;

        &:hover {
          background: #f3f4f6;
          color: #111827;
        }

        &.active {
          background: #eff6ff;
          color: #2563eb;
          font-weight: 500;
        }
      }

      /* Dropdown submenu */
      .dropdown {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        min-width: 200px;
        background: white;
        border-radius: 0.5rem;
        box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
      }

      &:hover .dropdown {
        display: block;
      }
    }
  }
}

/*
  Best practice: Keep nesting to 3 levels max.
  Deeper nesting creates overly specific selectors.
*/

Nesting Media Queries and Container Queries

/* Media queries can be nested inside selectors */
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;

  @media (min-width: 640px) {
    grid-template-columns: repeat(2, 1fr);
  }

  @media (min-width: 1024px) {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
  }

  @media (min-width: 1280px) {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Container queries nested */
.card-container {
  container-type: inline-size;
  container-name: card;
}

.card {
  padding: 1rem;

  @container card (min-width: 400px) {
    display: flex;
    gap: 1rem;

    .card-image {
      width: 150px;
      flex-shrink: 0;
    }
  }

  @container card (min-width: 600px) {
    padding: 2rem;

    .card-image {
      width: 250px;
    }
  }
}

/* Supports query nested */
.backdrop {
  background: rgba(0, 0, 0, 0.5);

  @supports (backdrop-filter: blur(10px)) {
    background: rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(10px);
  }
}

Nesting with Combinators

/* Direct child combinator */
.list {
  > li {
    padding: 0.75rem 0;
    border-bottom: 1px solid #e5e7eb;

    &:last-child {
      border-bottom: none;
    }

    > a {
      color: #2563eb;
      text-decoration: none;

      &:hover {
        text-decoration: underline;
      }
    }
  }
}

/* Adjacent sibling */
.heading {
  margin-bottom: 0.5rem;

  + p {
    margin-top: 0;
    color: #6b7280;
  }

  + .heading {
    margin-top: 2rem;
  }
}

/* General sibling */
.toggle-input {
  display: none;

  &:checked ~ .toggle-panel {
    display: block;
    animation: slideDown 0.2s ease;
  }
}

/* Attribute selectors */
.input {
  border: 1px solid #d1d5db;
  border-radius: 0.375rem;

  &[type="email"],
  &[type="password"] {
    padding-left: 2.5rem;
  }

  &[aria-invalid="true"] {
    border-color: #ef4444;
    background: #fef2f2;
  }

  &[disabled] {
    opacity: 0.5;
    background: #f9fafb;
  }
}

CSS Nesting vs Sass Nesting

FeatureNative CSSSass/SCSS
Basic nesting.parent { .child {} }Same
Parent selector&&
String concatenationNot supported&__element
@media nestingSupportedSupported
Variablesvar(--custom)$variable
MixinsNot available@mixin / @include
FunctionsLimited (calc, min, max)Full function system
Build step requiredNoYes

Migration from Sass

/* Sass (won't work in native CSS) */
.block {
  &__element {    /* BEM concatenation — NOT supported natively */
    color: red;
  }
  &--modifier {   /* BEM concatenation — NOT supported natively */
    color: blue;
  }
}

/* Native CSS equivalent */
.block {
  .block__element {
    color: red;
  }
  .block--modifier {
    color: blue;
  }
}

/* Or better — drop BEM and use native nesting */
.block {
  .element {
    color: red;
  }
  &.modifier {
    color: blue;
  }
}

Real-World Component Example

/* A complete dialog component with native nesting */
.dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: grid;
  place-items: center;
  z-index: 50;
  opacity: 0;
  transition: opacity 0.2s ease;

  &[data-open="true"] {
    opacity: 1;
  }

  .dialog {
    background: white;
    border-radius: 1rem;
    width: min(90vw, 500px);
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
    transform: scale(0.95);
    transition: transform 0.2s ease;

    [data-open="true"] & {
      transform: scale(1);
    }

    .dialog-header {
      padding: 1.5rem;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-bottom: 1px solid #e5e7eb;

      h2 {
        font-size: 1.25rem;
        font-weight: 600;
        margin: 0;
      }

      .close-btn {
        width: 2rem;
        height: 2rem;
        border: none;
        background: none;
        border-radius: 0.375rem;
        cursor: pointer;
        display: grid;
        place-items: center;

        &:hover {
          background: #f3f4f6;
        }
      }
    }

    .dialog-body {
      padding: 1.5rem;
      overflow-y: auto;
      flex: 1;

      p {
        line-height: 1.6;
        color: #4b5563;

        &:first-child {
          margin-top: 0;
        }
      }
    }

    .dialog-footer {
      padding: 1rem 1.5rem;
      border-top: 1px solid #e5e7eb;
      display: flex;
      justify-content: flex-end;
      gap: 0.75rem;
    }

    @media (max-width: 640px) {
      width: 100vw;
      max-height: 100vh;
      border-radius: 0;
      margin: 0;
    }
  }
}

Dark Mode with Nesting

/* Combine nesting with CSS custom properties for dark mode */
:root {
  --bg-primary: #ffffff;
  --bg-secondary: #f9fafb;
  --text-primary: #111827;
  --text-secondary: #6b7280;
  --border: #e5e7eb;

  @media (prefers-color-scheme: dark) {
    --bg-primary: #111827;
    --bg-secondary: #1f2937;
    --text-primary: #f9fafb;
    --text-secondary: #9ca3af;
    --border: #374151;
  }
}

.card {
  background: var(--bg-primary);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  padding: 1.5rem;

  .subtitle {
    color: var(--text-secondary);
  }

  /* Explicit dark mode class override */
  .dark & {
    --bg-primary: #111827;
    --text-primary: #f9fafb;
    --border: #374151;
  }
}

New in 2026: @scope and @layer with Nesting

CSS continues to evolve. Two powerful features now work beautifully with nesting:@scope for proximity-based styling and @layer for cascade management.

@scope with Nesting

/* @scope limits where styles apply */
@scope (.card) to (.card-footer) {
  /* Only applies between .card and .card-footer */
  p {
    color: #374151;
    line-height: 1.7;
  }

  a {
    color: #2563eb;
    text-decoration: underline;

    &:hover {
      color: #1d4ed8;
    }
  }
}

/* Nested @scope */
.dashboard {
  @scope (.widget) to (.widget-footer) {
    h3 {
      font-size: 1rem;
      font-weight: 600;
    }

    .metric {
      font-size: 2rem;
      font-weight: 700;
      color: #059669;
    }
  }
}

@layer with Nesting

/* Cascade layers + nesting for organized styles */
@layer base, components, utilities;

@layer base {
  body {
    font-family: system-ui, sans-serif;
    line-height: 1.5;
    color: #111827;
  }
}

@layer components {
  .button {
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;

    &.primary {
      background: #3b82f6;
      color: white;

      &:hover { background: #2563eb; }
    }

    &.secondary {
      background: #e5e7eb;
      color: #374151;

      &:hover { background: #d1d5db; }
    }
  }
}

@layer utilities {
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
  }
}

@starting-style for Entry Animations

/* New in 2026: animate elements when they first appear */
.toast {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.3s, transform 0.3s;

  @starting-style {
    opacity: 0;
    transform: translateY(20px);
  }

  &.leaving {
    opacity: 0;
    transform: translateY(-20px);
  }
}

/* Dialog with @starting-style */
dialog[open] {
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.2s, transform 0.2s;

  @starting-style {
    opacity: 0;
    transform: scale(0.95);
  }

  &::backdrop {
    background: rgba(0, 0, 0, 0.5);
    transition: background 0.2s;

    @starting-style {
      background: transparent;
    }
  }
}

Best Practices

  • Limit nesting depth to 3 levels — deeper nesting creates specificity problems and fragile selectors
  • Use & for pseudo-classes and chained selectors — it makes the relationship explicit
  • Nest media queries inside selectors — keeps responsive styles co-located with the component
  • Avoid nesting element selectors deeply — prefer class-based selectors for maintainability
  • Use CSS custom properties instead of Sass variables — they cascade and respond to context
  • PostCSS nesting plugin — if you need to support older browsers, use postcss-nesting as a fallback

Experiment with CSS nesting patterns using our CSS Formatter tool. For migrating from Tailwind utility classes to custom CSS nesting, read our CSS to Tailwind Migration guide. If you are building responsive layouts with nesting, check out our CSS Grid Layout Cheat Sheet.

𝕏 Twitterin LinkedIn
Apakah ini membantu?

Tetap Update

Dapatkan tips dev mingguan dan tool baru.

Tanpa spam. Berhenti kapan saja.

Coba Alat Terkait

{ }CSS Minifier / Beautifier🎨CSS FormatterTWCSS to Tailwind

Artikel Terkait

CSS Container Queries: Panduan Lengkap 2026

Kuasai CSS container queries: sintaks @container, size containment dan unit container.

CSS :has() Selector: The Parent Selector You Always Wanted (2026)

Master the CSS :has() pseudo-class. Parent selection, form validation without JS, and advanced patterns.

Tips Tailwind CSS 2026: Teknik Lanjutan

Kuasai teknik lanjutan Tailwind CSS.