IlmHamroh
JavaScript Full-stack/6-qism. CSS: harakat, zamonaviy CSS va dizayn36/57-dars20 daqiqa
Mundarija (41)

Tailwind CSS 4: komponent naqshlari — cn(), cva va @apply

Qisqacha: Tailwind'da takrorlanadigan uzun class ro'yxati CSS class'iga emas, komponentga yig'iladi: ro'yxat bir marta yoziladi va hamma joyda qayta ishlatiladi. JavaScript loyihalarida buning uchun ikki vosita bor. cn() (clsx + tailwind-merge) class'larni shartga qarab qo'shadi va to'qnashganini (px-3 va px-6) bittaga keltiradi. cva esa turi, olcham kabi variantlarni tartibli yozadi. @apply utilitalarni CSS class'ga yig'adi — u faqat HTML'ni o'zingiz boshqarmaydigan joylar uchun. Class nomini qismlardan yig'ish (bg-${rang}) ishlamaydi — to'liq nom yozing.

Bu darsda

  • "Iflos HTML" muammosini va uning to'rt yechimini (sikl, komponent, @apply, variant funksiyasi) farqlaysiz.
  • @apply qachon o'rinli, qachon zararli ekanini asoslaysiz va v4 dagi @reference xatosini tuzatasiz.
  • clsx, tailwind-merge va cn() natijalarini o'qiysiz, o'z tokenlaringiz uchun tailwind-merge ni sozlaysiz.
  • cva bilan tugma variantlarini (turi, olcham, compoundVariants) yozasiz.
  • Prettier plagini, Tailwind IntelliSense va canonicalize buyrug'ini ishlatasiz, dinamik class xatosidan qochasiz.

Oldin bilishingiz kerak: Tailwind CSS 3: v4 konfiguratsiyasi, Tailwind CSS 2: variantlar, CSS Modules, CSS metodologiyalari.

1. Nega bu kerak?

Oldingi darsda «Bahor» tokenlarini Tailwind'ga uladik. Endi aksiya sahifasida "Stolni band qilish" tugmasi bor:

html
<a class="inline-flex items-center gap-xs rounded-m bg-asosiy
  px-m py-xs font-bold text-fon hover:bg-asosiy-toq
  focus-visible:outline-2 focus-visible:outline-offset-2
  focus-visible:outline-asosiy" href="bron/">
  Stolni band qilish
</a>

O'n bitta class. Tugma sahifada uch joyda, menyu va bron sahifalarida yana beshta. Dizayner "tugma burchagi kattaroq bo'lsin" desa — sakkizta faylda qidirib almashtirish kerak. Bittasini unutsangiz, sayt har xil tugmalar bilan qoladi.

Bu — Tailwind'ga eng ko'p aytiladigan tanqid: "iflos HTML". HTML ichi class bilan to'lib ketadi, bir xil ro'yxat ko'p marta takrorlanadi. CSS metodologiyalari darsida utility-first yondashuvning kamchiligi sifatida shuni ko'rgan edik.

Tailwind jamoasining javobi: takrorni CSS'ga emas, komponentga yig'ing. Bugun shu javobning to'rt shaklini ko'ramiz va har birini qachon tanlashni o'rganamiz. Xuddi choyxonadagi kabi: Rustam aka har buyurtmada osh retseptini qayta yozmaydi — retsept bitta daftarda, oshxona uni qayta-qayta ishlatadi.

Dars JavaScript kutubxonalariga tayanadi. JavaScript'ni 08-qismda, React'ni 17-qismda o'rganamiz. Hozir kodni o'qishni o'rganamiz: har misoldan keyin u nima qilishini so'zlar bilan aytamiz. Kodni o'zingiz ishga tushirmoqchi bo'lsangiz — mashqlar bo'limida buyruqlar bor.

2. Takrorning to'rt yechimi

2.1 Sikl: takror aslida bir joyda

Menyudagi uchta taom kartasi. HTML'da uchta bir xil article — takror. Lekin real saytda bu kartalar qo'lda yozilmaydi: ular ro'yxatdan (masalan, bazadagi menyudan) sikl bilan chiqariladi. Sikl ichida class ro'yxati bir marta yoziladi. JavaScript'da siklni 08-qismda ko'rasiz.

