انتقالات CSS
ما هي انتقالات CSS؟
توفر انتقالات CSS طريقة للتحكم في سرعة وتوقيت تغييرات الخصائص على العنصر. بدون الانتقالات، عندما تغير خاصية CSS -- مثل تغيير لون خلفية الزر عند التمرير -- يحدث التغيير فورياً في إطار واحد. مع الانتقالات، يمكنك جعل هذا التغيير يحدث تدريجياً على مدة محددة، مما يخلق حركات سلسة ومصقولة تجعل واجهتك تبدو متجاوبة وحية.
الانتقالات هي واحدة من أبسط وأكثر الأدوات تأثيراً في مجموعة أدوات CSS الخاصة بك. لا تتطلب أي JavaScript ولا تعريفات إطارات مفتاحية ولا مكتبات حركة. ببساطة تعلن عن الخصائص التي يجب أن تنتقل ومدة الانتقال واختيارياً دالة التوقيت والتأخير المراد استخدامهما. يتعامل المتصفح مع جميع الإطارات الوسيطة (المسماة الاستيفاء) تلقائياً. هذا يجعل الانتقالات الخيار المثالي لتغييرات الحالة التفاعلية مثل تأثيرات التمرير وأنماط التركيز والحالات النشطة وتبديل الفئات.
من المهم فهم أن الانتقالات تفاعلية -- تستجيب للتغييرات في قيم الخصائص. لا تعمل من تلقاء نفسها مثل حركات CSS. يحتاج الانتقال إلى محفز: تغيير حالة ناتج عن فئة زائفة مثل :hover أو :focus، أو إضافة أو إزالة فئة عبر JavaScript، أو أي آلية أخرى تغير قيمة خاصية. ثم يستوفي الانتقال بسلاسة بين القيمة القديمة والقيمة الجديدة.
خاصية transition-property
خاصية transition-property تحدد خصائص CSS التي يجب تحريكها عند تغيير قيمها. يمكنك استهداف خاصية واحدة أو عدة خصائص مفصولة بفواصل أو استخدام الكلمة المفتاحية all لنقل كل خاصية قابلة للحركة دفعة واحدة.
تحديد خصائص الانتقال
/* انتقال خاصية واحدة */
.button {
background-color: #3498db;
transition-property: background-color;
}
.button:hover {
background-color: #2980b9;
}
/* انتقال عدة خصائص محددة */
.card {
background-color: white;
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition-property: transform, box-shadow, background-color;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
background-color: #f8f9fa;
}
/* انتقال جميع الخصائص القابلة للحركة */
.element {
transition-property: all;
}
/* استخدام "none" لتعطيل جميع الانتقالات */
.no-transitions {
transition-property: none;
}
transition-property: all في كود الإنتاج ما لم تكن تريد تحديداً تحريك كل خاصية. عندما تستخدم all، أي تغيير خاصية -- بما في ذلك التغييرات التي لم تنوِ تحريكها -- سينتقل. هذا يمكن أن يسبب حركات غير متوقعة، والأهم، مشاكل في الأداء. قم دائماً بإدراج الخصائص المحددة التي تريد نقلها.خاصية transition-duration
خاصية transition-duration تحدد المدة التي يستغرقها الانتقال ليكتمل، محددة بالثواني (s) أو الملي ثانية (ms). المدة 0s تعني أن التغيير فوري (بدون انتقال). المدة المثالية تعتمد على نوع التفاعل وحجم التغيير الجاري تحريكه.
تعيين مدة الانتقال
/* المدة بالثواني */
.fade {
opacity: 1;
transition-property: opacity;
transition-duration: 0.3s;
}
.fade.is-hidden {
opacity: 0;
}
/* المدة بالملي ثانية */
.slide {
transform: translateX(0);
transition-property: transform;
transition-duration: 250ms;
}
/* مدد مختلفة لخصائص مختلفة */
.multi-speed {
background-color: white;
transform: scale(1);
transition-property: background-color, transform;
transition-duration: 0.5s, 0.2s;
/* لون الخلفية يأخذ 0.5 ثانية، التحويل يأخذ 0.2 ثانية */
}
خاصية transition-timing-function
خاصية transition-timing-function تتحكم في منحنى التسارع للانتقال -- كيف يتم حساب القيم الوسيطة بين حالتي البداية والنهاية. هذا ما يجعل الانتقال يبدو طبيعياً أو سريعاً أو ميكانيكياً. يوفر CSS عدة دوال توقيت مدمجة، ويمكنك أيضاً تعريف منحنيات مخصصة.
دوال التوقيت المدمجة
يقدم CSS خمس دوال توقيت بكلمات مفتاحية تغطي منحنيات الحركة الأكثر شيوعاً:
ease-- دالة التوقيت الافتراضية. تبدأ ببطء وتتسارع في المنتصف وتتباطأ في النهاية. تنتج الحركة الأكثر طبيعية ومناسبة لمعظم الانتقالات.linear-- سرعة ثابتة من البداية للنهاية بدون تسارع أو تباطؤ. مفيدة لخصائص مثل تغييرات اللون أو الشفافية حيث يبدو التسهيل غير طبيعي.ease-in-- تبدأ ببطء وتتسارع نحو النهاية. تخلق شعوراً بشيء يكتسب زخماً. جيدة للعناصر التي تغادر الشاشة.ease-out-- تبدأ بسرعة وتتباطأ نحو النهاية. تخلق شعوراً بشيء يستقر في مكانه. جيدة للعناصر التي تدخل الشاشة.ease-in-out-- تجمع بين ease-in و ease-out: تبدأ ببطء وتتسارع في المنتصف وتتباطأ في النهاية. متماثلة وسلسة. جيدة للعناصر التي تتحرك على الشاشة.
مقارنة دوال التوقيت المدمجة
.box {
width: 100px;
height: 100px;
background: var(--primary);
transition-property: transform;
transition-duration: 0.6s;
}
.box-ease { transition-timing-function: ease; }
.box-linear { transition-timing-function: linear; }
.box-ease-in { transition-timing-function: ease-in; }
.box-ease-out { transition-timing-function: ease-out; }
.box-ease-in-out { transition-timing-function: ease-in-out; }
/* عند التمرير، تتحرك جميع الصناديق نفس المسافة،
لكن كل واحد يتبع منحنى سرعة مختلف */
.box:hover {
transform: translateX(200px);
}
منحنيات مخصصة مع cubic-bezier()
دالة cubic-bezier() تمنحك تحكماً كاملاً في منحنى تسارع الانتقال من خلال تعريف منحنى بيزيه التكعيبي بأربع معاملات: cubic-bezier(x1, y1, x2, y2). تمثل المعاملات إحداثيات نقطتي تحكم (P1 و P2) على المنحنى، حيث تتراوح قيم x من 0 إلى 1 (تمثل الوقت) ويمكن لقيم y أن تتجاوز 0 و 1 (مما يسمح بتأثيرات التجاوز).
دوال توقيت بيزيه التكعيبي المخصصة
/* دخول سريع -- بداية سريعة، استقرار لطيف */
.snappy {
transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
}
/* تجاوز مرتد -- قيمة y2 تتجاوز 1 */
.bouncy {
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* تسهيل معياري من Material Design */
.material-standard {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
/* تباطؤ Material Design (دخول) */
.material-decelerate {
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
/* تسارع Material Design (خروج) */
.material-accelerate {
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
}
/* تأثير زنبركي مرتد */
.spring {
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
y فوق 1.0 تخلق تأثير تجاوز (الخاصية تتجاوز القيمة المستهدفة قبل الاستقرار)، والقيم تحت 0.0 تخلق تراجعاً.التوقيت المتدرج مع steps()
دالة steps() تنشئ دالة توقيت تقسم الانتقال إلى عدد محدد من الفواصل المتساوية، منتجةً حركة منفصلة إطاراً بإطار بدلاً من حركة سلسة. هذا مفيد لحركات أوراق الرسوم المتحركة وتأثيرات الآلة الكاتبة وعدادات العد التنازلي حيث تريد قفزات مميزة بدلاً من استيفاء سلس.
استخدام steps() للحركات المنفصلة
/* steps(عدد_الخطوات, الاتجاه) */
/* تأثير الآلة الكاتبة -- التنقل خلال كل موضع حرف */
.typewriter {
width: 0;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid var(--text-dark);
transition-property: width;
transition-duration: 2s;
transition-timing-function: steps(20, end);
}
.typewriter.is-typing {
width: 20ch; /* عرض 20 حرف */
}
/* حركة ورقة الرسوم المتحركة */
.sprite {
width: 64px;
height: 64px;
background: url("spritesheet.png") 0 0;
transition-property: background-position;
transition-duration: 0.8s;
transition-timing-function: steps(8);
}
.sprite:hover {
background-position: -512px 0; /* 8 إطارات * 64 بكسل */
}
خاصية transition-delay
خاصية transition-delay تحدد مدة الانتظار قبل بدء الانتقال. تقبل قيماً بالثواني أو الملي ثانية. التأخير الإيجابي يجعل الانتقال ينتظر قبل البدء. التأخير السلبي يجعل الانتقال يبدأ فوراً لكن في منتصف الحركة، كما لو أن هذا المقدار من الوقت قد مضى بالفعل.
استخدام تأخير الانتقال
/* تأخير بسيط -- انتظار 0.2 ثانية قبل بدء تغيير اللون */
.button {
background-color: var(--primary);
transition-property: background-color;
transition-duration: 0.3s;
transition-delay: 0.2s;
}
/* تأخير سلبي -- البدء عند 0.1 ثانية من الحركة */
.quick-start {
opacity: 0;
transition-property: opacity;
transition-duration: 0.5s;
transition-delay: -0.1s;
}
/* تأخيرات متدرجة لتأثيرات متتالية */
.stagger-item:nth-child(1) { transition-delay: 0s; }
.stagger-item:nth-child(2) { transition-delay: 0.05s; }
.stagger-item:nth-child(3) { transition-delay: 0.1s; }
.stagger-item:nth-child(4) { transition-delay: 0.15s; }
.stagger-item:nth-child(5) { transition-delay: 0.2s; }
/* تأخيرات مختلفة لخصائص مختلفة */
.multi-delay {
transform: translateY(0);
opacity: 1;
transition-property: transform, opacity;
transition-duration: 0.3s, 0.3s;
transition-delay: 0s, 0.1s;
/* التحويل يبدأ فوراً، الشفافية تنتظر 0.1 ثانية */
}
الاختصار transition
خاصية transition المختصرة تجمع transition-property و transition-duration و transition-timing-function و transition-delay في تصريح واحد. الصيغة هي: transition: خاصية مدة دالة-التوقيت تأخير. يمكنك تحديد عدة انتقالات مفصولة بفواصل، كل منها بخاصيتها ومدتها ودالة توقيتها وتأخيرها.
صيغة اختصار الانتقال
/* الاختصار الكامل: خاصية مدة دالة-التوقيت تأخير */
.element {
transition: background-color 0.3s ease 0s;
}
/* حذف القيم الاختيارية (دالة التوقيت افتراضياً ease، التأخير 0s) */
.element {
transition: background-color 0.3s;
}
/* عدة انتقالات في تصريح واحد */
.card {
transition: transform 0.3s ease,
box-shadow 0.3s ease 0.05s,
background-color 0.2s linear;
}
/* نقل جميع الخصائص (استخدم بحذر) */
.simple {
transition: all 0.3s ease;
}
/* أنماط شائعة */
.button {
transition: background-color 0.2s ease,
color 0.2s ease,
border-color 0.2s ease,
box-shadow 0.2s ease;
}
transition-duration والثانية كـ transition-delay. كتابة transition: opacity 0.1s 0.3s تعني مدة 0.1 ثانية مع تأخير 0.3 ثانية -- وليس العكس. إذا قدمت قيمة وقت واحدة فقط، فهي المدة ويكون التأخير افتراضياً 0 ثانية.الخصائص القابلة للحركة مقابل غير القابلة للحركة
ليس كل خاصية CSS يمكن نقلها. الخاصية تكون قابلة للحركة إذا كان المتصفح يستطيع حساب قيم وسيطة ذات معنى بين حالتي البداية والنهاية. فهم الخصائص القابلة للحركة والتي ليست كذلك ضروري لكتابة انتقالات فعالة.
الخصائص الشائعة القابلة للحركة
- خصائص الألوان:
colorوbackground-colorوborder-colorوoutline-colorوbox-shadow - الخصائص البعدية:
widthوheightوmin-widthوmax-widthوpaddingوmarginوborder-width - التموضع:
topوrightوbottomوleft - التحويل:
transform(الإزاحة والدوران والتحجيم والانحراف) - البصرية:
opacityوfilterوbackdrop-filterوclip-path - نموذج الصندوق:
border-radiusوbox-shadowوoutline-width
الخصائص غير القابلة للحركة
display-- لا يمكن الاستيفاء بين block و flex و none وغيرها. هذا هو الإحباط الأكثر شيوعاً. لا يمكنك نقل ظهور أو اختفاء عنصر مع display. استخدمopacityوvisibilityبدلاً من ذلك.position-- لا يمكن النقل بين static و relative و absolute و fixed.font-family-- لا يمكن الاستيفاء بين عائلات الخطوط.content-- لا يمكن نقل خاصية المحتوى المستخدمة في العناصر الزائفة.
حلول بديلة للخصائص غير القابلة للحركة
/* المشكلة: لا يمكن نقل display: none إلى display: block */
/* الحل 1: استخدام الشفافية والرؤية */
.modal {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal.is-open {
opacity: 1;
visibility: visible;
}
/* الحل 2: استخدام التحويل للنقل خارج الشاشة */
.panel {
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.panel.is-visible {
transform: translateX(0);
}
/* الحل 3: استخدام max-height لحركة الارتفاع */
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.accordion-content.is-expanded {
max-height: 500px; /* تعيين قيمة أكبر من المحتوى */
}
خاصية will-change
خاصية will-change تخبر المتصفح بالخصائص التي تنوي تحريكها في المستقبل القريب، مما يسمح له بإعداد التحسينات مسبقاً. عندما تعلن will-change: transform، قد يرفع المتصفح العنصر إلى طبقة تركيب خاصة به ويحسب مسبقاً موارد GPU اللازمة لحركات التحويل. هذا يمكن أن يحسن الأداء بشكل كبير للحركات المعقدة.
استخدام will-change للأداء
/* تلميح بأن التحويل سيتغير عند التمرير */
.card {
transition: transform 0.3s ease;
}
.card:hover {
will-change: transform;
transform: translateY(-4px);
}
/* للعناصر التي تتحرك بشكل متكرر */
.progress-bar {
will-change: width;
transition: width 0.5s ease;
}
/* لا تفعل هذا -- تطبيق will-change على كل شيء يهدر الذاكرة */
/* * { will-change: transform, opacity; } -- سيء */
will-change. كل عنصر مع will-change يستهلك ذاكرة GPU إضافية لأن المتصفح ينشئ طبقة تركيب جديدة له. تطبيق will-change على عشرات العناصر سيؤدي فعلياً إلى تدهور الأداء باستنفاد ذاكرة GPU. استخدمها باعتدال وفقط على العناصر التي تحتاج التحسين حقاً.تشغيل الانتقالات
يحتاج الانتقال إلى محفز للبدء. يجب أن تتغير قيم خصائص CSS فعلياً لكي يعمل الانتقال. إليك أكثر الطرق شيوعاً لتشغيل الانتقالات:
محفزات الفئات الزائفة
التشغيل بالفئات الزائفة
/* :hover -- المحفز الأكثر شيوعاً */
.button {
background: var(--primary);
transition: background 0.2s ease;
}
.button:hover {
background: var(--primary-light);
}
/* :focus و :focus-visible */
.input {
border: 2px solid var(--border-light);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
/* :active -- التشغيل أثناء الضغط على الزر */
.button:active {
transform: scale(0.97);
}
/* :checked -- التشغيل عند تغيير مربع الاختيار أو زر الراديو */
.toggle-track {
background: #ccc;
transition: background 0.2s ease;
}
.toggle-input:checked + .toggle-track {
background: var(--primary);
}
/* :valid و :invalid -- حالات التحقق من النماذج */
.input {
border: 2px solid var(--border-light);
transition: border-color 0.3s ease;
}
.input:valid {
border-color: #27ae60;
}
.input:invalid:not(:placeholder-shown) {
border-color: #e74c3c;
}
محفزات الفئات (عبر JavaScript)
التشغيل بتغييرات الفئات
/* تعريف الانتقال في الحالة الأساسية */
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s ease;
}
/* الفئة التي تشغل الانتقال */
.sidebar.is-open {
transform: translateX(0);
}
/* انزلاق الإشعار */
.notification {
opacity: 0;
transform: translateY(-20px);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.notification.is-visible {
opacity: 1;
transform: translateY(0);
}
/* تبديل السمة */
.app {
background-color: #ffffff;
color: #333333;
transition: background-color 0.4s ease, color 0.4s ease;
}
.app.dark-theme {
background-color: #1a1a2e;
color: #e0e0e0;
}
أحداث الانتقال في JavaScript
تطلق انتقالات CSS أحداث JavaScript يمكنك الاستماع إليها للتنسيق أو التنظيف أو التسلسل. هناك ثلاثة أحداث انتقال: transitionstart و transitionrun و transitionend. الأكثر استخداماً هو transitionend، الذي يطلق عندما يكتمل الانتقال.
الاستماع لأحداث الانتقال
/* CSS */
.modal {
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.modal.is-open {
opacity: 1;
visibility: visible;
}
/* JavaScript: الاستماع لـ transitionend */
/* const modal = document.querySelector(".modal");
modal.addEventListener("transitionend", (event) => {
// event.propertyName: أي خاصية CSS انتهت
// event.elapsedTime: المدة بالثواني
if (event.propertyName === "opacity") {
if (!modal.classList.contains("is-open")) {
// النافذة تلاشت بالكامل -- تنظيف
modal.remove();
}
}
});
// مهم: transitionend يطلق مرة واحدة لكل خاصية
// إذا نقلت transform و opacity، تحصل على حدثين
// فلتر بواسطة propertyName لتجنب تشغيل التنظيف مرتين */
transitionend يطلق مرة واحدة لكل خاصية تنتهي من الانتقال. إذا كان لديك transition: opacity 0.3s, transform 0.3s، ستتلقى حدثي transitionend -- واحد لـ opacity وواحد لـ transform. تحقق دائماً من event.propertyName إذا كنت تريد الاستجابة فقط لاكتمال خاصية محددة. كن على علم أيضاً أن transitionend لا يطلق إذا تم مقاطعة الانتقال.الأداء: أي الخصائص أرخص في التحريك؟
يتضمن عرض المتصفح ثلاث مراحل رئيسية: التخطيط (حساب المواضع والأحجام) و الرسم (ملء البكسلات) و التركيب (دمج الطبقات). خصائص CSS المختلفة تشغل مراحل مختلفة عندما تتغير، وهذا له تأثير كبير على أداء الحركة.
مستويات الأداء
- التركيب فقط (الأرخص):
transformوopacityيتم التعامل معهما بالكامل بواسطة مركب GPU. لا تشغل التخطيط أو الرسم. هذا يعني أنها يمكن أن تتحرك بمعدل 60 إطار في الثانية حتى على الأجهزة منخفضة القوة. فضل دائماًtransformلتغييرات الموضع والحجم والدوران، وopacityلتغييرات الرؤية. - الرسم فقط (معتدل):
colorوbackground-colorوbox-shadowوborder-colorتشغل إعادة الرسم لكن ليس إعادة حساب التخطيط. مقبولة للتحريك لكن أكثر تكلفة من خصائص التركيب فقط. - تشغيل التخطيط (الأكثر تكلفة):
widthوheightوpaddingوmarginوtopوleftوborder-widthوfont-sizeتشغل جميعها إعادة حساب التخطيط. تجنب تحريك هذه الخصائص كلما أمكن.
الأداء: انتقالات جيدة مقابل سيئة
/* سيء: تحريك خصائص التخطيط */
.card-bad {
left: 0;
top: 0;
width: 200px;
transition: left 0.3s, top 0.3s, width 0.3s;
}
.card-bad:hover {
left: 20px; /* يشغل التخطيط */
top: -10px; /* يشغل التخطيط */
width: 220px; /* يشغل التخطيط */
}
/* جيد: استخدام التحويلات بدلاً من ذلك */
.card-good {
transition: transform 0.3s ease;
}
.card-good:hover {
transform: translate(20px, -10px) scale(1.1);
/* التركيب فقط -- لا تخطيط أو رسم يتم تشغيله */
}
/* سيء: تحريك box-shadow (يشغل الرسم) */
.card-shadow-bad {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
.card-shadow-bad:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
}
/* جيد: استخدام عنصر زائف لحركة الظل */
.card-shadow-good {
position: relative;
}
.card-shadow-good::after {
content: "";
position: absolute;
inset: 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
opacity: 0;
transition: opacity 0.3s ease;
border-radius: inherit;
z-index: -1;
}
.card-shadow-good:hover::after {
opacity: 1;
/* تغيير الشفافية فقط (تركيب) -- الظل نفسه لا يتحرك */
}
أمثلة عملية
الآن دعونا نطبق كل ما تعلمناه على مكونات واجهة واقعية. هذه الأمثلة توضح انتقالات بجودة احترافية لعناصر واجهة المستخدم الشائعة.
زر تفاعلي مع انتقالات متعددة
انتقالات أزرار مصقولة
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: var(--primary);
color: white;
border: 2px solid var(--primary);
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s ease,
color 0.2s ease,
border-color 0.2s ease,
transform 0.15s ease,
box-shadow 0.2s ease;
}
/* التمرير: تفتيح ورفع */
.btn:hover {
background: var(--primary-light);
border-color: var(--primary-light);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* نشط: الضغط للأسفل */
.btn:active {
transform: translateY(0);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
/* التركيز: حلقة لإمكانية الوصول */
.btn:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.4);
}
/* معطل: مظهر باهت */
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
قائمة منسدلة مع انتقالات
قائمة منسدلة متحركة
.dropdown {
position: relative;
}
.dropdown-menu {
position: absolute;
top: calc(100% + 4px);
left: 0;
min-width: 200px;
background: var(--bg-white);
border: 1px solid var(--border-light);
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
padding: 0.5rem 0;
/* خصائص الانتقال */
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition: opacity 0.2s ease,
visibility 0.2s ease,
transform 0.2s ease;
}
.dropdown.is-open .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* عناصر القائمة */
.dropdown-item {
display: block;
width: 100%;
padding: 0.5rem 1rem;
background: none;
border: none;
cursor: pointer;
color: var(--text-dark);
transition: background-color 0.15s ease;
}
.dropdown-item:hover {
background: var(--bg-light);
}
أكورديون مع توسع/طي سلس
أكورديون مع انتقالات CSS
.accordion-item {
border: 1px solid var(--border-light);
border-radius: 8px;
margin-bottom: 0.5rem;
overflow: hidden;
}
.accordion-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 1rem 1.25rem;
background: var(--bg-white);
border: none;
cursor: pointer;
font-weight: 600;
transition: background-color 0.2s ease;
}
.accordion-header:hover {
background: var(--bg-light);
}
/* لوحة المحتوى */
.accordion-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.accordion-item.is-open .accordion-body {
grid-template-rows: 1fr;
}
.accordion-body-inner {
overflow: hidden;
}
.accordion-body-inner p {
padding: 0 1.25rem 1rem;
margin: 0;
}
grid-template-rows: 0fr إلى 1fr هي واحدة من أفضل الطرق لتحريك الارتفاع من صفر إلى تلقائي في CSS. على عكس خدعة max-height (التي تتطلب تخمين قيمة قصوى وغالباً ما تكون التوقيتات غير متطابقة)، نهج الشبكة ينتقل بسلاسة إلى الارتفاع الفعلي للمحتوى. اضبط overflow: hidden على العنصر الداخلي واستخدم grid-template-rows على الغلاف.الانتقالات العكسية: توقيت مختلف للفتح والإغلاق
أحياناً تريد انتقالاً مختلفاً عندما تعود حالة. مثلاً، القائمة المنسدلة قد تفتح بسرعة لكن تغلق ببطء أكثر، أو العكس. يمكنك تحقيق هذا بوضع تصريحات transition مختلفة في الحالة الأساسية (لانتقال "الإغلاق") والحالة النشطة (لانتقال "الفتح").
انتقالات غير متماثلة
/* الانتقال على الحالة الأساسية يتحكم في العكس (الإغلاق) */
.tooltip {
opacity: 0;
transform: translateY(8px);
/* الإغلاق: تلاشي بطيء */
transition: opacity 0.4s ease, transform 0.4s ease;
}
/* الانتقال على الحالة النشطة يتحكم في الأمام (الفتح) */
.trigger:hover .tooltip {
opacity: 1;
transform: translateY(0);
/* الفتح: ظهور سريع */
transition: opacity 0.15s ease, transform 0.15s ease;
}
/* نمط آخر: فتح سريع، إغلاق بطيء مع تأخير */
.menu {
opacity: 0;
visibility: hidden;
/* الإغلاق: بطيء مع تأخير 0.1 ثانية */
transition: opacity 0.3s ease 0.1s,
visibility 0.3s ease 0.1s;
}
.menu-parent:hover .menu {
opacity: 1;
visibility: visible;
/* الفتح: فوري، بدون تأخير */
transition: opacity 0.2s ease 0s,
visibility 0.2s ease 0s;
}
تمرين عملي
ابنِ معرض مكونات تفاعلي يوضح انتقالات CSS. أنشئ صفحة تحتوي على العناصر التالية، كل منها يستخدم الانتقالات: (1) زر مع حالات التمرير والتركيز والنشاط ينتقل في لون الخلفية والتحويل (رفع طفيف عند التمرير، ضغط عند النشاط) وظل الصندوق. (2) مفتاح تبديل منسق بـ CSS ينتقل في لون المسار وموضع الإبهام عند تحديد مربع اختيار مخفي. (3) مكون بطاقة حيث يسبب التمرير رفع البطاقة مع ظل وتكبير الصورة قليلاً وتلاشي تراكب مع نص. (4) أكورديون بثلاثة أقسام حيث النقر على رأس يوسع أو يطوي المحتوى بسلاسة باستخدام تقنية grid-template-rows. (5) شريط إشعارات ينزلق من أعلى الصفحة عند إضافة فئة is-visible. استخدم مدد مناسبة (100-300 ملي ثانية للعناصر التفاعلية)، ودوال التوقيت الصحيحة (ease-out للدخول، ease-in للخروج)، وتأكد من أن جميع الحركات تستخدم فقط transform و opacity حيثما أمكن لأفضل أداء. اختبر مع لوحة الأداء في أدوات مطور المتصفح للتحقق من عدم حدوث اضطراب في التخطيط أثناء الانتقالات.