اخبار وب

CSS Nesting بومی؛ selectorها، Specificity و مثال کامل

آموزش CSS Nesting بومی با دو کارت کامل؛ تفاوت &، حالت والد و فرزند، Specificity، focus، Media Query، سازگاری مرورگر و تفاوت با Sass.

1404/11/10 2 دقیقه مطالعه 1,374 بازدید
CSS Nesting بومی؛ selectorها، Specificity و مثال کامل

CSS Nesting بومی اجازه می‌دهد بعضی قواعد مرتبط را داخل قاعده والد بنویسید. برای یک کامپوننت مثل کارت، می‌توانید سبک عنوان، متن و حالت لینک را در همان محدوده نگه دارید. این قابلیت بخشی از CSS مرورگر است و برای استفاده از خود آن الزاماً به Sass نیاز ندارید. اما شباهت نوشتاری با ابزارهای قبلی، به معنی یکسان بودن تمام قواعد، خروجی یا میزان سازگاری نیست.

هدف Nesting کوتاه کردن هر فایل به هر قیمت نیست. ساختار خوب باید رابطه selectorها را روشن نگه دارد و تغییر یک کامپوننت را آسان کند. اگر چندین سطح تو در تو بسازید، فهم قاعده نهایی سخت می‌شود و Specificity می‌تواند به شکل ناخواسته بالا برود. در این راهنما، نمونه کامل با دو کارت می‌بینید و بعد درباره &، فرزند مستقیم، حالت‌ها و مهاجرت از Sass تصمیم می‌گیریم.

Nesting چگونه selector را به والد وصل می‌کند؟

در یک قاعده تو در تو، selector داخلی در ارتباط با قاعده بیرونی تفسیر می‌شود. نماد & به selector والد اشاره می‌کند. برای مثال، وقتی داخل .card می‌نویسید & > h2، عنوانی را هدف می‌گیرید که فرزند مستقیم کارت است. این شکل نوشتن، وابستگی را صریح نشان می‌دهد و کمک می‌کند عنوان بیرون کامپوننت از آن تأثیر نگیرد.

وجود فاصله اهمیت دارد. & .badge عنصر دارای کلاس badge را داخل والد انتخاب می‌کند، اما &.is-featured خود عنصر والد را در حالتی انتخاب می‌کند که کلاس دوم را هم دارد. اگر این دو را جابه‌جا کنید، نتیجه به عنصر دیگری مربوط می‌شود یا اصلاً چیزی انتخاب نمی‌شود. برای استفاده درست باید markup را کنار CSS بخوانید، نه فقط شکل ظاهری indentation را.

مثال کامل با HTML و CSS

کد زیر را در فایل nesting-cards.html ذخیره کنید و در مرورگری با پشتیبانی CSS Nesting باز کنید. هیچ کتابخانه، تصویر یا ابزار build لازم ندارد. دو کارت وجود دارند و یکی کلاس is-featured دارد. در صفحه باریک، کارت‌ها در یک ستون دیده می‌شوند؛ از عرض 44rem به بعد دو ستون دارند. لینک هر دو کارت به یک بخش توضیحات واقعی در همان صفحه می‌رود.

