CSS Modules — nima, qanday ishlaydi, global va local

~15 daq · React · Stilizatsiya

Muammodan boshlaylik

Siz katta jamoada ishlayapsiz. Bir kuni Jasur .title klassini yozdi — sariq rang, katta shrift. Siz ham .title yozdingiz — boshqa komponent uchun, qizil rang. Brauzerda ikkalasi ham bir sahifaga tushdi. Qaysi .title ustun kelishini hech kim aniq bilmaydi.

Ertasi kuni Malika .btn yozdi. Siz ham .btn yozdingiz — boshqa joylarda. Stillar bir-birini bosib ketdi. Qaysi komponent qaysi stildan kelganini aniqlash uchun DevTools da uzoq vaqt sarfladingiz.

Bu — global CSS ning asosiy muammosi. Katta loyihalarda klasslar bir-birini bosib ketadi, qaysi stil qayerdan kelganini aniqlash qiyinlashadi, birovning stilini o'zgartirsangiz boshqa joyni buzasiz.

CSS Modules aynan shu muammoni hal qilish uchun yaratilgan.


CSS Modules nima?

CSS Modules — bu oddiy CSS fayl, lekin unda yozilgan barcha klasslar avtomatik ravishda faqat o'sha komponentga tegishli bo'ladi. Boshqa komponentga o'tib ketmaydi.

Fayl nomi .module.css bilan tugaydi — ana shu farq. Button.module.css — CSS Module. Button.css — oddiy global CSS.

Hayotiy o'xshatma: tasavvur qiling, har bir xonaning o'z kalit raqami bor — 101, 102, 103. Siz faqat o'z xonangizning kalitini ishlatasiz, boshqa xonaga kirmaydigan kalit. CSS Modules ham shunday — har klassga noyob raqam qo'shib, faqat o'z komponentida ishlashini ta'minlaydi.


Qanday ishlaydi?

Siz yozasiz:

/* Button.module.css */
.btn {
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
}

.btn:hover {
  background: #2563eb;
}

Brauzerga yetib borganda Vite/Webpack bu klassni o'zgartiradi:

.btn  →  .btn_xK92a   (yoki _btn_abc12 — build vositasiga qarab)

Oxiridagi tasodifiy belgilar — shu komponent uchun noyob imzo. Boshqa komponentdagi .btn boshqa imzo oladi. Ular bir-biriga tegmaydi.


Birinchi misol

src/
  components/
    Button/
      Button.jsx
      Button.module.css
/* Button.module.css */
.btn {
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: 14px;
}

.primary {
  background: #3b82f6;
}

.danger {
  background: #ef4444;
}

.outline {
  background: transparent;
  border: 1px solid #3b82f6;
  color: #3b82f6;
}
// Button.jsx
import styles from './Button.module.css';

export function Button({ variant = 'primary', children }) {
  return (
    <button className={styles.btn}>
      {children}
    </button>
  );
}

styles.btn — bu oddiy string: "btn_xK92a". Brauzerga shunday yetib boradi.

// Ishlatish
<Button>Saqlash</Button>

Bir nechta klass qo'shish

import styles from './Button.module.css';

export function Button({ variant = 'primary', children }) {
  return (
    // styles.btn + styles[variant] — ikki klass birlashadi
    <button className={`${styles.btn} ${styles[variant]}`}>
      {children}
    </button>
  );
}
<Button variant="danger">O'chirish</Button>
// className="btn_xK92a danger_mP3q1"

Nima uchun styles[variant]? Chunki variant — o'zgaruvchi. styles.primary, styles.danger, styles.outline — bularni dinamik olish uchun [] ishlatiladi.


Klas nomi notog'ri bo'lsa nima bo'ladi?

// styles.notExists → undefined
<button className={styles.notExists}>

undefined className ga o'tadi — brauzerda class="undefined" bo'ladi. Bu ko'rinishda xato qilmaslik uchun TypeScript bilan ishlash qulay — u mavjud bo'lmagan klassni ko'rsatib beradi.


Global va Local — farqi

CSS Modules da ikkita rejim bor: local (standart) va global.

Local — standart holat

/* styles.module.css */
.title {
  font-size: 24px;
}
/* Brauzerda: .title_xK92a { font-size: 24px } */

Barcha klasslar standart holda local — noyob imzo oladi.

:global — butun sahifaga tegishli

Ba'zan siz uchinchi tomon kutubxona klassini yoki butun ilova bo'ylab ishlaydigan klassni o'zgartirmoqchi bo'lasiz. :global shu uchun:

/* styles.module.css */
.wrapper {
  padding: 20px;
}

/* Bu klass imzo olMaydi — global qoladi */
:global(.react-datepicker) {
  font-size: 13px;
  border-radius: 8px;
}

