Nesting در CSS خالص؛ دیگه نیازی به Sass نیست

اگه قبلاً با Sass یا Less کار کرده باشید، احتمالاً با مفهوم nesting آشنایید؛ یعنی نوشتن selector ها تو دل هم، به‌جای تکرار کردن selector والد برای هر فرزند. تا همین چند وقت پیش، این قابلیت فقط با یه preprocessor امکان‌پذیر بود. اما حالا CSS خودش، بدون نیاز به هیچ ابزار اضافه‌ای، از nesting پشتیبانی می‌کنه. تو این درس می‌خوایم ببینیم این ویژگی چطور کار می‌کنه.

مشکلی که nesting حلش می‌کنه

فرض کنید یه کارت دارید با چند بخش داخلش. بدون nesting، معمولاً این‌جوری می‌نویسیدش:

.card {
  padding: 16px;
  border-radius: 8px;
}

.card .title {
  font-size: 1.2rem;
}

.card .description {
  color: gray;
}

.card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

می‌بینید که .card چهار بار تکرار شده. حالا با nesting همین کد رو این‌جوری می‌نویسیم:

.card {
  padding: 16px;
  border-radius: 8px;

  .title {
    font-size: 1.2rem;
  }

  .description {
    color: gray;
  }

  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
}

همون نتیجه، ولی خیلی خواناتر و بدون تکرار.

نماد &: اشاره به خود selector والد

علامت & جایگزین selector والد می‌شه. این برای حالت‌هایی مثل pseudo-class ها، pseudo-element ها، یا وقتی می‌خواید یه کلاس دیگه رو کنار selector فعلی بذارید ضروریه:

.btn {
  background: blue;

  &:hover {
    background: darkblue;
  }

  &.disabled {
    opacity: 0.5;
    pointer-events: none;
  }

  &::before {
    content: "→";
  }
}

بدون &، اگه فقط بنویسید :hover { ... } داخل .btn، مرورگر اون رو به‌عنوان یه selector فرزند (descendant) تفسیر می‌کنه، نه حالت hover خود .btn. برای pseudo-class ها و pseudo-element ها، استفاده از & اجباریه.

Nesting بدون &: چه زمانی نیازی بهش نیست؟

وقتی selector تو در تو، یه descendant selector ساده باشه (یعنی یه selector تو دل selector دیگه، مثل مثال .title و .description بالا)، نیازی به & نیست؛ مرورگر خودش می‌فهمه که منظورتون .card .title هست:

.card {
  .title {
    font-size: 1.2rem;
  }
}

/* دقیقاً معادل: */
.card .title {
  font-size: 1.2rem;
}

Nesting با combinator ها

می‌تونید از combinator هایی مثل >، + و ~ هم داخل nesting استفاده کنید:

.list {
  & > li {
    border-bottom: 1px solid #eee;
  }

  & + .list {
    margin-top: 20px;
  }
}

Nesting با media query و سایر at-rule ها

یکی از کاربردی‌ترین بخش‌های nesting اینه که می‌تونید @media، @supports و امثالش رو هم مستقیم داخل یه selector بنویسید، بدون این‌که مجبور باشید ازش بیرون بیاید:

.sidebar {
  width: 300px;

  @media (max-width: 768px) {
    width: 100%;
  }
}

این خیلی خواناتر از روش قدیمیه که باید یه @media جدا و جدا از تعریف اصلی .sidebar می‌نوشتید و کد به‌هم مرتبط، از هم پخش می‌شد.

می‌شه چند سطح تو در تو رفت؟

بله، هیچ محدودیت فنی‌ای برای عمق nesting وجود نداره:

.card {
  .header {
    .title {
      font-weight: bold;
    }
  }
}

ولی این به این معنی نیست که باید همیشه این کار رو بکنید.

بهترین روش: عمق nesting رو معمولاً بیشتر از ۲ یا ۳ سطح نبرید. هرچی عمیق‌تر برید، specificity selector نهایی بالاتر می‌ره و override کردنش تو جاهای دیگه سخت‌تر می‌شه. علاوه بر این، کد عمیقاً تو در تو، خوندنش هم سخت‌تره.

اشتباه رایج: فراموش کردن & قبل از یه کلاس ترکیبی

یه اشتباه رایج اینه که کسی می‌خواد یه حالت مثل .card.featured رو بسازه (یعنی وقتی هر دو کلاس روی یه المنت هستن، نه یکی داخل دیگری)، ولی فراموش می‌کنه از & استفاده کنه:

.card {
  /* اشتباه: این می‌شه .card .featured (فرزند) */
  .featured {
    border: 2px solid gold;
  }

  /* درست: این می‌شه .card.featured (همون المنت) */
  &.featured {
    border: 2px solid gold;
  }
}

این فرق بین یه descendant selector و یه compound selector هست؛ و اگه به & دقت نکنید، ممکنه رفتار selector تون کاملاً چیز دیگه‌ای بشه که انتظارش رو نداشتید.

پشتیبانی مرورگرها

نیتیو CSS nesting تو مرورگرهای مدرن (Chrome، Firefox، Safari، Edge نسخه‌های اخیر) پشتیبانی می‌شه. اگه پروژه‌تون نیاز به پشتیبانی از مرورگرهای خیلی قدیمی‌تر داره، همچنان می‌تونید از Sass استفاده کنید یا با ابزارهایی مثل PostCSS این سینتکس رو به CSS معمولی تبدیل کنید.

جمع‌بندی

CSS nesting اجازه می‌ده selector ها رو تو دل هم بنویسید، بدون تکرار selector والد. علامت & جای selector والد رو می‌گیره و برای pseudo-class، pseudo-element و compound selector ها ضروریه، ولی برای descendant selector های ساده لازم نیست. می‌شه @media و at-rule های دیگه رو هم مستقیم داخل selector nest کرد. فقط حواستون باشه عمق nesting رو زیاد نبرید تا کد خوانا بمونه.