Feature Detection و @supports؛ این بار در خدمت یه استراتژی کامل

تو فصل ۲۱ با @supports آشنا شدیم و سینتکس پایه‌ش (شرط ویژگی-مقدار، not، and/or، selector()) رو یاد گرفتیم. تو درس قبل هم دیدیم که feature detection پایه‌ی اصلی Progressive Enhancement و Graceful Degradation هست. حالا وقتشه این دو تا رو به هم وصل کنیم و ببینیم @supports دقیقاً چطور تو یه استراتژی واقعی سازگاری مرورگر به کار می‌ره، فراتر از سینتکس خامش.

یادآوری خیلی کوتاه

از فصل ۲۱ یادمونه: @supports (ویژگی: مقدار) یه بلوک کامل CSS رو شرطی می‌کنه، و همیشه باید جفت ویژگی-مقدار رو تست کنیم، نه فقط اسم ویژگی، چون ممکنه خود ویژگی شناخته‌شده باشه ولی یه مقدار خاصش نه.

Feature Detection یعنی دقیقاً چی؟ و چرا با CSS خالص ممکنه؟

Feature detection یعنی به‌جای پرسیدن «کدوم مرورگره؟»، مستقیم بپرسیم «آیا این قابلیت خاص وجود داره؟». نکته‌ی جالب اینه که @supports این کار رو دقیقاً همون‌جوری که مرورگر خودش CSS رو پردازش می‌کنه انجام می‌ده؛ یعنی هیچ حدس یا اسم‌بردنی درکار نیست، مستقیم از خود موتور رندر مرورگر سؤال می‌شه «آیا تو این declaration رو معتبر و قابل‌اجرا می‌دونی؟». همین باعث می‌شه این روش، برخلاف چک کردن دستی «User Agent» (که تو جاوااسکریپت رایجه ولی قابل جعل و ناپایداره)، همیشه دقیق و به‌روز بمونه، حتی برای مرورگرهایی که هنوز اصلاً وجود ندارن.

ساختن یه لایه‌ی Progressive Enhancement کامل با @supports

تو درس قبل، یه مثال سه‌لایه (block، flex، grid) دیدیم. حالا بیایید همون الگو رو با یه ویژگی که قبلاً کمتر بهش پرداختیم گسترش بدیم: backdrop-filter (افکت شیشه‌ی مات، که تو فصل‌های رنگ و افکت‌های بصری این دوره بهش اشاره‌ی مستقیمی نداشتیم ولی مفهومش شبیه box-shadow ست):

.modal {
  background: rgba(255, 255, 255, 0.95); /* پایه: تقریباً کاملاً مات */
}

@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
  .modal {
    background: rgba(255, 255, 255, 0.6); /* بهبود: نیمه‌شفاف چون پشتش محو می‌شه */
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px); /* پیشوند سافاری */
  }
}

نکته‌ی مهم اینجا استفاده از or برای چک کردن هم نسخه‌ی استاندارد و هم نسخه‌ی پیشونددار (-webkit-) هست؛ بعضی مرورگرها (خصوصاً نسخه‌های قدیمی‌تر Safari) یه ویژگی رو فقط با پیشوند مخصوص خودشون می‌شناسن، و یه @supports کامل باید هر دو حالت رو پوشش بده، وگرنه بخشی از کاربرها به‌اشتباه لایه‌ی پایه رو می‌بینن با این‌که مرورگرشون واقعاً از این افکت پشتیبانی می‌کنه.

ترکیب @supports با آمار Analytics: کِی واقعاً لازمه؟

تو درس قبل گفتیم تصمیم استفاده از یه ویژگی باید بر اساس آمار واقعی کاربرها باشه، نه فقط عدد جهانی. این دقیقاً جاییه که @supports وارد عمل می‌شه: اگه آمار پروژه‌تون نشون بده ۹۹٫۵٪ کاربرها از مرورگرهایی استفاده می‌کنن که یه ویژگی رو پشتیبانی می‌کنن، شاید نوشتن یه بلوک کامل @supports ارزشش رو نداشته باشه (چون هزینه‌ی نگهداری کد اضافه، بیشتر از فایده‌ش برای اون ۰٫۵٪ باقی‌مونده‌ست). اما اگه اون ویژگی برای عملکرد حیاتی سایت (نه فقط تزئین) لازمه، حتی یه درصد کوچیک هم می‌تونه توجیه‌کننده‌ی نوشتن fallback باشه؛ این دقیقاً همون تصمیمیه که باید آگاهانه و مورد‌به‌مورد گرفته بشه، نه با یه قانون ثابت برای همه‌ی پروژه‌ها.

یه الگوی رایج: @supports برای کل layout، نه تک‌تک ویژگی‌ها