:global(.active) {
  font-weight: 600;
}
.wrapper  →  .wrapper_xK92a  (local — o'zgartirildi)
.react-datepicker  →  .react-datepicker  (global — o'zgarmadi)

Nima uchun :global kerak? Uchinchi tomon kutubxonalar (React DatePicker, Swiper, Ant Design) o'zlarining klasslarini ishlatadi. Siz ularni CSS Module ichidan o'zgartirmoqchi bo'lsangiz — :global yozing, aks holda o'zgartirishingiz ular klassiga yetib bormaydi.

:local — global faylda local klass

Agar butun faylingiz global bo'lsa (masalan, global stillar uchun) va shu ichida bitta klassni local qilmoqchi bo'lsangiz:

/* global.css — oddiy CSS, Modules emas */
/* Bu stillar global ishlaydi */

:local(.specialCard) {
  border: 1px solid red;
}
/* Brauzerda: .specialCard_xK92a */

Amalda bu juda kam ishlatiladi — odatda .module.css faylda ishlaysiz va hammasi local bo'ladi.


composes — klass meros olish

CSS Modules da bir klassdan boshqasini "meros" qilish mumkin:

/* base.module.css */
.flexCenter {
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Card.module.css */
.card {
  composes: flexCenter from './base.module.css'; /* ← tashqi fayldan */
  background: white;
  padding: 20px;
  border-radius: 8px;
}

.header {
  composes: flexCenter; /* ← o'z faylidan */
  border-bottom: 1px solid #eee;
  padding-bottom: 12px;
}
// Brauzerda card elementi ikkita klass oladi:
// className="card_abc flexCenter_xyz"
// ikkalasining stillari ishlaydi

composes — CSS Modules ning kuchli xususiyati. SCSS dagi @extend ga o'xshaydi, lekin build vaqtida ishlaydi — runtime xarajati yo'q.


Amaliy misol — to'liq komponent

/* UserCard.module.css */
.card {
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  padding: 20px;
  display: flex;
  gap: 16px;
  align-items: center;
  transition: box-shadow 0.2s;
}

.card:hover {
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}

.avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.info {
  flex: 1;
}

.name {
  font-size: 15px;
  font-weight: 600;
  color: #111827;
  margin-bottom: 2px;
}

.role {
  font-size: 13px;
  color: #6b7280;
}

.badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.badgeAdmin {
  composes: badge;
  background: rgba(239, 68, 68, 0.1);
  color: #dc2626;
}

.badgeUser {
  composes: badge;
  background: rgba(59, 130, 246, 0.1);
  color: #2563eb;
}
// UserCard.jsx
import styles from './UserCard.module.css';

export function UserCard({ user }) {
  const badgeClass = user.role === 'admin'
    ? styles.badgeAdmin
    : styles.badgeUser;

  return (
    <div className={styles.card}>
      <img
        src={user.avatar}
        alt={user.name}
        className={styles.avatar}
      />
      <div className={styles.info}>
        <p className={styles.name}>{user.name}</p>
        <p className={styles.role}>{user.email}</p>
      </div>
      <span className={badgeClass}>{user.role}</span>
    </div>
  );
}

Ushbu komponent boshqa komponentning stillariga ta'sir qilmaydi — hech qanday nom konflikti yo'q.


Tez-tez uchraydigan xatolar

Defis bilan yozilgan klassga murojaat

/* styles.module.css */
.primary-btn {
  background: blue;
}
// ❌ Xato — defis JavaScript da ruxsat etilmaydi
<div className={styles.primary-btn}>

// ✅ To'g'ri — kvadrat qavslar bilan
<div className={styles['primary-btn']}>

Shuning uchun CSS Modules da klasslarni camelCase yozish qoidaga aylangan: primaryBtn, cardHeader, errorText — shunda styles.primaryBtn to'g'ridan-to'g'ri ishlaydi.


Klass mavjudligini tekshirmaslik

// styles.loading mavjud emas — className="undefined" bo'ladi
<div className={styles.loading}>

Agar klass dinamik bo'lsa va ba'zan bo'lmasligi mumkin bo'lsa:

// ✅ To'g'ri — undefined o'tmaslik uchun
<div className={isLoading ? styles.loading : ''}>

:global haddan tashqari ko'p ishlatish

/* ❌ Yomon — CSS Modules ning maqsadini yo'qotadi */
:global(.title) { color: red; }
:global(.card) { background: white; }
:global(.btn) { padding: 10px; }

:global faqat uchinchi tomon kutubxonalari yoki haqiqiy global stillar uchun. Har doim local klasslarni afzal ko'ring.


CSS Modules vs boshqa yondashuv lar

CSS Modules:
  + Qurilish vaqtida — runtime xarajati yo'q
  + Oddiy CSS sintaksisi — yangi narsa o'rganish shart emas
  + Har qanday framework bilan ishlaydi
  - className="string" — bir nechta klass dinamik boshqarish ba'zan og'ir

Styled Components:
  + Props bilan dinamik stil — juda qulay
  + TypeScript integratsiya yaxshi
  - Runtime xarajat bor — brauzerda CSS yaratialdi
  - Katta paket hajmi

Tailwind CSS:
  + HTML ichida — alohida fayl yo'q
  + Juda tez prototiplash
  - className juda uzun bo'lishi mumkin
  - CSS sintaksisini bilish shart emas degan yanglish tushuncha

CSS Modules — CSS ni yaxshi bilganlar uchun ideal. Hech qanday yangi sintaksis yo'q, hamma narsa oddiy CSS — faqat nom konflikti muammosi hal qilingan.


Xulosa

CSS Modules — nom konflikti muammosini hal qiladigan oddiy mexanizm. .module.css bilan tugaydigan fayl yozasiz, barcha klasslar avtomatik noyob nom oladi, boshqa komponentga o'tib ketmaydi.

Eslab qoling:

  • .module.css — CSS Modules fayli
  • import styles from './Component.module.css' — import qilish
  • styles.className — klass nomiga murojaat
  • styles['defis-li-nom'] — defis bo'lsa kvadrat qavslar
  • :global(.klass) — uchinchi tomon kutubxonalari uchun
  • composes — klass meros olish
  • Klasslarni camelCase yozish — eng qulay yo'l

Keyingi dars

36-dars: Styled Components — CSS in JS, props bilan dinamik stil