Урок 14. Решения

📁 Раздел: Состояния и темы ⏱️ Время изучения: ~45 мин 🎯 Сложность: Средняя

⚡ Кратко

Три решения с живым результатом и объяснением выбора длительностей.

Решение задания 1

<button class="rounded-md bg-emerald-600 px-4 py-2 text-sm font-medium text-white
               transition-colors duration-150 hover:bg-emerald-700
               active:scale-95 active:transition-transform active:duration-75
               focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-600">
  Отправить
</button>

Почему так: цвет меняется за 150 мс, а «нажатие» — за 75 мс: отклик на действие должен быть быстрее, чем декоративный переход. scale не влияет на соседей, потому что не пересчитывает раскладку.

Решение задания 2

Настоящая карточка

1 240 ₽

<div class="grid max-w-md gap-4 sm:grid-cols-2">
  <div class="rounded-xl border border-slate-200 p-4">
    <div class="h-24 animate-pulse rounded-lg bg-slate-200"></div>
    <div class="mt-3 h-3 w-3/4 animate-pulse rounded bg-slate-200"></div>
    <div class="mt-2 h-3 w-1/2 animate-pulse rounded bg-slate-200"></div>
    <div class="mt-4 h-8 animate-pulse rounded-md bg-slate-200"></div>
  </div>
  <div class="rounded-xl border border-slate-200 p-4">
    <div class="h-24 rounded-lg bg-slate-100"></div>
    <h3 class="mt-3 text-sm font-medium text-slate-900">Настоящая карточка</h3>
    <p class="mt-1 text-sm text-slate-500">1 240 ₽</p>
    <button class="mt-4 h-8 w-full rounded-md bg-slate-900 text-xs text-white">В корзину</button>
  </div>
</div>

Почему так: скелетон повторяет размеры реальной карточки, поэтому при появлении данных ничего не «прыгает». Это главное требование к заглушкам — совпадение геометрии.

Решение задания 3

📄 src/index.css
@theme {
  --animate-slide-in: slide-in 0.25s ease-out;
}

@keyframes slide-in {
  from { opacity: 0; transform: translateX(1rem); }
  to   { opacity: 1; transform: translateX(0); }
}
<div class="motion-safe:animate-slide-in rounded-lg border border-sky-200 bg-sky-50 p-4">
  <p class="text-sm text-sky-900">Файл загружен</p>
</div>

Почему так: motion-safe: включает анимацию только тем, кто не просил её отключить. Обратный вариант (animate-slide-in motion-reduce:animate-none) тоже верен — выбирайте тот, что читается понятнее в вашем коде.

Файл загружен

Пример появления с анимацией.

<div class="max-w-sm animate-fade-up rounded-lg border border-sky-200 bg-sky-50 p-4">
  <p class="text-sm font-medium text-sky-900">Файл загружен</p>
  <p class="mt-1 text-xs text-sky-800">Пример появления с анимацией.</p>
</div>