یک بار در یک پروژه‌ی فروشگاهی، مدیر محصول با یک جمله ساده تمام بحث را جمع کرد: «سایت خوب کار می‌کند، ولی حس گرانی نمی‌دهد.» بعد از چند روز بازبینی، فهمیدیم تفاوت با یک رقیب درجه‌یک، در دکمه‌ای نبود که کار می‌کرد یا نه؛ در این بود که وقتی ماوس روی آن می‌رفت، تغییر رنگ بی‌هیچ نرمی و در یک فریم اتفاق می‌افتاد. همان لحظه، جادوی ترنزیشن در CSS (CSS Transition) برای من روشن‌تر شد: ترنزیشن یک قابلیت ظاهری نیست، یک سیگنالِ اعتماد است که به کاربر می‌گوید این سایت به جزئیات اهمیت می‌دهد.

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

چرا ترنزیشن این‌قدر مهم است؟

در روان‌شناسی ادراک، تغییر ناگهانی چیزی که کاربر انتظار پویایی از آن را دارد، یک سیگنال منفی است. دقیقاً همان حس که وقتی دکمه‌ای بی‌هیچ واسطه از آبی به قرمز می‌پرد، تجربه‌ای مکانیکی و قدیمی به کاربر می‌دهد. از این زاویه، ترنزیشن فقط یک لایه‌ی ظاهری نیست؛ لایه‌ای است که نرمی، پاسخ‌گویی و پرداختگی را منتقل می‌کند. سه دلیل که در پروژه‌های واقعی به آن رسیده‌ام:

  • پاسخ‌گویی: ترنزیشن به کاربر می‌گوید «فهمیدم روی تو کلیک کردی»، قبل از اینکه نتیجه‌ی عمل ظاهر شود. در سایت‌هایی که خطاهای فرم بلافاصله تکان می‌دهند بدون هیچ نرمی، حس بی‌محلی به کاربر منتقل می‌شود.
  • جهت‌دهی: وقتی عنصری از راست وارد و از چپ خارج می‌شود، جهت حرکت به کاربر می‌گوید از کجا آمده و به کجا می‌رود. این یک راهنمای ناوبری نامرئی است که در رابط‌های مدرن بسیار کار می‌کند.
  • تصویر برند: سایت‌های معتبر و برندهای بزرگ، تقریباً همیشه انیمیشن‌های نرم دارند. این تصادفی نیست؛ همان تفاوت بین یک فروشگاه آنلاین معمولی و یک برند حرفه‌ای، در همین جزئیات کوچک شکل می‌گیرد.

در ده‌ها پروژه‌ای که بازبینی کرده‌ام، تبدیل حتی ۱۰ مورد hover خطی به hover نرم، در بازخورد کاربران اثر محسوسی داشته. اما این همه‌ی ماجرا نیست — ترنزیشن خوب، در جای درست و با زمان‌بندی درست باید بنشیند. برای درک کلی مسیر حرکت در CSS، CSS مدرن از Flexbox تا Grid را کنار این بخش بخوانید.

ترنزیشن، ظاهر را عوض نمی‌کند؛ فقط به آن اجازه می‌دهد در ذهن کاربر آرام بگیرد. تفاوت‌شان همان تفاوت بین حس گران و ارزان است.

آناتومی ترنزیشن: چهار خاصیت پایه

ترنزیشن در CSS از چهار جزء ساخته می‌شود که همه‌ی قدرت این ابزار در ترکیب همین چهارتاست:

.button {
  background: #0055ff;
  transition-property: background, transform;
  transition-duration: 200ms;
  transition-timing-function: ease-out;
  transition-delay: 0ms;
}

کوتاه‌نوشت همین چهار خط:

.button {
  transition: background 200ms ease-out 0ms,
              transform 200ms ease-out 0ms;
}

سه نکته‌ی مهم که در پروژه‌ها بارها روی آن‌ها تأکید کرده‌ام:

  • ترنزیشن روی حالت پایه قرار می‌گیرد، نه روی :hover. اگر آن را روی :hover بنویسید، هنگام خروج ماوس، مرورگر نمی‌داند چطور برگردد و تغییر بی‌واسطه اتفاق می‌افتد. این یکی از بیشترین اشتباهات تازه‌کارهاست.
  • کوتاه‌نوشت را محدود نگه دارید. هر لیست طولانی در transition، نگهداری را سخت می‌کند. دو یا سه خاصیت در هر ترنزیشن، معمولاً کافی است.
  • از transition: all دوری کنید مگر اینکه واقعاً همه‌ی خاصیت‌ها را می‌خواهید انیمیت کنید. این مقدار راحت اما خطرناک است؛ تغییرات ناخواسته‌ای که هیچ‌وقت انتظارشان را نداشتید، نرم می‌شوند و می‌توانند به باگ‌های بصری عجیب منجر شوند.

