Tailwind CSS — React bilan sozlash, classNames kutubxonasi

~20 daq · React · Stilizatsiya

Muammodan boshlaylik

Yangi loyiha boshlayapsiz. Tugma kerak — ko'k fon, oq matn, yumaloq burchak, hover da to'qroq. CSS Modules bilan:

/* Button.module.css */
.btn {
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
}
.btn:hover {
  background: #2563eb;
}
<button className={styles.btn}>Saqlash</button>

Ishlaydi. Lekin bu jarayon har safar takrorlanadi: yangi element — yangi fayl, yangi klass nomi o'ylab topish, stillar yozish, import qilish. Oddiy kartochka uchun 40-50 qator CSS.

Tailwind boshqacha yondashadi: klass nomlari o'zi CSS bo'ladi.


Tailwind CSS nima?

Tailwind — utility-first CSS framework. Har bir klass bitta CSS xususiyatni anglatadi:

bg-blue-500   →  background-color: rgb(59 130 246)
text-white    →  color: rgb(255 255 255)
px-5          →  padding-left: 1.25rem; padding-right: 1.25rem
py-2.5        →  padding-top: 0.625rem; padding-bottom: 0.625rem
rounded-md    →  border-radius: 0.375rem

Siz CSS yozmaysiz — tayyor klasslarni qo'shib ketasiz:

<button className="bg-blue-500 text-white px-5 py-2.5 rounded-md hover:bg-blue-600">
  Saqlash
</button>

Hayotiy o'xshatma: Lego o'yini. Har bir Lego g'ishtchasi — bitta xususiyat. Siz ularni kerakli tarzda qo'shib, xohlagan shaklni yaratasiz. Alohida bloklarsiz yaxlit narsa yasay olmaysiz, lekin har qanday narsani qurish mumkin.


React + Vite bilan o'rnatish

npm install tailwindcss @tailwindcss/vite

vite.config.js ga plugin qo'shing:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(), // ← qo'shing
  ],
});

src/index.css ga import:

@import "tailwindcss";

Tamom. Boshqa fayl, boshqa sozlama kerak emas. Tailwind v4 da konfiguratsiya fayli ham shart emas — avtomatik ishlaydi.


Asosiy klasslar — tez ko'rish

Padding va Margin

p-4    →  padding: 1rem (16px)
px-4   →  padding-left + right: 1rem
py-2   →  padding-top + bottom: 0.5rem
pt-3   →  padding-top: 0.75rem
m-4    →  margin: 1rem
mx-auto →  margin-left: auto; margin-right: auto
mt-8   →  margin-top: 2rem

Raqamlar rem da: 4 → 1rem (16px), 2 → 0.5rem (8px), 8 → 2rem (32px).

Ranglar

bg-blue-500   →  ko'k fon (o'rtacha)
bg-blue-100   →  och ko'k fon
bg-blue-900   →  to'q ko'k fon
text-gray-700 →  to'q kulrang matn
text-red-500  →  qizil matn
border-gray-200 → och kulrang chegara

Raqamlar: 100 — eng och, 900 — eng to'q.

O'lchamlar

w-full    →  width: 100%
w-1/2     →  width: 50%
w-64      →  width: 16rem (256px)
h-screen  →  height: 100vh
h-12      →  height: 3rem (48px)
max-w-lg  →  max-width: 32rem

Flexbox

flex          →  display: flex
items-center  →  align-items: center
justify-between → justify-content: space-between
gap-4         →  gap: 1rem
flex-col      →  flex-direction: column
flex-1        →  flex: 1 1 0%

Matn

text-sm    →  font-size: 0.875rem (14px)
text-lg    →  font-size: 1.125rem (18px)
text-2xl   →  font-size: 1.5rem (24px)
font-bold  →  font-weight: 700
font-medium →  font-weight: 500
leading-relaxed → line-height: 1.625
tracking-wide →  letter-spacing: 0.025em

Chegara va yumaloqlik

