IlmHamroh
JavaScript Full-stack/6-qism. CSS: harakat, zamonaviy CSS va dizayn8/57-dars19 daqiqa
Mundarija (34)

Micro-interaktsiyalar CSS'da: tugma javobi, loader, progress bar va skeleton

Qisqacha: Micro-interaktsiya — foydalanuvchi amaliga interfeysning kichik javobi: tugma bosilganda biroz "botadi", yuborish paytida yuklash belgisi aylanadi, kontent kelguncha uning kulrang skeleti turadi. CSS'da ular arzon xususiyatlar bilan quriladi: bosilish — scale: 0.97, spinner — conic-gradient + rotate, skeleton ustidagi yorug'lik — psevdo-elementning translate i. Kutish holatini ekran o'quvchiga aria-busy va role="status" aytadi, harakat esa faqat prefers-reduced-motion: no-preference da qo'shiladi.

Bu darsda

  • Micro-interaktsiya qanday qismlardan iborat va qachon kerakligini tushuntira olasiz.
  • Tugmaga bosilish javobini, spinner va progress bar'ni faqat CSS bilan yasaysiz.
  • Skeleton va uning ustida yuruvchi yorug'likni arzon usulda qurasiz, aria-busy bilan ekran o'quvchiga ham aytasiz.
  • Ro'yxat elementlarini --i o'zgaruvchisi bilan navbatma-navbat (stagger) paydo qilasiz.
  • Animatsiya tamoyillarini va tayyor kutubxonalarni (Animate.css, AutoAnimate, Lottie) taniysiz.

Oldin bilishingiz kerak: prefers-reduced-motion va harakat etikasi, Rendering pipeline va animatsiya samaradorligi, Gradientlar, CSS o'zgaruvchilari 2.

1. Nega bu kerak?

Aziz «Bahor» saytida stol band qiladi. "Yuborish" tugmasini bosadi. Hech narsa bo'lmaydi: tugma o'zgarmadi, sahifa jim. Aslida so'rov ketgan, server javob tayyorlayapti — lekin Aziz buni bilmaydi. U yana bosadi. Va yana. Jasur aka ertasi kuni bitta mehmondan uchta bir xil bron oladi.

Muammo — javob yo'qligi. Hayotda har amalga javob bor: lift tugmasi yonadi, choynak shivirlaydi, eshik qo'ng'irog'i jiringlaydi. Interfeysda bu javoblarni kichik animatsiyalar beradi. Ular micro-interaktsiyalar deb ataladi.

Gradientlar darsida "kontent kelguncha kulrang bloklar ustida yuradigan yorug' chiziq ham gradient" degan edik. Bugun o'sha skeletni ham, spinner va progress bar'ni ham quramiz.

2. Micro-interaktsiya nima?

2.1 To'rt qism

Micro-interaktsiya (micro-interaction) — bitta kichik vazifa atrofidagi javob: tugmani bosish, "yoqdi" belgisi, parol ko'rsatish. Uning to'rt qismi bor:

flowchart LR
  T["Turtki<br/>bosish, hover"] --> Q["Qoida<br/>nima bo'ladi"]
  Q --> J["Javob<br/>ko'rinadigan o'zgarish"]
  J --> H["Holat<br/>kutish, tayyor, xato"]

Nimaga qarang: foydalanuvchi faqat javobni ko'radi. Qoida — kod ichida, javob esa — ekranda. Yaxshi javob foydalanuvchiga uch narsani aytadi: "sizni eshitdim", "ishlayapman", "tayyor" (yoki "xato").

2.2 Qancha kutishda qanday javob

Nielsen Norman Group — odamlar saytdan qanday foydalanishini o'rganadigan tadqiqot kompaniyasi. Ularning mashhur "kutish chegaralari" bor — foydalanuvchi kutishni qanday sezishi haqida:

Kutish Foydalanuvchi sezadi Javob
~0.1 soniyagacha "Darhol" Tugmaning o'zi o'zgaradi (bosilish, rang)
~1 soniyagacha Fikri uzilmaydi Odatda loader shart emas
1–10 soniya Kutyapti Spinner yoki skeleton
10 soniyadan ko'p Chalg'iydi Progress bar: qancha qoldi

Qoida: juda tez javobga spinner qo'ymang — u bir lahza miltillab, ko'zni charchatadi. Kutish uzaysa — foydalanuvchiga holatni aniqroq ko'rsating.

3. Tugma: bosilish javobi

3.1 "Botish" effekti