Hozir, oddiy HTML'da takror bo'lsa, VS Code'ning ko'p kursorli tahriri yordam beradi (VS Code'da tez tahrirlash): bir xil class'larni Ctrl + D bilan birma-bir belgilab, hammasini birdan o'zgartirasiz. Tailwind hujjati ham kichik takror uchun aynan shuni tavsiya qiladi.

2.2 Komponent: asosiy yechim

Tugma — butun saytda bir xil ko'rinadigan narsa. Zamonaviy loyihalarda u komponent bo'ladi: HTML bo'lagini nomlab, qayta ishlatish imkoni. React'da (17-qism) bu shunday ko'rinadi:

jsx
export function BronTugma({ children }) {
  return (
    <a
      href="bron/"
      className="inline-flex rounded-m bg-asosiy px-m py-xs
        font-bold text-fon hover:bg-asosiy-toq"
    >
      {children}
    </a>
  );
}

O'qilishi: "BronTugma degan komponent — shu class'li a tegi, ichiga nima berilsa, o'sha matn". Sahifalarda endi <BronTugma>Stolni band qilish</BronTugma> yoziladi. className — React'da class atributining nomi. Class ro'yxati bitta faylda; burchakni o'zgartirish — bitta joy. React bilmasangiz ham g'oya aniq: retsept bir joyda, ishlatish ko'p joyda.

Vue, Svelte, Astro — hammasida komponent bor. Oddiy HTML saytda esa (masalan, «Bahor» hozir shunday) komponent yo'q. U yerda keyingi yechim kerak.

2.3 @apply: utilitalarni CSS class'ga yig'ish

@apply — Tailwind direktivasi: utilitalar ro'yxatini oddiy CSS class ichiga "yoyadi".

html
<script
  src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4">
</script>
<style type="text/tailwindcss">
  @theme {
    --color-asosiy: oklch(47.5% 0.1 147.7);
    --color-urgu: oklch(53.7% 0.153 39.8);
  }
  @layer components {
    .bron-havola {
      @apply inline-block rounded-lg bg-asosiy px-4 py-2
        font-bold text-white hover:bg-urgu;
    }
  }
</style>
<p class="flex flex-wrap gap-2 p-4">
  <a class="bron-havola" href="bron/">@apply</a>
  <a class="bron-havola bg-urgu" href="bron/">+ bg-urgu</a>
  <a class="rounded-lg bg-urgu bg-asosiy px-4 py-2 text-white"
    href="bron/">urgu, asosiy</a>
  <a class="rounded-lg bg-asosiy bg-urgu px-4 py-2 text-white"
    href="bron/">asosiy, urgu</a>
</p>

Nimaga qarang, birinchi ikki tugmaga: birinchisi yashil — .bron-havola ning o'zi. Ikkinchisi qizg'ish: bg-urgu utilitasi components qatlamidagi .bron-havola dan kuchliroq (Tailwind CSS 3 darsidagi qatlamlar). Ya'ni @apply li class'ni utilita bilan ustidan yozish mumkin — @layer components shuning uchun kerak. Oxirgi ikki tugmani pastroqda muhokama qilamiz.

@apply qayerda to'g'ri tanlov:

  • HTML'ni siz yozmaysiz. Markdown'dan chiqqan blog matni, CMS (sayt matnini dasturchisiz tahrirlash tizimi) muharriri, begona kutubxona yasagan HTML — ularga class qo'yib bo'lmaydi. .maqola a { @apply text-asosiy underline; } yagona yo'l. (Blog matni uchun tayyor yechim ham bor — Tailwind CSS 5 darsidagi prose.)
  • Komponentsiz kichik sayt. «Bahor» kabi oddiy HTML saytda bitta-ikkita takrorlanadigan element (.bron-havola).
  • Begona komponentni bezash. Kutubxona o'z class'ini beradi, siz unga tokenlaringizni qo'yasiz.

Qayerda zararli: hamma narsani @apply ga yig'ish. Tailwind hujjati ochiq ogohlantiradi: "HTML toza ko'rinsin" deb @apply ishlatsangiz, oddiy CSS yozgan bo'lasiz — faqat qo'shimcha qadam bilan. Class nomlarini o'ylab topish, faylma-fayl sakrash, ishlatilmay qolgan class'lar — utility-first qochgan hamma muammo qaytadi.

2.4 Variant funksiyasi

Tugmaning bir necha turi bor: asosiy (yashil), urg'u (qizg'ish), chegarali. O'lchami ham ikki xil. Komponent ichida buni shartlar bilan yozish tez chalkashadi. Buning uchun cva kabi vositalar bor — pastda, «cva: variantlarni tartibli yozish» bo'limida. Undan oldin ikki kichik yordamchi kerak.

Tekshirib ko'ring: Sizning React loyihangizda Karta komponenti bor. Hamkasbingiz .karta { @apply rounded-m bg-yuza p-m shadow-1; } yozib, komponentda className="karta" qilmoqchi. Nima deysiz?

Javob

Kerak emas. Komponent allaqachon "retsept daftari": class ro'yxati bitta faylda turibdi. @apply qo'shimcha nom (.karta), qo'shimcha fayl va qo'shimcha qatlam beradi, foyda bermaydi. @apply — komponent yo'q joy uchun.

3. @apply tuzoqlari

3.1 Alohida faylda: @reference

Vue yoki Svelte komponentining <style> bloki, CSS Modules fayli (CSS Modules) — ular alohida yig'iladi va kirish.css dagi temani bilmaydi. Tekshirdik: alohida.css da @apply rounded-lg bg-asosiy px-4 py-2; yozib, CLI'dan o'tkazdik:

text
Error: Cannot apply unknown utility class `rounded-lg`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive

Tarjimasi: "Noma'lum rounded-lg utilitasini qo'llab bo'lmaydi. CSS Modules yoki shunga o'xshash narsa ishlatyapsizmi va @reference yo'qmi?" Xato birinchi class'da to'xtadi — rounded-lg Tailwind'ning oddiy utilitasi, lekin bu fayl Tailwind'ni umuman bilmaydi.