border         →  border-width: 1px
border-2       →  border-width: 2px
rounded        →  border-radius: 0.25rem
rounded-md     →  border-radius: 0.375rem
rounded-lg     →  border-radius: 0.5rem
rounded-full   →  border-radius: 9999px (doira)

Holat va Responsive klasslar

Hover, Focus, Active

Tailwind da pseudo-klass lar prefix bilan yoziladi:

<button className="
  bg-blue-500
  hover:bg-blue-600
  active:bg-blue-700
  focus:outline-none
  focus:ring-2
  focus:ring-blue-300
">
  Bosing
</button>

hover: — hover holatida, focus: — fokus holatida, active: — bosilganda.

Responsive

Tailwind mobile-first ishlaydi. Prefix yo'q = barcha ekran. Prefix bor = shu ekrandan katta:

sm:   →  min-width: 640px
md:   →  min-width: 768px
lg:   →  min-width: 1024px
xl:   →  min-width: 1280px
<div className="
  grid
  grid-cols-1
  sm:grid-cols-2
  lg:grid-cols-3
  gap-4
">

Mobilda 1 ustun, 640px dan 2 ustun, 1024px dan 3 ustun.

Dark Mode

<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">

dark: — foydalanuvchi qurilmasi dark modeda bo'lsa ishlaydi.


Amaliy komponentlar

Tugma

function Button({ children, variant = 'primary', disabled }) {
  return (
    <button
      disabled={disabled}
      className="
        inline-flex items-center gap-2
        px-5 py-2.5
        rounded-md
        text-sm font-medium
        transition-colors duration-200
        disabled:opacity-50 disabled:cursor-not-allowed
        bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700
      "
    >
      {children}
    </button>
  );
}

disabled:opacity-50 — disabled holatda opacity. duration-200 — transition tezligi.

Kartochka

function Card({ title, description, tag }) {
  return (
    <div className="
      bg-white
      border border-gray-200
      rounded-xl
      p-5
      shadow-sm
      hover:shadow-md
      transition-shadow duration-200
    ">
      <span className="
        text-xs font-semibold uppercase tracking-wider
        text-blue-600 bg-blue-50
        px-2.5 py-1 rounded-full
        mb-3 inline-block
      ">
        {tag}
      </span>
      <h3 className="text-base font-semibold text-gray-900 mb-2">{title}</h3>
      <p className="text-sm text-gray-500 leading-relaxed">{description}</p>
    </div>
  );
}

Muammo: className uzun va noto'g'ri

Tailwind ishlatganda tez bir muammo chiqadi:

<button className="inline-flex items-center gap-2 px-5 py-2.5 rounded-md text-sm font-medium transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed bg-blue-500 text-white hover:bg-blue-600">

Bu qator juda uzun — o'qish qiyin, variant boshqarish yanada qiyin:

// ❌ Variantlarni shunday boshqarish noto'g'ri
<button className={`px-5 py-2.5 rounded-md ${variant === 'danger' ? 'bg-red-500 hover:bg-red-600' : 'bg-blue-500 hover:bg-blue-600'} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}>

Bu yerda clsx (yoki classnames) kutubxonasi yordamga keladi.


clsx — dinamik klasslar uchun

npm install clsx

clsx — klasslarni qulay birlashtirish uchun yordamchi:

import clsx from 'clsx';

// Oddiy birlashtirish
clsx('px-4', 'py-2', 'rounded') // → "px-4 py-2 rounded"

// Shartli klasslar — object syntax
clsx({
  'bg-blue-500': true,
  'bg-red-500': false,
  'opacity-50': disabled,
}) // → "bg-blue-500 opacity-50"

// Aralash
clsx('px-4', { 'bg-blue-500': isPrimary, 'bg-red-500': isDanger })

Endi tugma komponentini qayta yozamiz:

import clsx from 'clsx';

const variantKlasslari = {
  primary: 'bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700',
  danger:  'bg-red-500  text-white hover:bg-red-600  active:bg-red-700',
  success: 'bg-green-500 text-white hover:bg-green-600',
  outline: 'bg-transparent border border-blue-500 text-blue-500 hover:bg-blue-50',
  ghost:   'bg-transparent text-gray-600 hover:bg-gray-100',
};

const sizeKlasslari = {
  sm: 'px-3 py-1.5 text-xs',
  md: 'px-5 py-2.5 text-sm',
  lg: 'px-6 py-3 text-base',
};

function Button({
  children,
  variant = 'primary',
  size = 'md',
  fullWidth = false,
  disabled = false,
  className,
}) {
  return (
    <button
      disabled={disabled}
      className={clsx(
        // Asosiy stillar — har doim
        'inline-flex items-center justify-center gap-2',
        'rounded-md font-medium',
        'transition-colors duration-200',
        'focus:outline-none focus:ring-2 focus:ring-offset-2',

        // Variant
        variantKlasslari[variant],

        // O'lcham
        sizeKlasslari[size],

        // Shartli
        {
          'w-full': fullWidth,
          'opacity-50 cursor-not-allowed pointer-events-none': disabled,
        },

        // Tashqaridan qo'shimcha klasslar
        className
      )}
    >
      {children}
    </button>
  );
}
<Button>Saqlash</Button>
<Button variant="danger" size="sm">O'chirish</Button>
<Button variant="outline" fullWidth>Kirish</Button>
<Button disabled>Yuborilmoqda...</Button>

{/* Tashqaridan qo'shimcha klass */}
<Button className="mt-4">Davom etish</Button>

Endi variant qo'shish oson — variantKlasslari ga yangi kalit yozasiz. className ham clsx ichida va tashqi klasslar ham qabul qilinadi.


tailwind-merge — klass ziddiyatini hal qilish

clsx bir muammoni hal qilmaydi: bir xil xususiyatning ikki klassi to'qnashsa nima bo'ladi?

// p-4 va px-6 to'qnashdi — qaysi ustun keladi?
clsx('p-4', 'px-6') // → "p-4 px-6"
// Brauzer ikkalasini ko'radi, cascade qoidasiga ko'ra oxirgisi ustun keladi
// Lekin bu tasodifiy — boshqa tartibda yozilsa boshqa natija

tailwind-merge bu muammoni hal qiladi — ziddiyatli Tailwind klasslarni aqlli birlashtiradi:

npm install tailwind-merge
import { twMerge } from 'tailwind-merge';

twMerge('p-4 px-6')        // → "p-4 px-6" emas, "px-6 p-4" tartibida to'g'rilaydi
twMerge('text-sm text-lg') // → "text-lg" (oxirgisi ustun)
twMerge('bg-blue-500', 'bg-red-500') // → "bg-red-500"

Amalda clsx va twMerge birgalikda ishlatiladi:

import clsx from 'clsx';
import { twMerge } from 'tailwind-merge';

// Ikkalasini birlashtiruvchi yordamchi funksiya
export function cn(...inputs) {
  return twMerge(clsx(inputs));
}
import { cn } from '@/lib/utils'; // yoki qaerda saqlagan bo'lsangiz

function Button({ className, variant, ...props }) {
  return (
    <button
      className={cn(
        'px-4 py-2 rounded-md bg-blue-500 text-white',
        variant === 'danger' && 'bg-red-500',
        className // tashqi klass bg-green-500 bo'lsa — o'sha ustun keladi
      )}
      {...props}
    />
  );
}

// Tashqaridan padding o'zgartirish — to'g'ri ishlaydi
<Button className="px-8">Keng tugma</Button>
// → "py-2 rounded-md bg-blue-500 text-white px-8"
// px-4 olib tashlandi, px-8 qoldi

cn funksiyasi — React ekotizimida juda keng tarqalgan pattern. Shadcn/ui, Radix UI, Headless UI bilan ishlaydigan har qanday loyihada shu funksiyani ko'rasiz.


Amaliy misol — to'liq forma

import { cn } from '@/lib/utils';

function FormInput({ label, error, className, ...props }) {
  return (
    <div className="flex flex-col gap-1.5">
      {label && (
        <label className="text-sm font-medium text-gray-700">
          {label}
        </label>
      )}
      <input
        className={cn(
          'w-full px-3 py-2',
          'border rounded-md',
          'text-sm text-gray-900',
          'placeholder:text-gray-400',
          'outline-none transition-colors duration-200',
          'focus:ring-2 focus:ring-blue-300 focus:border-blue-500',
          error
            ? 'border-red-400 focus:ring-red-200 focus:border-red-400'
            : 'border-gray-300',
          className
        )}
        {...props}
      />
      {error && (
        <p className="text-xs text-red-500">{error}</p>
      )}
    </div>
  );
}

// Ishlatish
function LoginForm() {
  return (
    <div className="max-w-md mx-auto mt-16 p-8 bg-white rounded-2xl shadow-sm border border-gray-200">
      <h2 className="text-2xl font-bold text-gray-900 mb-6">Kirish</h2>

      <div className="flex flex-col gap-4">
        <FormInput
          label="Email"
          type="email"
          placeholder="siz@example.com"
        />
        <FormInput
          label="Parol"
          type="password"
          placeholder="••••••••"
          error="Parol kamida 8 ta belgi bo'lishi kerak"
        />

        <button className="
          w-full py-2.5 mt-2
          bg-blue-500 hover:bg-blue-600
          text-white text-sm font-medium
          rounded-md
          transition-colors duration-200
        ">
          Kirish
        </button>

        <p className="text-center text-sm text-gray-500">
          Hisobingiz yo'qmi?{' '}
          <a href="/register" className="text-blue-500 hover:underline font-medium">
            Ro'yxatdan o'ting
          </a>
        </p>
      </div>
    </div>
  );
}

Tez-tez uchraydigan xatolar

Dinamik klass nomlari to'liq yozilmasa

// ❌ Xato — Tailwind build vaqtida bu klassni topib, CSS ga qo'sha olmaydi
const rang = 'blue';
<div className={`bg-${rang}-500`}>

// ✅ To'g'ri — to'liq klass nomi yoziladi
const klasslar = {
  blue: 'bg-blue-500',
  red:  'bg-red-500',
};
<div className={klasslar[rang]}>

Tailwind statik analiz bilan ishlaydi: u faqat to'liq yozilgan klass nomlarini CSS ga qo'shadi. Dinamik birlashtirilgan nomlarni ko'rmas — build da yo'qoladi.


!important o'rniga ! prefiksi

// Tailwind da important
<div className="!text-red-500">  {/* text-red-500 !important */}
<div className="!p-0">           {/* padding: 0 !important */}

!important CSS da kuchli qurol, ehtiyotkorlik bilan ishlatiladi.


Spacing raqamlarini adashtirish

p-4  →  16px   (1rem)
p-5  →  20px   (1.25rem)
p-6  →  24px   (1.5rem)
p-8  →  32px   (2rem)
p-10 →  40px   (2.5rem)
p-12 →  48px   (3rem)

Har bir birlik 0.25rem (4px). p-4 = 4 × 4px = 16px. Bu qoidani bilsangiz, raqamlarni hisoblash oson bo'ladi.


Xulosa

Tailwind — utility-first yondashuv. Har bir klass bitta CSS xususiyat. Siz alohida CSS fayl yozmaysiz — klasslarni to'g'ridan-to'g'ri JSX da qo'shib ketasiz.

clsx va tailwind-merge birgalikda (cn funksiyasi) — dinamik va ziddiyatsiz klass boshqarish uchun standart pattern.

Eslab qoling:

  • npm install tailwindcss @tailwindcss/vite — o'rnatish
  • @import "tailwindcss" — CSS ga import
  • hover:, focus:, disabled: — pseudo-klass prefikslar
  • sm:, md:, lg: — responsive prefikslar
  • dark: — dark mode
  • Dinamik klasslar to'liq yozilishi kerak — bg-${rang}-500 ishlamaydi
  • clsx — shartli klasslar uchun
  • twMerge — ziddiyatli Tailwind klasslarni hal qilish
  • cn = twMerge(clsx(...)) — ikkalasini birlashtiruvchi standart yordamchi

Foydali havolalar


Keyingi dars

38-dars: clsx va classnames — dinamik class boshqarish