Mundarija (40)
- Bu darsda
- 1. Nega bu kerak?
- 2. Komponentning uch qatlami
- 2.1 Xulq, ko'rinish, egalik
- 2.2 Qo'lda yozilgan oyna: nima yetishmaydi
- 3. Styled kitlar: hammasi tayyor
- 3.1 MUI misolida
- 3.2 Kuchli va zaif tomonlari
- 3.3 Mashhur styled kitlar
- 4. Headless primitivlar: xulq tayyor, ko'rinish sizniki
- 4.1 Radix Dialog jonli
- 4.2 Konsolni o'qiymiz
- 4.3 Mashhur headless kutubxonalar
- 5. Copy-paste: kod sizniki
- 5.1 shadcn/ui g'oyasi
- 5.2 Registr nima
- 5.3 Yangilash muammosi
- 6. Bir xil ekran — to'qqiz yo'l: o'lchov
- 6.1 Tajriba sharti
- 6.2 Hajm
- 6.3 Klaviatura va a11y
- 6.4 CSP: kutubxona sizning xavfsizlik siyosatingizga mosmi?
- 7. Qaror mezonlari
- 7.1 Yetti savol
- 7.2 Qaror daraxti
- 7.3 Kanon tajribasi: vazifalar qanday tanladi
- 8. Ko'p uchraydigan xatolar
- 8.1 Ikki kitni aralashtirish
- 8.2 Styled kit bilan kurashish
- 8.3 "Headless — demak a11y o'zim"
- 8.4 CSP'ni production'da bilib qolish
- 8.5 "Eng mashhuri — demak eng yaxshisi"
- 9. Mashqlar
- 1-mashq (oson): qaysi tur?
- 2-mashq (o'rta): Radix Popover
- 3-mashq (qiyin): o'z ConfirmDialog ingiz
- 4-mashq: o'z loyihangiz uchun qaror
- 10. Real ishda
- Xulosa
- Manbalar
UI kutubxonalar xaritasi React'da: styled, headless va copy-paste
Qisqacha: React UI kutubxonalari uch turga bo'linadi. Styled kit (MUI, Ant Design) — xulq ham, ko'rinish ham tayyor: tez boshlanadi, lekin o'z dizayningizga moslash qimmat. Headless kutubxona (Radix, React Aria, Base UI) — faqat xulqni beradi: klaviatura, fokus, ARIA; ko'rinish to'liq sizda. Copy-paste (shadcn/ui) — headless ustiga tayyor ko'rinish, lekin kod
node_modulesga emas, sizningsrc/papkangizga ko'chiriladi va siz uning egasiga aylanasiz. Tanlov — taxmin bilan emas: hajm, a11y, CSP va moslash narxini o'lchab qilinadi.
Bu darsda
- UI komponentining uch qatlamini (xulq, ko'rinish, egalik) ajratasiz va har kutubxona qaysi qatlamni berishini aytasiz.
- Qo'lda yozilgan oyna bilan Radix oynasini jonli solishtirib, kutubxona aslida nimani "sotishini" ko'rasiz.
- Bir xil ekranni to'qqiz xil yo'l bilan qurgan tajribamiz raqamlarini o'qiysiz: hajm, kod qatori, klaviatura, CSP.
- Loyiha uchun kutubxona tanlash mezonlarini qo'llaysiz va
vazifalarnega aynan shunday tanlaganini tushuntira olasiz.
Oldin bilishingiz kerak: Dizayn tizimi: tokenlar va variantlar, Headless komponentlar, Fokus boshqaruvi, Brauzer xavfsizlik modeli: CSP.
1. Nega bu kerak?
O'tgan qismning oxirgi darsida fayl yuklash oynasini yasadik. 19-qism tugadi: vazifalar da sahifalar, server bilan ishlash va formalar bor. Endi ilova ko'rinishi va qulayligi bilan shug'ullanamiz.
«Bahor» oshxonasida Jasur aka yangi admin panel so'radi. Unda buyurtmani o'chirishdan oldin tasdiq oynasi, har qatorda "⋯" menyusi, pastda qisqa xabar (toast) bo'lishi kerak. Sardor (stajyor) birinchi kuni shunday dedi: "Oyna — bu div va useState. Yarim soatlik ish."
Haqiqatda esa oyna — eng qiyin komponentlardan biri. U ochilganda fokus ichiga o'tishi kerak. Tab tugmasi oynadan chiqib ketmasligi, Esc bilan yopilishi kerak. Yopilganda fokus o'sha tugmaga qaytishi kerak. Malika (ekran o'quvchi) "dialog, Vazifa o'chirilsinmi?" deb eshitishi kerak. Orqadagi sahifa esa bosilmasligi va o'qilmasligi lozim. Bularning har biri — alohida tuzoq. Fokus boshqaruvi darsida ularni bittalab yechganmiz.
18-qismda vazifalar ning menyusini o'zimiz yozdik: useMenyu, Menyu.tsx va boshqaruv hooki. Ular yuzlab qator kod va o'nlab test talab qildi. Keyingi darslarda shu menyuni tayyor kutubxonaga almashtiramiz — git diff da 453 ta o'chirilgan qator chiqadi. Lekin bu bepul emas: brauzer 27.2 KB ko'proq yuklaydi.
Shuning uchun bu darsda bitta savolga javob beramiz: qaysi ishni kutubxonaga topshirish kerak va qaysi kutubxonaga? MUI, Ant Design, Radix, shadcn/ui, React Aria — ular bir-biridan juda farq qiladi. Xaritasiz birinchi uchragan "eng mashhur" kutubxona o'rnatiladi va loyihaga mos emasligi kech bilinadi.
Hayotiy o'xshatish: uyni jihozlash. Tayyor mebel do'konidan to'liq to'plam olsangiz — tez, lekin hamma narsa do'kon uslubida. Faqat karkas va mexanizm (tortma relslari, eshik oshiqlari) olib, ustini o'zingiz bezasangiz — erkin, lekin ish ko'p. Uchinchi yo'l — usta chizmasini va tayyor detallarni uyingizga olib kelib, o'zingiz yig'asiz. Endi mebel to'liq sizniki: buzilsa, o'zingiz tuzatasiz.
2. Komponentning uch qatlami
2.1 Xulq, ko'rinish, egalik
Har UI komponentini uch qatlamga ajratish mumkin:
| Qatlam | Nima kiradi | Misol (oyna) |
|---|---|---|
| Xulq | holat, klaviatura, fokus, ARIA | Esc yopadi, fokus ichida |
| Ko'rinish | ranglar, o'lcham, animatsiya | oq quti, qora fon |
| Egalik | kod qayerda turadi, kim yangilaydi | node_modules yoki src/ |
Kutubxona turlari shu qatlamlardan qaysi birini sizga berishi bilan farq qiladi:
flowchart LR
S["Styled kit<br/>MUI, AntD"] --> X1["Xulq ✓<br/>Ko'rinish ✓<br/>Kod: npm"]
H["Headless<br/>Radix, Base UI"] --> X2["Xulq ✓<br/>Ko'rinish — siz<br/>Kod: npm"]
C["Copy-paste<br/>shadcn/ui"] --> X3["Xulq ✓ (headless)<br/>Ko'rinish ✓<br/>Kod: src/"]Nimaga qarang: uch yo'lning hammasida xulq tayyor. Farq — ko'rinishni kim yozadi va kod kimga tegishli. Oxirgi qatordagi "Kod: src/" — copy-paste yondashuvining asosiy g'oyasi.
2.2 Qo'lda yozilgan oyna: nima yetishmaydi
Sardorning "yarim soatlik" oynasini tekshirib ko'ramiz. audit funksiyasi oynaga ekran o'quvchi nigohi bilan qaraydi: roli bormi, nomi bormi, fokus ichidami. «Ishga tushir» ni bosing, keyin «O'chirish» ni:
import { useEffect, useState } from "react";
// Oynaga ekran o'quvchi nigohi bilan qaraydi
function audit(box) {
const nameId = box.getAttribute("aria-labelledby");
const title = nameId && document.getElementById(nameId);
console.log("role:", box.getAttribute("role"));
console.log("nomi:", title ? title.textContent : "yo'q");
console.log("fokus ichida:", box.contains(document.activeElement));
}
function Report() {
useEffect(() => {
audit(document.querySelector(".oyna"));
}, []);
return null;
}
export default function App() {
const [open, setOpen] = useState(false);
return (
<main>
<h1>Vazifalar</h1>
<p>Non olish</p>
<button onClick={() => setOpen(true)}>O'chirish</button>
{open && (
<div className="oyna">
<h2>Vazifa o'chirilsinmi?</h2>
<button onClick={() => setOpen(false)}>Bekor qilish</button>
<Report />
</div>
)}
</main>
);
}Konsolda:
role: null
nomi: yo'q
fokus ichida: falseEkranda oyna chiqdi — ko'radigan odam uchun hammasi joyida. Konsol esa boshqa narsani aytadi:
role: null— Malika uchun bu oddiydiv. Ekran o'quvchi "dialog ochildi" demaydi.nomi: yo'q— oynaning nomi yo'q. Sarlavha bor, lekin u oynaga bog'lanmagan (aria-labelledbyyo'q).fokus ichida: false— Bobur (faqat klaviatura) hali ham tashqarida. Tab bossa, orqadagi sahifa bo'ylab yuradi.
Konsol ko'rsatmaganlari ham bor: Esc ishlamaydi, orqadagi sahifa bosiladi, yopilganda fokus qaytmaydi. Bularning hammasini Modal oyna va skroll bloklash va Fokus boshqaruvi darslarida qo'lda yechganmiz. Yechish mumkin — lekin har komponent uchun qaytadan.
Tekshirib ko'ring: Uch qatlamdan qaysi biri Sardorning oynasida "tayyor", qaysi biri yo'q?
Javob
Ko'rinish bor: oyna chiziladi, matn ko'rinadi. Xulq deyarli yo'q: rol, nom, fokus, Esc, orqa fonni yashirish — hech biri. Egalik esa to'liq Sardorda: kod src/ da, uni o'zi yozgan. Kutubxonasiz yo'lda xulq qatlami eng qimmat qism bo'lib chiqadi — UI kutubxonalarning asosiy qiymati ham aynan shu qatlamda.
3. Styled kitlar: hammasi tayyor
3.1 MUI misolida
Styled kit (tayyor dizaynli kutubxona) — xulqni ham, ko'rinishni ham beradigan kutubxona. Siz faqat komponentni chaqirasiz: <Button>, <Dialog>, <Snackbar>. Do'kondan to'liq mebel to'plamini olgandek.
Eng mashhuri — MUI (Material UI, 9.4.0). U Google'ning Material Design uslubidagi komponentlarni beradi. Biz tajriba uchun bir xil ekranni MUI'da ham yasadik (tajriba/kitlar/src/mui.tsx, telefonga sig'ishi uchun qisqartirilgan):
import {
Button, Dialog, DialogActions, DialogContent,
DialogContentText, DialogTitle, Snackbar,
} from "@mui/material";
// ... ro'yxat: har qatorda O'chirish tugmasi
<Dialog
open={tanlangan !== null}
onClose={() => setTanlangan(null)}
aria-labelledby="sarlavha"
>
<DialogTitle id="sarlavha">{sarlavha}</DialogTitle>
<DialogContent>
<DialogContentText>{tavsif(tanlangan ?? "")}</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={() => setTanlangan(null)}>
Bekor qilish
</Button>
<Button color="error" onClick={ochir}>O'chirish</Button>
</DialogActions>
</Dialog>
<Snackbar open={xabar !== ""} message={xabar} />Bitta ham CSS qatori yo'q, lekin ekranda soya, burchaklar, animatsiya — hammasi bor. Butun variant 32 qator kod bo'ldi, to'qqiz yo'lning eng qisqasi.
3.2 Kuchli va zaif tomonlari
Kuchli tomoni — tezlik. Admin panel, ichki tizim, prototip — dizayner yo'q, muddat qisqa. Jadval, sana tanlash, forma kabi murakkab komponentlar ham tayyor.
Zaif tomoni — moslash narxi. Kit o'z dizayn tilida yozilgan. «Bahor»ning yashil tokenlarini berish mumkin: MUI'da createTheme, Ant Design'da ConfigProvider va token bilan. Lekin dizayner "tugma burchagi boshqacha, oynaning tuzilishi boshqacha" desa, kit bilan kurashishga tushasiz. Ichki elementning klassini topish, ustidan yozish, versiya yangilanganda yana tuzatish kerak bo'ladi.
Yana bir narx — hajm va ishlash usuli. Ko'p kitlar stilini CSS-in-JS bilan beradi. MUI va Chakra — Emotion kutubxonasi orqali, Ant Design — o'z tizimi orqali. Bu degani, sahifa ochilganda JavaScript CSS qoidalarini hisoblab, <style> tegini o'zi qo'shadi. Bu tez ishlaydi, lekin keyinroq ko'ramizki, qattiq xavfsizlik siyosati bilan to'qnashadi.
3.3 Mashhur styled kitlar
| Kit | O'ziga xosligi | Kursda |
|---|---|---|
| MUI | Material Design, eng keng ekotizim (MUI X jadval va sana) | MUI |
| Ant Design | korporativ admin panel, kuchli jadval va forma | Ant Design |
| Mantine | 120 ga yaqin komponent va 81 ta hook | boshqa kitlar |
| Chakra UI, HeroUI | moslashuvchan tema; HeroUI — Tailwind ustida | boshqa kitlar |
Har biriga alohida dars bor — u yerda tema, jadval va formalarini ko'ramiz. Hozir faqat turini bilish yetadi.
4. Headless primitivlar: xulq tayyor, ko'rinish sizniki
4.1 Radix Dialog jonli
Headless primitiv (headless primitive) — ko'rinishi yo'q, faqat xulqi bor kichik qurilish bloki: Dialog, Popover, Menu, Tabs. Headless komponentlar darsida o'z headless hookimizni yozgan edik. Endi kutubxona variantini ko'ramiz. Mebel o'xshatishida bu — karkas va mexanizm, ustini o'zingiz bezaysiz.
Eng mashhurlaridan biri — Radix UI. Uning oynasi @radix-ui/react-dialog paketida. Natija oynasida Radix bor — quyidagi misol haqiqiy kutubxona bilan ishlaydi. Ko'rinish — oddiy style bilan: natija oynasida Tailwind sinflari ishlamaydi, loyihada esa bu joyda Tailwind sinflari yoziladi. audit — o'sha funksiya, qo'shimcha uchta o'lchov bilan:
import { useEffect } from "react";
import * as Dialog from "@radix-ui/react-dialog";
const styleCount = () => document.querySelectorAll("style").length;
const stylesBefore = styleCount();
function audit(box) {
const nameId = box.getAttribute("aria-labelledby");
const title = nameId && document.getElementById(nameId);
console.log("role:", box.getAttribute("role"));
console.log("nomi:", title ? title.textContent : "yo'q");
console.log("fokus ichida:", box.contains(document.activeElement));
}
function Report() {
useEffect(() => {
// Radix fokusni o'z effektida ko'chiradi — biroz kutamiz
const timer = setTimeout(() => {
audit(document.querySelector('[role="dialog"]'));
const root = document.getElementById("root");
const hidden = root.getAttribute("aria-hidden");
console.log("#root aria-hidden:", hidden);
console.log("body style:", document.body.getAttribute("style"));
console.log("yangi <style>:", styleCount() - stylesBefore);
}, 100);
return () => clearTimeout(timer);
}, []);
return null;
}
const overlay = {
position: "fixed",
inset: 0,
background: "rgb(0 0 0 / 0.5)",
};
const panel = {
position: "fixed",
top: "20%",
left: "1rem",
right: "1rem",
padding: "1rem",
borderRadius: 8,
background: "white",
};
export default function App() {
return (
<main>
<h1>Vazifalar</h1>
<p>Non olish</p>
<Dialog.Root>
<Dialog.Trigger>O'chirish</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay style={overlay} />
<Dialog.Content style={panel}>
<Dialog.Title>Vazifa o'chirilsinmi?</Dialog.Title>
<Dialog.Description>
«Non olish» ro'yxatdan o'chiriladi.
</Dialog.Description>
<Dialog.Close>Bekor qilish</Dialog.Close>
<Report />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</main>
);
}Konsolda:
role: dialog
nomi: Vazifa o'chirilsinmi?
fokus ichida: true
#root aria-hidden: true
body style: pointer-events: none;
yangi <style>: 1Ekranda:
Vazifalar
Non olish
O'chirish
Vazifa o'chirilsinmi?
«Non olish» ro'yxatdan o'chiriladi.
Bekor qilish4.2 Konsolni o'qiymiz
Biz bitta ham role, aria-*, focus() yoki keydown yozmadik. Shunga qaramay:
role: dialogvanomi: Vazifa o'chirilsinmi?— RadixDialog.Contentga rol qo'ydi vaDialog.Titleniaria-labelledbyorqali bog'ladi. Malika endi "dialog, Vazifa o'chirilsinmi?" deb eshitadi.fokus ichida: true— fokus birinchi tugmaga («Bekor qilish») ko'chdi. Tab oynadan chiqmaydi: bu fokus tuzog'i, Radix uni o'zi qo'ydi. Esc bossangiz, oyna yopiladi va fokus «O'chirish» ga qaytadi — o'z loyihangizda sinab ko'ring.#root aria-hidden: true— ilovaning qolgan qismi ekran o'quvchidan yashirildi. Malika oyna ochiq turganda orqadagi ro'yxatni "ko'rmaydi".body style: pointer-events: none;— orqadagi sahifani sichqoncha bilan bosib bo'lmaydi.yangi <style>: 1— Radix sahifaga bitta<style>tegi qo'shdi. U skrollni bloklash uchun: oyna ochiqligida orqa sahifa aylanmasin. Bu satrni eslab qoling — CSP haqidagi bo'limda u muhim bo'ladi.
Oyna body ga chizildi — #root dan tashqariga (Portallar darsidagi createPortal). Natija oynasi body dagi portallarni ham ko'rsatadi, shuning uchun «Ekranda» ro'yxatida ilova matnidan keyin oyna matni keladi.
Ko'rinishni esa biz berdik: ikki obyekt va style prop. Radix "qanday ko'rinsin?" deb so'ramaydi — qaysi tegga qaysi stil, qanday animatsiya — hammasi sizniki.
Tekshirib ko'ring: Misoldan
<Dialog.Title>ni olib tashlab, o'rniga oddiy<h2>qo'ysangiz, konsolning qaysi qatori o'zgaradi?
Javob
nomi: qatori. Radix aria-labelledby ni o'zi qo'yadi, lekin u faqat Dialog.Title ning id siga ishora qiladi. Oddiy <h2> da bu id yo'q — getElementById hech narsa topmaydi va nomi: yo'q chiqadi. Headless kutubxona xulqni beradi, lekin uning qismlarini to'g'ri ishlatish sizning vazifangiz. Title — "shart" qism: ko'rinmasligi kerak bo'lsa ham, uni ekran o'quvchi uchun qoldirib, ko'zdan yashiriladi.
4.3 Mashhur headless kutubxonalar
| Kutubxona | Kim yozadi | O'ziga xosligi |
|---|---|---|
| Radix UI | WorkOS (2022 gacha — Modulz) | o'nlab primitiv, asChild; shadcn/ui'ning birinchi asosi |
| React Aria | Adobe | eng chuqur a11y va xalqaro qo'llab-quvvatlash; hooklar va komponentlar |
| Base UI | Radix, Floating UI va MUI mualliflari | yangi (1.x); shadcn 4 ning standart asosi |
| Headless UI | Tailwind Labs | kichik to'plam, React va Vue uchun |
Ularning API'si siz bilgan naqshlardan qurilgan: Dialog.Root + qismlar — compound komponent, asChild — polymorphic komponent. Shuning uchun 18-qismdan keyin ularni o'qish qiyin emas. Radix'ni Radix chuqur darsida batafsil ko'ramiz.
5. Copy-paste: kod sizniki
5.1 shadcn/ui g'oyasi
Uchinchi yo'l — copy-paste (nusxa ko'chirib olish). Uning eng mashhur vakili — shadcn/ui. G'alati tomoni: shadcn/ui'ni npm'dan import qilmaysiz. Buyruq komponent kodini loyihangizga yozadi:
npx shadcn@4.21.4 add alert-dialogNatijada loyihangizda yangi fayl paydo bo'ladi: odatda src/components/ui/alert-dialog.tsx (papka sozlamada belgilanadi, vazifalar da — src/ui/). Uning ichida — Radix (yoki Base UI) primitivlari va Tailwind sinflari. Endi bu fayl sizniki: xohlagan qatorini o'zgartirasiz, keraksiz qismini o'chirasiz. Import esa o'z papkangizdan bo'ladi:
import { AlertDialog } from "@/ui/alert-dialog";Tajribadagi shadcn varianti shunday (qisqartirilgan):
import { AlertDialog } from "radix-ui";
import { Toaster, toast } from "sonner";
const tugma =
"min-h-11 rounded-lg border border-zinc-300 bg-white px-3 py-2";
<AlertDialog.Root>
<AlertDialog.Trigger className={tugma}>
O'chirish
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay className="fixed inset-0 bg-black/50" />
<AlertDialog.Content className="fixed top-1/2 left-1/2 ...">
<AlertDialog.Title className="text-lg font-bold">
{sarlavha}
</AlertDialog.Title>
{/* Description, Cancel, Action */}
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>Bu headless Radix + Tailwind ko'rinishi. Mebel o'xshatishida — tayyor chizma va detallar, lekin yig'ish va egalik sizda.
5.2 Registr nima
Kod qayerdan keladi? Komponentlar registri (registry) — komponentlar katalogi: har komponent uchun fayllar matni, qaysi npm paketlarga bog'liqligi va qaysi papkaga yozilishi. npm registri paketlarni saqlasa, shadcn registri fayllarni saqlaydi. CLI registrdan JSON oladi, fayllarni src/ ga yozadi, bog'liqliklarni o'rnatadi.
Registr faqat shadcn'niki emas. Boshqa jamoalar o'z registrlarini chiqaradi, kompaniyalar esa ichki dizayn tizimini registr qilib, hamma loyihalarga shu yo'l bilan tarqatadi. CLI'ga paket nomi o'rniga URL berish mumkin.
flowchart LR
R["Registr<br/>(JSON: fayllar)"] --> CLI["npx shadcn add"]
CLI --> F["src/ui/*.tsx<br/>sizning kod"]
CLI --> N["node_modules<br/>radix, cn…"]
F --> A["Ilova"]
N --> ANimaga qarang: CLI ikki joyga yozadi. Ko'rinish va yig'ish — src/ ga (sizniki), xulq — node_modules ga (hamon paket). Ya'ni copy-paste'da ham Radix npm paketi qoladi, faqat uning ustidagi qatlam sizga o'tadi.
5.3 Yangilash muammosi
Kod sizniki bo'lgani — ham kuch, ham yuk. MUI yangi versiya chiqarsa, npm update bilan hamma tugmalaringiz yangilanadi. shadcn komponentiga tuzatish chiqsa, u sizga o'zi kelmaydi. Faylni allaqachon o'zgartirgan bo'lsangiz, qayta add qilish o'zgarishlaringizni bosib ketadi. Shunda yangi kodni qo'lda solishtirib ko'chirasiz.
Amaliy qoida: add dan oldin commit qiling, add dan keyin git diff bilan CLI nima o'zgartirganini o'qing. Xulq qatlami (Radix) baribir npm'da — uning xato tuzatishlari odatdagidek npm orqali keladi. Shu bilan birga semver va lock fayl qoidalari ham saqlanadi. CLI'ning o'z tuzoqlari bor — ularni keyingi darsda haqiqiy chiqish bilan ko'ramiz.
Tekshirib ko'ring: shadcn/ui orqali qo'shilgan tugmaning burchagini o'zgartirish uchun nimani tahrirlaysiz:
node_modulesdagi faylnimi yokisrc/dagi faylnimi? MUI tugmasida-chi?
Javob
shadcn'da — src/ui/button.tsx ni: sinflar o'sha faylda, u sizniki. MUI'da node_modules ga tegilmaydi (keyingi npm install o'zgarishni o'chiradi). U yerda tema (createTheme dagi shape.borderRadius yoki komponent styleOverrides) yoki sx prop orqali o'zgartirasiz. Farq aynan "egalik" qatlamida: kod qayerda tursa, o'zgartirish yo'li ham o'sha yerda.
6. Bir xil ekran — to'qqiz yo'l: o'lchov
6.1 Tajriba sharti
Taxminiy gaplar o'rniga o'lchadik. Kanon tajribasi (tajriba/kitlar/, 2026-10-14) — bir xil kichik ekran to'qqiz xil yo'l bilan qurildi:
- uchta vazifa, har birida «O'chirish» tugmasi;
- bosilganda tasdiq oynasi, tasdiqlansa — toast «… o'chirildi.»;
- Vite 8.3.3, React 19.3.0, build sozlamasi —
vazifalarniki.
Har variant brauzerda (Chrome 155) vazifalar ning xavfsizlik siyosati (CSP) ostida va faqat klaviatura bilan sinaldi. Tekshiruv tartibi: Enter → oyna ochildimi, fokus ichidami → Esc → oyna yopildimi, fokus «O'chirish» ga qaytdimi → yana ochib, tasdiqlash → toast chiqdimi.
6.2 Hajm
Brauzer yuklaydigan JS + CSS, gzip bilan (React'ning o'zi ham ichida, shuning uchun eng kichigi ham 66.8 KB):
- native dialog (kanon)66,8 KB
- shadcn (Radix + sonner)90,8 KB
- Base UI93,2 KB
- React Aria95,1 KB
- MUI112,9 KB
- Chakra UI151 KB
- Ant Design174,4 KB
Manba: O'lchandi: tajriba/kitlar, Vite 8.3.3, React 19.3.0, gzip -9, natijalar/tajriba/kitlar-hajm.txt, 2026-10-14
Grafikka sig'maganlari: Mantine — 139.8 KB (shundan CSS 35.9), HeroUI — 154.7 KB (CSS 39.1). Uch narsani ko'ramiz:
- Brauzerning o'zi (
<dialog>+ o'z toast'imiz) — eng arzoni. Kutubxona emas, brauzer xulqi (Modal oyna). - Headless va copy-paste — native'dan 24–28 KB ortiq. Faqat ishlatilgan primitivlar yuklanadi.
- Styled kitlar — 46 dan 108 KB gacha ortiq. Bitta oyna uchun ham tema tizimi, CSS-in-JS dvigateli va umumiy yordamchilar yuklanadi.
Kod qatori esa teskari: MUI — 32 qator, Mantine — 33, Ant Design — 37. Base UI esa 54, React Aria — 48, native — 44 qator. Styled kit kamroq kod yozdiradi, lekin ko'proq bayt yuklatadi. Bu — ayirboshlash (trade-off): dasturchi vaqti yoki foydalanuvchining interneti.
6.3 Klaviatura va a11y
| Variant | Oyna roli | Fokus ochilganda | Esc → fokus qaytdi |
|---|---|---|---|
| native, MUI | dialog |
«Bekor qilish» | |
| shadcn, Base UI, Chakra | alertdialog |
«Bekor qilish» | |
| React Aria | alertdialog |
oynaning o'zi | |
| HeroUI | alertdialog |
oynaning o'zi | yopilmadi (standart sozlama) |
Mantine va Ant Design jadvalda yo'q — ular CSP tufayli umuman ochilmadi (keyingi bo'lim). Qolganlarning deyarli hammasi o'tdi — kutubxonalarning asosiy va'dasi (xulq) bajarildi. Lekin HeroUI'da Esc tasdiq oynasini yopmadi. Bu xato emas, ataylab qilingan: HeroUI'ning tasdiq oynasida Esc standart bo'yicha o'chirilgan ("muhim savol — tasodifan yopilmasin"). Manba kodidagi o'sha sozlamani boshqa kitlar darsida ko'ramiz. Baribir, bu sizning ilovangiz kutgan xulq bilan farq qiladi. Saboq: "a11y'li kutubxona" — va'da, kafolat emas. Har yangi kutubxonani o'z ssenariyingiz bilan klaviaturada sinang.
alertdialog — tasdiq yoki ogohlantirish oynasining alohida roli. U foydalanuvchidan javob kutadi. Ekran o'quvchi uni oddiy dialog dan qat'iyroq e'lon qiladi. React Aria fokusni tugmaga emas, oynaning o'ziga qo'yadi — Malika avval savolni eshitadi. Bu ham to'g'ri usul.
6.4 CSP: kutubxona sizning xavfsizlik siyosatingizga mosmi?
vazifalar 13-qismdan beri qattiq CSP bilan ishlaydi (Brauzer xavfsizlik modeli): style-src 'self' — faqat o'z faylimizdagi CSS, sahifaga JavaScript qo'shgan <style> tegi bloklanadi. Yana Trusted Types — innerHTML ga oddiy satr yozish taqiqlangan (XSS va DOM xavfsizligi). Natija:
| Variant | CSP buzilishi | Ilova ishladimi |
|---|---|---|
| native, Base UI | 0 | |
| React Aria, MUI, HeroUI | 1 | , lekin stil bloklandi |
| Chakra UI | 3 | , lekin stil bloklandi |
| shadcn (Radix AlertDialog + sonner) | 4 | , lekin stil bloklandi |
| Mantine, Ant Design | Trusted Types | ochilmadi |
Brauzer konsolida har buzilish shunday ko'rinadi (kitlar-ishlat.txt dan, qisqartirilgan):
Applying inline style violates the following Content Security Policy directive 'style-src 'self''. Either the 'unsafe-inline' keyword, a has…Tarjimasi: "Inline stilni qo'llash Content Security Policy'ning style-src 'self' qoidasini buzadi. 'unsafe-inline' kalit so'zi yoki hash kerak…". Mantine va Ant Design'da esa xabar boshqacha: This document requires 'TrustedHTML' assignment. — "bu hujjat TrustedHTML tayinlashni talab qiladi". Ular stil yoki belgilarni innerHTML orqali qo'shadi, Trusted Types buni to'xtatadi va ilova umuman chizilmaydi.
Radix oynasidagi yangi <style>: 1 ni eslang — aynan shu teg. Natija oynasida inline <style> ga ruxsat bor, shuning uchun jonli misol ishladi. vazifalar ning CSP'si ostida esa bu teg bloklanadi.
Ilova ochilsa ham, "stil bloklandi" degani — skroll bloklash yoki animatsiya kutilganidek ishlamasligi mumkin. Uni "tuzatish" uchun CSP'ga 'unsafe-inline' qo'shish — xavfsizlik devorini teshish (Brauzer xavfsizlik modeli darsidagi birinchi xato).
Shuning uchun kanonda tasdiq oynasi native <dialog> da qoldi, toast — o'zimizniki. Base UI — CSP buzilishi 0 bo'lgan yagona kutubxona.
Tekshirib ko'ring: Kichik kafe sayti CSP ishlatmaydi. CSP jadvali u uchun ahamiyatsizmi?
Javob
Bugun — deyarli ha: CSP yo'q bo'lsa, <style> qo'shish hech narsani buzmaydi. Lekin ikkita "lekin" bor. Birinchidan, CSP — XSS'ga qarshi eng kuchli himoyalardan biri; sayt o'sib, login va to'lov qo'shilsa, uni qo'yish kerak bo'ladi. Shunda kutubxonani almashtirish qimmatga tushadi. Ikkinchidan, bank, davlat va korporativ loyihalarda CSP ko'pincha talab. Kutubxonani tanlashda "kelajakda qattiq CSP qo'ya olamanmi?" degan savol ham bor.
7. Qaror mezonlari
7.1 Yetti savol
Kutubxona tanlashdan oldin shu savollarga javob yozing:
- Dizayn kimniki? Dizayner o'z dizayn tizimini chizganmi yoki "chiroyli va tez bo'lsa bo'ldi"mi?
- Ilova turi. Ichki admin panel (jadval, forma ko'p) yoki mijozlar uchun mahsulot (har piksel muhim)?
- Hajm byudjeti. Foydalanuvchilar sekin internetda va telefondami? Har 20–100 KB qanchaga tushadi?
- A11y talabi. Ekran o'quvchi va klaviatura bilan ishlash shartmi (davlat va bank xizmatlarida — ko'pincha shart)?
- CSP. Hozir yoki kelajakda
style-src 'self'va Trusted Types bo'ladimi? - Stil usuli. Loyihada Tailwind bormi? CSS Modules? Kitning stil tizimi siznikiga to'g'ri keladimi?
- Uzoq muddat. Kim yangilaydi? Kutubxona faol rivojlanyaptimi, so'nggi reliz qachon? Kod kimniki bo'lishi qulay?
7.2 Qaror daraxti
Savollarni qisqa yo'lga aylantirsak:
flowchart TD
A{"O'z dizayn<br/>tizimi bormi?"} -->|yo'q, tezlik muhim| B["Styled kit<br/>MUI, AntD"]
A -->|ha| C{"Tailwind<br/>ishlatasizmi?"}
C -->|ha| D["Copy-paste<br/>shadcn/ui"]
C -->|yo'q| E["Headless<br/>Radix, React Aria"]
B --> F{"Qattiq CSP<br/>kerakmi?"}
D --> F
E --> F
F -->|ha| G["CSP ostida o'lchang<br/>yoki Base UI, native"]Nimaga qarang: CSP savoli oxirida turibdi va har yo'lni tekshiradi. U kutubxona turiga emas, aniq kutubxonaga bog'liq — shuning uchun javob faqat o'lchov bilan beriladi.
Bu daraxt — boshlanish nuqtasi, qonun emas. Masalan, Ant Design — o'zbek korporativ loyihalarida keng tarqalgan. Jamoangiz uni yaxshi bilsa, bu ham kuchli dalil.
7.3 Kanon tajribasi: vazifalar qanday tanladi
Bu dars uchun vazifalar da kod o'zgarmaydi — o'rniga 20-qismning tanlovi va sababi:
| Komponent | Tanlov | Nega |
|---|---|---|
| Tasdiq oynasi | native <dialog> (13-qismdan) |
66.8 KB, CSP 0, klaviatura |
| Toast | o'zimizniki (18-qismdan) | sonner +8.9 KB va 2 CSP buzilishi |
| Menyular, tooltip | Radix (shadcn usulida ko'chirilgan) | murakkab xulq; modal={false} bilan CSP 0 |
| Tugma variantlari | cva + Tailwind |
dizayn tokenlari bilan |
Ya'ni bitta kutubxona "hammasini" qilmaydi. Har komponent uchun alohida qaror, alohida o'lchov. Radix menyuni shadcn/ui II darsida qo'shamiz. Modal rejimida Radix ham <style> qo'shadi va CSP'ni buzadi, modal={false} bilan — 0. O'lchovi — Radix chuqur darsida. Butun 20-qism oxirida birinchi yuklash 151.4 KB dan 201.8 KB ga o'sadi — har qo'shimcha o'z darsida raqam bilan.
Maslahat: Qarorni yozib qo'ying.
vazifalarda bunday qarorlarTEXNIK-QARZ.mdva commit tanasida turadi: "nima tanlandi, nimalar bilan solishtirildi, raqamlar, qachon qayta ko'rish kerak". Bir yildan keyin "nega MUI emas?" deb so'rashsa, javob tayyor.
8. Ko'p uchraydigan xatolar
8.1 Ikki kitni aralashtirish
MUI tugmasi, Ant Design jadvali va shadcn oynasi bitta sahifada — uch dizayn tili, uch tema tizimi, hajm esa yig'indisi. Bitta asosiy yo'l tanlang. Istisno faqat aniq sabab bilan bo'ladi (masalan, faqat Ant Design'da bor murakkab jadval).
8.2 Styled kit bilan kurashish
Har komponentda !important, ichki klass nomlarini (.MuiButton-root) ushlash, "dizaynerimiz boshqacha chizgan". Bu belgi — noto'g'ri tur tanlangan. Dizayn butunlay o'zingizniki bo'lsa, headless yoki copy-paste arzonroq tushadi.
8.3 "Headless — demak a11y o'zim"
Aksincha: headless kutubxonaning asosiy qiymati — aynan a11y va klaviatura. Siz faqat ko'rinishni berasiz. Lekin qismlarni to'g'ri ishlatish kerak: Title siz oyna nomsiz qoladi («Radix Dialog jonli» bo'limidagi savol).
8.4 CSP'ni production'da bilib qolish
Dev serverda CSP odatda yo'q — hammasi ishlaydi. Production'da konsol qizil xatolarga to'ladi yoki ilova oq ekran bo'lib qoladi (Mantine, Ant Design misoli). Yangi kutubxonani build qilingan ilovada, production CSP'si ostida sinang.
8.5 "Eng mashhuri — demak eng yaxshisi"
npm'dagi yuklanishlar soni — mashhurlik, mos kelish emas. U yetti savolning o'rnini bosmaydi.
9. Mashqlar
Mashqlar kurs/mashqlar/20/01-ui-xarita/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi tur?
- Xulq va ko'rinish tayyor, kod
node_modulesda: kit - Faqat xulq (klaviatura, fokus, ARIA), ko'rinish sizda:
- Kod
src/ga ko'chiriladi, siz egasisiz: - Komponent fayllari katalogi, CLI undan o'qiydi: komponentlar
- Ant Design
vazifalarCSP'sida ochilmadi, chunkiinnerHTMLni to'sdi
Yechim
Styled kit (MUI, Ant Design) — hammasi tayyor, o'zgartirish tema orqali. Headless (Radix, React Aria, Base UI) — xulq tayyor, ko'rinish sizniki. Copy-paste (shadcn/ui) — kod sizning src/ papkangizda, lekin xulq hamon headless paketda. Registr — fayllar katalogi (npm registri esa paketlar katalogi). Trusted Types innerHTML ga oddiy satrni yozishni taqiqlaydi — kutubxona shunday qilsa, ilova chizilmaydi.
2-mashq (o'rta): Radix Popover
«Bahor» menyusida osh narxi yonida «Narx haqida» tugmasi bo'lsin. Bosilganda kichik oyna chiqsin: "Narxga ko'k choy kiradi" va «Yopish» tugmasi. Talablar:
@radix-ui/react-popoverdanRoot,Trigger,Portal,Content,Close—Dialogbilan bir xil anatomiya;- ko'rinish — oddiy
style(oq fon, chegara, ichki bo'shliq); - ichidagi
Reportkomponenti konsolga uchta narsani yozsin: tugmaningaria-expandedqiymati, fokus qaysi tugmada va nechta yangi<style>qo'shilgani.
Avval taxmin qiling: Popover ham Dialog kabi <style> qo'shadimi?
Yechim
import { useEffect } from "react";
import * as Popover from "@radix-ui/react-popover";
const styleCount = () => document.querySelectorAll("style").length;
const stylesBefore = styleCount();
function Report() {
useEffect(() => {
const timer = setTimeout(() => {
const trigger = document.querySelector("main button");
const expanded = trigger.getAttribute("aria-expanded");
const focused = document.activeElement.textContent;
console.log("aria-expanded:", expanded);
console.log("fokus:", focused);
console.log("yangi <style>:", styleCount() - stylesBefore);
}, 100);
return () => clearTimeout(timer);
}, []);
return null;
}
const card = {
padding: "0.75rem",
border: "1px solid #c9d6cf",
borderRadius: 8,
background: "white",
};
export default function App() {
return (
<main>
<p>Osh — 35 000 so'm</p>
<Popover.Root>
<Popover.Trigger>Narx haqida</Popover.Trigger>
<Popover.Portal>
<Popover.Content style={card}>
<p>Narxga ko'k choy kiradi</p>
<Popover.Close>Yopish</Popover.Close>
<Report />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
</main>
);
}Konsolda:
aria-expanded: true
fokus: Yopish
yangi <style>: 0Ekranda:
Osh — 35 000 so'm
Narx haqida
Narxga ko'k choy kiradi
Yopish<style> qo'shilmadi: Popover sukut bo'yicha modal emas — orqa sahifa bosiladi va aylanadi, skrollni bloklash kerak emas. Uslub tegi ham shu sabab yo'q. Xulq esa baribir tayyor: aria-expanded «true» bo'ldi, fokus oyna ichiga ko'chdi. Xulosa: bitta kutubxonaning ham har primitivi CSP'ga turlicha ta'sir qiladi. Shuning uchun kanon menyuni modal={false} bilan ishlatadi.
3-mashq (qiyin): o'z ConfirmDialog ingiz
Copy-paste g'oyasini qo'lda bajaring. Radix Dialog ustida o'zingizning ConfirmDialog komponentingizni yozing — go'yo u src/ui/confirm-dialog.jsx faylida turibdi:
- props:
label(tugmaning ekran o'quvchi uchun to'liq nomi),title,onConfirm; - tugmada ko'rinadigan matn — «O'chirish», to'liq nomi esa
aria-labelda, ko'rinadigan matn bilan boshlansin:O'chirish: Non olish(ko'radigan va eshitadigan odam bir xil so'zni topadi); - oynada sarlavha (
Dialog.Title), «Bekor qilish» va «Ha, o'chirish» tugmalari (ikkalasiDialog.Close, ikkinchisionConfirmni chaqiradi); Descriptionkerak emas —Contentgaaria-describedby={undefined}bering (Radix hujjatidagi usul: "tavsif ataylab yo'q").
App da ikki vazifa: «Non olish», «Choy damlash». Tasdiqlangan vazifa ro'yxatdan o'chsin, role="status" li qatorda «… o'chirildi» chiqsin. Sinov: «Non olish» ni o'chiring.
Yechim
import { useState } from "react";
import * as Dialog from "@radix-ui/react-dialog";
// "src/ui/confirm-dialog.jsx" — kod sizniki: matn, tugmalar, uslub
function ConfirmDialog({ label, title, onConfirm }) {
return (
<Dialog.Root>
<Dialog.Trigger aria-label={label}>O'chirish</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Content aria-describedby={undefined}>
<Dialog.Title>{title}</Dialog.Title>
<Dialog.Close>Bekor qilish</Dialog.Close>{" "}
<Dialog.Close onClick={onConfirm}>
Ha, o'chirish
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
export default function App() {
const [tasks, setTasks] = useState(["Non olish", "Choy damlash"]);
const [message, setMessage] = useState("");
return (
<main>
<ul>
{tasks.map((task) => (
<li key={task}>
{task}{" "}
<ConfirmDialog
label={`O'chirish: ${task}`}
title={`«${task}» o'chirilsinmi?`}
onConfirm={() => {
setTasks((list) => list.filter((t) => t !== task));
setMessage(`«${task}» o'chirildi`);
}}
/>
</li>
))}
</ul>
<p role="status">{message}</p>
</main>
);
}Ekranda:
Choy damlash O'chirish
«Non olish» o'chirildiOyna yopildi, «Non olish» ro'yxatdan ketdi, holat qatori xabar berdi. ConfirmDialog — kichik shadcn: xulq Radix'dan (npm), ko'rinish va matnlar sizda. Ertaga dizayner "«Ha, o'chirish» qizil bo'lsin" desa — kutubxonani kutmasdan, faylni o'zingiz o'zgartirasiz. Esc va fokusning qaytishini o'z loyihangizda sinang: «O'chirish» → Esc → fokus yana «O'chirish» da.
4-mashq: o'z loyihangiz uchun qaror
Siz qilmoqchi bo'lgan (yoki qilayotgan) loyihani oling: masalan, «Bahor» uchun buyurtma paneli yoki maktab baholari sayti. «Qaror mezonlari» dagi yetti savolga bir qatordan javob yozing. Keyin turni (styled, headless, copy-paste) va aniq kutubxonani tanlang. Oxirida "qachon qayta ko'rib chiqaman" degan bitta shart yozing. Javobni kurs/mashqlar/20/01-ui-xarita/QAROR.md ga saqlang.
Yechim
Namuna (buyurtma paneli):
- Dizayn — dizayner yo'q, «Bahor» ranglari bor. 2. Ichki admin panel: jadval va forma ko'p. 3. Ofisdagi kompyuterlar, internet yaxshi — hajm muhim emas. 4. Klaviatura — ha, ekran o'quvchi — hozircha talab yo'q. 5. CSP — hozir yo'q. 6. Tailwind yo'q. 7. Kichik jamoa, tez yetkazish kerak.
Tanlov: styled kit — Ant Design (jadval, forma, sana tayyor; tema token bilan yashil). Qayta ko'rish sharti: "panel mijozlarga ochilsa yoki CSP talab qilinsa — headless'ga o'tishni o'lchab ko'ramiz". Boshqa javob ham to'g'ri bo'lishi mumkin — muhimi, har tanlov savollarga tayansin.
10. Real ishda
- Ish e'lonlarida: "MUI yoki Ant Design", "shadcn/ui, Radix, Tailwind" — ko'p uchraydigan talablar. Turini tushunsangiz, yangi kitni tez o'rganasiz: API'lar o'xshash naqshlardan qurilgan.
- Korporativ loyihalar (bank, ichki tizimlar): ko'pincha Ant Design yoki MUI — tezlik va tayyor jadval muhimroq.
- Startap va mahsulot jamoalari: shadcn/ui + Tailwind yoki o'z dizayn tizimi + Radix/React Aria. Ko'rinish brendning bir qismi.
- Intervyu savollari: "Headless komponent nima va qachon kerak?", "shadcn/ui kutubxonadan nimasi bilan farq qiladi?", "UI kitni tanlashda nimalarga qaraysiz?", "Kutubxona CSP bilan qanday to'qnashishi mumkin?".
Xulosa
- UI komponent uch qatlamdan iborat: xulq, ko'rinish, egalik. Eng qimmati — xulq (fokus, klaviatura, ARIA).
- Styled kit (MUI, Ant Design) — hammasi tayyor, kam kod, lekin ko'p bayt (bir xil ekran: 112.9–174.4 KB) va moslash qimmat.
- Headless (Radix, React Aria, Base UI) — xulq tayyor, ko'rinish sizniki (93–95 KB).
- Copy-paste (shadcn/ui) — kod
src/da, siz egasisiz; tuzatishlar o'zi kelmaydi. - "A11y'li" — va'da: HeroUI'da Esc oynani yopmadi (standart sozlamasi shunday). CSP ostida esa ko'p kutubxona
<style>qo'shadi, Mantine va Ant Design umuman ochilmadi. - Tanlov — yetti savol va o'lchov bilan.
vazifalar: native dialog va o'z toast, menyu va tooltip — Radix.
Keyingi dars: shadcn/ui I: qanday ishlaydi va sozlash — vazifalar ga Tailwind v4, @theme tokenlari, cn va cva bilan tugma, @/ alias va shadcn CLI'ning haqiqiy chiqishi bilan tuzoqlari.
Manbalar
- Radix Primitives: "Dialog", "Popover", "Accessibility" — radix-ui.com/primitives/docs
- React Aria: "Dialog", "Accessibility" — react-spectrum.adobe.com/react-aria
- Base UI: "Alert Dialog" — base-ui.com
- shadcn/ui: "Introduction", "CLI", "Registry" — ui.shadcn.com/docs
- MUI: "Dialog", "Theming" — mui.com/material-ui
- WAI-ARIA Authoring Practices: "Dialog (Modal) Pattern", "Alert and Message Dialogs Pattern" — w3.org/WAI/ARIA/apg
- MDN: "Content-Security-Policy: style-src", "Trusted Types API" — developer.mozilla.org
- Radix tarixi: "Modulz has been acquired by WorkOS" — modulz.app/blog (2022); Base UI: "From the creators of Radix, Floating UI, and Material UI" — base-ui.com
- Kanon tajribasi:
natijalar/tajriba/kitlar-hajm.txt,kitlar-ishlat.txt(2026-10-14)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!