Tarkibga o'tish

15 β€” Holat variantlari

⬅️ Oldingi: 14 β€” Soya, filter va mask Β· 🏠 README Β· Keyingi: 16 β€” Dark mode ➑️

Bu bobda: Tailwind'ning eng kuchli imkoniyatlaridan birini β€” holat variantlarini o'rganamiz. Bu β€” sahifani JavaScript yozmasdan interaktiv qilish san'ati. variant:utility modelini chuqurlashtiramiz; interaksiya holatlari (hover:, active:, focus-visible:, focus-within:); forma holatlari (disabled:, checked:, user-invalid:, placeholder-shown:); strukturaviy variantlar (first:/odd:/empty:); psevdo-elementlar (before:/after:/placeholder:/marker:/file:); va asosiy ikki naqsh β€” group (ota boshqaradi) va peer (sibling boshqaradi); v4'ning has-* kombinatori; hamda atribut variantlari (data-*/aria-*). Bobning oxirida siz CSS-only toggle, ochiluvchi karta va o'zini tekshiradigan forma quryapsiz β€” bitta ham onclicksiz.


15.1 Avval nega? β€” JavaScript'siz interaktivlik

Eslang, 03-bobda variant tushunchasi bilan tanishgandik: hover:bg-indigo-700 β€” "ustiga sichqoncha kelganda foni o'zgarsin". O'shanda biz buni shunchaki bir misol sifatida ko'rgandik. Endi esa shu g'oyani to'liq quvvati bilan ochamiz.

Savol shunday: tugma bosilganda rangi o'zgarsin, input noto'g'ri to'ldirilganda qizarsin, kartaning ustiga kelinganda yashirin tugma chiqsin β€” bularning hammasini odatda JavaScript bilan qilardingiz, to'g'rimi? element.addEventListener('mouseover', ...), holat (state), DOM o'zgartirish... Ko'p kod, ko'p xato manbai.

Lekin bu interaktivlikning katta qismi aslida sof CSS bilan β€” psevdo-klasslar (:hover, :checked, :focus) orqali β€” hal bo'ladi. Brauzer bu holatlarni o'zi kuzatib turadi. Tailwind sizga shu psevdo-klasslarni klass darajasida beradi: utility oldiga variant prefiksi qo'ysangiz, "shu utility faqat shu holatda ishlasin" degani.