مدت‌زمان: جایی که سرعت اشتباه، حس اشتباه می‌سازد

مدت‌زمان (duration) اولین چیزی است که وقتی ترنزیشن مصنوعی به‌نظر می‌رسد، باید بازبینی شود. جدول زیر، محدوده‌هایی است که در پروژه‌ها استفاده کرده‌ام:

محدودهحسکاربرد
۵۰ تا ۱۰۰ میلی‌ثانیهآن‌ی، پاسخ‌گویی بسیار سریعتغییر رنگ دکمه در hover، تغییری که کاربر نباید متوجهش شود
۱۵۰ تا ۲۵۰ میلی‌ثانیهنرم، حرفه‌ای، پایدارانتخاب پیش‌فرض من برای ۹۰٪ موارد UI
۳۰۰ تا ۵۰۰ میلی‌ثانیهآرام، روایت‌محورورود و خروج مودال، تغییر صفحه، انیمیشن‌های بصری
بالای ۵۰۰ میلی‌ثانیهکند، در تعامل‌های معمول آزاردهندهفقط برای انیمیشن‌های اتمسفریک و بصری بزرگ

قاعده‌ای که در پروژه‌ها به‌طور جدی رعایت می‌کنم: هر چه کاربر تعامل بیشتری با آن عنصر دارد، ترنزیشن باید کوتاه‌تر باشد. دکمه‌های اصلی که کاربر روزانه ده بار می‌بیند، نباید بیش از ۱۵۰ میلی‌ثانیه ترنزیشن داشته باشند. در پروژه‌ای که کارت‌های محصول را با ۴۰۰ میلی‌ثانیه نرم کرده بودیم، وقتی کاربر روی چند محصول پشت‌سرهم ماوس می‌برد، حس سنگینی داشت. کاهش به ۲۰۰ میلی‌ثانیه، همان حس را حرفه‌ای‌تر کرد.

نکته‌ی دیگری که در پروژه‌ها درونی‌اش کرده‌ام: مدت‌زمان را در متغیرهای CSS نگه دارید تا در کل سایت یکدست باشد:

:root {
  --duration-fast: 150ms;
  --duration-normal: 250ms;
  --duration-slow: 400ms;
}

Easing: قاتل حس مصنوعی

مهم‌تر از مدت‌زمان، منحنی زمان‌بندی (easing) است. یک ترنزیشن ۲۰۰ میلی‌ثانیه‌ای با easing نامناسب، مصنوعی‌تر از یک ترنزیشن ۵۰۰ میلی‌ثانیه‌ای با easing درست به‌نظر می‌رسد. پنج مقدار پرکاربرد:

  • linear: سرعت ثابت. تقریباً هیچ‌وقت در UI استفاده نمی‌کنم جز انیمیشن‌های تکراری مکانیکی مثل چرخش نامحدود.
  • ease: پیش‌فرض مرورگر. متعادل اما کلیشه‌ای؛ حس «همه‌جا یکی» دارد.
  • ease-in: آرام شروع، تند تمام. برای خروج عناصر از دید.
  • ease-out: تند شروع، آرام تمام. برای ورود عناصر و پاسخ به تعامل. این انتخاب پیش‌فرض من است.
  • cubic-bezier(...): منحنی سفارشی. برای برندهایی که حس حرکتی خاصی می‌خواهند.

یک منحنی که در چند پروژه به نتیجه‌ی خوبی رسید و امروز جزو استانداردهای شخصی‌ام شده:

:root {
  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-in: cubic-bezier(0.8, 0.2, 1, 0.2);
}

.button {
  transition: background var(--duration-fast) var(--ease-out);
}

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

اگر قرار باشد فقط یک تصمیم از این مقاله را در پروژه ببرید، همان ease-out با منحنی سفارشی است. همین یک خط، بیشترین تفاوت را در حس حرفه‌ای می‌سازد.

تأخیر و ترنزیشن‌های ترتیبی

خاصیت transition-delay به شما اجازه می‌دهد شروع ترنزیشن را با تأخیر زمان‌بندی کنید. کاربرد اصلی این ابزار، ساخت ترنزیشن‌های ترتیبی (staggered) است که در ظاهر، حسی از روایت بصری ایجاد می‌کند:

.card:nth-child(1) { transition-delay: 0ms; }
.card:nth-child(2) { transition-delay: 60ms; }
.card:nth-child(3) { transition-delay: 120ms; }

این الگو در پروژه‌هایی که لیست کارت‌های محصول یا مقالات داشتند، استفاده‌ی خوبی داشت. اما یک هشدار از تجربه: تأخیرهای طولانی‌تر از ۱۵۰ میلی‌ثانیه در تعامل‌های معمول، حس بی‌پاسخ بودن سایت را می‌دهد. برای hover، توصیه می‌کنم همیشه صفر بماند و برای انیمیشن ورود عناصر استفاده شود.

Transition یا Animation؟ تصمیم‌نامه‌ی عملی

این سؤال در تیم‌ها زیاد پیش می‌آید و پاسخ صریح این است: تفاوت در حالت‌مداری و پیچیدگی است.

معیارTransitionAnimation
حالت‌های درگیردو حالت (قبل و بعد)چند حالت با keyframes
تریگرتغییر یک خاصیت (مثل :hover)مستقل، بعد از اجرا
تکراریک‌بار در هر تغییرقابل تکرار (infinite)
کنترل از JSبا تغییر کلاسبا animation-play-state و ...
کاربرد ایده‌آلhover، focus، مودال، درجه‌ی روشناییلودر، افکت بصری چندمرحله‌ای

الگویی که در پروژه‌ها به‌کار می‌برم: ابتدا همیشه با transition امتحان می‌کنم. اگر نیاز از چارچوب transition فراتر رفت — مثلاً سه مرحله میانی یا تکرار — به سراغ @keyframes می‌روم. جزئیات کامل آن سمت، در انیمیشن در CSS آمده است.

الگوهای واقعی از پروژه‌ها

سه الگویی که بیش از همه در پروژه‌هایم تکرار شده‌اند:

  1. hover دکمه با تغییر لایه‌ی داخلی: به‌جای تغییر مستقیم background-color که در بعضی رنگ‌ها حس مصنوعی دارد، از یک لایه‌ی داخلی با transform: scaleX استفاده می‌کنم که از چپ به راست پر می‌شود. تغییر روی GPU اجرا می‌شود و روان‌تر است.
  2. ترنزیشن کارت محصول با ترکیب سایه و transform: ترکیب box-shadow و translateY(-4px) روی hover، یک حس «شناور» طبیعی می‌سازد. مدت‌زمان: ۲۰۰ میلی‌ثانیه با ease-out.
  3. ورود مودال با fade و scale: ترکیب opacity و transform: scale(0.96) به سمت scale(1)، الگویی است که در تمام پروژه‌های شخصی من به‌عنوان پیش‌فرض نشسته است. برای خروج، از ease-in و مدت‌زمان کوتاه‌تر استفاده می‌کنم؛ چون خروج باید سریع‌تر از ورود باشد.

برای ترکیب این الگوها با چیدمان، پیشنهاد می‌کنم فلکس باکس در CSS و گرید در CSS را در کنار این بخش ببینید. اگر روی پروژه‌های وردپرسی کار می‌کنید، این ترنزیشن‌ها را در قالب چایلد بگذارید تا با آپدیت قالب از دست نروند.

اشتباهاتی که در پروژه‌ها دیدم

  • ترنزیشن روی :hover به‌جای حالت پایه: هنگام خروج، تغییر بی‌واسطه اتفاق می‌افتد. این شایع‌ترین خطاست و بازبینی‌اش کمتر از ده ثانیه وقت می‌گیرد.
  • انیمیت کردن width، height، margin یا top: هر فریم، layout را بازمحاسبه می‌کنند. جایگزین: transform: scale و translate.
  • مدت‌زمان‌های متناقض در یک پروژه: دکمه‌ای ۱۰۰ میلی‌ثانیه، کارتی ۶۰۰ میلی‌ثانیه. کاربر این ناهماهنگی را حس می‌کند، حتی اگر نتواند توضیحش بدهد. راه‌حل: متغیرهای مشترک در :root.
  • transition: all در کامپوننت‌های پیچیده: تغییرات ناخواسته‌ای مثل تغییر ارتفاع در اثر بازشدن متن، نرم می‌شوند و حس عجیبی می‌دهند.
  • تکیه بر :hover به‌عنوان تنها تعامل: روی موبایل، :hover وجود ندارد. همیشه حالت‌های :focus-visible و :active را هم طراحی کنید.

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