Haqiqiy tugma bosilganda biroz botadi. CSS'da buni :active (Holat pseudo-klasslari) va scale beradi:

html
<style>
  body { font-family: system-ui, sans-serif; }
  .bron-havola {
    display: inline-block;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    color: #fffaf0;
    background: #2f6b3a;
    text-decoration: none;
  }
  .bron-havola:hover {
    background: #1a5d1a;
  }
  @media (prefers-reduced-motion: no-preference) {
    .bron-havola {
      transition: scale 0.1s ease-out, background-color 0.15s;
    }
    .bron-havola:active {
      scale: 0.97;
    }
  }
</style>
<a class="bron-havola" href="#bron">Stolni band qilish</a>

Nimaga qarang: havolani bosib turing — u 3 foizga kichrayib "botadi", qo'yib yuborsangiz qaytadi. Hover'da fon to'qroq bo'ladi. scale — kompozitor xususiyati (Rendering pipeline), qo'shni elementlar siljimaydi.

Uchta raqam:

  • 0.97 — sezilarli, lekin "sakramaydi". 0.9 ko'pchilik uchun juda kuchli.
  • 0.1 soniya — bosilish javobi darhol bo'lishi kerak ("~0.1 soniya" chegarasi).
  • @media ichida — "botish" ham harakat. Shuning uchun :active qoidasi faqat harakatga ruxsat bo'lsa ishlaydi. Kamaytirilgan rejimda javobni hover rangi beradi.

3.2 Yuborilmoqda holati

Tugma bosildi, so'rov ketdi. Endi tugma ikki narsani aytishi kerak: "ishlayapman" va "qayta bosmang". Namoyish:

html
<style>
  body { font-family: system-ui, sans-serif; }
  button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-block-size: 2.75rem;
    padding-inline: 1.5rem;
    border: 0;
    border-radius: 0.5rem;
    font: inherit;
    color: #fffaf0;
    background: #2f6b3a;
  }
  button[aria-disabled="true"] {
    background: #5b6660;
  }
  .aylana {
    inline-size: 1em;
    block-size: 1em;
    border: 2px solid currentColor;
    border-inline-end-color: transparent;
    border-radius: 50%;
  }
  @keyframes aylan {
    to { rotate: 1turn; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .aylana {
      animation: aylan 0.8s linear infinite;
    }
  }
</style>
<button type="button">Yuborish</button>
<button type="button" aria-disabled="true">
  <span class="aylana" aria-hidden="true"></span>
  Yuborilmoqda…
</button>

Nimaga qarang: ikkinchi tugma kulrang, ichida kichik halqa aylanadi, matn "Yuborilmoqda…" ga almashgan. Halqa — oddiy chegara, uning bir tomoni shaffof. Halqadagi aria-hidden="true" uni ekran o'quvchidan yashiradi: halqa bezak, matnning o'zi yetadi.

Bir tugmadan ikkinchisiga o'tishni (aria-disabled qo'yish, matnni almashtirish) JavaScript qiladi. Tugmalar tizimini to'liq Tugmalar tizimi darsida quramiz. Holatni almashtirishni esa DOM darslarida o'rganamiz.

3.3 Xato va muvaffaqiyat javobi

Micro-interaktsiyaning oxirgi qismi — holat: ish tugadi, lekin qanday? Ikki holatning ham o'z javobi bo'lsin.

Xato. Bron formasida telefon raqami noto'g'ri. Ko'p saytlar maydonni chapga-o'ngga "silkitadi" — xuddi odam boshini chayqagandek. Bu kuchli harakat, shuning uchun u faqat qo'shimcha bo'ladi. Asosiy javob — harakatsiz: qizg'ish chegara va maydon ostidagi aniq matn (Qulay va accessible forma darsidagi aria-describedby bilan bog'langan xato matni).

css
@keyframes silkinish {
  25%, 75% { translate: -4px 0; }
  50% { translate: 4px 0; }
}

/* Asosiy javob — rang (harakatsiz) */
input[aria-invalid="true"] {
  border-color: #b4461a;
}

/* Qo'shimcha — bir martalik silkinish, faqat ruxsat bo'lsa */
@media (prefers-reduced-motion: no-preference) {
  input[aria-invalid="true"] {
    animation: silkinish 0.3s ease-in-out;
  }
}

Silkinish bir marta, 0.3 soniya va 4 piksel — sezilarli, lekin bosh aylantirmaydi. aria-invalid ni JavaScript qo'yadi. Atribut paydo bo'lgan zahoti animatsiya bir marta ishlaydi.