<!-- Toza CSS'dagi g'oya... -->
<style>
  .btn { background: #6366f1; }
  .btn:hover { background: #4f46e5; }
</style>

<!-- ...Tailwind'da bitta qatorda -->
<button class="bg-indigo-500 hover:bg-indigo-700">Saqlash</button>

Mana shu prefiks = holat-shart g'oyasi butun bobning yuragi. Va bu β€” aynan inline style="" atributi hech qachon qila olmaydigan narsa: inline style hoverni, focusni, darkni bilmaydi. Variantlar β€” utility-first'ning shu kamchilikni yopadigan ustunligi.

πŸ“Œ Atamalar. Psevdo-klass β€” element holatini bildiruvchi CSS shart (:hover, :checked, :focus). Psevdo-element β€” elementning bir qismi yoki "soxta" qo'shimcha bo'lagi (::before, ::placeholder, ::marker). Variant β€” utility'ni shartga bog'lovchi prefiks (hover:, first:, data-[state=open]:).


15.2 Modelni chuqurlashtirish β€” shartlar qatlami

Variant β€” bu utility'ga shart qo'shadi. Bir nechta variant qo'ysangiz, ular zanjirlanadi (stacking), va utility faqat hamma shart bir vaqtda rost bo'lgandagina qo'llanadi:

<button class="dark:hover:bg-indigo-700">...</button>

Buni o'qib chiqamiz: utility β€” bg-indigo-700 (eng oxirida, "nima"). Oldidagi ikki prefiks ikki shart: dark: (qorong'u rejim VA) hover: (ustiga sichqoncha kelgan). Demak β€” "qorong'u rejimda va sichqoncha ustida bo'lganda foni indigo-700". Faqat ikkalasi birga.

variant:utility modeli β€” dark:hover:bg-indigo-700 ikki shartni qatlamlaydi, faqat ikkalasi rost bo'lganda fon qo'llanadi; oddiy bg-indigo-600 esa doim ishlaydi

Diqqat qiling: prefikssiz bg-indigo-600 esa shartsiz β€” u doim, asos sifatida ishlaydi. Variantlarning butun mohiyati shu: ular utility'ni muayyan vaziyatga qamab qo'yadi.

πŸ’‘ Tartib. Prefikslar zanjirida tartib odatda muhim emas (dark:hover: va hover:dark: bir xil ishlaydi), lekin jamoa ichida bitta tartibni izchil tuting. Faqat bir-ikki maxsus holatda (masalan group-hover:focus:) tartib mantiqiy ahamiyat kasb etadi β€” buni quyiroqda ko'ramiz.


15.3 Interaksiya holatlari β€” hover, active, focus

Eng tez-tez ishlatiladigan to'rt holat. Ularni tugma misolida ko'raylik:

  • hover: β€” sichqoncha element ustida turganda. Tugmalar, havolalar uchun klassik.
  • active: β€” element bosilib turilgan payt (sichqoncha tugmasi qo'yib yuborilgunicha). "Bosildi" hissini beradi.
  • focus: β€” element fokusda (Tab bilan yoki bosilganda).
  • focus-visible: β€” fokus, lekin faqat klaviatura orqali kelganda. Bu β€” eng muhim noziklik (quyida).
<button class="rounded-lg bg-indigo-500 px-4 py-2 font-medium text-white
               hover:bg-indigo-700
               active:scale-95
               focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:outline-none">
  Saqlash
</button>

Bu bitta tugma β€” to'rt-besh holatda turlicha ko'rinadi:

Bitta tugma besh holatda: normal, hover to'qroq fon, active bosilgan, focus-visible ring halqasi va disabled xira

focus: emas, focus-visible: β€” a11y nozikligi

Bu β€” boshlovchilar deyarli har doim adashadigan joy. Agar siz focus:ring ishlatsangiz, sichqoncha bilan bosganda ham atrofda halqa chiqadi β€” bu ko'pchilikka "xato"dek, xunuk tuyuladi. Shuning uchun ko'pchilik developerlar fokus halqasini umuman o'chirib tashlaydi (outline-none) β€” bu esa klaviatura foydalanuvchilari uchun katta muammo: ular endi qaerda turganini ko'rmaydi.

To'g'ri yechim β€” focus-visible:. Brauzer aqlli: u "fokus klaviaturadanmi yoki sichqonchadanmi" ni ajratadi. focus-visible: halqani faqat klaviatura bilan kelganda ko'rsatadi. Sichqoncha bilan bosgan odam halqani ko'rmaydi, Tab bilan kelgan odam esa ko'radi. Hamma xursand.

πŸ’‘ Qoida. Fokus ko'rinishi uchun deyarli har doim focus-visible: ni tanlang, focus: emas. (Halqa β€” ring β€” haqida 13-bobda gaplashgandik; u aynan fokus indikatori uchun ideal, chunki layoutni surmasdan element atrofida chiziladi.)

focus-within: β€” ota bola fokusiga javob beradi

focus-within: β€” input guruhlari uchun ajoyib. U "ichimdagi biror element fokusda bo'lsa" degani. Masalan, qidiruv maydoni: ichidagi input fokuslanganda butun o'rab turgan quti chetini yoritamiz:

<div class="flex items-center gap-2 rounded-lg border border-slate-300 px-3 py-2
            focus-within:border-indigo-500 focus-within:ring-2 focus-within:ring-indigo-200">
  <span>πŸ”</span>
  <input class="flex-1 outline-none" placeholder="Qidirish...">
</div>

Foydalanuvchi input'ga bosganda, butun quti "jonlanadi" β€” garchi fokus aslida input'da bo'lsa ham. Bu sof CSS β€” JavaScript yo'q.


15.4 Forma holatlari

Formalar β€” holat variantlarining eng katta iste'molchisi. Mana asosiylari.

disabled: β€” o'chirilgan element

<button class="bg-indigo-600 text-white
               disabled:opacity-50 disabled:cursor-not-allowed" disabled>
  Yuborish
</button>

disabled:opacity-50 β€” o'chiq element xiralashadi; disabled:cursor-not-allowed β€” sichqoncha "taqiqlangan" belgisini ko'rsatadi. Foydalanuvchi darrov "bu hozir bosilmaydi" deb tushunadi.

checked: β€” custom checkbox, radio, toggle

Brauzerning standart checkbox'i xunuk va stillanmaydi. Yechim: uni yashirib (appearance-none), o'zimiz chizamiz va checked: bilan belgilangan holatni boshqaramiz:

<input type="checkbox"
       class="size-5 appearance-none rounded border border-slate-300
              checked:border-indigo-600 checked:bg-indigo-600">

Belgilanganida β€” bg-indigo-600 bilan ko'k bo'ladi, aks holda oq qoladi. (To'liq, ichida "tasdiq belgisi" bo'lgan custom checkbox'ni quyida peer bilan ham quramiz.)

required:, valid:/invalid: va v4'ning user-valid:/user-invalid:

Bu β€” juda muhim UX nozikligi. invalid: psevdo-klassi input bo'sh yoki noto'g'ri bo'lishi bilanoq ishga tushadi β€” ya'ni foydalanuvchi hali bitta harf ham yozmasdan turib forma "qizarib" turadi. Bu yoqimsiz.

v4 yechim beradi: user-invalid: va user-valid:. Bular faqat foydalanuvchi maydon bilan ishlaganidan keyin (yozib, keyin chiqib ketganda) ishlaydi. Natija ancha tabiiy:

<input type="email" required placeholder="email@misol.uz"
       class="rounded-lg border border-slate-300 px-3 py-2
              user-valid:border-green-500
              user-invalid:border-red-500 user-invalid:bg-red-50">

Foydalanuvchi email'ni noto'g'ri kiritib, maydondan chiqsa β€” chet qizaradi va fon engil qizg'ish bo'ladi. To'g'ri kiritsa β€” yashil. Lekin sahifa endigina ochilganda hech narsa qizarmaydi.

πŸ’‘ invalid: vs user-invalid:. "Darrov tekshiruv" kerak bo'lmasa (deyarli hech qachon kerak emas), user-invalid: ni tanlang β€” u xatoni faqat o'rinli paytda ko'rsatadi.

placeholder-shown: va read-only:

placeholder-shown: β€” input bo'sh (placeholder ko'rinib turganda) ishlaydi. Floating label naqshida juda foydali (quyida peer bilan ko'ramiz). read-only: β€” faqat o'qish uchun maydonni boshqacha ko'rsatadi:

<input readonly value="O'zgartirib bo'lmaydi"
       class="read-only:bg-slate-100 read-only:text-slate-500">

15.5 Strukturaviy variantlar β€” element ro'yxatdagi o'rni

Bu variantlar elementning boshqalar orasidagi o'rniga qaraydi. Jadval va ro'yxatlar uchun ajoyib.

  • first: / last: β€” birinchi / oxirgi bola.
  • odd: / even: β€” toq / juft (zebra-chiziqli jadval).
  • only: β€” yagona bola (boshqa siblingi yo'q).
  • empty: β€” ichi bo'sh element (matn ham, bola ham yo'q) β€” bo'sh konteynerni yashirish uchun.
  • nth-3: / nth-[3n+1]: β€” aniq tartib raqami yoki formulasi bo'yicha.
<!-- Zebra-chiziqli ro'yxat -->
<ul>
  <li class="px-4 py-2 odd:bg-white even:bg-slate-50">Qator 1</li>
  <li class="px-4 py-2 odd:bg-white even:bg-slate-50">Qator 2</li>
  <li class="px-4 py-2 odd:bg-white even:bg-slate-50">Qator 3</li>
</ul>

<!-- Birinchi/oxirgi elementdan ortiqcha chegarani olib tashlash -->
<li class="border-b last:border-b-0">...</li>

<!-- Bo'sh bo'lsa, butunlay yashir -->
<p class="empty:hidden">{{ xabar }}</p>

empty:hidden β€” agar {{ xabar }} bo'sh bo'lsa, paragraf umuman ko'rinmaydi (ortiqcha bo'sh joy qolmaydi). Strukturaviy variantlar takrorlanuvchi elementlarda β€” siklda yaratilgan ro'yxatlarda β€” eng qadrli, chunki bitta klass to'plami har bir elementga o'z o'rniga qarab turlicha ta'sir qiladi.


15.6 Psevdo-elementlar β€” before, after, placeholder, marker, file

Bu variantlar elementning psevdo-elementlarini stillaydi. Diqqat: before:/after: uchun content-[''] majburiy β€” usiz psevdo-element umuman yaratilmaydi.

<!-- "Majburiy" yulduzchasi (after bilan) -->
<label class="after:ml-0.5 after:text-red-500 after:content-['*']">Ism</label>

<!-- Dekorativ chiziq (before bilan, bo'sh content kerak) -->
<span class="relative before:absolute before:-bottom-1 before:left-0 before:h-0.5
             before:w-full before:bg-indigo-500 before:content-['']">Sarlavha</span>

Yana bir nechta foydali psevdo-element varianti:

  • placeholder: β€” placeholder matnini stillaydi: placeholder:text-slate-400 placeholder:italic.
  • selection: β€” foydalanuvchi belgilab (ajratib) olgan matn rangini boshqaradi: selection:bg-indigo-200.
  • marker: β€” ro'yxat belgilarini (β€’, raqamlar): marker:text-indigo-500.
  • first-letter: / first-line: β€” birinchi harf (yirik bosh harf β€” drop cap) yoki birinchi qator.
  • file: β€” <input type="file"> ichidagi tugma. Standart fayl tugmasi xunuk; uni shunday stillaymiz:
<input type="file"
       class="file:mr-4 file:rounded-lg file:border-0 file:bg-indigo-50
              file:px-4 file:py-2 file:text-indigo-700 hover:file:bg-indigo-100">

15.7 group β€” ota boshqaradigan naqsh

Endi eng kuchli ikki naqshga keldik. Birinchisi β€” group.

Muammo: kartaning ustiga sichqoncha kelganda, uning ichidagi tugma yoki matn o'zgarsin. Lekin hover: faqat o'sha elementning o'z holatini biladi β€” bola ota'ning hoveriga "qarayolmaydi". Yechim:

  1. Otaga class="group" qo'yasiz.
  2. Bola group-hover: (yoki group-focus:) ishlatadi β€” u endi ota holatiga javob beradi.
<div class="group rounded-xl border border-slate-200 p-5 hover:border-indigo-400">
  <h3 class="font-semibold text-slate-900 group-hover:text-indigo-600">Mahsulot nomi</h3>
  <p class="text-sm text-slate-500">Qisqa tavsif</p>

  <!-- Odatda yashirin; karta hover'da chiqadi -->
  <button class="mt-3 opacity-0 transition group-hover:opacity-100">
    Batafsil β†’
  </button>
</div>

Kartaning istalgan joyiga sichqoncha kelsa: sarlavha rangi o'zgaradi va "Batafsil" tugmasi paydo bo'ladi. Bu β€” karta/ro'yxat elementlari uchun eng asosiy naqsh.

Nomli guruhlar β€” ichma-ich joylashganda

Agar guruhlar bir-birining ichida bo'lsa, group-hover: chalkashadi: qaysi guruh? Yechim β€” guruhga nom berasiz (group/menu) va bolada shu nomni ishlatasiz (group-hover/menu:):

<div class="group/card ...">
  <div class="group/menu ...">
    <span class="group-hover/card:text-indigo-600">Karta hover'iga</span>
    <span class="group-hover/menu:text-amber-600">Menyu hover'iga</span>
  </div>
</div>

Endi har bir bola aynan qaysi guruhga javob berishini aniq bilasiz.


15.8 peer β€” sibling boshqaradigan naqsh

Ikkinchi naqsh β€” peer. Bu groupga o'xshaydi, lekin ota–bola emas, sibling–sibling munosabati uchun. Eng ko'p: input holatiga keyingi element javob beradi.

  1. Input'ga class="peer" qo'yasiz.
  2. Undan keyin keladigan sibling peer-checked: / peer-focus: / peer-invalid: / peer-placeholder-shown: ishlatadi.

group va peer farqi: group β€” ota elementga .group qo'yiladi, bola ota holatiga javob beradi (ota->bola); peer β€” oldingi sibling input'ga .peer qo'yiladi, keyingi sibling input holatiga javob beradi (sibling->sibling)

⚠️ Eng katta peer xatosi β€” tartib. CSS sibling selektori faqat oldinga qaray oladi. Demak peer input HTML'da stillanadigan sibling'dan OLDIN turishi shart. Teskari tartib hech narsa qilmaydi.

CSS-only toggle switch

Klassik misol β€” checkbox'ni yashirib, label ichida chiroyli "toggle" chizamiz; holatni peer-checked: boshqaradi:

<label class="inline-flex cursor-pointer items-center gap-3">
  <input type="checkbox" class="peer sr-only">

  <!-- Yo'lak: peer belgilanganda ko'k bo'ladi -->
  <span class="relative h-6 w-11 rounded-full bg-slate-300 transition
               peer-checked:bg-indigo-600
               after:absolute after:left-0.5 after:top-0.5 after:size-5
               after:rounded-full after:bg-white after:transition after:content-['']
               peer-checked:after:translate-x-5"></span>

  <span class="text-sm text-slate-700">Bildirishnomalar</span>
</label>

sr-only β€” checkbox'ni ko'zdan yashiradi (lekin ekran o'quvchi uchun qoldiradi). Bosganda: yo'lak ko'karadi (peer-checked:bg-indigo-600), oq doira o'ngga suriladi (peer-checked:after:translate-x-5). Bitta ham JavaScript qatori yo'q.

Floating label va xato xabari

peer-placeholder-shown: bilan input bo'sh bo'lganda label katta va pastda, yozila boshlanganda kichrayib yuqoriga "suzib chiqadi". peer-invalid: bilan esa xato xabarini faqat kerak bo'lganda ko'rsatamiz:

<input type="email" required placeholder="Email"
       class="peer rounded-lg border border-slate-300 px-3 py-2">

<!-- Xato xabari: input noto'g'ri bo'lsagina ko'rinadi -->
<p class="mt-1 hidden text-sm text-red-600 peer-[&.user-invalid]:block">
  To'g'ri email kiriting
</p>

15.9 has-* β€” bola asosida otani stillash (v4)

group ota holatini bolaga uzatadi. has-* esa teskari: bola asosida otani stillaydi. Bu β€” v4'ning kuchli yangiligi (CSS :has() selektori ustiga qurilgan).

<!-- Ichida belgilangan checkbox bo'lsa, butun karta yoritiladi -->
<label class="block rounded-xl border-2 border-slate-200 p-4
              has-[:checked]:border-indigo-600 has-[:checked]:bg-indigo-50">
  <input type="radio" name="reja" class="mr-2">
  Pro reja β€” $20/oy
</label>

<!-- Ichida rasm bo'lsa, boshqacha fon -->
<div class="p-4 has-[img]:bg-slate-100">...</div>

Birinchi misol β€” tanlanadigan karta naqshi: foydalanuvchi radio'ni tanlasa, butun karta atrofida ko'k chegara va engil fon paydo bo'ladi. Bir nechta shunday karta qo'ysangiz β€” chiroyli "reja tanlash" UI'si, JavaScript'siz.

has-* ni group bilan ham qo'shsa bo'ladi (group-has-[...]:) β€” masalan, ota ichida biror element bo'lsa, butun guruhga ta'sir qilish uchun.


15.10 Atribut variantlari β€” JS holatiga ko'prik

Ba'zan holat HTML atributida saqlanadi β€” ayniqsa headless UI kutubxonalar (Radix, Headless UI), Alpine.js yoki sizning JS kodingiz data-* / aria-* atributlarini o'zgartirganda. Tailwind shu atributlarga to'g'ridan-to'g'ri javob bera oladi:

<!-- JS yoki kutubxona data-state="open" qo'yganda -->
<div data-state="closed"
     class="h-0 overflow-hidden transition-all data-[state=open]:h-auto">
  Akkordeon mazmuni
</div>

<!-- aria-expanded ga qarab strelkani aylantirish -->
<button aria-expanded="false" class="aria-expanded:rotate-180">β–Ό</button>

<!-- Jadval sarlavhasi β€” saralash yo'nalishi -->
<th aria-sort="ascending" class="aria-[sort=ascending]:text-indigo-600">Sana</th>

Bu β€” sof-CSS variantlar bilan JS-boshqariladigan holat o'rtasidagi ko'prik. JS faqat atributni almashtiradi (data-state="open"), butun ko'rinish o'zgarishini esa Tailwind variantlari ko'taradi. Logika va stil ajralgan β€” toza.


15.11 Boshqa foydali variantlar (qisqacha)

Tez-tez kerak bo'ladigan yana bir nechta variant:

  • motion-reduce: β€” foydalanuvchi "harakatni kamaytirish" sozlamasini yoqsa, animatsiyani so'ndiring: motion-reduce:transition-none. Hurmat va a11y belgisi.
  • print: β€” bosib chiqarishda boshqacha ko'rinish: print:hidden (navbar'ni chop etmaslik).
  • supports-[...]: β€” brauzer biror CSS xususiyatni qo'llasagina: supports-[backdrop-filter]:bg-white/60.
  • not-*: β€” negatsiya: not-hover:opacity-70 (hover'da bo'lmaganda).
  • *: va **: β€” to'g'ridan-to'g'ri bolalar (*:) yoki barcha avlodlar (**:) uchun: *:rounded-lg β€” har bir bevosita bola dumaloq bo'ladi.
  • in-*: β€” groupga o'xshaydi, lekin ota'ga group klassi shart emas: in-focus:opacity-100 β€” istalgan ota fokusda bo'lsa.

15.12 Amaliy misol β€” interaktiv "reja tanlash" paneli

O'rgangan naqshlarni birlashtiramiz: uchta tanlanadigan karta (has-[:checked]:), har biri fokus-ringli, hammasi JavaScript'siz:

<form class="grid gap-3 sm:grid-cols-3">
  <label class="cursor-pointer rounded-xl border-2 border-slate-200 p-4 transition
                hover:border-slate-300
                has-[:checked]:border-indigo-600 has-[:checked]:bg-indigo-50
                has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-indigo-300">
    <input type="radio" name="reja" class="sr-only">
    <span class="block font-semibold text-slate-900">Boshlang'ich</span>
    <span class="block text-sm text-slate-500">Bepul</span>
  </label>

  <label class="cursor-pointer rounded-xl border-2 border-slate-200 p-4 transition
                hover:border-slate-300
                has-[:checked]:border-indigo-600 has-[:checked]:bg-indigo-50
                has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-indigo-300">
    <input type="radio" name="reja" class="sr-only">
    <span class="block font-semibold text-slate-900">Pro</span>
    <span class="block text-sm text-slate-500">$20/oy</span>
  </label>

  <label class="cursor-pointer rounded-xl border-2 border-slate-200 p-4 transition
                hover:border-slate-300
                has-[:checked]:border-indigo-600 has-[:checked]:bg-indigo-50
                has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-indigo-300">
    <input type="radio" name="reja" class="sr-only">
    <span class="block font-semibold text-slate-900">Jamoa</span>
    <span class="block text-sm text-slate-500">$50/oy</span>
  </label>
</form>

Tahlil:

  • sr-only β€” haqiqiy radio yashirin, lekin klaviatura/ekran-o'quvchi uchun ishlaydi.
  • has-[:checked]: β€” tanlangan karta ko'k chegara + engil fon oladi.
  • has-[:focus-visible]: β€” Tab bilan kelganda butun karta atrofida halqa (a11y).
  • hover: β€” ustiga kelganda yengil ishora.

Uch xulq-atvor β€” bitta ham addEventListenersiz.


15.13 Tez-tez uchraydigan xatolar

  • focus: ni focus-visible: o'rniga ishlatish. Sichqoncha bilan bosganda ham xunuk halqa chiqadi. Fokus ko'rinishi uchun focus-visible: tanlang.
  • peer ni keyin qo'yish. peer input stillanadigan sibling'dan oldin turishi shart β€” CSS oldinga qaray oladi, orqaga emas.
  • before:/after: da content-[''] ni unutish. Psevdo-element content bersagina paydo bo'ladi. Bo'sh bo'lsa ham content-[''] shart.
  • group klassini ota'ga qo'ymaslik. group-hover: ishlashi uchun ota'da albatta class="group" (yoki nomli group/x) bo'lishi kerak. Bola o'zicha "qaysi ota" ekanini bilmaydi.
  • invalid: bilan darrov qizartirish. Sahifa ochilishi bilan bo'sh forma qizaradi. user-invalid: ishlating β€” u faqat foydalanuvchi maydon bilan ishlagandan keyin yonadi.
  • Hammasiga JavaScript yozish. Toggle, akkordeon, tanlanadigan karta, hover-reveal β€” bularning ko'pi sof CSS bilan (peer/group/has-*/checked:) hal bo'ladi. Avval variantlarni o'ylang.

πŸ”­ Oldinga qarash. Bu variantlardan eng ko'p formalar foyda ko'radi β€” input, checkbox, toggle, validatsiya. To'liq forma UI komponentlarini 23-bobda shu variantlardan keng foydalanib quramiz. Sof CSS darajasidagi UI holatlari haqida HTML & CSS kitobining shu bobida ham ko'rishingiz mumkin. Keyingi bob esa β€” eng ko'p so'raladigan variant: dark mode.


Mashqlar

1-mashq. Quyidagi klassni shartlarga ajrating va bitta jumlada ma'nosini ayting: dark:focus-visible:ring-indigo-400. Qaysi qism(lar) variant, qaysi biri utility?

Yechim
  • dark: β€” variant (shart: qorong'u rejim).
  • focus-visible: β€” variant (shart: klaviatura orqali fokus).
  • ring-indigo-400 β€” utility (halqa rangi).

Ma'nosi: "Qorong'u rejimda va element klaviatura bilan fokuslanganda atrofida indigo-400 rangli halqa chizilsin." Ikkala shart ham bir vaqtda rost bo'lishi kerak. Prefikssiz bo'lsa, ring-indigo-400 doim ishlardi.

2-mashq. Bir dasturchi tugmaga focus:ring-2 yozdi va sichqoncha bilan bosganda atrofida halqa chiqib qolganidan shikoyat qilyapti. Muammo nimada va qanday tuzatasiz?

Yechim

focus: har qanday fokusda β€” sichqoncha bilan bosilganda ham β€” halqani ko'rsatadi, shu sababli xunuk tuyuladi. To'g'ri yechim β€” focus-visible: ishlatish:

<button class="focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:outline-none">
  ...
</button>

focus-visible: halqani faqat klaviatura (Tab) orqali fokuslangandagina ko'rsatadi. Sichqoncha bilan bosganda halqa chiqmaydi, lekin klaviatura foydalanuvchilari uchun ko'rinish saqlanadi β€” a11y buzilmaydi. (Halqani umuman outline-none bilan o'chirib tashlamang!)

3-mashq. Karta ustiga sichqoncha kelganda ichidagi "Ko'rish" tugmasi paydo bo'lsin (asosan yashirin). Kerakli klasslarni yozing.

Yechim

group naqshi: ota'ga group, tugmaga group-hover::

<div class="group rounded-xl border p-4">
  <h3>Karta sarlavhasi</h3>
  <button class="opacity-0 transition group-hover:opacity-100">Ko'rish</button>
</div>

group ota'da β€” kartaning istalgan joyiga sichqoncha kelishini "eshitadi". opacity-0 β€” tugma asosan ko'rinmas. group-hover:opacity-100 β€” karta hover'ida ko'rinadi. transition β€” silliq paydo bo'lish. Diqqat: group klassini ota'ga qo'yish shart, aks holda group-hover: ishlamaydi.

4-mashq. CSS-only toggle switch'da yo'lak (track) belgilanganda ko'k bo'lib, ichidagi oq doira o'ngga sursin β€” hammasi JavaScript'siz. Asosiy klasslarni yozing (to'liq emas, faqat peer mantig'i).

Yechim

peer naqshi: checkbox'ga peer, undan keyingi yo'lak peer-checked: bilan:

<label class="inline-flex items-center gap-2">
  <input type="checkbox" class="peer sr-only">
  <span class="relative h-6 w-11 rounded-full bg-slate-300
               peer-checked:bg-indigo-600
               after:absolute after:left-0.5 after:top-0.5 after:size-5
               after:rounded-full after:bg-white after:transition after:content-['']
               peer-checked:after:translate-x-5"></span>
</label>

Asosiy g'oyalar: (1) peer checkbox oldin turadi, yo'lak keyin β€” CSS faqat oldinga qaray oladi. (2) peer-checked:bg-indigo-600 β€” belgilanganda yo'lak ko'karadi. (3) Doira after: psevdo-elementi (shuning uchun after:content-[''] shart), peer-checked:after:translate-x-5 bilan o'ngga suriladi.

5-mashq. Uchta "reja" kartasidan birini tanlaganda, faqat tanlangani ko'k chegara va engil fon olsin. Har bir karta <label> ichida radio bilan. v4'ning qaysi variantidan foydalanasiz va nega group/peer emas?

Yechim

has-* variantidan β€” aniqrog'i has-[:checked]:. Sabab: bu yerda ota (label) ichidagi bola (radio) holatiga qarab stillanishi kerak. group β€” teskari yo'nalish (ota β†’ bola), peer β€” sibling β†’ sibling; ikkalasi ham "bola β†’ ota" munosabatini ifodalay olmaydi. has-* aynan shu uchun:

<label class="rounded-xl border-2 border-slate-200 p-4
              has-[:checked]:border-indigo-600 has-[:checked]:bg-indigo-50">
  <input type="radio" name="reja" class="sr-only">
  Pro reja
</label>

Radio belgilanganda :has(:checked) rost bo'ladi va butun label ko'k chegara + engil fon oladi. Faqat tanlangani o'zgaradi, chunki har bir label faqat o'z ichidagi radio'ni "ko'radi".

6-mashq (debugging). Quyidagi peer kodi ishlamayapti β€” input fokuslanganda label rangi o'zgarmayapti. Xatoni toping va tuzating.

<label class="peer-focus:text-indigo-600">Email</label>
<input type="email" class="peer">
Yechim

Muammo β€” tartib. CSS sibling selektori (~) faqat oldinga qaray oladi: peer element stillanadigan element'dan oldin turishi shart. Bu yerda input.peer label'dan keyin kelyapti, shuning uchun label uni "ko'rmaydi".

Tuzatish β€” peer input'ni label'dan oldinga qo'yish:

<input type="email" class="peer" placeholder="Email">
<label class="peer-focus:text-indigo-600">Email</label>

Endi input fokuslanganda label indigo bo'ladi. Qoida: peer har doim o'ziga javob beradigan sibling'dan oldin yozilishi kerak. (Agar vizual tartib teskari kerak bo'lsa, flex-col-reverse bilan ko'rinishni almashtiring β€” DOM tartibi esa peer uchun to'g'ri qoladi.)

7-mashq (qiyinroq). Forma input'i email noto'g'ri kiritilganda qizarsin, lekin sahifa endigina ochilganda (foydalanuvchi hali hech narsa yozmaganda) qizarmasin. Qaysi variantni ishlatasiz va nega oddiy invalid: mos kelmaydi?

Yechim

user-invalid: (v4) variantini ishlatamiz:

<input type="email" required placeholder="email@misol.uz"
       class="border border-slate-300
              user-invalid:border-red-500 user-invalid:bg-red-50">

Nega invalid: emas: invalid: psevdo-klassi input qiymati noto'g'ri bo'lishi bilanoq β€” ya'ni bo'sh required maydon uchun sahifa ochilishi bilan β€” ishga tushadi. Natijada foydalanuvchi hali bir harf ham yozmasdan turib forma "qizarib" turadi, bu yomon UX.

user-invalid: esa faqat foydalanuvchi maydon bilan ishlaganidan keyin (yozib, undan chiqib ketganda) qizartiradi. Bu β€” tabiiy, "men sizni xato qildingiz deb ayblashga shoshmayman" his-tuyg'usi. Shu sababli validatsiya stillarida deyarli har doim user-invalid:/user-valid: afzal.


⬅️ Oldingi: 14 β€” Soya, filter va mask Β· 🏠 README Β· Keyingi: 16 β€” Dark mode ➑️