دسترس‌پذیری و احترام به کاربر

یک قطعه‌ی سه‌خطی که در تمام پروژه‌های اخیرم به‌عنوان استاندارد وجود دارد:

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

این کد به کاربرانی که در سیستم‌عامل‌شان «کاهش حرکت» را فعال کرده‌اند احترام می‌گذارد. مسئله صرفاً زیبایی نیست؛ برای بعضی کاربران با اختلال حرکتی، انیمیشن‌های نرم می‌تواند باعث سرگیجه شود. اگر می‌خواهید این لایه را در چارچوب گسترده‌تر ببینید، استانداردهای دسترس‌پذیری وب و WCAG چیست مسیر کامل را نشان می‌دهند. این سه خط کد، تفاوت بین یک سایت حرفه‌ای و یک سایت بی‌توجه است.

تست ترنزیشن روی دستگاه واقعی

سه ابزار که در پروژه‌هایم به‌کار گرفته‌ام:

  1. Performance Tab در Chrome DevTools: با ضبط در حین اجرای ترنزیشن، می‌بینید کدام فریم‌ها کندترند. اگر نوار زرد (Long Task) روی main thread ظاهر شد، مقصر انیمیت کردن خاصیت‌های چیدمان است.
  2. Rendering Tab و Paint Flashing: با فعال کردن این گزینه، هر ناحیه که دوباره Paint می‌شود با رنگ سبز نشان داده می‌شود. ترنزیشن بهینه، ناحیه‌ی کوچکی را سبز می‌کند.
  3. گوشی واقعی با اینترنت سلفون: استاندارد طلایی. اگر روی گوشی میان‌رده‌ی خودتان روان است، احتمالاً برای ۹۰٪ کاربران هم روان خواهد بود.

روش کامل تست در تست چیدمان واکنش‌گرا در مرورگرها آمده است. اگر روی پروژه‌ای وردپرسی هستید، توجه داشته باشید که ترنزیشن‌ها روی بستر موجود سوار می‌شوند؛ اگر قالب شما به‌طور ذاتی سنگین است، مهاجرت به قالب سبک بیشترین اثر را دارد.

لایه‌ای زیر سینتکس ترنزیشن

اینجا وارد لایه‌ای می‌شوم که در پروژه‌های معمولی دیده نمی‌شود اما برای مهندسان پلتفرم و توسعه‌دهنده‌های ارشد اهمیت دارد. آنچه موتور رندر با ترنزیشن شما می‌کند، در چهار مفهوم خلاصه می‌شود:

  1. Compositor-only Transitions و Layer Promotion: وقتی ترنزیشن روی transform یا opacity باشد، مرورگر عنصر را به یک لایه‌ی compositor ارتقا می‌دهد. این لایه در ریسمانی جدا از main thread اجرا می‌شود و به همین دلیل، ترنزیشن حتی در حضور جاوااسکریپت سنگین هم روان می‌ماند. اما هر لایه‌ی اضافه، حافظه‌ی GPU مصرف می‌کند؛ در صفحاتی که ده‌ها کارت هم‌زمان ترنزیشن دارند، تعداد بالای لایه‌ها می‌تواند خودش به گلوگاه تبدیل شود.
  2. Style Recalculation و Cascade در زمان ترنزیشن: هر تغییر کلاس یا حالت که ترنزیشن را تحریک می‌کند، ابتدا باعث اجرای مجدد cascade برای آن عنصر می‌شود. اگر انتخابگرهای شما پیچیده باشند (مثلاً .a .b .c .d)، این مرحله می‌تواند قابل توجه شود. به همین دلیل، در پروژه‌های پرتعامل، بازبینی انتخابگرها با ترنزیشن‌ها همراه می‌شود. جزئیات بیشتر در انتخابگرهای CSS.
  3. Interpolation و دسته‌بندی خاصیت‌ها: مرورگر برای هر خاصیت، یک روش درون‌یابی متفاوت دارد. اعداد (طول، رنگ، شفافیت) با درون‌یابی عددی حل می‌شوند. اما بعضی خاصیت‌ها (مثل display و visibility) ذاتاً درون‌یابی‌پذیر نیستند و پرش ناگهانی دارند. این توضیح می‌دهد چرا ورود و خروج مودال با display: none/block هرگز کاملاً نرم نمی‌شود؛ راه‌حل، ترکیب با opacity و transform، یا استفاده از مقادیر جدید display با allow-discrete است که در مرورگرهای مدرن پشتیبانی می‌شود.
  4. Interaction با کارایی جاوااسکریپت: اگر هم‌زمان با ترنزیشن CSS، جاوااسکریپت شما در حال اجرای taskهای طولانی است، compositor می‌تواند آزادانه کار کند اما main thread گلوگاه می‌شود و در نتیجه ترنزیشن‌هایی که به layout یا paint نیاز دارند کند می‌شوند. برای درک این تعادل و راه‌حل‌های سطح پایین‌تر، بهینه سازی جاوااسکریپت را در کنار این بخش بخوانید. اگر روی پروژه‌های وردپرسی هستید، معمولاً این بار از سمت افزونه‌ها هم تحت فشار است؛ نقشه‌ی کامل در تأثیر افزونه‌ها بر سرعت سایت آمده. برای مطالعه‌ی موازی این لایه با سرعت کلی سایت، بهینه‌سازی سرعت سایت و بهینه‌سازی CSS دید وسیع‌تری می‌دهند.

