استعلامات الميزات مع @supports
ما هي استعلامات الميزات؟
استعلامات الميزات هي آلية CSS تتيح لك اختبار ما إذا كان المتصفح يدعم زوجا معينا من خاصية-قيمة CSS قبل تطبيق مجموعة من الأنماط. تستخدم قاعدة @supports، التي تعمل بشكل مشابه لـ @media لكن بدلا من اختبار خصائص منفذ العرض، تختبر دعم ميزات CSS. إذا كان المتصفح يدعم الميزة، يتم تطبيق الأنماط داخل كتلة @supports. إذا لم يكن كذلك، يتم تجاهلها بصمت.
تحل استعلامات الميزات مشكلة أساسية في تطوير الويب: كيف تستخدم ميزات CSS الجديدة مع ضمان حصول المتصفحات القديمة على تجربة قابلة للاستخدام؟ قبل @supports، اعتمد المطورون على مكتبات كشف الميزات المبنية على JavaScript مثل Modernizr، أو استخدموا حيلا وحلولا بديلة لاكتشاف قدرات المتصفح. مع @supports، كشف الميزات مدمج مباشرة في CSS -- إنه سريع وموثوق ولا يتطلب أي JavaScript.
المفهوم وراء استعلامات الميزات يسمى التحسين التدريجي: تبدأ بتجربة أساسية تعمل في كل مكان، ثم تضيف طبقة من الأنماط المحسنة للمتصفحات التي تدعم الميزات المتقدمة. يضمن هذا النهج عدم حصول أي مستخدم على تخطيط معطل، بينما يحصل المستخدمون ذوو المتصفحات الحديثة على أفضل تجربة ممكنة. في هذا الدرس، ستتقن كل جانب من جوانب @supports، من الصياغة الأساسية إلى الأنماط المتقدمة لبناء أوراق أنماط قوية ومستقبلية.
صياغة @supports الأساسية
أبسط شكل لـ @supports يختبر ما إذا كان المتصفح يفهم تصريح CSS معينا (زوج خاصية-قيمة). إذا تعرف المتصفح على الخاصية واعتبر القيمة صالحة، يتم تقييم الشرط كصحيح وتُطبق الأنماط داخل الكتلة.
صياغة @supports الأساسية
/* اختبار إذا كان المتصفح يدعم display: grid */
@supports (display: grid) {
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
/* اختبار إذا كان المتصفح يدعم backdrop-filter */
@supports (backdrop-filter: blur(10px)) {
.glass-panel {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
}
/* اختبار إذا كان المتصفح يدعم aspect-ratio */
@supports (aspect-ratio: 16 / 9) {
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
}
/* اختبار إذا كان المتصفح يدعم دالة clamp() */
@supports (font-size: clamp(1rem, 2vw, 2rem)) {
.responsive-text {
font-size: clamp(1rem, 1.5vw + 0.5rem, 2rem);
}
}
@supports تصريح CSS كاملا ملفوفا بأقواس -- الخاصية والقيمة معا. لا يمكنك اختبار خاصية وحدها بدون قيمة (مثلا، @supports (display) غير صالح). يتحقق المتصفح مما إذا كان يستطيع تحليل وقبول ذلك التصريح المحدد. لا يطبق فعليا تصريح الاختبار على أي عنصر؛ فقط يتحقق مما إذا كان يفهمه.أهمية القيمة
القيمة التي تختبرها مهمة. قد يدعم المتصفح خاصية لكن ليس قيمة محددة لتلك الخاصية. على سبيل المثال، جميع المتصفحات تدعم display، لكن ليست جميعها تدعم display: grid أو display: subgrid. اختبر دائما تركيبة الخاصية-القيمة الدقيقة التي تنوي استخدامها.
اختبار قيم محددة
/* هذا يختبر إذا كان grid مدعوما */
@supports (display: grid) {
/* تخطيط شبكي حديث */
}
/* هذا يختبر إذا كان subgrid مدعوما (ميزة أحدث) */
@supports (grid-template-columns: subgrid) {
.nested-grid {
display: grid;
grid-template-columns: subgrid;
}
}
/* اختبار دالة ألوان محددة */
@supports (color: oklch(0.7 0.15 180)) {
:root {
--primary: oklch(0.6 0.2 250);
--secondary: oklch(0.7 0.15 180);
}
}
/* اختبار دعم استعلامات الحاوية */
@supports (container-type: inline-size) {
.card-wrapper {
container-type: inline-size;
}
}
/* اختبار الحركات المدفوعة بالتمرير */
@supports (animation-timeline: scroll()) {
.progress-bar {
animation-timeline: scroll();
}
}
المعاملات المنطقية: not وand وor
تدعم استعلامات الميزات ثلاثة معاملات منطقية تتيح لك إنشاء شروط معقدة. تعمل هذه المعاملات بشكل مشابه لنظيراتها في استعلامات @media وتتبع نفس قواعد الأسبقية.
معامل not
معامل not ينفي شرطا. إنه مفيد لتوفير أنماط بديلة عندما لا تكون ميزة مدعومة. يجب أن تتبع كلمة not الشرط بين أقواس.
استخدام معامل not
/* تطبيق أنماط عندما لا يكون grid مدعوما */
@supports not (display: grid) {
.container {
display: flex;
flex-wrap: wrap;
}
.container > * {
flex: 0 0 calc(33.333% - 2rem);
margin: 1rem;
}
}
/* بديل عندما لا يتوفر backdrop-filter */
@supports not (backdrop-filter: blur(10px)) {
.glass-panel {
background: rgba(255, 255, 255, 0.85);
/* خلفية شبه شفافة صلبة كبديل */
}
}
/* بديل عندما لا يكون :has() مدعوما */
@supports not (selector(:has(*))) {
/* بدائل مبنية على JavaScript أو تنسيق أبسط */
.parent-with-child {
border: 2px solid blue;
}
}
معامل and
معامل and يجمع عدة شروط يجب أن تكون جميعها صحيحة. هذا مفيد عندما تعتمد أنماطك المحسنة على توفر عدة ميزات في نفس الوقت.
استخدام معامل and
/* يجب دعم كل من grid وgap */
@supports (display: grid) and (gap: 1rem) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
}
/* يتطلب كلا من الخصائص المخصصة وclamp() */
@supports (--custom: value) and (font-size: clamp(1rem, 2vw, 3rem)) {
:root {
--heading-size: clamp(1.5rem, 3vw + 0.5rem, 3rem);
--body-size: clamp(1rem, 1.2vw + 0.5rem, 1.25rem);
}
h1 { font-size: var(--heading-size); }
body { font-size: var(--body-size); }
}
/* شروط متعددة لتحسين معقد */
@supports (display: grid) and (aspect-ratio: 1) and (object-fit: cover) {
.image-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
}
.image-grid img {
aspect-ratio: 1;
object-fit: cover;
width: 100%;
}
}
معامل or
معامل or يكون صحيحا عندما يتحقق شرط واحد على الأقل. هذا مفيد بشكل خاص عندما تحتوي ميزة على نسخ مسبقة من البائع وتريد التحقق مما إذا كانت أي منها مدعومة.
استخدام معامل or
/* التحقق من أي نسخة مدعومة من backdrop-filter */
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
.frosted-glass {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
}
/* التحقق من أي دعم لتثبيت الموضع */
@supports (position: sticky) or (position: -webkit-sticky) {
.sticky-header {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 100;
}
}
/* التحقق من ميزات تخطيط حديثة مختلفة */
@supports (display: grid) or (display: flex) {
.layout {
/* على الأقل flex أو grid متاح */
}
}
دمج المعاملات
يمكنك دمج معاملات not وand وor، لكن يجب استخدام الأقواس لجعل التجميع صريحا. لا يسمح CSS بمزج and وor بدون أقواس لتجنب الغموض.
دمج معاملات متعددة
/* تجميع صريح بالأقواس */
@supports ((display: grid) and (gap: 1rem)) or (display: flex) {
/* شبكة مع فجوة، أو على الأقل فليكس بوكس */
.modern-layout {
/* الأنماط هنا */
}
}
/* not مدمج مع and */
@supports (display: grid) and (not (grid-template-columns: subgrid)) {
/* الشبكة مدعومة، لكن الشبكة الفرعية ليست كذلك */
.fallback-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/* تحجيم أعمدة يدوي بدل الشبكة الفرعية */
}
}
/* استعلام متعدد الشروط معقد */
@supports ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)))
and (not (hanging-punctuation: first)) {
/* يوجد دعم ضبابية لكن ليس علامات الترقيم المعلقة */
}
and وor على نفس المستوى بدون أقواس تجميع صريحة. الاستعلام @supports (a: b) and (c: d) or (e: f) غير صالح. يجب أن تكتب إما @supports ((a: b) and (c: d)) or (e: f) أو @supports (a: b) and ((c: d) or (e: f)). سيتجاهل المتصفح كتلة @supports بأكملها إذا كانت صياغة الشرط غير سليمة.اختبار دعم المحددات مع @supports selector()
بالإضافة إلى اختبار أزواج الخاصية-القيمة، يمكن لـ @supports أيضا اختبار ما إذا كان المتصفح يفهم محدد CSS معينا. يستخدم هذا دالة selector() داخل شرط @supports. هذا قيم بشكل خاص للأصناف الزائفة الأحدث مثل :has() و:is() و:where().
اختبار دعم المحددات
/* اختبار إذا كان المتصفح يدعم :has() */
@supports selector(:has(*)) {
/* استخدام :has() لتحديد الأب */
.form-group:has(:invalid) {
border-left: 3px solid #e74c3c;
padding-left: 1rem;
}
.card:has(img) {
grid-template-rows: auto 1fr auto;
}
.nav:has(.dropdown:hover) {
background: rgba(0, 0, 0, 0.05);
}
}
/* اختبار دعم :focus-visible */
@supports selector(:focus-visible) {
button:focus {
outline: none;
}
button:focus-visible {
outline: 2px solid #3498db;
outline-offset: 2px;
}
}
/* اختبار دعم :is() */
@supports selector(:is(a, b)) {
:is(h1, h2, h3, h4) {
line-height: 1.2;
margin-bottom: 0.5em;
}
:is(article, section, aside) :is(h1, h2, h3) {
color: #333;
}
}
/* اختبار دعم :user-valid / :user-invalid */
@supports selector(:user-valid) {
input:user-valid {
border-color: #2ecc71;
}
input:user-invalid {
border-color: #e74c3c;
}
}
selector() ما إذا كان المتصفح يستطيع تحليل المحدد، وليس ما إذا كانت أي عناصر تطابقه حاليا. حتى لو كان :has(*) سيطابق صفر عناصر على الصفحة، فإن شرط @supports selector(:has(*)) يُقيم كصحيح طالما أن المتصفح يفهم الصنف الزائف :has().التحسين التدريجي مع @supports
التحسين التدريجي هو الفلسفة الأساسية وراء استعلامات الميزات. تكتب خطا أساسيا يعمل في جميع المتصفحات، ثم تعزز التجربة للمتصفحات التي تدعم الميزات المتقدمة. هذا يضمن عدم حصول أي شخص على تجربة معطلة بينما تحصل المتصفحات الحديثة على أفضل نسخة.
نمط التحسين التدريجي
/* الخطوة 1: الأساس -- يعمل في كل متصفح */
.card-grid {
display: block;
}
.card-grid .card {
max-width: 400px;
margin: 0 auto 2rem;
}
/* الخطوة 2: تحسين لفليكس بوكس (دعم واسع جدا) */
@supports (display: flex) {
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card-grid .card {
flex: 1 1 300px;
max-width: none;
margin: 0;
}
}
/* الخطوة 3: تحسين إضافي للشبكة (دعم واسع) */
@supports (display: grid) {
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
.card-grid .card {
flex: unset;
}
}
/* الخطوة 4: تحسين إضافي للشبكة الفرعية */
@supports (grid-template-columns: subgrid) {
.card-grid .card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
}
في هذا المثال، كل كتلة @supports تضيف طبقة من التحسين. المتصفحات الأساسية ترى بطاقات مكدسة. المتصفحات القادرة على فليكس بوكس ترى تخطيطا متعدد الأعمدة مرنا. المتصفحات القادرة على الشبكة تحصل على تخطيط ملاءمة تلقائية أفضل. ومتصفحات الشبكة الفرعية تحصل على محتوى بطاقات محاذى تماما عبر الشبكة. كل مستخدم يحصل على تخطيط وظيفي؛ المستخدمون الحديثون يحصلون على الأفضل.
التخطيطات البديلة: بديل فليكس بوكس للشبكة
أحد أكثر حالات الاستخدام شيوعا لـ @supports هو توفير بديل مبني على فليكس بوكس لتخطيطات شبكة CSS. بينما دعم الشبكة الآن شبه عالمي تقريبا، يوضح هذا النمط التقنية جيدا وما زال ذا صلة لميزات مثل الشبكة الفرعية.
بديل فليكس بوكس للشبكة
/* تخطيط بديل مع فليكس بوكس */
.dashboard {
display: flex;
flex-wrap: wrap;
margin: -0.75rem;
}
.dashboard .widget {
flex: 1 1 300px;
margin: 0.75rem;
}
.dashboard .widget--wide {
flex: 1 1 100%;
}
/* تخطيط محسن مع الشبكة */
@supports (display: grid) {
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin: 0;
}
.dashboard .widget {
margin: 0;
}
.dashboard .widget--wide {
grid-column: 1 / -1;
}
.dashboard .widget--tall {
grid-row: span 2;
}
}
كشف الميزات مقابل كشف المتصفح
كشف الميزات وكشف المتصفح نهجان مختلفان جذريا للتعامل مع التوافق عبر المتصفحات. فهم لماذا كشف الميزات أفضل أمر ضروري لكتابة CSS قابل للصيانة.
كشف المتصفح (يسمى أيضا "استنشاق وكيل المستخدم") يتضمن التحقق من المتصفح الذي يستخدمه المستخدم وتطبيق أنماط محددة بناء على اسم المتصفح وإصداره. هذا النهج هش وصعب الصيانة وعرضة للأخطاء.
كشف الميزات يتضمن التحقق مما إذا كانت قدرة محددة متاحة، بغض النظر عن المتصفح الذي يوفرها. هذا ما يفعله @supports. إنه أكثر قوة لأنه يسأل "هل تستطيع فعل هذا؟" بدلا من "من أنت؟"
كشف الميزات مقابل كشف المتصفح
/* سيء: كشف المتصفح بحيل CSS */
/* هذه تستهدف متصفحات محددة وتنكسر بسهولة */
/* استهداف Safari فقط (حيلة هشة) */
@media not all and (min-resolution: 0.001dpcm) {
/* أنماط Safari فقط -- لا تفعل هذا */
}
/* جيد: كشف الميزات مع @supports */
/* اختبر القدرة الفعلية، وليس المتصفح */
@supports (backdrop-filter: blur(10px)) {
/* أي متصفح يدعم هذا يحصل على النمط */
.overlay {
backdrop-filter: blur(10px);
background: rgba(0, 0, 0, 0.3);
}
}
@supports not (backdrop-filter: blur(10px)) {
/* أي متصفح لا يدعم هذا يحصل على البديل */
.overlay {
background: rgba(0, 0, 0, 0.7);
}
}
/* لماذا كشف الميزات أفضل:
1. المتصفحات الجديدة تحصل تلقائيا على الأنماط الصحيحة
2. إذا أضاف متصفح دعما، يعمل فورا
3. لا تحتاج لتحديث قائمة المتصفحات
4. يختبر القدرة الفعلية، وليس الهوية */
@supports هو النهج الصحيح.أنماط @supports الشائعة
لنستكشف أكثر الأنماط شيوعا في العالم الحقيقي لاستخدام @supports. هذه أنماط ستلجأ إليها بشكل متكرر في كود الإنتاج.
اختبار دعم الشبكة
استعلامات ميزات الشبكة
/* دعم الشبكة الأساسي */
@supports (display: grid) {
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
}
/* دعم الشبكة الفرعية */
@supports (grid-template-columns: subgrid) {
.card-grid .card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
}
}
/* تخطيط البناء (تجريبي) */
@supports (grid-template-rows: masonry) {
.masonry-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-template-rows: masonry;
gap: 1rem;
}
}
اختبار clamp() والدوال الحديثة
دوال CSS الحديثة
/* clamp() للطباعة المتدفقة */
@supports (font-size: clamp(1rem, 2vw, 3rem)) {
h1 {
font-size: clamp(2rem, 4vw + 0.5rem, 4rem);
}
p {
font-size: clamp(1rem, 1.2vw + 0.5rem, 1.25rem);
}
}
/* بديل للمتصفحات بدون clamp() */
@supports not (font-size: clamp(1rem, 2vw, 3rem)) {
h1 {
font-size: 2rem;
}
@media (min-width: 768px) {
h1 {
font-size: 3rem;
}
}
@media (min-width: 1200px) {
h1 {
font-size: 4rem;
}
}
}
/* دوال min() وmax() */
@supports (width: min(90%, 1200px)) {
.container {
width: min(90%, 1200px);
margin-inline: auto;
}
}
اختبار backdrop-filter
تأثير الزجاج المصنفر مع بديل
/* الأنماط الأساسية (البديل) */
.modal-overlay {
background: rgba(0, 0, 0, 0.7);
}
.glass-card {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(255, 255, 255, 0.3);
}
/* محسن مع backdrop-filter */
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
.modal-overlay {
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.glass-card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.2);
}
}
اختبار دعم :has()
تحديد الأب مع بديل :has()
/* البديل: الاعتماد على أصناف مضافة بـ JavaScript */
.form-group.has-error {
border-left: 3px solid #e74c3c;
}
.card.has-image {
display: grid;
grid-template-columns: 200px 1fr;
}
/* محسن: استخدام :has() للكشف التلقائي */
@supports selector(:has(*)) {
.form-group:has(:invalid:not(:placeholder-shown)) {
border-left: 3px solid #e74c3c;
}
.form-group:has(:focus) {
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
.card:has(img) {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@supports مع الخصائص المخصصة
يمكنك اختبار ما إذا كان المتصفح يدعم خصائص CSS المخصصة (متغيرات CSS) باستخدام @supports. بما أن الخصائص المخصصة مدعومة الآن عالميا في المتصفحات الحديثة، فهذا مفيد بشكل رئيسي كشبكة أمان لدعم المتصفحات القديمة.
اختبار دعم الخصائص المخصصة
/* اختبار إذا كانت الخصائص المخصصة مدعومة */
@supports (--custom: value) {
:root {
--primary: #3498db;
--secondary: #2ecc71;
--text: #333;
--bg: #ffffff;
--spacing: 1.5rem;
--radius: 8px;
}
.button {
background: var(--primary);
color: white;
padding: var(--spacing);
border-radius: var(--radius);
}
}
/* بديل للمتصفحات بدون خصائص مخصصة */
@supports not (--custom: value) {
.button {
background: #3498db;
color: white;
padding: 1.5rem;
border-radius: 8px;
}
}
CSS.supports() في JavaScript
وظيفة @supports متاحة أيضا في JavaScript من خلال دالة CSS.supports(). يتيح لك هذا تشغيل نفس كشف الميزات في سكريبتاتك، وهو مفيد لإضافة أصناف أو تحميل البوليفيل أو الاختيار بين تطبيقات JavaScript مختلفة بناء على قدرات CSS.
استخدام CSS.supports() في JavaScript
// الطريقة 1: وسيطتان (خاصية، قيمة)
const hasGrid = CSS.supports('display', 'grid');
const hasSubgrid = CSS.supports('grid-template-columns', 'subgrid');
const hasBackdropFilter = CSS.supports('backdrop-filter', 'blur(10px)');
console.log('Grid:', hasGrid);
console.log('Subgrid:', hasSubgrid);
console.log('Backdrop:', hasBackdropFilter);
// الطريقة 2: وسيطة واحدة (سلسلة شرط كاملة)
const hasGridAndGap = CSS.supports('(display: grid) and (gap: 1rem)');
// الطريقة 3: اختبار دعم المحددات
const hasHas = CSS.supports('selector(:has(*))');
const hasFocusVisible = CSS.supports('selector(:focus-visible)');
// الاستخدام العملي: إضافة أصناف ميزات إلى الجسم
if (CSS.supports('display', 'grid')) {
document.body.classList.add('supports-grid');
}
if (CSS.supports('selector(:has(*))')) {
document.body.classList.add('supports-has');
} else {
// تحميل بوليفيل أو استخدام تحديد أب مبني على JavaScript
loadScript('has-polyfill.js');
}
// كشف الميزات قبل تطبيق أنماط ديناميكية
function applyGlassEffect(element) {
if (CSS.supports('backdrop-filter', 'blur(10px)')
|| CSS.supports('-webkit-backdrop-filter', 'blur(10px)')) {
element.style.backdropFilter = 'blur(10px)';
element.style.background = 'rgba(255, 255, 255, 0.1)';
} else {
element.style.background = 'rgba(255, 255, 255, 0.85)';
}
}
CSS.supports() في JavaScript متزامنة وسريعة جدا. لا تسبب إعادة حساب التخطيط أو إعادة التدفق. يمكنك استدعاؤها بأمان عدة مرات أثناء تهيئة الصفحة لإعداد أعلام الميزات. فكر في إنشاء كائن مساعد يخزن النتائج مؤقتا لإعادة الاستخدام في جميع أنحاء تطبيقك.دمج @supports مع @media
يمكن استخدام استعلامات الميزات واستعلامات الوسائط معا لإنشاء أنماط عالية الاستهداف تعتمد على كل من قدرات المتصفح وخصائص منفذ العرض. يمكنك تداخلها داخل بعضها بأي ترتيب.
دمج @supports و@media
/* @media داخل @supports */
@supports (display: grid) {
.product-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 600px) {
.product-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 900px) {
.product-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 1200px) {
.product-grid {
grid-template-columns: repeat(4, 1fr);
}
}
}
/* @supports داخل @media */
@media (min-width: 768px) {
@supports (backdrop-filter: blur(10px)) {
.sticky-nav {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
}
}
@supports not (backdrop-filter: blur(10px)) {
.sticky-nav {
background: rgba(255, 255, 255, 0.95);
}
}
}
/* الدمج مع prefers-color-scheme */
@supports (color: oklch(0.5 0.2 240)) {
@media (prefers-color-scheme: dark) {
:root {
--bg: oklch(0.15 0.02 260);
--text: oklch(0.9 0.02 260);
--primary: oklch(0.65 0.25 250);
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: oklch(0.98 0.01 260);
--text: oklch(0.2 0.02 260);
--primary: oklch(0.55 0.2 250);
}
}
}
متى لا تستخدم @supports
بينما @supports قوي، هناك حالات يكون فيها استخدامه غير ضروري أو زائدا أو حتى نتائجه عكسية. معرفة متى تتخطى استعلامات الميزات مهمة بنفس قدر معرفة كيفية استخدامها.
عندما تتدهور الخاصية بأمان
العديد من خصائص CSS متوافقة مع الإصدارات السابقة بشكل طبيعي. إذا لم يفهم المتصفح خاصية، فإنه ببساطة يتجاهل التصريح وينتقل. في هذه الحالات، يضيف @supports تعقيدا بدون فائدة.
حالات يكون فيها @supports غير ضروري
/* غير ضروري: border-radius يتدهور بأمان */
/* المتصفحات القديمة تظهر فقط زوايا مربعة */
@supports (border-radius: 8px) {
.card { border-radius: 8px; }
}
/* فقط اكتب: */
.card { border-radius: 8px; }
/* غير ضروري: box-shadow يتدهور بأمان */
.card { box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
/* غير ضروري: الخصائص المخصصة عند استخدامها مع بدائل */
.card {
color: #333; /* بديل */
color: var(--text); /* تحسين */
/* سطر البديل يتعامل مع المتصفحات غير المدعومة */
}
/* غير ضروري: عندما يكون الدعم عالميا تقريبا */
/* فليكس بوكس مدعوم من 99%+ من المتصفحات */
.nav { display: flex; }
متى يجب استخدام @supports
حالات يكون فيها @supports قيما
/* قيم: عندما يغير التحسين نموذج التخطيط بالكامل */
.container { display: flex; flex-wrap: wrap; }
.container .item { flex: 0 0 calc(33.333% - 2rem); margin: 1rem; }
@supports (display: grid) {
.container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; }
.container .item { margin: 0; }
}
/* قيم: عندما يحتاج البديل خصائص مختلفة تماما */
@supports not (aspect-ratio: 16 / 9) {
.video-wrapper {
position: relative;
padding-bottom: 56.25%;
height: 0;
}
.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
@supports (aspect-ratio: 16 / 9) {
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
.video-wrapper iframe {
width: 100%;
height: 100%;
}
}
/* قيم: عند اختبار ميزات أحدث بدعم محدود */
@supports (grid-template-rows: masonry) {
/* تخطيط البناء */
}
@supports selector(:has(*)) {
/* تحديد الأب */
}
@supports عندما يتطلب البديل خصائص CSS مختلفة أو هياكل تخطيط مختلفة، وتخطاه عندما لا يكون للخاصية غير المدعومة أي تأثير بصري (تدهور أمان). إذا كانت إزالة تصريح خاصية واحد من CSS الخاص بك سينتج عنه تخطيط مقبول، فربما لا تحتاج @supports.أمثلة عملية للتحسين التدريجي
لنبني عدة أمثلة واقعية توضح كيفية استخدام @supports للتحسين التدريجي في كود الإنتاج.
المثال 1: شريط تنقل حديث
شريط تنقل تدريجي
/* الأساس: تنقل بسيط */
.main-nav {
background: white;
padding: 1rem 2rem;
border-bottom: 1px solid #eee;
}
.nav-links {
list-style: none;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.nav-links a {
color: #333;
text-decoration: none;
padding: 0.5rem 1rem;
}
/* تحسين: ثابت مع ضبابية خلفية */
@supports (position: sticky) {
.main-nav {
position: sticky;
top: 0;
z-index: 100;
}
}
@supports (backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px)) {
.main-nav {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
}
/* تحسين: :has() لمؤشر الحالة النشطة */
@supports selector(:has(*)) {
.nav-links li:has(a[aria-current="page"]) {
border-bottom: 2px solid #3498db;
}
}
المثال 2: معرض صور متجاوب
معرض صور تدريجي
/* الأساس: تخطيط عمودي بسيط */
.gallery {
column-count: 2;
column-gap: 1rem;
}
.gallery img {
width: 100%;
display: block;
border-radius: 8px;
}
/* تحسين: تخطيط شبكي */
@supports (display: grid) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
column-count: unset;
}
}
/* تحسين: aspect-ratio لتحجيم متسق */
@supports (aspect-ratio: 4 / 3) {
.gallery img {
aspect-ratio: 4 / 3;
object-fit: cover;
}
}
/* تحسين: البناء للارتفاعات الديناميكية */
@supports (grid-template-rows: masonry) {
.gallery {
grid-template-rows: masonry;
}
.gallery img {
aspect-ratio: unset;
}
}
/* تحسين: استعلامات الحاوية لعناصر المعرض */
@supports (container-type: inline-size) {
.gallery .gallery-item {
container-type: inline-size;
}
@container (min-width: 300px) {
.gallery-caption {
display: flex;
justify-content: space-between;
align-items: center;
}
}
}
المثال 3: تنسيق نماذج حديث
تحسينات تدريجية للنماذج
/* الأساس: أنماط نموذج نظيفة */
.form-field {
margin-bottom: 1.5rem;
}
.form-field label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: #333;
}
.form-field input,
.form-field textarea {
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1rem;
}
.form-field input:focus,
.form-field textarea:focus {
border-color: #3498db;
outline: none;
}
/* تحسين: :focus-visible لتجربة أفضل */
@supports selector(:focus-visible) {
.form-field input:focus,
.form-field textarea:focus {
outline: none;
border-color: #ddd;
}
.form-field input:focus-visible,
.form-field textarea:focus-visible {
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
}
/* تحسين: :has() لتنسيق واعي بالأب */
@supports selector(:has(*)) {
.form-field:has(input:focus-visible) label {
color: #3498db;
}
.form-field:has(:valid:not(:placeholder-shown)) label {
color: #2ecc71;
}
}
/* تحسين: accent-color لعناصر التحكم الأصلية */
@supports (accent-color: auto) {
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
accent-color: #3498db;
}
}
تصحيح أخطاء استعلامات @supports
عندما لا تعمل استعلامات @supports كما هو متوقع، إليك تقنيات لتصحيح أخطائها.
تقنيات التصحيح
// 1. استخدم أدوات المطور في المتصفح للتحقق من تقييم @supports
// في أدوات Chrome DevTools، يمكنك رؤية كتل @supports في لوحة الأنماط
// 2. استخدم JavaScript لاختبار شرط بسرعة
console.log('Grid:', CSS.supports('display', 'grid'));
console.log('Subgrid:', CSS.supports('grid-template-columns', 'subgrid'));
console.log(':has():', CSS.supports('selector(:has(*))'));
console.log('Nesting:', CSS.supports('selector(&)'));
/* 3. استخدم مؤشرا مرئيا للتصحيح */
@supports (display: grid) {
body::after {
content: "Grid: YES";
position: fixed;
bottom: 10px;
right: 10px;
background: green;
color: white;
padding: 0.5rem 1rem;
border-radius: 4px;
font-size: 12px;
z-index: 99999;
}
}
/* 4. أخطاء شائعة للتحقق منها */
/* خطأ: اختبار خاصية بدون قيمة */
@supports (display) { /* غير صالح */ }
/* خطأ: أقواس مفقودة حول الشرط */
@supports display: grid { /* غير صالح */ }
/* صحيح: تصريح كامل بين أقواس */
@supports (display: grid) { /* صالح */ }
@supports خطأ في الصياغة (مثل أقواس مفقودة أو شرط غير صالح)، يتم تجاهل الكتلة بأكملها بصمت. لن يرمي المتصفح خطأ أو يحذرك. هذا يجعل من المهم التحقق مرتين من صياغة @supports بعناية. استخدم أدوات المطور في المتصفح للتحقق من أن الكتلة يتم تقييمها بشكل صحيح.التمرين 1: بناء ورقة أنماط تحسين تدريجي
أنشئ ورقة أنماط لصفحة منشور مدونة تتحسن تدريجيا من الأساس إلى أحدث CSS. ابدأ بتخطيط أساسي باستخدام display: block فقط وطباعة أساسية. الطبقة 1: استخدم @supports (display: flex) لإنشاء رأس مبني على فليكس بوكس مع شعار وتنقل. الطبقة 2: استخدم @supports (display: grid) لإنشاء تخطيط مبني على الشبكة مع شريط جانبي ومنطقة محتوى رئيسية. الطبقة 3: استخدم @supports (backdrop-filter: blur(10px)) لإضافة رأس ثابت بزجاج مصنفر. الطبقة 4: استخدم @supports selector(:has(*)) لتمييز عنصر جدول المحتويات للقسم المرئي حاليا. الطبقة 5: استخدم @supports (animation-timeline: scroll()) لإضافة شريط تقدم قراءة مدفوع بالتمرير. لكل طبقة، يجب أن يكون البديل مقبولا بصريا. اختبر استعلامات الميزات باستخدام CSS.supports() في وحدة تحكم المتصفح وتحقق من أن كل طبقة مستقلة -- تعطيل واحدة لا ينبغي أن يكسر الأخريات.
التمرين 2: إنشاء مكتبة مكونات واعية بالميزات
ابنِ ثلاثة مكونات واجهة مستخدم يستخدم كل منها @supports للتحسين التدريجي. المكون 1: .glass-card يستخدم backdrop-filter لتأثير الزجاج المصنفر عند دعمه، مع العودة إلى خلفية صلبة شبه شفافة. قم بتضمين فحوصات البائع المسبقة مع معامل or. المكون 2: .smart-form يستخدم @supports selector(:has(*)) لتنسيق مجموعات النماذج بناء على حالات المدخلات (تركيز، صالح، غير صالح). بدون دعم :has()، استخدم نهجا مبنيا على الأصناف كبديل. المكون 3: .fluid-grid يبدأ بتخطيط فليكس بوكس، يترقى إلى الشبكة مع @supports (display: grid)، ويترقى أكثر إلى الشبكة الفرعية مع @supports (grid-template-columns: subgrid) لمحتوى بطاقات محاذى تماما. لكل مكون، قم بتضمين مقتطف JavaScript يستخدم CSS.supports() يسجل مستوى التحسين الذي يستخدمه المتصفح. ادمج @supports مع استعلامات @media بحيث يكون مكون الشبكة عمودا واحدا على الهاتف بغض النظر عن دعم الشبكة.