Tuzatish — fayl boshiga:

css
@reference "./kirish.css";

.bron-havola {
  @apply rounded-lg bg-asosiy px-4 py-2;
}

Natija:

css
.bron-havola {
  border-radius: var(--radius-lg, 0.5rem);
  background-color: var(--color-asosiy, oklch(47.5% 0.1 147.7));
  padding-inline: calc(var(--spacing, 0.25rem) * 4);
  padding-block: calc(var(--spacing, 0.25rem) * 2);
}

Nimaga qarang: @reference temani o'qidi, lekin natijaga Preflight yoki :root tokenlarini qo'shmadi — faqat .bron-havola. Shuning uchun har var() ichida zaxira qiymat bor: o'zgaruvchi topilmasa ham ishlaydi. @import yozsangiz edi, har komponent faylida butun Tailwind qayta chiqardi.

3.2 Boshqa tuzoqlar

  • @apply ichida variant ishlaydi: hover:bg-urgu, md:px-6. Natijada @media (hover: hover) va :hover bilan chiqadi.
  • O'z class'ingizni @apply qilib bo'lmaydi. @apply bron-havola — faqat @utility bilan yozilgan bo'lsa ishlaydi (Tailwind CSS 3).
  • Tartib sizniki emas. @apply ichidagi class'lar tartibi natijadagi deklaratsiyalar tartibini belgilamaydi — Tailwind ularni o'z tartibida yozadi. Ikki qarama-qarshi class (px-4 px-6) yozmang.

4. clsx, tailwind-merge va cn()

4.1 clsx — shartli class

clsx ni CSS Modules darsida ko'rgan edik: nomlarni bo'sh joy bilan birlashtiradi, false bo'lganlarini tashlaydi. Tailwind bilan ham xuddi shunday. Node'da tekshirdik (clsx 2.1.1):

js
import { clsx } from "clsx";

const tugadi = true;
const faol = false;
console.log(
  clsx("taom-karta", tugadi && "opacity-50", faol && "ring-2"),
);
console.log(clsx("p-4", { "opacity-50": tugadi, "ring-2": faol }));

Konsolda:

text
taom-karta opacity-50
p-4 opacity-50

Ikkinchi qatordagi { ... } — obyekt (obyektlar — 08-qismda): "kalit — class nomi, qiymat — shart". Shart true bo'lgan class qoladi. Natija bir xil, yozuv shakli boshqa.

4.2 Muammo: to'qnashuv

Tugma komponenti asosiy rangni beradi, ishlatayotgan sahifa esa "bu yerda qizg'ish bo'lsin" deb qo'shimcha class uzatadi. clsx ikkalasini ham qoldiradi:

js
console.log(clsx("px-4 py-2 bg-asosiy", "bg-urgu"));
text
px-4 py-2 bg-asosiy bg-urgu

Qaysi biri yutadi? Yuqoridagi natijaning oxirgi ikki tugmasiga qayting. Uchinchisida bg-urgu bg-asosiy, to'rtinchisida bg-asosiy bg-urgu — ikkalasi ham qizg'ish. Kaskad darsidan eslang: og'irligi teng ikki qoidadan CSS faylida pastdagisi yutadi, class atributidagi tartib ahamiyatsiz. Tailwind esa natija faylini o'z tartibida yozadi. Demak, bg-asosiy ni bg-urgu ustidan yozishning imkoni yo'q — tartibni qanday qo'ysangiz ham.

4.3 tailwind-merge — to'qnashganini olib tashlash

tailwind-merge — class ro'yxatini o'qib, bir xususiyatga tegadigan class'lardan faqat oxirgisini qoldiradigan kutubxona. Tekshirdik (tailwind-merge 3.7.0):

js
import { twMerge } from "tailwind-merge";

console.log(twMerge("px-4 py-2 bg-asosiy", "bg-urgu"));
console.log(twMerge("px-4 py-2", "p-3"));
console.log(twMerge("p-3", "px-4"));
console.log(twMerge("hover:bg-asosiy bg-oq", "hover:bg-urgu"));

Konsolda:

text
px-4 py-2 bg-urgu
p-3
p-3 px-4
bg-oq hover:bg-urgu

O'qilishi:

  1. bg-asosiy olib tashlandi — bg-urgu keyin kelgan.
  2. p-3 to'rt tomonni beradi, px-4 py-2 ni butunlay qoplaydi — ular ketdi.
  3. Teskarisi: px-4 faqat yon tomonlar, p-3 ning tepa-pastini qoplamaydi. Ikkalasi qoladi, CSS'da px-4 yon tomonlarda yutadi.
  4. hover: li class'lar faqat o'zaro raqobatlashadi. bg-oq hover'siz — u qoldi.

Ya'ni tailwind-merge Tailwind'ning class ma'nosini biladi. Oddiy "takrorni o'chirish" emas.

4.4 cn() = ikkalasi birga

Ikkala kutubxona deyarli doim birga ishlatiladi. shadcn/ui (Tailwind CSS 5) loyihalarida bu kichik funksiya lib/utils faylida turadi:

js
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...kirishlar) {
  return twMerge(clsx(kirishlar));
}

O'qilishi: "cn ga berilgan hamma narsani avval clsx bilan birlashtir (shartlar, false lar), keyin twMerge bilan to'qnashuvlarni tozala". ...kirishlar — "nechta argument berilsa, hammasi" (rest parametrlar — 08-qismda).

js
console.log(cn("rounded-lg px-4 py-2 bg-asosiy text-oq",
  tugadi && "bg-kul-300", undefined, null));
text
rounded-lg px-4 py-2 text-oq bg-kul-300

Taom tugagan — tugma kulrang, bg-asosiy ketdi. undefined va null jimgina tashlandi. React komponentlarida bu naqsh shunday ishlatiladi: className={cn("...asosiy class'lar", props.className)} — komponent o'z class'larini beradi, ishlatgan joy ustidan yozishi mumkin.

4.5 Tuzoq: tailwind-merge sizning tokenlaringizni bilmaydi

«Bahor» tokenlari bilan tekshirdik — va kutilmagan natija oldik:

js
console.log(twMerge("text-step-1 text-urgu", "text-asosiy"));
console.log(twMerge("p-m", "p-s"));
text
text-asosiy
p-m p-s

Birinchi qatorda shrift o'lchami (text-step-1) ham o'chib ketdi! tailwind-merge step-1 ni tanimaydi va uni rang deb o'yladi: "text-urgu, text-step-1, text-asosiy — uchta rang, oxirgisi qolsin". Ikkinchi qatorda aksincha: m va s noma'lum — to'qnashuv sezilmadi.

Tuzatish — kutubxonaga tokenlarni aytish:

js
import { extendTailwindMerge } from "tailwind-merge";

const twMerge = extendTailwindMerge({
  extend: {
    theme: {
      text: ["step--1", "step-0", "step-1", "step-2", "step-3"],
      spacing: ["2xs", "xs", "s", "m", "l", "xl", "2xl"],
      color: ["fon", "yuza", "matn", "asosiy", "urgu", "ikkinchi"],
    },
  },
});

Endi:

text
text-step-1 text-asosiy
p-s

Qoida: @theme ga o'z nomingizni qo'shdingizmi — tailwind-merge sozlamasiga ham qo'shing. Aks holda xato ko'rinmaydi: sahifa biror joyda "sababsiz" kichik shriftga tushadi.

Tekshirib ko'ring: twMerge("mt-2 mb-4", "my-6") nima qaytaradi? twMerge("my-6", "mt-2") chi?

Javob

Birinchisi — my-6: u tepa va pastni beradi, oldingi mt-2 va mb-4 ni to'liq qoplaydi. Ikkinchisi — my-6 mt-2: mt-2 faqat tepani o'zgartiradi, my-6 ning pastki qismi kerak — ikkalasi qoladi. «tailwind-merge — to'qnashganini olib tashlash» bo'limidagi p-3 px-4 bilan bir xil mantiq: keng class keyin kelsa — torlarini o'chiradi, tor class keyin kelsa — ikkalasi qoladi.

5. cva: variantlarni tartibli yozish

5.1 Birinchi variantlar

cva (class-variance-authority) — komponent variantlarini jadval kabi yozish uchun kichik kutubxona. «Bahor» tugmasi, tekshirdik (class-variance-authority 0.7.1):

js
import { cva } from "class-variance-authority";

const tugma = cva("inline-flex items-center rounded-lg font-bold", {
  variants: {
    turi: {
      asosiy: "bg-asosiy text-oq hover:bg-asosiy-toq",
      urgu: "bg-urgu text-oq",
      chegara: "border-2 border-asosiy text-asosiy",
    },
    olcham: {
      kichik: "px-3 py-1 text-sm",
      katta: "px-6 py-3 text-lg",
    },
  },
  compoundVariants: [
    { turi: "urgu", olcham: "katta", class: "uppercase" },
  ],
  defaultVariants: { turi: "asosiy", olcham: "kichik" },
});

console.log(tugma());
console.log(tugma({ turi: "urgu", olcham: "katta" }));
console.log(tugma({ turi: "chegara", class: "w-full" }));

Konsolda:

text
inline-flex items-center rounded-lg font-bold bg-asosiy text-oq hover:bg-asosiy-toq px-3 py-1 text-sm
inline-flex items-center rounded-lg font-bold bg-urgu text-oq px-6 py-3 text-lg uppercase
inline-flex items-center rounded-lg font-bold border-2 border-asosiy text-asosiy px-3 py-1 text-sm w-full

Tuzilishi:

  • Birinchi argument — asos: har turdagi tugmada bo'ladigan class'lar.
  • variants — o'lchovlar. turi va olcham — o'lchov nomlari, ichidagilar — ularning qiymatlari. Har qiymatga o'z class'lari.
  • compoundVariants — kombinatsiya: "urg'u va katta bo'lsa — qo'shimcha uppercase". Ikkinchi qatorda aynan shu ishladi.
  • defaultVariants — hech narsa berilmasa. Birinchi qatorda tugma() bo'sh chaqirildi — asosiy va kichik tugma chiqdi.
  • class — joyida qo'shimcha: uchinchi qatorda w-full.