یه اشتباه ظریف (که فرق داره با اشتباه رایج فصل ۲۱ که درباره‌ی نوشتن مقدار نامعتبر بود) اینه که برای هر ویژگی کوچیک یه @supports جدا بنویسیم، در حالی که اون‌ها به‌هم وابسته‌ن:

/* شکننده: اگه فقط یکی از این دو پشتیبانی بشه، chaos ایجاد می‌شه */
@supports (display: grid) {
  .layout { display: grid; }
}
@supports (gap: 1rem) {
  .layout { gap: 1rem; }
}

/* بهتر: یه بلوک واحد برای کل رفتار وابسته به هم */
@supports (display: grid) and (gap: 1rem) {
  .layout {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
  }
}

وقتی چند ویژگی به‌هم وابسته‌ن (مثل اینجا که gap فقط تو کنار display: grid معنا داره)، باید تو یه @supports واحد جمع بشن، نه پخش بشن؛ این دقیقاً همون اصلی‌یه که تو فصل ۲۱ درباره‌ی «فعال یا غیرفعال کردن یک‌جای یه ست هماهنگ از استایل‌ها» بهش اشاره کردیم، ولی اینجا با تمرکز رو استراتژی سازگاری، نه فقط سینتکس.

وقتی @supports خودش پشتیبانی نشه چی؟

یه سؤال منطقی: اگه یه مرورگر خیلی قدیمی حتی خود @supports رو نشناسه چی؟ خبر خوب اینه که @supports خودش به‌اندازه‌ی کافی قدیمیه (تقریباً تو همه‌ی مرورگرهای فعال امروزی هست) که این سناریو عملاً نادره. ولی اگه فرضاً این اتفاق بیفته، مرورگر کل بلوک @supports رو (چون at-rule ناشناخته‌ست) نادیده می‌گیره؛ یعنی نتیجه دقیقاً همون چیزیه که با یه Progressive Enhancement درست انتظار می‌ره: کاربر فقط لایه‌ی پایه (که بیرون از @supports نوشته شده) رو می‌بینه، نه یه صفحه‌ی خراب.

محدودیت مهم: @supports فقط سینتکس رو چک می‌کنه، نه رفتار درست

تو فصل ۲۱ به این اشاره کردیم که @supports فقط می‌گه «مرورگر این سینتکس رو قبول می‌کنه»، نه «این ویژگی بدون باگ اجرا می‌شه». این محدودیت وقتی با استراتژی سازگاری ترکیب می‌شه، یعنی برای ویژگی‌های خیلی جدید یا کمتر تست‌شده، حتی اگه @supports بگه true، باید جداگانه هم رو مرورگرهای واقعی (یا با ابزارهای DevTools که تو فصل قبل دیدیم) تست کنید، خصوصاً قبل از تکیه‌ی کامل روش برای یه بخش حیاتی.

نکته مهم: بهترین موقع برای نوشتن یه @supports، همون لحظه‌ای هست که برای اولین بار از یه ویژگی جدید استفاده می‌کنید، نه بعداً که یادتون رفته یا وقت ندارید. عادت به «همیشه چک کن، همیشه لایه‌ی پایه بذار» باید از همون ابتدای نوشتن کد شکل بگیره، دقیقاً همون چیزی که تو درس قبل به‌عنوان اشتباه رایج (فکر کردن به fallback فقط تو آخر کار) بهش اشاره کردیم.

اشتباه رایج: استفاده از @supports به‌جای تست واقعی روی مرورگرهای هدف

یه اشتباه رایج اینه که فکر کنیم چون یه بلوک @supports نوشتیم، دیگه نیازی به تست دستی نیست. @supports فقط تضمین می‌کنه کد شما تو مرورگرهای بدون پشتیبانی، اجرا نمی‌شه (و به‌جاش لایه‌ی پایه فعاله)؛ ولی تضمین نمی‌کنه لایه‌ی بهبود، تو مرورگرهایی که واقعاً پشتیبانی می‌کنن، دقیقاً همون‌جوری که تصور می‌کردید به نظر برسه. این دو مسئله‌ی جدا هستن و هرکدوم روش تست خودشون رو می‌خوان.

جمع‌بندی

@supports ابزار اصلی پیاده‌سازی feature detection تو CSSه و پایه‌ی فنی هر دو استراتژی Progressive Enhancement و Graceful Degradation که تو درس قبل دیدیم. برای ویژگی‌های به‌هم‌وابسته، شرط‌ها رو تو یه بلوک واحد جمع کنید نه پخش؛ همیشه هم نسخه‌ی استاندارد و هم نسخه‌ی پیشونددار رو با or پوشش بدید؛ و تصمیم بگیرید که آیا واقعاً بر اساس آمار پروژه‌تون به یه fallback نیاز دارید یا نه. یادتون باشه @supports فقط سینتکس رو تضمین می‌کنه، نه رفتار بدون‌باگ، پس تست دستی هنوز جای خودش رو داره.