Muvaffaqiyat. "Rahmat! Bron qabul qilindi" xabari harakat etikasi darsidagi uslubda: joyida opacity bilan paydo bo'ladi, role="status" esa uni ekran o'quvchiga o'qib beradi. Muvaffaqiyat xabarini o'z-o'zidan yo'qotmang — foydalanuvchi uni o'qib ulgurmasligi mumkin.

Maslahat: Faqat rang bilan javob bermang: rangni ajratmaydigan odam qizil chegarani sezmasligi mumkin (Kontent accessibility'si). Rang + matn + (xohlasangiz) belgi — uchalasi birga.

Tekshirib ko'ring: Nega bosilish javobiga width ni kamaytirish emas, scale tanlandi? Ikki sabab ayting.

Javob

Birinchi: width har kadrda layout chaqiradi, scale — faqat kompozitor (Rendering pipeline). Ikkinchi: width o'zgarsa, tugma yonidagi elementlar ham siljiydi — butun qator "titraydi". scale esa faqat tugmaning ko'rinishini o'zgartiradi, uning layout'dagi joyi o'sha-o'sha qoladi.

4. Spinner va progress bar

4.1 Spinner: conic-gradient + rotate

Gradientlar darsidagi conic-gradient — aylana bo'ylab rang. Uning ustiga markazda fon rangli doira qo'ysak — halqa chiqadi. Halqani aylantirsak — spinner:

html
<style>
  body { font-family: system-ui, sans-serif; background: #fffaf0; }
  .spinner {
    inline-size: 3rem;
    aspect-ratio: 1;
    border-radius: 50%;
    background:
      radial-gradient(closest-side, #fffaf0 70%, transparent 71%),
      conic-gradient(#2f6b3a 0 30%, #e8f1ec 0);
    animation: nafas 1.2s ease-in-out infinite alternate;
  }
  @keyframes nafas {
    to { opacity: 0.35; }
  }
  @keyframes aylan {
    to { rotate: 1turn; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .spinner {
      animation: aylan 0.9s linear infinite;
    }
  }
</style>
<div role="status">
  <div class="spinner"></div>
  <p>Menyu yuklanmoqda…</p>
</div>

Nimaga qarang: yashil yoy och halqa ustida aylanadi. Ikki fon qatlami: pastda — konus (30% yashil, qolgani och), ustida — markazi fon rangli doira. radial-gradient ning 70% va 71% orasidagi keskin chegara halqaning ichki chetini chizadi.

Kamaytirilgan rejimda halqa aylanmaydi — joyida sekin "nafas oladi" (shaffoflik o'zgaradi). Bu harakat etikasi darsida aytilgan istisno: yuklash belgisi cheksiz bo'lishi mumkin, lekin harakatsiz. E'tibor bering: nafas — asosiy holat, aylan — faqat ruxsat bo'lsa. Bu ham no-motion-first.

role="status" — Jonli hududlar darsidagi holat xabari: "Menyu yuklanmoqda…" ekran o'quvchida o'qiladi.

4.2 Progress bar: qancha qoldi

Aniq foiz ma'lum bo'lsa — HTML'ning o'z progress elementi (Guruhlash va natija elementlari darsidan). Uni brend rangiga bo'yashning eng qisqa yo'li — accent-color:

html
<style>
  body { font-family: system-ui, sans-serif; }
  progress {
    inline-size: 100%;
    accent-color: #2f6b3a;
  }
  .chiziq {
    block-size: 4px;
    overflow: hidden;
    background: #e8f1ec;
  }
  .chiziq::before {
    content: "";
    display: block;
    inline-size: 40%;
    block-size: 100%;
    background: #2f6b3a;
    translate: -100% 0;
  }
  @keyframes yurish {
    to { translate: 250% 0; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .chiziq::before {
      animation: yurish 1.4s ease-in-out infinite;
    }
  }
</style>
<label for="yuklash">Rasm yuklanmoqda: 70%</label>
<progress id="yuklash" max="100" value="70">70%</progress>
<p>Noma'lum davomiylik:</p>
<div class="chiziq" role="status" aria-label="Yuklanmoqda"></div>

Nimaga qarang: tepada — 70 foizga to'lgan yashil progress bar. Baseline ma'lumotiga ko'ra accent-color hali Baseline emas: ro'yxatda Android'dagi Chrome yo'q, Safari esa uni faqat 26.2 versiyadan qo'llaydi. Qo'rqinchli emas — tushunmagan brauzer progress ni o'zining standart rangida chizadi, u baribir ishlaydi. Pastda — ingichka chiziq ustida yashil bo'lak chapdan o'ngga qayta-qayta yuguradi. Bu "qancha qolgani noma'lum, lekin ishlayapman" belgisi.

Yuguruvchi bo'lak translate bilan suriladi, left bilan emas. translate foizi bo'lakning o'z enidan olinadi, chiziqnikidan emas. Bo'lak chiziqning 40% ini egallaydi. Chiziqning to'liq eni esa 100 ÷ 40 = 2.5 bo'lak, ya'ni bo'lakning 250% i. Shuning uchun translate: 250% bo'lakni chiziqning o'ng chetidan chiqarib yuboradi. Kamaytirilgan rejimda bo'lak chap chetda turadi — shuning uchun aria-label va ko'rinadigan matn muhim.

Maslahat: progress ning ichki qismlarini to'liq qayta chizish uchun brauzerga xos psevdo-elementlar kerak — Forma elementlarini zamonaviy stillash darsida ko'ramiz. Ko'p saytga accent-color yetadi.

Tekshirib ko'ring: Kamaytirilgan rejimda spinner nega butunlay to'xtatilmadi?

Javob

To'xtagan spinner "sahifa qotib qoldi" degan ma'no beradi. Spinner'ning vazifasi — "ishlayapman" deyish, u tirik ko'rinishi kerak. Shuning uchun aylanish (harakat) olib tashlandi, lekin shaffoflikning sekin o'zgarishi (harakat emas) qoldi. Bu — o'chirish emas, almashtirish.

5. Skeleton: kontent shakli oldindan

5.1 Nega spinner emas?

Spinner "kuting" deydi. Skeleton (skelet ekran) esa "mana bu yerda karta bo'ladi, bu yerda sarlavha" deydi: kontent kelguncha uning kulrang shaklini ko'rsatadi. Afzalliklari:

  • Kutish qisqaroq tuyuladi — ko'z sahifa tuzilishini oldindan o'qiydi.
  • Kontent kelganda sahifa "sakramaydi" — skelet bilan bir xil o'lchamda bo'lsa, joy oldindan band. Google'ning CLS ko'rsatkichi aynan shu sakrashni o'lchaydi (Core Web Vitals).

5.2 Quramiz

html
<style>
  body { font-family: system-ui, sans-serif; }
  .royxat {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 1rem;
  }
  .skelet {
    display: grid;
    gap: 0.5rem;
    padding: 1rem;
    border: 2px solid #e8f1ec;
    border-radius: 0.5rem;
  }
  .skelet span {
    position: relative;
    overflow: hidden;
    block-size: 1rem;
    border-radius: 0.25rem;
    background: #e8f1ec;
  }
  .skelet span:first-child {
    block-size: 1.5rem;
    inline-size: 60%;
  }
  .skelet span::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
      transparent, rgb(255 255 255 / 0.8), transparent);
    translate: -100% 0;
  }
  @keyframes yaltirash {
    to { translate: 100% 0; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .skelet span::after {
      animation: yaltirash 1.5s ease-in-out infinite;
    }
  }
  .korinmas {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
</style>
<section aria-busy="true">
  <h2>Bugungi taomlar</h2>
  <p class="korinmas" role="status">Menyu yuklanmoqda</p>
  <div class="royxat" aria-hidden="true">
    <div class="skelet"><span></span><span></span></div>
    <div class="skelet"><span></span><span></span></div>
  </div>
</section>

Nimaga qarang: sarlavha ostida ikkita karta shakli — yo'g'onroq "sarlavha" chizig'i va "narx" chizig'i. Ular ustidan chapdan o'ngga oq yorug'lik yuradi. Telefon enida kartalar ustma-ust turadi.

Uch qaror:

  • Yorug'lik — ::after ning translate i. Ko'p qo'llanmalarda background-position animatsiya qilinadi — bu har kadrda paint (Rendering pipeline darsidagi jadval). Bizniki — faqat kompozitor.
  • aria-hidden="true" — bo'sh kulrang bloklar ekran o'quvchi uchun ma'nosiz. Uning o'rniga korinmas matn "Menyu yuklanmoqda" deydi (position darsidagi texnika).
  • aria-busy="true" — "bu hudud hozir yangilanyapti" (Jonli hududlar). Kontent kelgach JavaScript uni false qiladi va skeletni haqiqiy kartalar bilan almashtiradi.

5.3 Holatni atribut bilan bog'lash

«Bahor» kodida alohida "skelet" class'ini qo'shish o'rniga, aria-busy ning o'zini selektor qilsa bo'ladi: holat HTML'da bir marta yoziladi, CSS esa uni o'qiydi (Atribut selektorlari):

css
[aria-busy="true"] .taom-karta {
  color: transparent; /* matn hali yo'q — faqat shakl */
  background: #e8f1ec;
}

Bu yondashuvda JavaScript faqat bitta atributni almashtiradi — ko'rinishni CSS o'zi hal qiladi.

Tekshirib ko'ring: Skelet kartasining balandligi haqiqiy kartadan 40 piksel past. Kontent kelganda nima bo'ladi?

Javob

Sahifa sakraydi: har karta 40 pikselga o'sadi va ostidagi hamma narsa pastga suriladi. Bu layout siljishi — foydalanuvchi bosmoqchi bo'lgan tugma qochib ketishi mumkin. Skelet haqiqiy kontent bilan bir xil o'lchamda bo'lsin: bir xil padding, qator balandligi va rasm uchun aspect-ratio.

6. Stagger: navbatma-navbat paydo bo'lish

6.1 G'oya

Ro'yxatdagi 6 ta karta birdaniga paydo bo'lsa — "shart" etadi. Har biri oldingisidan 80 ms keyin chiqsa — ko'z ro'yxatni yuqoridan pastga "o'qib" chiqadi. Bu stagger (navbatma-navbat kechikish) deb ataladi.

Kechikish karta tartibiga bog'liq: 0, 80, 160 ms... CSS o'zgaruvchilari 2 darsidagi usul — har elementga --i raqamini berib, calc() bilan hisoblash.

6.2 --i qayerdan keladi

Ko'p qo'llanmalarda raqam HTML'da yoziladi: style="--i: 2". «Bahor»da bu ishlamaydi — CSP inline style atributini to'sadi (CSS nima darsida ko'rgan edik). Shuning uchun raqamni CSS'ning o'zida :nth-child() bilan beramiz (Tuzilma pseudo-klasslari):

html
<style>
  body { font-family: system-ui, sans-serif; }
  summary { font-weight: bold; }
  ul { padding-inline-start: 1.25rem; }
  li:nth-child(2) { --i: 1; }
  li:nth-child(3) { --i: 2; }
  li:nth-child(4) { --i: 3; }
  @keyframes kirish {
    from { opacity: 0; translate: 0 0.75rem; }
  }
  @media (prefers-reduced-motion: no-preference) {
    details[open] li {
      animation: kirish 0.4s ease-out backwards;
      animation-delay: calc(var(--i, 0) * 80ms);
    }
  }
</style>
<details>
  <summary>Bugungi menyu</summary>
  <ul>
    <li>Osh — 35 000 so'm</li>
    <li>Lag'mon — 28 000 so'm</li>
    <li>Manti — 30 000 so'm</li>
    <li>Ko'k choy — 5 000 so'm</li>
  </ul>
</details>

Nimaga qarang: "Bugungi menyu" ni oching — to'rt taom yuqoridan pastga birin-ketin, 80 ms oraliq bilan suzib chiqadi. Yopib qayta ochsangiz, takrorlanadi.

  • var(--i, 0) — birinchi li da --i yo'q, zaxira 0 ishlaydi: kechikishsiz.
  • backwards — kechikish paytida element birinchi kadrda (ko'rinmas) turadi. Usiz to'rtinchi taom 240 ms ko'rinib turib, keyin yo'qolib, qayta paydo bo'lardi.
  • Kamaytirilgan rejimda ro'yxat darhol, to'liq ko'rinadi.

Maslahat: Stagger'ni 5–8 elementdan ko'pga qo'llamang. 30 ta kartada oxirgisi 2.4 soniya kutadi — foydalanuvchi uchun bu sekin sayt. Kelajakda sibling-index() funksiyasi elementning tartib raqamini o'zi beradi — uni Yangi paydo bo'layotgan CSS darsida ko'ramiz.

7. Animatsiya tamoyillari

1981-yilda Disney animatorlari "tirik" harakatning 12 tamoyilini yozishgan. Interfeys uchun eng foydalilari beshta:

  • Vaqt (timing). Tez — yengil va aniq, sekin — og'ir. UI uchun 100–300 ms.
  • Sekin kirish, sekin chiqish (ease in / ease out). Haqiqiy narsa birdan tezlashmaydi. Paydo bo'lish — ease-out, yo'qolish — ease-in (Transition).
  • Tayyorlanish (anticipation). Sakrashdan oldin biroz cho'kish. Tugmaning "botishi" — shu.
  • Davom etish (follow-through). To'xtagan narsa biroz "tebranadi". linear() prujina egri chizig'i shu uchun (Transition).
  • Sahnalashtirish (staging). Bir vaqtda bitta asosiy harakat — ko'z qayerga qarashni bilsin.

Qolgan tamoyillar (cho'zilish va siqilish, mubolag'a va boshqalar) ko'proq multfilm va o'yinlar uchun.

8. Tayyor kutubxonalar

O'zingiz yozish shart bo'lmagan holatlar ham bor. Uchta mashhur vositani tanib qo'ying (npm va JavaScript ulashni keyingi qismlarda o'rganamiz — hozir o'rnatish shart emas):

Vosita Nima Qachon
Animate.css Tayyor @keyframes to'plami: class qo'shasiz — element sakraydi, silkinadi Tez prototip; lekin ko'p effekti — "ko'z-ko'z uchun"
AutoAnimate Bir qator JavaScript: ro'yxatga element qo'shilsa yoki o'chsa — o'zi silliq animatsiya qiladi Ro'yxat, jadval, vazifalar
Lottie Dizayner After Effects (Adobe'ning animatsiya dasturi) da chizgan harakat maxsus faylga saqlanadi va saytda o'ynatiladi Murakkab illyustratsiya, logotip animatsiyasi

Har vositada ham prefers-reduced-motion ni tekshiring: Animate.css 4-versiyasi kamaytirilgan rejimda o'z animatsiyalarini bir lahzaga qisqartiradi (harakat etikasi darsidagi "xavfsizlik to'ri" usuli), lekin boshqa kutubxonada buni o'zingiz sozlashingiz kerak bo'lishi mumkin.

9. Ko'p uchraydigan xatolar

9.1 Har tez amalga spinner

So'rov 150 ms da qaytadi, spinner esa bir lahza miltillab o'chadi — foydalanuvchi "nimadir buzildi" deb o'ylaydi. Tuzatish: spinnerni kechiktirib ko'rsating. Birinchi 400 ms u shaffof tursin — so'rov tez qaytsa, spinner umuman ko'rinmaydi:

css
@keyframes korinish {
  from { opacity: 0; }
}

.spinner {
  /* 0.4 s kutadi, keyin 0.2 s da paydo bo'ladi */
  animation: korinish 0.2s 0.4s backwards;
}

backwards kechikish paytida birinchi kadrni (opacity: 0) ushlab turadi (keyframes va animation). Bu harakat emas, shaffoflik — shuning uchun @media shart emas. Yana bir yo'l: JavaScript spinnerni o'zi kechikish bilan qo'shadi.

9.2 Skeleton background-position bilan

Har kadrda paint — ayniqsa 20 ta skelet kartada telefon qiziydi. Tuzatish: «Skeleton» bo'limidagi ::after + translate.

9.3 Ekran o'quvchi skeletni "o'qiydi"

aria-hidden yo'q — Malika NVDA'da "bo'sh, bo'sh, bo'sh" eshitadi. Tuzatish: skeletga aria-hidden="true", hududga aria-busy="true", yonida korinmas holat matni.

9.4 Bosilish javobi faqat harakat bilan

:active da faqat scale — kamaytirilgan rejimda tugma hech qanday javob bermaydi. Tuzatish: rang yoki soya o'zgarishi @media dan tashqarida qolsin.

10. Mashqlar

1-mashq (oson): Qaysi javob?

Har holat uchun javob turini tanlang: tugmaning o'z javobi, spinner, skeleton yoki progress bar.

  1. "Savatga qo'shish" bosildi, javob 80 ms da keladi.
  2. «Bahor» menyusi sahifasi ochilyapti, taomlar 2 soniyada yuklanadi.
  3. 50 MB video yuklanyapti, 40 soniya ketadi.
  4. Bron yuborildi, server 3 soniya o'ylaydi.

Ishora: «Qancha kutishda qanday javob» jadvali.

Yechim
  1. Tugmaning o'z javobi (bosilish, rang). 0.1 soniyadan kam — loader kerak emas.
  2. Skeleton — kontentning shakli ma'lum (taom kartalari), sahifa sakramaydi.
  3. Progress bar (progress elementi) — uzoq kutish, foiz ma'lum.
  4. Tugma ichida spinner + "Yuborilmoqda…" matni — qayta bosishning oldini oladi.

2-mashq (o'rta): Spinner'ni «Bahor» rangida qayta yozing

Stajyor spinnerni shunday yozdi:

css
.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e8f1ec;
  border-top-color: #2f6b3a;
  border-radius: 50%;
  animation: aylan 1s linear infinite;
}
@keyframes aylan {
  to { transform: rotate(360deg); }
}

Uni ikkita talab bo'yicha tuzating: (1) kamaytirilgan rejimda aylanmasin, lekin "tirik" ko'rinsin; (2) no-motion-first bo'lsin.

Ishora: «Spinner» bo'limidagi nafas animatsiyasi.

Yechim
css
.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e8f1ec;
  border-top-color: #2f6b3a;
  border-radius: 50%;
  animation: nafas 1.2s ease-in-out infinite alternate;
}

@keyframes nafas {
  to { opacity: 0.35; }
}
@keyframes aylan {
  to { rotate: 1turn; }
}

@media (prefers-reduced-motion: no-preference) {
  .spinner {
    animation: aylan 1s linear infinite;
  }
}

Asosiy holat — harakatsiz nafas, aylanish — faqat ruxsat bo'lsa. Eski transform: rotate(360deg) o'rniga alohida rotate: 1turn yozdik. Ikkalasi bir xil aylantiradi, lekin alohida xususiyat boshqa transform bilan to'qnashmaydi (2D transformlar).

Bu yerdagi .spinner — namoyish nomi. «Bahor» fayliga ko'chirishda nom kerak bo'lsa, [role="status"] ichidagi elementni tanlang.

3-mashq (qiyin): Taom kartalari skeleti

«Bahor» menyusida taomlar yuklanguncha section ga aria-busy="true" qo'yiladi va ichida uchta bo'sh .taom-karta turadi (h3 va p bo'sh). Faqat CSS yozing:

  1. Yuklanayotgan hudud ichidagi .taom-karta ning h3 va p si kulrang to'rtburchak bo'lsin (h3 — 60% en, p — 40%), bo'sh bo'lsa ham balandligini yo'qotmasin.
  2. Kartalar ustidan yorug'lik yursin — faqat kompozitorda va faqat ruxsat bo'lsa.
  3. aria-busy false bo'lishi bilan hamma skelet uslublari o'z-o'zidan yo'qolsin.

Ishora: [aria-busy="true"] selektori; bo'sh element balandligi — min-block-size (O'lchamlar); lh birligi (O'lcham birliklari).

Yechim
css
[aria-busy="true"] .taom-karta {
  position: relative;
  overflow: hidden;
}

[aria-busy="true"] .taom-karta :is(h3, p) {
  min-block-size: 1lh;
  border-radius: 0.25rem;
  background: #e8f1ec;
}
[aria-busy="true"] .taom-karta h3 {
  inline-size: 60%;
}
[aria-busy="true"] .taom-karta p {
  inline-size: 40%;
}

[aria-busy="true"] .taom-karta::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    transparent, rgb(255 255 255 / 0.7), transparent);
  translate: -100% 0;
}

@keyframes yaltirash {
  to { translate: 100% 0; }
}

@media (prefers-reduced-motion: no-preference) {
  [aria-busy="true"] .taom-karta::after {
    animation: yaltirash 1.5s ease-in-out infinite;
  }
}
  • 1lh — bir qator balandligi: bo'sh h3 va p haqiqiy matn bilan bir xil balandlikda turadi, kontent kelganda sahifa sakramaydi.
  • Hamma qoida [aria-busy="true"] ga bog'langan: JavaScript atributni false qilgan zahoti skelet yo'qoladi — CSS'da hech narsa o'chirilmaydi.
  • Yorug'lik butun kartadan yuradi (bitta ::after), har chiziqdan alohida emas — kamroq qatlam.

4-mashq: Portfolio qadami — tugma javobi va kartalar stagger'i

  1. Aloqa formasidagi button ga bosilish javobini qo'shing: scale: 0.97, 0.1 soniya. Kamaytirilgan rejimda ham tugma javob bersin (fon to'qroq bo'lsin).
  2. loyihalar/index.html da kartalar sahifa ochilganda navbatma-navbat (stagger) paydo bo'lsin: 80 ms oraliq, 0.4 soniya.
  3. Hamma harakat no-motion-first; hover rangi faqat hover: hover qurilmalarda (Mobil qurilma tuzoqlari).

Ishora: tugma rangi --rang-asosiy (Yakuniy loyiha); to'qroq rang — Ranglar 3 dagi nisbiy rang yoki color-mix().

Yechim

asosiy.css ning «Komponentlar» qismiga, forma va kartalar qoidalaridan keyin:

css
/* Tugma: bosilganda to'qroq — harakatsiz javob */
button:active {
  background-color:
    color-mix(in oklch, var(--rang-asosiy), black 15%);
}

/* Kartalar tartib raqami (stagger uchun) */
.loyihalar > article:nth-child(2) { --i: 1; }
.loyihalar > article:nth-child(3) { --i: 2; }
.loyihalar > article:nth-child(4) { --i: 3; }

@keyframes kirish {
  from {
    opacity: 0;
    translate: 0 1rem;
  }
}

@media (prefers-reduced-motion: no-preference) {
  button {
    transition: scale 0.1s ease-out;
  }
  button:active {
    scale: 0.97;
  }
  .loyihalar > article {
    animation: kirish 0.4s ease-out backwards;
    animation-delay: calc(var(--i, 0) * 80ms);
  }
}
  • black 15% — brend rangiga 15% qora aralashtiriladi; qorong'i rejimda ham (u yerda --rang-asosiy och yashil) to'qroq chiqadi va farq seziladi.
  • :active — bosish paytida, sensorli ekranda ham ishlaydi. Shuning uchun u hover: hover ichiga olinmadi.
  • Uchinchi-to'rtinchi karta hozir bo'lmasa ham, qoida zarar qilmaydi — kelajakdagi loyiha uchun tayyor.
  • Kartalar stagger'i vaqtincha: Scroll-driven animatsiyalar darsida uni skroll bilan paydo bo'lish almashtiradi. Tugma javobi esa qoladi.
  • backwards — kechikayotgan kartalar ochilishda bir lahza ko'rinib qolmaydi. Animatsiya tugagach karta oddiy holatida, opacity: 0 hech qayerda yozilmagan (animation: none tuzog'i).

11. Real ishda

  • Har ilovada. Telegram'da xabar yuborilganda soat belgisi, keyin bitta va ikkita "ptichka" — micro-interaktsiya: "yuborilmoqda", "yetkazildi", "o'qildi". Click va Payme to'lov tugmasi bosilgach aylanuvchi belgi va "Muvaffaqiyatli" ekrani.
  • Skeletonlar — YouTube, Facebook va ko'p onlayn do'konlarda: lenta yuklanguncha kulrang kartalar.
  • Dizayn tizimlari (Material Design, Apple HIG) har komponentning hover, bosilgan, yuklanayotgan holatlarini alohida tasvirlaydi. Figma'da ular variant sifatida chiziladi (Figma dasturchi uchun 2).
  • React va boshqa freymvorklar (kursda keyinroq) — yuklash holati uchun maxsus imkoniyatlar bor, lekin ko'rinish baribir shu CSS.
  • Intervyuda so'raladi: "Skeleton va spinner farqi?", "Skeleton yorug'ligini qanday samarali animatsiya qilasiz?", "Yuklash holatini ekran o'quvchiga qanday aytasiz?".

Xulosa

  • Micro-interaktsiya: turtki → qoida → javob → holat. Har amalga ko'rinadigan javob bo'lsin.
  • Bosilish — scale: 0.97, 0.1 soniya; javobning harakatsiz qismi (rang) @media dan tashqarida.
  • Spinner — conic-gradient + rotate; kamaytirilgan rejimda — sekin opacity, to'xtab qolmaydi.
  • Skeleton — kontent o'lchamida; yorug'lik — ::after ning translate i; aria-hidden + aria-busy + holat matni.
  • Stagger — --i ni :nth-child() bilan bering, calc() bilan kechiktiring, backwards ni unutmang.
  • Tayyor kutubxona ham prefers-reduced-motion ni hurmat qilishini tekshiring.

Keyingi dars: Brauzer mosligi: Baseline, caniuse va @supports — yangi CSS'ni qachon ishlatish mumkinligini aniqlash va uni xavfsiz qo'shishni o'rganamiz.

Manbalar

  • Nielsen Norman Group: "Response Times: The 3 Important Limits", "Skeleton Screens 101" — nngroup.com
  • MDN: "conic-gradient()", "accent-color", "aria-busy", "ARIA: status role", "animation-delay" — developer.mozilla.org
  • web.dev: "Animations and performance", "Optimize Cumulative Layout Shift" — web.dev
  • Animate.css — animate.style; AutoAnimate — auto-animate.formkit.com; Lottie — airbnb.io/lottie
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Micro-interaktsiyalar CSS'da: tugma javobi, loader, progress bar va skeleton — IlmHamroh