Bu — CSS metodologiyalari darsidagi BEM modifikatorining JavaScript'dagi egizagi: .tugma--urgu.tugma--katta o'rniga tugma({ turi: "urgu", olcham: "katta" }).

5.2 Ikki tuzoq

Noma'lum qiymat jim o'tadi. tugma({ olcham: "ulkan" }) natijasi — inline-flex items-center rounded-lg font-bold bg-asosiy text-oq hover:bg-asosiy-toq. O'lcham class'lari yo'q, xato ham yo'q. Tugma kichrayib qoladi va sababi ko'rinmaydi. TypeScript bilan (Nega TypeScript, 15-qism) bunday xato yozish paytida tutiladi — cva variantlar tipini o'zi chiqaradi.

cva to'qnashuvni tozalamaydi. Buni ko'rish uchun soddaroq tugma oldik — asosi qisqa, faqat ikki tur:

js
const tugma = cva("rounded-lg px-3 py-1 font-bold", {
  variants: {
    turi: { asosiy: "bg-asosiy text-oq", urgu: "bg-urgu text-oq" },
  },
  defaultVariants: { turi: "asosiy" },
});

console.log(tugma({ class: "px-6" }));
console.log(cn(tugma({ turi: "urgu" }), "px-6"));
text
rounded-lg px-3 py-1 font-bold bg-asosiy text-oq px-6
rounded-lg py-1 font-bold bg-urgu text-oq px-6

Birinchi qatorda px-3 va px-6 ikkalasi qoldi — va biz bilamizki, yutadigani CSS fayl tartibiga bog'liq. Ikkinchi qatorda cn() px-3 ni olib tashladi. Shuning uchun shadcn/ui'da doim cn(tugma({ ... }), className) shakli.

5.3 tailwind-variants

Shu vazifaning boshqa kutubxonasi — tailwind-variants. Farqi: u ichida tailwind-merge'ni o'zi chaqiradi va murakkab komponent bo'laklarini (slots) ham variantlaydi. Tekshirdik (tailwind-variants 3.3.1):

js
import { tv } from "tailwind-variants";

const tugma = tv({
  base: "inline-flex rounded-lg px-3 py-1 font-bold",
  variants: {
    turi: { asosiy: "bg-asosiy text-oq", urgu: "bg-urgu text-oq" },
  },
  defaultVariants: { turi: "asosiy" },
});

console.log(tugma({ turi: "urgu", class: "px-6" }));
text
inline-flex rounded-lg py-1 font-bold bg-urgu text-oq px-6

px-3 o'zi ketdi. Qaysi birini tanlash — jamoa kelishuvi: shadcn/ui cva'ni ishlatadi, HeroUI kabi to'plamlar — tailwind-variants'ni.

Tekshirib ko'ring: Dizayner tugmaning uchinchi o'lchamini so'radi — orta (px-4 py-2). cva'da nechta joyni o'zgartirasiz? Tugma ishlatilgan sakkizta sahifada-chi?

Javob

Bitta joy: variants.olcham ga orta: "px-4 py-2" qatori. Sahifalarga tegilmaydi — ular hozircha kichik yoki katta ni so'raydi, yangi o'lchamni esa kerak joyda tugma({ olcham: "orta" }) deb chaqirasiz. Standart o'lcham o'zgarishi kerak bo'lsa — yana bitta qator, defaultVariants. Variantlar jadvali aynan shuning uchun: qaror bir joyda.

flowchart LR
  A["Asos<br/>class'lari"] --> V["cva:<br/>variantlar"]
  V --> C["clsx:<br/>shartlar"]
  C --> M["tailwind-merge:<br/>to'qnashuv"]
  M --> H["class=&quot;...&quot;"]

Nimaga qarang: har vosita bitta ishni qiladi va natijani keyingisiga uzatadi. cn(tugma({...}), qoshimcha) — aynan shu zanjir.

6. Dinamik class nomlari

6.1 bg-${rang} ishlamaydi

Taom kartasining rangi ma'lumotdan kelsin: aksiyada — urg'u, oddiy kunda — asosiy. JavaScript'da shunday yozish vasvasasi bor:

js
const rang = "urgu";
karta.className = `taom-karta bg-${rang} p-4`;

`...${rang}...` — shablon satr (shablon satrlar, 08-qism): ${rang} o'rniga o'zgaruvchi qiymati qo'yiladi. Brauzerda natija bg-urgu bo'ladi — lekin rang chiqmaydi.

Sababi — Tailwind fayllarni ishga tushirmaydi, faqat matnini o'qiydi. Tekshirdik: shu fayl skanerlanganda natijada .p-4 bor, .bg-urgu esa yo'q. Tailwind fayl matnida bg-${rang} ni ko'rdi — bunday class yo'q. bg-urgu degan to'liq so'z esa faylda umuman yozilmagan.

6.2 To'g'ri yo'llar

