Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. lucide-react: har ikonka — komponent
- 2.1 Birinchi ikonka
- 2.2 Props: o'lcham, chiziq, rang
- 3. A11y: bezak yoki ma'no
- 3.1 Uch holat
- 3.2 Faqat ikonkali tugma
- 3.3 Ikonkaga aria-label bersa-chi?
- 4. Matn bilan bir qatorda
- 5. Bundle hajmi: nomli import va icons[nom]
- 5.1 Tree-shaking
- 5.2 Kanon o'lchovi
- 5.3 icons[nom] tuzog'i
- 6. Boshqa ikonka kutubxonalari
- 7. Vazifalar qadami: tugmalarda lucide ikonkalari (holat 08)
- 7.1 Nima o'zgaradi
- 7.2 Fayllar
- 7.3 e2e testi yiqildi — va bu yaxshi
- 7.4 Tekshiruvlar va commit
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): props
- 2-mashq (o'rta): telefondagi ikonka-tugmalar
- 3-mashq (qiyin): holat belgisi — ma'noli ikonka
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
React'da ikonkalar: lucide-react, a11y va bundle hajmi
Qisqacha: Har lucide ikonka — alohida React komponent:
import { Pencil } from "lucide-react", keyin<Pencil size={16} />. IkonkacurrentColorbilan matn rangini oladi va o'ziaria-hidden="true"qo'yadi — ya'ni bezak. Faqat ikonkali tugmaning nomiaria-labelda bo'lishi shart. Nomli import faqat ishlatilgan ikonkalarni bundle'ga oladi (vazifalarda 9 ta ikonka — +2.0 KB),icons[nom]esa hammasini (+161 KB).
Bu darsda
- lucide ikonkasi DOM'da qanday
<svg>bo'lishini va props'lari (size,strokeWidth,absoluteStrokeWidth,color) nima o'zgartirishini jonli ko'rasiz. - Ikonka qachon bezak (
aria-hidden) va qachon ma'noli ekanini, faqat ikonkali tugmaga nom berishni bilasiz. - Ikonkani matn bilan bir qatorga tekislaysiz (
inline-flex,gap,shrink-0). - Nomli import,
icons[nom]vaDynamicIconning bundle'ga ta'sirini kanon o'lchovida ko'rasiz. - react-icons, Heroicons, Tabler va Iconify'ni farqlaysiz.
vazifalartugmalariga ikonka qo'shasiz (holat 08) va e2e testi nega yiqilganini tushunasiz.
Oldin bilishingiz kerak: SVG HTML ichida va ikonkalar, SVG'ni CSS bilan stillash va ikonka tizimi, Rasm, SVG va assetlar komponentlarda, shadcn/ui I.
1. Nega bu kerak?
vazifalar dagi tugmalar hozircha faqat so'z: «Tahrirlash», «O'chirish», «Saqlash». Uch vazifada bu yetadi. Lekin ro'yxat uzaysa, ko'z so'zni o'qimaydi — shaklni qidiradi. Qalam belgisi — tahrir, chelak — o'chirish. Bu belgilar butun dunyoda bir xil tushuniladi.
Rasm, SVG va assetlar darsida SVG'ni qo'lda komponentga aylantirgan edik. 40 ta ikonka kerak bo'lsa-chi? Har birini Figma'dan olib, tozalab, komponent qilish — bir kun. Tayyor ikonka kutubxonasi buni bitta importga aylantiradi.
Lekin ikkita xavf bor. Birinchisi — a11y: ikonka ko'z uchun, Malika (ekran o'quvchi) uchun esa u yo jim, yo "rasm" deb noma'lum. Ikkinchisi — hajm: noto'g'ri import bilan bitta ikonka uchun mingtasini yuklab yuborish mumkin. Bugun ikkalasini ham o'lchab ko'ramiz.
Hayotiy o'xshatish: yo'l belgilari. Haydovchi matnni o'qimaydi — shaklni taniydi. Lekin belgining ostida doim kichik yozuv ham bor: shakl tushunarsiz bo'lsa, so'z yordam beradi.
2. lucide-react: har ikonka — komponent
2.1 Birinchi ikonka
lucide-react — Lucide to'plamining React varianti. SVG ikonka tizimi darsida Lucide'ni tilga olgan edik. Kanon versiyasi — 1.52.0:
npm i -E lucide-react@1.52.0Natija oynasida lucide bor (tanlangan ~109 ikonka). Ikonkani chizamiz va DOM'dagi atributlarini o'qiymiz:
import { useEffect } from "react";
import { Pencil } from "lucide-react";
export default function App() {
useEffect(() => {
const svg = document.querySelector("svg");
for (const a of svg.attributes) {
console.log(a.name, "=", a.value);
}
}, []);
return (
<button>
<Pencil /> Tahrirlash
</button>
);
}Konsolda:
xmlns = http://www.w3.org/2000/svg
width = 24
height = 24
viewBox = 0 0 24 24
fill = none
stroke = currentColor
stroke-width = 2
stroke-linecap = round
stroke-linejoin = round
class = lucide lucide-pencil
aria-hidden = trueEkranda:
TahrirlashQatorlarni o'qiymiz:
viewBox = 0 0 24 24— ikonka 24 × 24 katakli "chizma qog'ozi"da chizilgan.width/heightqancha bo'lmasin, rasm shu nisbatda cho'ziladi (SVG HTML ichida).fill = none,stroke = currentColor— Lucide — chiziqli (outline) ikonkalar: ichi bo'sh, faqat chiziq. Chiziq rangi —currentColor, ya'ni ota elementning matn rangi. Tugma matni qizil bo'lsa, ikonka ham qizil — alohida rang berish shart emas.class = lucide lucide-pencil— CSS'da topish uchun: barcha lucide ikonkalari va aynan qalam.aria-hidden = true— ikonka ekran o'quvchidan yashirilgan. Malika «Tahrirlash, tugma» ni eshitadi, "rasm" ni emas.
"Ekranda" faqat «Tahrirlash» — SVG ichida matn yo'q.
2.2 Props: o'lcham, chiziq, rang
Bitta ikonka besh xil:
import { useEffect } from "react";
import { Trash2 } from "lucide-react";
export default function App() {
useEffect(() => {
for (const s of document.querySelectorAll("svg")) {
console.log(
s.getAttribute("width"),
s.getAttribute("stroke-width"),
s.getAttribute("stroke"),
);
}
}, []);
return (
<div style={{ fontSize: 40 }}>
<Trash2 />
<Trash2 size={16} />
<Trash2 size={48} strokeWidth={1.5} color="#c0392b" />
<Trash2 size={48} absoluteStrokeWidth />
<Trash2 size="1em" />
</div>
);
}Konsolda:
24 2 currentColor
16 2 currentColor
48 1.5 #c0392b
48 1 currentColor
1em 2 currentColorsize— eni va bo'yi birga. Standart — 24.strokeWidth— chiziq qalinligiviewBoxbirliklarida. 48 pikselli ikonkada 1.5 birlik ekranda 3 piksel bo'ladi.absoluteStrokeWidth— chiziq ekranda doim 2 piksel bo'lsin. 48 pikselda lucidestroke-widthni o'zi 1 ga tushirdi: 2 × 24 / 48 = 1. Katta va kichik ikonkalar yonma-yon tursa, chiziqlar bir xil qalin ko'rinadi.color—strokega yoziladi. Odatda kerak emas:currentColoryaxshiroq, rang CSS'dan keladi.size="1em"— ikonka shrift o'lchamiga ergashadi (bu yerda 40px). Sarlavha ichida katta, izohda kichik — avtomatik.
Maslahat: Ikonka nomlari:
Trash2— 1.x datrashning taxallusi (alias). Shuning uchun sinfidalucide-trashham bor. Eski koddaTrash2, yangisidaTrash— ikkalasi bitta ikonka. Natija oynasida ro'yxatda yo'q ikonkani import qilsangiz (masalanRocket), o'zbekcha xato barcha mavjud nomlar ro'yxati bilan chiqadi — loyihangizda esa 1 866 ta ikonkaning hammasi bor (1.52.0 versiyasiningiconsobyektida sanadik).
3. A11y: bezak yoki ma'no
3.1 Uch holat
Ikonka sahifada uch xil vazifada keladi:
| Holat | Misol | Kim nom beradi |
|---|---|---|
| Matn yonida — bezak | « Tahrirlash» | matn; ikonka aria-hidden |
| Yolg'iz, tugma ichida | «» tugmasi | tugmaning aria-label i |
| Yolg'iz, ma'lumot sifatida | jadvalda " to'langan" belgisi | ikonkaning o'zi (role="img" + aria-label) |
Birinchisi — eng ko'p. Lucide uni standart qilib qo'ygan, biz hech narsa yozmaymiz.
3.2 Faqat ikonkali tugma
Telefonda joy tor: qatordagi «O'chirish» faqat chelak bo'lib qoldi. Sardor yozdi:
import { useEffect } from "react";
import { Trash2 } from "lucide-react";
export default function App() {
useEffect(() => {
const [bad, good] = document.querySelectorAll("button");
console.log("❌ matn:", JSON.stringify(bad.textContent));
console.log("❌ aria-label:", bad.getAttribute("aria-label"));
console.log("✅ aria-label:", good.getAttribute("aria-label"));
console.log(
"✅ ikonka aria-hidden:",
good.querySelector("svg").getAttribute("aria-hidden"),
);
}, []);
return (
<div>
<button>
<Trash2 size={16} />
</button>{" "}
<button aria-label="O'chirish: Non olish">
<Trash2 size={16} />
</button>
</div>
);
}Konsolda:
❌ matn: ""
❌ aria-label: null
✅ aria-label: O'chirish: Non olish
✅ ikonka aria-hidden: trueBirinchi tugmaning nomi yo'q: matn bo'sh, aria-label yo'q, ikonka esa yashirin. Malika faqat «tugma» ni eshitadi — nima qilishini bilmaydi. axe buni button-name qoidasi bilan ushlaydi (React'da accessibility). Ikkinchisida nom tugmada, ikonka bezak bo'lib qoldi — to'g'ri yo'l. vazifalar dagi Tugma 18-qismdan beri buni tip darajasida majburlaydi: matnsiz va aria-label siz <Tugma /> — tsc xatosi.
Ko'zi ko'radigan foydalanuvchi uchun ham nom foydali: faqat ikonkali tugmaga tooltip qo'shiladi — lekin u aria-label o'rnini bosmaydi.
3.3 Ikonkaga aria-label bersa-chi?
import { useEffect } from "react";
import { CircleCheck } from "lucide-react";
export default function App() {
useEffect(() => {
const svg = document.querySelector("svg");
console.log("aria-hidden:", svg.getAttribute("aria-hidden"));
console.log("aria-label:", svg.getAttribute("aria-label"));
console.log("role:", svg.getAttribute("role"));
}, []);
return (
<p>
Buyurtma №12 <CircleCheck size={16} aria-label="to'langan" />
</p>
);
}Konsolda:
aria-hidden: null
aria-label: to'langan
role: nullLucide aria-label ni ko'rib, aria-hidden ni qo'ymadi — ikonka ekran o'quvchiga ochildi. Lekin role yo'q. <svg> ga rol berilmasa, ba'zi ekran o'quvchi va brauzer juftliklari uni rasm deb tanimaydi va nomini o'qimaydi. Ishonchli shakl — role="img" ni o'zingiz qo'shish: <CircleCheck role="img" aria-label="to'langan" />. Yanada ishonchlisi — ko'rinmas matn: <span className="sr-only">to'langan</span> (React'da accessibility dagi "faqat ekran o'quvchi uchun" sinfi).
Tekshirib ko'ring: «Tahrirlash» tugmasida qalam ikonkasi va matn bor. Sardor ikonkaga ham
aria-label="qalam"qo'shdi. Malika nimani eshitadi?
Javob
Taxminan «qalam Tahrirlash, tugma». aria-label qo'shilgani uchun lucide aria-hidden ni olib tashladi va ikonka tugma nomining bir qismi bo'ldi. Ma'lumot takrorlandi va chalkashdi: "qalam" — rasmning tavsifi, harakat emas. Qoida: matn yonidagi ikonka — doim bezak, unga nom bermang.
4. Matn bilan bir qatorda
Ikonka va matnni yonma-yon qo'ysangiz, ko'pincha ikonka biroz yuqorida yoki pastda "suzadi". Sabab: <svg> — inline element, u matnning tayanch chizig'iga (baseline) qo'yiladi. Ikki yo'l bor.
1. Tugma — inline-flex. Kanon Tugma ning asos sinflari (holat 08):
tugma inline-flex min-h-bosish items-center justify-center gap-2 …
[&_svg]:shrink-0inline-flex items-center— ikonka va matn vertikal markazda (Flexbox).gap-2— orasida 8 piksel; matn oldiga bo'sh joy ({" "}) shart emas.[&_svg]:shrink-0— tugma tor bo'lib qolsa, flex elementlari qisiladi. Ikonka qisilsa — buzilgan chizma.shrink-0uni saqlaydi, uzun matn esa qatorga o'raladi (flex-shrink).[&_svg]— Tailwind'ning ixtiyoriy variant'i: "ichidagi svg ga" (Tailwind variantlar).
2. Matn ichida — vertical-align. Havola matni ichida ikonka (kanon «Batafsil ›»):
Batafsil
<ChevronRight size={16} className="inline align-[-0.15em]" />align-[-0.15em] — vertical-align: -0.15em: ikonkani tayanch chizig'idan biroz pastga tushirish. em — shrift o'lchamiga nisbatan, shuning uchun har o'lchamda to'g'ri.
5. Bundle hajmi: nomli import va icons[nom]
5.1 Tree-shaking
Tree-shaking (daraxtni silkitish) — bundler ishlatilmagan eksportlarni tashlab yuborishi (Tree shaking). lucide-react'ning package.json ida "sideEffects": false — "importning yon ta'siri yo'q, ishlatilmaganini bemalol tashlang". Har ikonka alohida modul. Shuning uchun:
import { Check, Pencil, Trash2, X } from "lucide-react";— build'ga faqat shu to'rttasi tushadi, 1 866 tasining qolgani emas.
5.2 Kanon o'lchovi
Kanon vazifalar da (holat 08, 9 ta ikonka) uch variantni o'lchadi (natijalar/08-ikonka-hajm.txt, birinchi yuklash JS, gzip):
lucide-react 1.52.0 — nomli import (kanon) 568126 181102 156126
qo'lda inline SVG (o'sha 9 yo'l) 564850 179748 155077
lucide icons[nom] — nom satrdan 1191965 340254 278994
# holat 04 (ikonkasiz): 563418 179115 154705Ustunlar: xom bayt, gzip, brotli. Holat 04 dan farqi:
- qo'lda inline SVG0,6 KB
- lucide — nomli importkanon2 KB
- lucide — icons[nom]161,1 KB
Manba: O'lchandi: holat 08, Vite 8.3.3, gzip -9, natijalar/08-ikonka-hajm.txt, 2026-10-14
Uch xulosa:
- Nomli import — +2.0 KB. Qo'lda yozilgan SVG'dan atigi 1.4 KB ko'p. Shu 1.4 KB evaziga: bir xil uslub,
size/strokeWidthprops, avtomatikaria-hiddenva yangilanishlar. icons[nom]— +161 KB. Buvazifalarning butun 19-qismgacha bo'lgan kodidan ham ko'p!- Bitta qator farq — 80 baravar narx.
5.3 icons[nom] tuzog'i
Bu kod qayerdan keladi? Ikonka nomi serverdan yoki sozlamadan satr bo'lib kelsa (masalan, taom turiga qarab ikonka):
// ❌ butun to'plam bundle'ga
import { icons } from "lucide-react";
function TaomIkonkasi({ nom }: { nom: string }) {
const Ikonka = icons[nom as keyof typeof icons];
return <Ikonka size={16} />;
}icons — 1 866 ta ikonka yig'ilgan obyekt. Bundler qaysi kalit kerakligini build paytida bilolmaydi: nom faqat ishlash paytida ma'lum. Shuning uchun hammasini oladi.
Ikonkalar to'plami ma'lum bo'lsa — aniq xarita. Kanon AmalTugmasi.tsx aynan shunday:
import { Check, Pencil, Trash2, X } from "lucide-react";
import type { LucideIcon } from "lucide-react";
// Record<Amal, ...> emas: "almashtir" — katak (checkbox), tugma emas
const IKONKALAR: Partial<Record<Amal, LucideIcon>> = {
tahrirla: Pencil,
ochir: Trash2,
saqla: Check,
bekor: X,
};Xaritada to'rtta nom — build to'rttasini oladi. Partial — har amalga ikonka shart emas. LucideIcon — ikonka komponentining turi.
Ikonkalar oldindan ma'lum bo'lmasa (masalan, foydalanuvchi o'z kategoriyasiga ikonka tanlaydi) — lucide-react/dynamic dagi DynamicIcon: <DynamicIcon name="pizza" />. U har ikonkani alohida kichik chunk sifatida, kerak bo'lganda yuklaydi (lazy va code splitting g'oyasi). Birinchi yuklash shishmaydi, lekin ikonka tarmoqdan keladi — bir lahza bo'sh joy.
Jonli misolda xarita naqshi:
import { useEffect } from "react";
import { Check, Pencil, Trash2, X } from "lucide-react";
const ICONS = {
edit: Pencil,
delete: Trash2,
save: Check,
cancel: X,
};
function ActionButton({ action, children }) {
const Icon = ICONS[action];
return (
<button>
{Icon !== undefined && <Icon size={16} />}
{children}
</button>
);
}
export default function App() {
useEffect(() => {
for (const b of document.querySelectorAll("button")) {
const svg = b.querySelector("svg");
const icon = svg ? svg.classList[1] : "ikonkasiz";
console.log(b.textContent, "→", icon);
}
}, []);
return (
<div>
<ActionButton action="edit">Tahrirlash</ActionButton>{" "}
<ActionButton action="delete">O'chirish</ActionButton>{" "}
<ActionButton action="toggle">Bajarildi</ActionButton>
</div>
);
}Konsolda:
Tahrirlash → lucide-pencil
O'chirish → lucide-trash
Bajarildi → ikonkasizXaritada yo'q amal (toggle) — undefined, tugma ikonkasiz chiziladi, xato yo'q. Icon katta harf bilan — JSX'da kichik harfli nom HTML teg deb o'qiladi (Komponentlar).
Tekshirib ko'ring: Jamoadoshingiz
import * as Icons from "lucide-react"yozdi va<Icons.Pencil />ishlatdi. Bundle +161 KB bo'ladimi?
Javob
Odatda yo'q. Icons.Pencil — xususiyatga statik murojaat: bundler build paytida faqat Pencil ishlatilganini ko'radi va qolganini tashlaydi (Vite/Rolldown namespace importini shunday tahlil qiladi). Muammo Icons[nom] — kvadrat qavsda o'zgaruvchi — bo'lganda boshlanadi: unda kalit faqat ishlash paytida ma'lum. Ishonch kerak bo'lsa — build'dan keyin hajmni o'lchang, kanon qilganidek.
6. Boshqa ikonka kutubxonalari
Kanon faqat lucide'ni o'lchadi, quyidagilar uchun hajm raqami aytmaymiz. Paketlarni o'rnatib, tuzilmasini ko'rdik (2026-10-17):
| Kutubxona | Tuzilma | Nimaga e'tibor |
|---|---|---|
| react-icons 5.7.0 | ko'p to'plam bitta paketda: react-icons/fa, /md, /lu (Lucide), /tb (Tabler)… |
to'plamni subpath'dan import qiling |
| @heroicons/react 2.2.0 | Tailwind jamoasidan; /24/outline, /24/solid, /20/solid, /16/solid |
o'lchamga qarab alohida chizilgan |
| @tabler/icons-react 3.49.0 | 6 308 eksport, nomlar IconPencil kabi |
chiziq qalinligi — stroke prop |
| @iconify/react 6.0.2 | ko'plab to'plamlar "nomi" bilan: <Icon icon="mdi:home" /> |
ikonkani ishlash paytida api.iconify.design dan yuklaydi |
Iconify'ga alohida e'tibor: paket kodida api.iconify.design manzili bor — ikonka ma'lumoti tarmoqdan keladi. vazifalar ning CSP'sida connect-src tashqi manzilni to'sadi: ikonka chiqmaydi. Kanon tajribasida dotLottie va Rive ham xuddi shunday CDN'dan yuklashga uringan va to'silgan (kutubxonalar.txt). Iconify'da ikonkalarni oldindan bundle'ga qo'shish yo'li bor (oflayn rejim) — CSP'li loyihada aynan shu kerak.
Tanlov qoidasi oddiy: shadcn loyihalari standart bo'yicha lucide ishlatadi (components.json dagi "iconLibrary": "lucide", shadcn/ui I darsida). Bitta loyihada bitta ikonka to'plami — aks holda chiziq qalinligi va uslub aralashadi.
7. Vazifalar qadami: tugmalarda lucide ikonkalari (holat 08)
7.1 Nima o'zgaradi
Qatordagi amallar, «Qo'shish», hisobot va foydalanuvchi menyusi — 9 ta ikonka: Pencil, Trash2, Check, X, Plus, ChevronRight, Download, ChevronDown, LogOut. Hammasi — matn yonida, ya'ni bezak.
git switch -c feat/ikonkalar
npm i -E lucide-react@1.52.07.2 Fayllar
ui/Tugma.tsx — asos sinflariga inline-flex … gap-2 … [&_svg]:shrink-0 («Matn bilan bir qatorda» bo'limi). Kanon qatori 120 belgi — kanon kodi o'zgarmaydi:
const tugmaSinflari = cva(
- "tugma min-h-bosish rounded-burchak border px-3 py-2 disabled:opacity-60",
+ // 20/#08: inline-flex + gap — ikonka (svg) matn bilan bir qatorda
+ "tugma inline-flex min-h-bosish items-center justify-center gap-2 rounded-burchak border px-3 py-2 disabled:opacity-60 [&_svg]:shrink-0",komponentlar/AmalTugmasi.tsx — IKONKALAR xaritasi («icons[nom] tuzog'i» bo'limida to'liq) va chizish:
+import { Check, Pencil, Trash2, X } from "lucide-react";
+import type { LucideIcon } from "lucide-react";
...
export function AmalTugmasi({ ref, amal, matn, nomi, onClick }: Props) {
+ const Ikonka = IKONKALAR[amal];
return (
<Tugma
ref={ref}
data-amal={amal}
aria-label={`${matn}: ${nomi}`}
onClick={onClick}
>
+ {Ikonka !== undefined && <Ikonka size={16} />}
{matn}
</Tugma>Kanon izohi tanlovni o'zi tushuntiradi (qisqartirib): "har ikonka alohida komponent, nomi bilan import qilinadi: build faqat shu to'rttasini oladi. Ikonka — bezak: matn ko'rinib turadi, lucide o'zi aria-hidden="true" qo'yadi".
Qolgan joylar — bittadan qator:
// YangiVazifaForma.tsx
<Tugma type="submit" id="qoshish-tugma" variant="asosiy">
+ <Plus size={16} />
{pending ? "Qo'shilmoqda…" : "Qo'shish"}
// OddiyQator.tsx — havola matnidan keyin
Batafsil
+ <ChevronRight size={16} className="inline align-[-0.15em]" />
// BatafsilStatistika.tsx
<Tugma className="hisobot-tugma data-[state=open]:border-asosiy">
+ <Download size={16} />
Hisobotni yuklab olish
// FoydalanuvchiMenyusi.tsx
{foydalanuvchi.ism}
+ <ChevronDown size={16} />
...
<DropdownMenuItem onSelect={chiqish}>
+ <LogOut size={16} />
ChiqishChevronDown — menyu ochilishini bildiradi. Ekran o'quvchiga buni aria-haspopup="menu" allaqachon aytadi (Radix chuqur), shuning uchun ikonka bezak bo'lib qolaveradi.
7.3 e2e testi yiqildi — va bu yaxshi
Brauzer ssenariysining birinchi ishga tushishi: 159 dan 158 o'tdi. Yiqilgani — 13-qismdagi XSS tekshiruvi (XSS va DOM xavfsizligi). U hujum matnini ("><svg onload=alert(2)>) qo'shib, keyin sahifadagi barcha img va svg ni sanardi: 0 bo'lishi kerak. Endi tugmalarda bizning svg'larimiz bor!
Tuzatish (kanon e2e.mjs, o'zgarish 9):
// 20 (o'zgarish 9): ilovaning o'z ikonkalari
// (lucide-react, class="lucide")
tekshir("XSS: hech qanday alert/onerror bajarilmadi",
[ogohlar, await p1.$$eval("img, svg:not(.lucide)",
(l) => l.length)], [[], 0]);svg:not(.lucide) — "lucide sinfi bo'lmagan svg" (Mantiqiy pseudo-klasslar). Hujum matnidan yasalgan svg'da bu sinf yo'q — u baribir sanaladi. Tekshiruvning ma'nosi o'zgarmadi, faqat o'z ikonkalarimiz chiqarildi. Shundan keyin: 159 .
Saboq: test yiqilganda testni o'chirmaymiz — nega yiqilganini tushunamiz. Bu yerda ilova to'g'ri, test esa yangi haqiqatni bilmas edi.
7.4 Tekshiruvlar va commit
git add -A
git commitCommit (kanon): feat: tugmalarda lucide ikonkalari (bezak, aria-hidden). Diff: 8 fayl, +45 −1.
Haqiqiy natijalar (holat 08, tekshiruv-08.txt):
npm test— node:testpass 198, VitestTests 65 passed (65);lint,format:check,tip,knip—exit 0.- Birinchi yuklash JS gzip: 179.1 → 181.1 KB (+2.0, «Kanon o'lchovi» bo'limidagi qator).
- Brauzer: e2e 159 (o'zgarish 9 bilan), e2e-18 72, e2e-19 100, e2e-20 17 — hammasi . axe — 0 buzilish (
08-axe.txt).
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
axe button-name, Malika "tugma" deydi |
faqat ikonkali tugmada nom yo'q | tugmaga aria-label |
| "qalam Tahrirlash" eshitiladi | matn yonidagi ikonkaga aria-label |
bezak ikonkaga nom bermang |
| bundle +161 KB | icons[nom] |
aniq xarita yoki DynamicIcon |
| ikonka matndan yuqorida/pastda | inline svg tayanch chizig'ida | inline-flex items-center yoki vertical-align |
| tor tugmada ikonka qisilgan | flex shrink | shrink-0 |
| Iconify ikonkasi chiqmaydi, CSP xatosi | api.iconify.design ga connect-src |
oflayn to'plam |
| testda svg soni o'zgardi | o'z ikonkalaringiz | svg:not(.lucide) |
9. Mashqlar
Mashqlar kurs/mashqlar/20/08-ikonkalar/ da.
1-mashq (oson): props
To'ldiring:
- Ikonka shrift o'lchamiga ergashsin:
size= - 32 pikselli ikonkada chiziq ekranda 2 piksel qolsin: prop
- Lucide chiziq rangi standart bo'yicha:
stroke= - Matn yonidagi ikonkaga lucide qo'yadigan atribut:
Yechim
size="1em"—width/height1em bo'ladi,font-sizega ergashadi.absoluteStrokeWidth— lucidestroke-widthni o'lcham bo'yicha qayta hisoblaydi (32 pikselda 1.5).currentColor— ota elementningcolori.aria-hidden="true"— ekran o'quvchidan yashiradi;aria-labelbersangiz, qo'yilmaydi.
2-mashq (o'rta): telefondagi ikonka-tugmalar
«Bahor» buyurtmalar ro'yxatida har qatorda ikki kichik tugma: Pencil va Trash2, matnsiz. Ularga to'g'ri nom bering — taom nomi bilan. Konsol tugmalarning aria-label ini va ikonkaning aria-hidden ini chiqarsin.
import { useEffect } from "react";
import { Pencil, Trash2 } from "lucide-react";
const orders = ["Osh", "Manti"];
export default function App() {
useEffect(() => {
for (const b of document.querySelectorAll("button")) {
const svg = b.querySelector("svg");
const hidden = svg.getAttribute("aria-hidden");
console.log(b.getAttribute("aria-label"), "| svg:", hidden);
}
}, []);
return (
<ul>
{orders.map((o) => (
<li key={o}>
{o}{" "}
<button>
<Pencil size={16} />
</button>{" "}
<button>
<Trash2 size={16} />
</button>
</li>
))}
</ul>
);
}Kutilgan konsol:
Tahrirlash: Osh | svg: true
O'chirish: Osh | svg: true
Tahrirlash: Manti | svg: true
O'chirish: Manti | svg: trueIshora: «Faqat ikonkali tugma» bo'limi; kanon AmalTugmasi dagi `${matn}: ${nomi}` naqshi.
Yechim
import { useEffect } from "react";
import { Pencil, Trash2 } from "lucide-react";
const orders = ["Osh", "Manti"];
export default function App() {
useEffect(() => {
for (const b of document.querySelectorAll("button")) {
const svg = b.querySelector("svg");
const hidden = svg.getAttribute("aria-hidden");
console.log(b.getAttribute("aria-label"), "| svg:", hidden);
}
}, []);
return (
<ul>
{orders.map((o) => (
<li key={o}>
{o}{" "}
<button aria-label={`Tahrirlash: ${o}`}>
<Pencil size={16} />
</button>{" "}
<button aria-label={`O'chirish: ${o}`}>
<Trash2 size={16} />
</button>
</li>
))}
</ul>
);
}Konsolda:
Tahrirlash: Osh | svg: true
O'chirish: Osh | svg: true
Tahrirlash: Manti | svg: true
O'chirish: Manti | svg: trueNomga taom ham kirdi: Malika Tab bilan yurganda "Tahrirlash: Osh", "O'chirish: Osh" ni eshitadi — o'nta «O'chirish» dan qaysi biri ekanini biladi. Ikonkalar bezak bo'lib qoldi: nom tugmada, ikki marta o'qilmaydi.
3-mashq (qiyin): holat belgisi — ma'noli ikonka
Buyurtma holati jadvalda faqat ikonka bilan ko'rsatiladi: "paid" → CircleCheck («to'langan»), "waiting" → Clock («kutilmoqda»), noma'lum holat → HelpCircle («noma'lum holat»). Ikonka yolg'iz, ya'ni ma'noli: role="img" va aria-label bilan. StatusIcon komponentini yozing — xarita va zaxira bilan.
Ishora: «icons[nom] tuzog'i» bo'limidagi xarita; zaxira — ?? (??, ?. va mantiqiy tayinlash).
Osh: role=img, label=to'langan, hidden=null
Manti: role=img, label=kutilmoqda, hidden=null
Lag'mon: role=img, label=noma'lum holat, hidden=nullYechim
import { useEffect } from "react";
import { CircleCheck, Clock, HelpCircle } from "lucide-react";
const STATUS = {
paid: { Icon: CircleCheck, label: "to'langan" },
waiting: { Icon: Clock, label: "kutilmoqda" },
};
const UNKNOWN = { Icon: HelpCircle, label: "noma'lum holat" };
function StatusIcon({ status }) {
const { Icon, label } = STATUS[status] ?? UNKNOWN;
return <Icon size={16} role="img" aria-label={label} />;
}
const orders = [
{ name: "Osh", status: "paid" },
{ name: "Manti", status: "waiting" },
{ name: "Lag'mon", status: "returned" },
];
export default function App() {
useEffect(() => {
for (const li of document.querySelectorAll("li")) {
const svg = li.querySelector("svg");
console.log(
`${li.textContent.trim()}: role=${svg.getAttribute("role")},`,
`label=${svg.getAttribute("aria-label")},`,
`hidden=${svg.getAttribute("aria-hidden")}`,
);
}
}, []);
return (
<ul>
{orders.map((o) => (
<li key={o.name}>
{o.name} <StatusIcon status={o.status} />
</li>
))}
</ul>
);
}Konsolda:
Osh: role=img, label=to'langan, hidden=null
Manti: role=img, label=kutilmoqda, hidden=null
Lag'mon: role=img, label=noma'lum holat, hidden=nullSTATUS[status] ?? UNKNOWN — xaritada yo'q holat (returned) zaxiraga tushadi, ilova yiqilmaydi. role="img" qo'shildi, aria-hidden yo'q — ikonka ekran o'quvchiga "rasm, to'langan" bo'lib o'qiladi. Rang (yashil/sariq) qo'shsangiz ham, ma'no faqat rangda qolmasin — shakl va nom bor (WCAG: rang yagona belgi emas).
4-mashq: Vazifalar qadami
feat/ikonkalar branch'ida «Vazifalar qadami» bo'limidagi oltita faylni o'zgartiring, lucide-react@1.52.0 ni aniq versiya bilan o'rnating va e2e XSS tekshiruvini svg:not(.lucide) ga moslang. Keyin commit.
Yechim
Tekshiruv ro'yxati:
- DevTools Elements: har ikonka —
<svg class="lucide lucide-…" aria-hidden="true">, tugma matni vaaria-labeljoyida. - Tor oynada (360 px) ikonkalar qisilmagan, matn bilan markazda.
npm test— node 198, Vitest 65;npm run tip,lint,knip— toza.- Build: birinchi yuklash JS gzip taxminan +2 KB. Agar +160 KB atrofida bo'lsa — kodda
iconsimporti qolgan. - e2e: XSS tekshiruvi
svg:not(.lucide)bilan o'tadi; boshqa tekshiruvlar o'zgarmagan.
10. Real ishda
- Dizayn tizimlari — ikonka to'plami tizimning bir qismi: bitta kutubxona, bitta chiziq qalinligi, o'lchamlar shkalasi (16, 20, 24). Kod ko'rigida "nega boshqa to'plamdan ikonka?" — tez-tez beriladigan savol.
- Bundle tahlili — "nega bundle 160 KB o'sdi?" savolining ko'p uchraydigan javobi —
iconsyoki butun to'plam importi. Bundle tahlili vositalari buni darhol ko'rsatadi. - Intervyu savollari: "faqat ikonkali tugmani qanday accessible qilasiz?", "tree-shaking nima va qachon ishlamaydi?", "
aria-hiddenqachon kerak?".
Xulosa
- lucide-react: har ikonka — komponent (
<Pencil size={16} />),currentColorbilan matn rangini oladi. size,strokeWidth,absoluteStrokeWidth,size="1em"— o'lcham va chiziq; rang — CSS'dan.- Matn yonidagi ikonka — bezak (
aria-hidden, lucide o'zi qo'yadi); faqat ikonkali tugma — tugmagaaria-label; yolg'iz ma'noli ikonka —role="img"+aria-label. - Tekislash:
inline-flex items-center gap-2vashrink-0, matn ichida —vertical-align. - Nomli import +2.0 KB,
icons[nom]+161 KB: nomlar aniq xaritada yokiDynamicIconbilan. - Iconify ikonkalarni tarmoqdan oladi — CSP'li loyihada oflayn to'plam kerak.
Keyingi dars: Toast bildirishnomalar — sonner bilan toast.promise va action, aria-live qanday ishlaydi, toast qachon noto'g'ri tanlov, va nega vazifalar o'z toast'ida qoldi (CSP o'lchovi bilan).
Manbalar
- Lucide: React guide, Accessibility,
absoluteStrokeWidth, Dynamic icons — lucide.dev/guide/packages/lucide-react (1.52.0) - react-icons — react-icons.github.io; Heroicons — heroicons.com; Tabler Icons — tabler.io/icons; Iconify — iconify.design/docs
- W3C: Using ARIA
role="img"; WAI-ARIA APG: accessible names for buttons — w3.org/WAI/ARIA/apg - MDN:
vertical-align, SVGstroke-width— developer.mozilla.org
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!