یک تجربه‌ی واقعی از پروژه‌ای که با مسئله‌ی نمایش نرم مودال روبرو شدیم: مودال با display: none/block و یک transition: opacity پیاده‌سازی شده بود. نتیجه، ورود بدون fade و یک پرش ناگهانی در لحظه‌ی تغییر display بود. راه‌حل تمیز: حذف display از ترنزیشن و استفاده از visibility که درون‌یابی‌پذیر است؛ همراه با یک transition: visibility 0s linear 200ms که تأخیر ورود را کنترل می‌کرد. یک خط پیچیده که در عوض، رفتار کاملاً حرفه‌ای ساخت. برای درک تفاوت این رویکرد با انیمیشن‌های چندمرحله‌ای، انیمیشن در CSS و مفاهیم پیشرفته جاوااسکریپت را در کنار این بخش ببینید. اگر روی رابط کاربری و طراحی کار می‌کنید، اصول طراحی رابط کاربری موفق و طراحی رابط کاربری چیست زاویه‌ی دیگری به همین مسئله می‌دهند.

یک ترنزیشن که به‌درستی از visibility استفاده می‌کند، بیشتر از ده خط جاوااسکریپت برای ساخت انیمیشن دستی ارزش دارد.

آخرین کلمه این مسیر

ترنزیشن را می‌توان در یک جمله خلاصه کرد: «لایه‌ی نرمی که بین حالت‌ها پل می‌زند و به کاربر حس پاسخ‌گویی می‌دهد.» سه درس که از این مسیر با خودم بردم:

  1. از حالت پایه شروع کنید. هر ترنزیشنی که روی :hover نوشته شده باشد، هنگام خروج می‌شکند. این یک قانون است که در تمام پروژه‌ها بدون استثنا اجرا می‌کنم.
  2. مدت‌زمان و easing را جدی بگیرید. تفاوت بین انیمیشن آماتور و حرفه‌ای، در همین دو عدد است. یک منحنی ease-out سفارشی و مدت‌زمان زیر ۲۵۰ میلی‌ثانیه، بیشترین تفاوت را می‌سازد.
  3. ترنزیشن را در متغیر نگه دارید. نه برای خود ترنزیشن، برای هماهنگی کل سایت. وقتی همه‌ی اجزا از یک منبع زمان‌بندی می‌خوانند، نتیجه یکپارچه به‌نظر می‌رسد و نگهداری هم ساده‌تر است.

مسیر یادگیری CSS با این نوشته تمام نمی‌شود. اگر می‌خواهید مرحله‌ی بعدی را بردارید، متغیرهای CSS، ریسپانسیو با CSS و انیمیشن در CSS سه قدم منطقی بعدی هستند. اگر هم به سمت کارایی می‌روید، بهینه‌سازی CSS و بهینه‌سازی سرعت سایت دید وسیع‌تری می‌دهند.

اگر در پروژه‌ای یک ترنزیشن عجیب داشتید — مثلاً مودالی که فقط در موبایل پرش ناگهانی داشت یا دکمه‌ای که فقط هنگام خروج رفتار متفاوتی نشان می‌داد — جزئیات سناریو را در دیدگاه بنویسید. مخصوصاً اگر با ترکیب visibility و display به یک نتیجه‌ی غیرمنتظره رسیده‌اید، همان تجربه برای خواننده‌ی بعدی از هر توضیح کلی ارزشمندتر است. 🎚️