To'liq nomlar lug'ati — eng toza yo'l:

js
const ranglar = {
  asosiy: "text-asosiy",
  urgu: "text-urgu",
};
karta.className = ranglar[rang];

Tekshirdik: text-asosiy va text-urgu natijaga kirdi — ikkalasi fayl matnida to'liq yozilgan.

Boshqa yo'llar:

  • @source inline("bg-urgu bg-asosiy") (Tailwind CSS 3) — ro'yxat oldindan ma'lum, lekin kodda to'liq yozib bo'lmaydi.
  • Holat atributi va variant: HTML'da data-holat="aksiya", class'da data-[holat=aksiya]:bg-urgu (Tailwind CSS 2). Class ro'yxati o'zgarmaydi, faqat atribut.
  • CSS o'zgaruvchisi: bg-(--karta-rang) va o'zgaruvchini JavaScript beradi. «Bahor»da ehtiyot bo'ling: style="..." atributini CSP to'sadi (CSS nima).

Qaysi birini tanlash? Variantlar soni oz va oldindan ma'lum bo'lsa (asosiy, urg'u) — lug'at yoki cva. Holat HTML'da allaqachon bor bo'lsa (aria-busy, data-holat) — variant: JavaScript faqat atributni almashtiradi, class'lar joyida qoladi. Qiymat oldindan noma'lum bo'lsa (foydalanuvchi tanlagan rang) — CSS o'zgaruvchisi. Uchala holatda ham bitta qoida: Tailwind ko'radigan fayl matnida har class to'liq yozilgan bo'lsin.

Tekshirib ko'ring: Kod `border-${holat}` deb yozilgan, holat — aksiya yoki tugadi. Tailwind natijasida .border-aksiya bo'ladimi? Qanday tuzatasiz?

Javob

Bo'lmaydi. Tailwind faylni ishga tushirmaydi, faqat matnini o'qiydi — matnda border-aksiya degan to'liq so'z yo'q. Tuzatish: to'liq nomlar lug'ati ({ aksiya: "border-aksiya", tugadi: "border-tugadi" }) yoki HTML'da data-holat atributi va data-[holat=aksiya]:... varianti. Ikkalasida ham class nomi faylda butunligicha yozilgan.

7. Ish qurollari

7.1 prettier-plugin-tailwindcss — class tartibi

Uzun class ro'yxatini o'qishni osonlashtiradigan kelishuv: tartib doim bir xil. Kod uslubi darsidagi Prettier uchun Tailwind jamoasining rasmiy plagini bor. Tekshirdik (prettier 3.9.9, plagin 0.8.1). .prettierrc:

json
{
  "plugins": ["prettier-plugin-tailwindcss"],
  "tailwindStylesheet": "./src/kirish.css",
  "printWidth": 70
}

tailwindStylesheet — v4 uchun: plagin temani shu fayldan o'qiydi (bg-asosiy ni tanishi uchun). printWidth — qator uzunligi chegarasi (standart 80). Oldin — class'lar aralash tartibda:

html
<a class="md:px-6 px-4 bron-havola hover:bg-urgu flex bg-asosiy"
  href="bron/">Stolni band qilish</a>

npx prettier --write dan keyin:

html
<a
  class="bron-havola flex bg-asosiy px-4 hover:bg-urgu md:px-6"
  href="bron/"
  >Stolni band qilish</a
>

Nimaga qarang: o'z class'imiz (bron-havola) — birinchi. Keyin layout (flex), keyin bezak (bg-asosiy, px-4), oxirida variantli class'lar (hover:, md:). Tartib natija CSS'dagi tartibga yaqin — ko'z o'rganib qoladi. Prettier class ro'yxatini ikki qatorga bo'lmaydi: ro'yxat uzun bo'lsa, qator ham uzun qoladi. >Stolni va </a ning g'alati ko'rinishi — Prettier'ning odatiy uslubi: u teg ichidagi matnga bo'sh joy qo'shmaslik uchun shunday bo'ladi.

7.2 Tailwind CSS IntelliSense

VS Code kengaytmasi (Tailwind Labs'niki): class yozganda taklif ro'yxati, class ustiga sichqoncha olib borsangiz — uning CSS'i, rang yonida kichik kvadrat. Eng foydalisi — to'qnashuv ogohlantirishi: bitta elementda px-4 px-6 bo'lsa, ikkalasining tagini chizadi. VS Code'da Extensions → "Tailwind CSS IntelliSense" (VS Code kengaytmalari).

7.3 canonicalize — class'ni qisqartirish

Tailwind 4.3.3 CLI'sida canonicalize buyrug'i bor. Tekshirdik:

bash
npx @tailwindcss/cli canonicalize --css src/kirish.css \
  "bg-(--color-asosiy)" "mt-[0.25rem]" "bg-[#fff]"
text
bg-asosiy
mt-1
bg-white

U ixtiyoriy qiymatni mavjud utilitaga aylantiradi (bg-white standart palitra tozalanmagan loyihada). Eski kodni tozalashda yoki AI yozgan class'larni tekshirishda qo'l keladi.

8. Ko'p uchraydigan xatolar

8.1 Hamma narsa @apply

.karta, .sarlavha, .matn, .tugma — hammasi @apply. Bu — Tailwind ustida qurilgan oddiy CSS, ikki qatlam murakkablik. Tuzatish: komponent bo'lsa — class'lar komponentda; @apply faqat HTML'ga qo'l yetmaydigan joyda.

8.2 @reference siz alohida fayl

Cannot apply unknown utility class. Tuzatish: fayl boshiga @reference "./kirish.css"; (yo'l shu faylga nisbatan).

8.3 clsx bilan ustidan yozish

clsx("bg-asosiy", "bg-urgu") — ikkalasi qoladi, natija CSS tartibiga bog'liq. Tuzatish: cn().

8.4 tailwind-merge sozlanmagan

text-step-1 "sababsiz" yo'qoladi. Tuzatish: extendTailwindMerge ga tokenlar.

8.5 Qismlardan yig'ilgan class

`bg-${rang}` — natijada yo'q. Tuzatish: to'liq nomlar lug'ati.

9. Mashqlar

Mashqlar kurs/mashqlar/06/tailwind/ da. JavaScript misollarini ishga tushirish uchun (npm — 16-qismda to'liq, hozir aynan takrorlang):

bash
npm pkg set type=module
npm install clsx tailwind-merge class-variance-authority
node cn.js

type=module — import yozuvi ishlashi uchun (PostCSS darsidagi ogohlantirishni eslang).

1-mashq (oson): tailwind-merge natijasini toping

Ishga tushirmasdan yozing, keyin tekshiring:

  1. twMerge("rounded-lg bg-asosiy", "rounded-none")
  2. twMerge("px-2 py-1", "p-4", "px-6")
  3. twMerge("text-sm font-bold", "text-lg")
  4. twMerge("md:px-4 px-2", "px-3")

Ishora: "tailwind-merge — to'qnashganini olib tashlash" bo'limidagi to'rt qoida.

Yechim
  1. bg-asosiy rounded-none — ikkala burchak class'i bir xususiyat, oxirgisi qoladi.
  2. p-4 px-6 — p-4 oldingi px-2 py-1 ni qoplaydi; keyin kelgan px-6 faqat yon tomonlar, p-4 qoladi.
  3. font-bold text-lg — text-sm va text-lg ikkalasi o'lcham. Bu yerda nomlar Tailwind'niki — tailwind-merge ularni taniydi.
  4. md:px-4 px-3 — md:px-4 boshqa variant, u faqat md: li class bilan raqobatlashadi.

Tekshirishda to'rttasi ham shunday chiqadi.

2-mashq (o'rta): «Bahor» nishoni (badge)

Taom kartasidagi kichik nishon: "Yangi", "Mashhur", "Achchiq". cva bilan yozing:

  • asos: inline-block rounded-toliq px-xs text-step--1 font-bold;
  • turi: yangi — bg-asosiy text-fon, mashhur — bg-urgu text-fon, achchiq — border border-urgu text-urgu;
  • standart turi — yangi.

nishon(), nishon({ turi: "achchiq" }) va nishon({ turi: "mashhur", class: "uppercase" }) natijalarini ayting.

Ishora: "Birinchi variantlar" bo'limidagi tugma — faqat bitta o'lchov (turi).

Yechim
js
import { cva } from "class-variance-authority";

const nishon = cva(
  "inline-block rounded-toliq px-xs text-step--1 font-bold",
  {
    variants: {
      turi: {
        yangi: "bg-asosiy text-fon",
        mashhur: "bg-urgu text-fon",
        achchiq: "border border-urgu text-urgu",
      },
    },
    defaultVariants: { turi: "yangi" },
  },
);

Natijalar (asos qisqartirildi — har birining boshida o'sha besh class):

text
... bg-asosiy text-fon
... border border-urgu text-urgu
... bg-urgu text-fon uppercase

yangi, mashhur, achchiq — «Bahor»ning taom-karta modifikatorlari (04-qismdan). cva'da ular class emas, variant qiymati bo'ldi.

3-mashq (qiyin): tuzoqlarni tuzating

Hamkasbingiz yozdi:

js
import { clsx } from "clsx";
import { cva } from "class-variance-authority";

const tugma = cva("rounded-m px-m py-xs", {
  variants: { turi: { asosiy: "bg-asosiy", urgu: "bg-urgu" } },
});

const rang = aksiyami ? "urgu" : "asosiy";
const cls = clsx(tugma({ turi: "asosiy" }), `bg-${rang}`, "px-l");

Uchta muammoni toping va tuzating.

Ishora: dinamik nom, clsx va cn() farqi, tailwind-merge sozlamasi.

Yechim
  1. `bg-${rang}` — Tailwind bu class'ni yasamaydi. Variantning o'zi bor: tugma({ turi: rang }).
  2. clsx to'qnashuvni tozalamaydi: px-m va px-l ikkalasi qoladi. cn() kerak.
  3. px-m, px-l — «Bahor» tokenlaridan yasalgan o'z utilitalarimiz (faqat Tailwind tajribasida). Oddiy tailwind-merge ularni tanimaydi va to'qnashuvni sezmaydi (p-m p-s misolini eslang). extendTailwindMerge bilan spacing ro'yxatini bering.
js
const rang = aksiyami ? "urgu" : "asosiy";
const cls = cn(tugma({ turi: rang }), "px-l");

cn — sozlangan twMerge ustidan qurilgan. Natija: rounded-m py-xs bg-urgu px-l (aksiya kunida).

4-mashq: Portfolio qadami — tugma @apply bilan

Portfolio'da aloqa formasining tugmasi brend rangida (Yakuniy loyiha). Uni oldingi darsdagi tajriba/tailwind/ da ikki usulda qayta yozing:

  1. karta.html ga forma tugmasini qo'shing: <button type="submit">Yuborish</button>. Faqat utilitalar bilan bezang: fon bg-asosiy, matn text-fon, ichki masofa px-m py-xs, burchak rounded-m, hover va focus-visible holatlari.
  2. Ikkinchi usul: kirish.css da @layer components ichida form button { @apply ...; }. HTML'dan class'larni olib tashlang.
  3. Ikkala natijani 360 pikselda solishtiring. dist/natija.css hajmi (bayt) qanday o'zgardi?
  4. Portfolio uchun qaysi yo'l to'g'ri? Bir gap bilan asoslang.

Ishora: portfolio'da tugmaga class qo'yilmaydi — teg selektori (Yakuniy loyiha darsidagi qoida).

Yechim

1-usul (HTML'da):

html
<button type="submit" class="rounded-m bg-asosiy px-m py-xs
  font-bold text-fon hover:bg-asosiy/90
  focus-visible:outline-2 focus-visible:outline-offset-2
  focus-visible:outline-asosiy">Yuborish</button>

2-usul (kirish.css):

css
@layer components {
  form button {
    @apply rounded-m bg-asosiy px-m py-xs font-bold text-fon
      hover:bg-asosiy/90 focus-visible:outline-2
      focus-visible:outline-offset-2 focus-visible:outline-asosiy;
  }
}

Ko'rinish bir xil. Hajm deyarli bir xil: 1-usulda har class alohida qoida, 2-usulda bitta form button qoidasi — farq bir necha o'n bayt, bu sizning faylingizga bog'liq. Hajm hal qiluvchi emas.

4: portfolio'da 2-usul to'g'ri — HTML'da class qo'yilmaydi (teg selektori qoidasi) va tugma bitta. Bu @apply ning o'rinli holati: HTML'ni "boshqara olmaymiz" — o'z qoidamiz shunday. Portfolio esa baribir oddiy CSS'da qoladi: tajriba faqat taqqoslash uchun.

10. Real ishda

  • shadcn/ui — cn() + cva naqshining eng mashhur namunasi. Uning Button komponenti aynan «cva» bo'limidagi tuzilma: variant (default, destructive, outline...) va size. Tailwind CSS 5 darsida uni o'rnatamiz.
  • Kod ko'rib chiqish (code review) da tez-tez uchraydigan izohlar: "@apply shart emas, komponent bor", "cn ishlating, clsx emas", "class dinamik yig'ilgan — build'da yo'qoladi".
  • Jamoa standarti: Prettier plagini va IntelliSense deyarli har Tailwind loyihasida — class tartibi haqida bahs bo'lmaydi.
  • Intervyuda so'raladi: "Tailwind'da iflos HTML muammosini qanday hal qilasiz?", "@apply qachon kerak?", "tailwind-merge nima uchun?", "Nega bg-${rang}-500 ishlamaydi?".

Xulosa

  • Tailwind'da takror komponentga yig'iladi: class ro'yxati bir joyda, ishlatish ko'p joyda.
  • @apply — HTML'ni boshqara olmaydigan joy uchun; alohida faylda @reference bilan, @layer components ichida.
  • clsx shart bo'yicha qo'shadi, tailwind-merge to'qnashganini olib tashlaydi, cn() — ikkalasi birga.
  • O'z tokenlaringizni extendTailwindMerge ga qo'shing, aks holda merge noto'g'ri class'ni o'chiradi.
  • cva — variantlar jadvali (variants, compoundVariants, defaultVariants); to'qnashuv uchun uni cn() ga o'rang.
  • Class nomi fayl matnida to'liq yozilsin — bg-${rang} Tailwind uchun ko'rinmas.

Keyingi dars: Tailwind CSS 5: plaginlar va ekotizim — prose, forma plagini, animatsiyalar, daisyUI va shadcn/ui kabi tayyor to'plamlar.

Manbalar

  • Tailwind CSS: "Styling with utility classes" (Managing duplication), "Functions and directives" (@apply, @reference), "Detecting classes in source files" — tailwindcss.com/docs
  • tailwind-merge: "Configuration" — github.com/dcastil/tailwind-merge
  • class-variance-authority — cva.style
  • tailwind-variants — tailwind-variants.org
  • prettier-plugin-tailwindcss — github.com/tailwindlabs/prettier-plugin-tailwindcss
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Tailwind CSS 4: komponent naqshlari — cn(), cva va @apply — IlmHamroh