<!doctype html>
<html lang="fa" dir="rtl">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>کارت با CSS Nesting</title>
  <style>
    body { margin: 0; padding: 1rem; font-family: sans-serif; color: #173d35; background: #f2f6f4; }
    .cards { display: grid; gap: 1rem; max-width: 60rem; margin: 2rem auto; }
    .card {
      padding: 1.25rem;
      border: 1px solid #a5bfb2;
      border-radius: 1rem;
      background: white;
      & > h2 { margin-top: 0; font-size: 1.25rem; }
      & > p { line-height: 1.8; }
      & .badge { display: inline-block; padding: .2rem .6rem; background: #e2f5eb; border-radius: .5rem; }
      &.is-featured { border-color: #086947; border-width: 2px; }
      & > a {
        color: #086947;
        text-underline-offset: .2em;
        &:hover { text-decoration-thickness: 2px; }
        &:focus-visible { outline: 2px solid #086947; outline-offset: 4px; }
      }
    }
    @media (min-width: 44rem) {
      .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    }
  </style>
</head>
<body>
  <main>
    <h1>دو کارت آموزشی</h1>
    <div class="cards">
      <article class="card is-featured">
        <span class="badge">منتخب</span>
        <h2>ساختار روشن CSS</h2>
        <p>سبک‌های عنوان، توضیح و لینک در محدوده همین کارت هستند.</p>
        <a href="#details">بیشتر بخوانید</a>
      </article>
      <article class="card">
        <h2>کارت دوم</h2>
        <p>هر دو کارت از ساختار یکسان استفاده می‌کنند.</p>
        <a href="#details">دیدن توضیحات</a>
      </article>
    </div>
    <section id="details">
      <h2>توضیحات نمونه</h2>
      <p>این عنوان بیرون card است و قواعد داخلی کارت آن را انتخاب نمی‌کنند.</p>
    </section>
  </main>
</body>
</html>

تمام کلاس‌هایی که در CSS استفاده شده‌اند در HTML وجود دارند: cards والد دو کارت است، card روی هر article قرار دارد و badge داخل کارت منتخب است. عنوان هر کارت فرزند مستقیم article است و لینک نیز در همان سطح قرار دارد. به همین دلیل، selectorهای & > h2 و & > a در این ساختار معنی دارند. اگر بعداً یک wrapper دور عنوان اضافه کنید، باید این رابطه را دوباره بررسی کنید.

بخش details بیرون کارت قرار دارد، بنابراین قاعده عنوان کارت آن را هدف نمی‌گیرد. این جداسازی کوچک نشان می‌دهد چرا selector محدود و روشن ارزش دارد. ساختار کامپوننت را فقط برای هماهنگ شدن با یک قطعه CSS نامشخص تغییر ندهید؛ ابتدا نقش عنصر در HTML را تعیین کنید و سپس قاعده‌ای بنویسید که همان نقش را انتخاب کند.

حالت والد با عنصر داخل والد فرق دارد

کارت منتخب در نمونه، دو کلاس card و is-featured دارد. selector بدون فاصله، یعنی &.is-featured، همین حالت را هدف می‌گیرد. اگر آن را به & .is-featured تبدیل کنید، دنبال یک عنصر فرزند با آن کلاس خواهید بود و border کارت تغییر نمی‌کند. این خطا به‌ویژه زمانی رخ می‌دهد که توسعه‌دهنده از ظاهر شبیه Sass نتیجه می‌گیرد که هر ترکیب نوشتاری همان خروجی قبلی را می‌دهد.

برای نام‌گذاری کلاس‌ها، همان قراردادی را نگه دارید که تیم می‌فهمد. اگر از کلاس‌های مستقل برای حالت استفاده می‌کنید، modifier را روی عنصر مناسب قرار دهید. اگر قرارداد دیگری دارید، رابطه selector و markup را با آن هماهنگ کنید. Nesting شما را به روش نام‌گذاری خاصی مجبور نمی‌کند، اما اشتباه در اتصال selectorها را هم خودکار اصلاح نمی‌کند.

رابطه مستقیم یا هر عمق از فرزند؟

استفاده از > انتخاب را به فرزند مستقیم محدود می‌کند. selector دارای فاصله، تمام فرزندان منطبق در عمق‌های مختلف را در نظر می‌گیرد. برای یک کارت ساده، هر دو شاید در نمونه فعلی نتیجه مشابه بدهند، ولی بعد از اضافه کردن یک کامپوننت داخلی تفاوت روشن می‌شود. یک عنوان داخل کارت تو در تو نباید بی‌دلیل سبک عنوان کارت بیرونی را بگیرد.

وقتی کامپوننت‌ها ترکیب می‌شوند، انتخاب محدودتر می‌تواند از نشت سبک جلوگیری کند. اما > را هم بدون توجه به ساختار اضافه نکنید. اگر markup در کنترل کتابخانه دیگری است و wrapper تغییر می‌کند، وابستگی به فرزند مستقیم ممکن است شکننده باشد. قرارداد ساختار و امکان تغییر آن را در تصمیم حساب کنید. مقاله CSS Grid با مثال کامل همین اهمیت رابطه والد و آیتم را در چیدمان نشان می‌دهد.

Specificity در قواعد تو در تو

Nesting به معنی کاهش خودکار Specificity نیست. selector نهایی همچنان بر اساس قواعد CSS در Cascade شرکت می‌کند و ترکیب selectorهای والد می‌تواند روی آن اثر بگذارد. به‌خصوص اگر فهرست والد شامل selectorهایی با وزن متفاوت باشد، نتیجه را مطابق قرارداد Nesting و ارتباط آن با :is بررسی کنید. توضیح دقیق این رفتار در راهنمای Nesting و Specificity آمده است.

برای تیم، راه ساده‌تر این است که بدون نیاز واقعی، IDها و زنجیره‌های طولانی را وارد سبک کامپوننت نکنید. اگر برای تغییر رنگ یک لینک مجبور به چند selector سنگین یا !important شده‌اید، احتمال دارد مرز سبک‌ها مناسب نباشد. ترتیب، Layerها و دامنه selector را بررسی کنید. افزودن یک سطح nesting دیگر، الزاماً تعارض قبلی را حل نمی‌کند.

حالت hover را با focus همراه کنید

در نمونه، لینک هم حالت hover و هم focus-visible دارد. کاربر صفحه‌کلید باید بتواند محل تمرکز را ببیند. کوتاه شدن کد با Nesting نباید باعث حذف رفتار دسترسی‌پذیر شود. اگر ظاهر hover را طراحی می‌کنید، بررسی کنید همان اطلاعات ضروری از راه دیگری برای کاربر لمسی و صفحه‌کلید در دسترس هست یا خیر.

به همین ترتیب، ارتباط یک حالت تعاملی با عنصر مناسب مهم است. تغییر سبک والد در hover ممکن است تزئینی باشد، اما نباید خواندن متن یا فهم لینک را وابسته به حرکت نشانگر کند. اطلاعات مهم را در markup و متن روشن قرار دهید. قواعد حالت برای تکمیل تجربه هستند و جای ساختار معنایی صفحه را نمی‌گیرند.

Nesting بومی جای تمام Sass را نمی‌گیرد

Sass علاوه بر Nesting، امکانات دیگری مثل variableهای زمان build، mixin و ابزارهای تولید CSS دارد. اگر پروژه فقط برای نوشتن selectorهای تو در تو از آن استفاده کرده، Nesting بومی می‌تواند بخشی از مسیر را ساده کند. اما اگر پروژه به قابلیت‌های دیگر وابسته است، حذف Sass نیازمند بررسی جداگانه است. وجود یک ویژگی بومی را با پوشش کامل تمام ابزار قبلی برابر نگیرید.

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

Media Query را کجا قرار دهیم؟

در مثال، Media Query بیرون قاعده cards آمده تا تغییر چیدمان در محل مستقلی خوانده شود. می‌توان بعضی at-ruleها را در ساختار تو در تو هم به کار برد، اما انتخاب جای آن‌ها بیشتر به خوانایی و قرارداد تیم مربوط است. بهتر است تغییر عرض برای یک بخش، میان چند سطح نامرتبط پنهان نشود. نویسنده بعدی باید بتواند بفهمد چه چیزی در عرض باریک و پهن تغییر می‌کند.

از طرف دیگر، گروه کردن تمام responsive ruleها در پایان یک فایل بسیار بزرگ هم همیشه مناسب نیست. فاصله زیاد میان قاعده پایه و تغییر آن، مرور کد را دشوار می‌کند. محدوده کامپوننت، اندازه فایل و ابزارهای تیم را در تصمیم حساب کنید. برای طراحی نقاط تغییر، مقاله Mobile First کمک می‌کند تصمیم را به چند اندازه دستگاه ثابت محدود نکنید.

سازگاری مرورگر و ابزار ساخت

قبل از استفاده در تولید، محیط‌های هدف و زنجیره build را بررسی کنید. ممکن است ابزار پروژه nesting را قبل از رسیدن به مرورگر تبدیل کند، یا CSS بومی همان‌طور ارسال شود. این دو مسیر باید روشن باشند؛ در غیر این صورت توسعه‌دهنده از رفتار مشاهده‌شده نتیجه نادرستی درباره پشتیبانی مرورگر می‌گیرد. راهنمای استفاده از CSS Nesting نمونه‌ها و قواعد اصلی را توضیح می‌دهد.

اگر یک ویژگی تزئینی در محیط قدیمی پشتیبانی نمی‌شود، تجربه پایه را قابل استفاده نگه دارید. اگر سبک حیاتی به آن وابسته است، راه تبدیل یا محدوده پشتیبانی را مشخص کنید. صرف درست دیده شدن صفحه روی رایانه توسعه‌دهنده، سیاست سازگاری پروژه محسوب نمی‌شود. نیاز کاربران و قرارداد استقرار، تعیین می‌کنند چه شکلی از CSS باید به آن‌ها برسد.

چطور nesting را خوانا نگه داریم؟

از ساختار کوتاه شروع کنید: والد کامپوننت، بخش‌های داخلی و حالت‌های واقعاً مربوط به همان بخش. اگر هر سطح برای توضیح سطح قبلی نیاز به یک جمله طولانی دارد، احتمالاً selector بیش از حد وابسته شده است. کامپوننت داخلی می‌تواند کلاس و محدوده مستقل داشته باشد. هدف این است که تغییر یک بخش، نیازمند فهم تمام درخت HTML صفحه نباشد.

CSS Nesting بومی ابزار مناسبی برای نزدیک نگه داشتن قواعد مرتبط است. ارزش آن زمانی روشن می‌شود که markup کامل، رابطه والد و فرزند و رفتار حالت‌ها معلوم باشند. در نمونه این مقاله، خواننده می‌تواند تمام صفحه را کپی کند و ببیند هر selector کدام عنصر را هدف می‌گیرد. همین وضوح را در پروژه حفظ کنید؛ کوتاه بودن فایل، بدون قابل فهم بودن رابطه‌ها، مزیت پایداری ایجاد نمی‌کند.

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!