Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Oyna qanday ochiladi
- 2.1 JavaScript'siz: invoker commands
- 2.2 Modal nima beradi
- 3. Orqa sahifa skrolli
- 3.1 Muammo
- 3.2 Uch qator
- 4. To'liq komponent
- 4.1 Tuzilish
- 4.2 «Bahor»: tezkor bron oynasi
- 4.3 Kodni o'qiymiz
- 5. Modal yoki popover?
- 6. «Bahor»ning komponentlar.css iga
- 7. Ko'p uchraydigan xatolar
- 7.1 :is() ichida psevdo-element
- 7.2 dialog { display: flex; }
- 7.3 Skroll bloklandi, sahifa "sakradi"
- 7.4 Butun oyna suriladi, tugmalar ko'rinmay qoladi
- 7.5 Sahifa doim bloklangan
- 7.6 Oyna ichida tugmalarning fokusi ko'rinmaydi
- 8. Mashqlar
- 1-mashq (oson): "Bronni bekor qilasizmi?"
- 2-mashq (o'rta): rasm to'liq ekranda
- 3-mashq (qiyin): o'ngdan chiqadigan filtr paneli
- 4-mashq: Portfolio qadami — "Ma'lumotlaringiz qanday ishlatiladi?"
- 9. Real ishda
- Xulosa
- Manbalar
CSS modal oyna: dialog, skroll bloklash va mobil "sheet"
Qisqacha: Modal oyna —
<dialog>, uni tugmacommand="show-modal"bilan JavaScript'siz ochadi. Oyna ochiqligida orqa sahifa surilmasligi uchun:html:has(dialog:modal) { overflow: hidden; }, skrollbar yo'qolganda sahifa sakramasligi uchun —html { scrollbar-gutter: stable; }, oyna ichidagi skroll sahifaga "o'tib ketmasligi" uchun —overscroll-behavior: contain. Uzun kontentda sarlavha va tugmalar joyida qoladi, faqat o'rtadagi qism suriladi. Telefonda oyna ekran pastiga yopishgan "sheet" bo'ladi.
Bu darsda
dialogni invoker commands bilan ochib-yopasiz va unga to'liq komponent tuzilishini berasiz: sarlavha, yopish tugmasi, kontent, harakatlar.- Orqa sahifa skrollini ikki "teshik"dan bloklaysiz va skrollbar sakrashini yo'q qilasiz.
- Uzun kontentli oynada faqat o'rtadagi qismni surilishini ta'minlaysiz.
- Telefonda oynani pastdan chiqadigan "sheet" ga aylantirasiz va harakatni kamaytirish rejimini hurmat qilasiz.
- Modal va popover orasidagi farqni bilib, qaysi birini qachon tanlashni aytasiz.
Oldin bilishingiz kerak: dialog: native modal oyna, Invoker commands, Top layer, Overflow va skroll konteynerlar.
1. Nega bu kerak?
O'tgan darsda forma komponentlarini yasadik. Endi ularni oynaga joylaymiz.
«Bahor» menyu sahifasi uzun: o'nlab taom. Mehmon pastda lag'monni ko'rdi va "Tezkor bron" tugmasini bosdi. Oyna ochildi, u ism va telefonni yozmoqchi — lekin barmog'i oyna yonidagi qorong'i joyga tegdi va sahifa suriladi. Oyna yopilgach, mehmon menyuning boshqa joyida — lag'monni yana qidiradi. Telefonda yana bir muammo: oyna ichidagi formani pastga surganda, oxiriga yetgach, orqadagi sahifa surila boshlaydi.
Top layer darsida oynani bezagan edik: parda, soya, ochilish animatsiyasi. Bu dars — to'liq komponent. Unda oldingi darslarning va'dalari bajariladi:
- Overflow darsida: "modal oyna ochilganda orqadagi sahifani to'xtatish" va
overscroll-behaviorning modal oynadagi o'rni. - Top layer darsida: to'liq modal komponenti.
- Vizual ierarxiya darsida: "Bronni bekor qilish" uchun tasdiqlash oynasi.
2. Oyna qanday ochiladi
2.1 JavaScript'siz: invoker commands
Invoker commands darsidan eslaymiz: tugmaga commandfor="oyna-id" va command="show-modal" yoziladi — brauzer dialog ni modal qilib ochadi. JavaScript'da xuddi shu ish dialog.showModal() metodi bilan qilinadi (09-qismda ko'rasiz). Yopish uchun ikki yo'l:
command="close"li tugma — oynani yopadi;<form method="dialog">ichidagi tugma — formani yubormaydi, oynani yopadi (dialog).
Invoker commands — yangi Baseline: 2025-yil dekabridan hamma asosiy brauzerda (Chrome 135, Firefox 144, Safari 26.2). Eski brauzerda tugma hech narsa qilmaydi. Muhim sahifada zaxira kerak — bir qator JavaScript (09-qismda). «Bahor»da oyna qo'shimcha qulaylik: tugma ishlamasa, "Tezkor bron" o'rniga oddiy havola bron/ sahifasiga olib boradi.
2.2 Modal nima beradi
command="show-modal" bilan ochilgan oyna — modal: u top layer'da (Top layer) va orqadagi sahifa inert — bosib ham, Tab bilan ham unga yetib bo'lmaydi. Esc oynani yopadi. Brauzer ochilganda fokusni oyna ichiga olib kiradi, yopilganda — ochgan tugmaga qaytaradi.
Lekin bitta narsani brauzer bermaydi: orqa sahifa skrolli bloklanmaydi. Tekshiramiz.
3. Orqa sahifa skrolli
3.1 Muammo
<style>
body {
margin: 0;
font-family: system-ui, sans-serif;
background: #fffaf0;
}
.uzun {
block-size: 150rem;
background: repeating-linear-gradient(#fffaf0 0 4rem,
#e8f1ec 4rem 8rem);
}
dialog {
max-block-size: 12rem;
border: 0;
border-radius: 1rem;
}
dialog::backdrop { background: #1f2a2499; }
.matn { block-size: 30rem; }
</style>
<p>
<button type="button" commandfor="oyna" command="show-modal">
❌ Oynani ochish
</button>
</p>
<div class="uzun"></div>
<dialog id="oyna">
<button type="button" commandfor="oyna" command="close">
Yopish
</button>
<p class="matn">Oyna ichidagi uzun matn. Uni pastga suring.</p>
</dialog>Nimaga qarang: oynani oching. Endi sichqonchani oyna yonidagi qorong'i pardaga olib boring va g'ildirakni aylantiring — orqadagi yo'l-yo'l sahifa suriladi. Keyin oyna ichida aylantiring: matn oxirigacha surilgach, g'ildirak orqadagi sahifani sura boshlaydi.
Chrome'da o'lchadik (sahifa 800 × 600): pardada 400 piksel aylantirish sahifani 400 pikselga surdi. Oyna ichida esa oyna kontenti oxiriga yetgach, qolgan aylantirish sahifaga o'tdi — sahifa 1 200 pikselga surildi. Ikki "teshik":
- Parda ustidagi skroll — parda sahifaning bir qismi emas, lekin skroll hodisasi hujjatga boradi.
- Skroll zanjiri (scroll chaining) — ichki skroll konteyneri oxiriga yetgach, surish uning otasiga "uzatiladi" (Overflow).
3.2 Uch qator
Ikki teshik va ularni yopadigan qatorlar — bir rasmda:
flowchart TD
A[Mehmon g'ildirakni aylantiradi] --> B{Qayerda?}
B -->|Pardada| C[Hujjat suriladi]
B -->|Oyna ichida| D[Oyna kontenti suriladi]
D -->|Oxiriga yetdi| E[Zanjir: sahifa suriladi]
C -.->|to'xtatadi| F["html:has(dialog:modal)<br>overflow: hidden"]
E -.->|uzadi| G["overscroll-behavior:<br>contain"]/* 1. Oyna ochiqligida sahifa surilmaydi */
html:has(dialog:modal) {
overflow: hidden;
}
/* 2. Skrollbar yo'qolganda sahifa sakramasin */
html {
scrollbar-gutter: stable;
}
/* 3. Oyna ichidagi skroll sahifaga o'tmasin */
dialog {
overscroll-behavior: contain;
}1. html:has(dialog:modal) — "ichida modal oyna bor hujjat" (Mantiqiy pseudo-klasslar). Oyna ochilishi bilan qoida ishlaydi, yopilishi bilan o'zi bekor bo'ladi — JavaScript kerak emas. Nega dialog[open] emas, :modal? [open] modal bo'lmagan oynada ham (masalan, sahifa ichidagi oddiy dialog open) mos keladi — o'shanda sahifani bloklash xato. :modal faqat show-modal bilan ochilganiga.
Ko'p maqolalarda body:has(dialog[open]) { overflow: hidden; } ni uchratasiz. body ga yozsa ham ishlaydi — html da overflow bo'lmasa, body niki butun oynaga o'tadi (Overflow). Chrome'da ikkalasini sinadik — natija bir xil. Biz html ni tanladik: skroll aynan unda.
2. scrollbar-gutter: stable — Overflow darsida ko'rgan xususiyat: skrollbar uchun joy doim ajratilgan. Busiz nima bo'ladi? Kompyuterda skrollbar ~15 piksel joy oladi. overflow: hidden uni olib tashlaydi — sahifa 15 pikselga kengayadi va markazdagi kontent o'ngga "sakraydi". Chrome'da o'lchadik: markazlangan blok 192.5 dan 200 pikselga siljidi. scrollbar-gutter: stable bilan — 192.5 da qoldi. Telefonda skrollbar kontent ustida chiziladi, u yerda bu muammo yo'q.
scrollbar-gutter — yangi Baseline: 2024-yil dekabridan. Eski brauzerda sakrash qoladi — noqulay, lekin hech narsa buzilmaydi. Narxi ham bor: qisqa, skrollsiz sahifada ham o'ngda bo'sh tasma turadi.
3. overscroll-behavior: contain — skroll zanjirini uzadi (Overflow). Savol tug'iladi: birinchi qator sahifani allaqachon to'xtatgan-ku, bu nega kerak? Telefonda oxiriga yetgan ro'yxat "cho'zilish" effektini va sahifani yangilashni (pull-to-refresh — ekranni pastga tortib yangilash) ham ishga tushiradi. contain ularni ham to'xtatadi. Chrome'da alohida tekshirdik: faqat contain bilan (qulfsiz) oyna ichidagi skroll sahifaga o'tmadi. Lekin parda ustidagi skroll sahifani surdi — shuning uchun ikkalasi ham kerak. Bu xususiyat rasman Baseline emas: Baseline ma'lumotiga ko'ra to'liq qo'llash Chrome, Edge va Firefox'da; Safari ro'yxatda yo'q (sababini Overflow darsida ko'rgan edik).
Tekshirib ko'ring: Sahifada ikkita oyna bor: biri modal ("Tezkor bron"), ikkinchisi sahifa ichidagi
<dialog open>("Bugungi aksiya", yopish tugmasi bilan). Stajyorhtml:has(dialog[open]) { overflow: hidden; }yozdi. Mehmon nimani sezadi?
Javob
Sahifa umuman surilmaydi — "Bugungi aksiya" oynasi sahifa ochilgandan beri open, demak shart doim bajariladi. Mehmon aksiyani yopmaguncha menyuni o'qiy olmaydi va sababini tushunmaydi. :modal bilan faqat haqiqiy modal oyna sahifani bloklaydi.
4. To'liq komponent
4.1 Tuzilish
Professional modal oyna to'rt qismdan iborat:
| Qism | Nima | Qoida |
|---|---|---|
| Sarlavha | Oyna nomi (h2) |
aria-labelledby bilan oynaga ulanadi |
| Yopish tugmasi | "" belgisi | Yuqori o'ng burchakda — mehmon uni shu yerda qidiradi |
| Kontent | Forma, matn | Uzun bo'lsa, faqat shu qism suriladi |
| Harakatlar | "Bekor qilish", "Yuborish" | Pastda, asosiy harakat oxirida (Tugmalar tizimi) |
Uzun formada sarlavha va tugmalar doim ko'rinib tursin: mehmon formaning o'rtasida "Yuborish" ni qidirib pastga surmasin.
4.2 «Bahor»: tezkor bron oynasi
<style>
:root {
--rang-fon: #fffaf0;
--rang-matn: #1f2a24;
--rang-asosiy: #2f6b3a;
--rang-asosiy-toq: #1a5d1a;
--rang-ikkinchi: #5b6660;
--rang-chegara: #c9d6cf;
}
html { scrollbar-gutter: stable; }
html:has(dialog:modal) { overflow: hidden; }
body {
margin: 0;
padding: 1rem;
font-family: system-ui, sans-serif;
background: var(--rang-fon);
color: var(--rang-matn);
}
button {
min-block-size: 2.75rem;
padding-inline: 1.25em;
border: 2px solid var(--rang-asosiy);
border-radius: 0.5rem;
background: var(--rang-asosiy);
color: var(--rang-fon);
font: inherit;
font-weight: 600;
}
button[command="close"] {
background: transparent;
color: var(--rang-asosiy);
}
:focus-visible {
outline: 3px solid var(--rang-asosiy-toq);
outline-offset: 2px;
box-shadow: 0 0 0 2px var(--rang-fon);
}
dialog :focus-visible { box-shadow: 0 0 0 2px white; }
/* Oyna */
dialog {
--_kirish: 0 1.5rem;
box-sizing: border-box;
inline-size: min(100% - 2rem, 30rem);
max-block-size: calc(100% - 2rem);
padding: 0;
border: 0;
border-radius: 1rem;
background: white;
color: var(--rang-matn);
box-shadow: 0 12px 32px #1f2a2440;
overscroll-behavior: contain;
}
dialog[open] {
display: flex;
flex-direction: column;
}
dialog::backdrop {
background: #1f2a2499;
backdrop-filter: blur(3px);
}
dialog form {
display: flex;
flex-direction: column;
min-block-size: 0;
}
dialog header, dialog footer {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
}
dialog header {
justify-content: space-between;
border-block-end: 1px solid var(--rang-chegara);
}
dialog h2 { margin: 0; font-size: 1.25rem; }
dialog header button {
inline-size: 2.75rem;
padding: 0;
border-color: transparent;
}
dialog form > div {
display: grid;
gap: 1rem;
padding: 1rem;
overflow-y: auto;
overscroll-behavior: contain;
}
dialog label { display: grid; gap: 0.25rem; font-weight: 600; }
dialog input {
min-block-size: 2.75rem;
padding-inline: 0.75em;
border: 2px solid var(--rang-ikkinchi);
border-radius: 0.5rem;
font: inherit;
}
dialog footer {
justify-content: end;
border-block-start: 1px solid var(--rang-chegara);
}
/* Telefon: pastdan chiqadigan sheet */
@media (width < 40rem) {
dialog {
--_kirish: 0 100%;
inline-size: 100%;
max-inline-size: 100%;
margin-block-end: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
}
/* Ochilish va yopilish */
dialog, dialog::backdrop {
transition:
opacity 0.25s ease-out,
display 0.25s allow-discrete,
overlay 0.25s allow-discrete;
opacity: 0;
}
dialog[open], dialog[open]::backdrop { opacity: 1; }
@starting-style {
dialog[open], dialog[open]::backdrop { opacity: 0; }
}
@media (prefers-reduced-motion: no-preference) {
dialog {
translate: var(--_kirish);
transition:
opacity 0.25s ease-out,
translate 0.25s ease-out,
display 0.25s allow-discrete,
overlay 0.25s allow-discrete;
}
dialog[open] { translate: 0 0; }
@starting-style {
dialog[open] { translate: var(--_kirish); }
}
}
</style>
<h1>Menyu</h1>
<p>Osh — 35 000, lag'mon — 28 000, manti — 30 000 so'm.</p>
<button type="button" commandfor="bron-oyna" command="show-modal">
Tezkor bron
</button>
<dialog id="bron-oyna" aria-labelledby="bron-sarlavha"
closedby="any">
<form action="https://example.com/bron" method="post">
<header role="none">
<h2 id="bron-sarlavha">Tezkor bron</h2>
<button type="button" commandfor="bron-oyna" command="close"
aria-label="Yopish">✕</button>
</header>
<div>
<label>Ismingiz
<input name="ism" autocomplete="name" required autofocus>
</label>
<label>Telefon raqamingiz
<input name="telefon" type="tel" autocomplete="tel"
required>
</label>
<label>Sana
<input name="sana" type="date" required>
</label>
<label>Mehmonlar soni
<input name="odam" type="number" min="1" max="20" value="2">
</label>
<label>Qo'shimcha istaklar (ixtiyoriy)
<input name="izoh">
</label>
</div>
<footer role="none">
<button type="button" commandfor="bron-oyna" command="close">
Bekor qilish
</button>
<button type="submit">Yuborish</button>
</footer>
</form>
</dialog>Nimaga qarang: "Tezkor bron" ni bosing. Kompyuterda (768, 1280) oq oyna ekran o'rtasida, 24 piksel pastdan ko'tarilib paydo bo'ladi; orqa sahifa qoraygan va xira. Yuqorida sarlavha va "", pastda ikki tugma — ular joyida turadi. O'rtadagi forma natija oynasiga sig'maydi: uni suring — faqat maydonlar suriladi. Kursor birinchi maydonda ("Ismingiz") yonib turibdi. Telefonda (360) oyna ekranning pastiga yopishgan, butun enda, faqat tepa burchaklari yumaloq — u pastdan suzib chiqadi.
Uch yo'l bilan yoping: "", "Bekor qilish", Esc — va pardaga bosish. Telefon enida o'ngda ingichka bo'sh tasma ko'rinishi mumkin — bu kompyuter skrollbari uchun ajratilgan joy (scrollbar-gutter, pastda). Haqiqiy telefonda skrollbar kontent ustida chiziladi, tasma bo'lmaydi.
Chrome'da tekshirdik (natija oynasi 400 piksel balandlikda):
- oyna balandligi 368 piksel (
100% - 2rem); kontent qismi 230 piksel, ichidagi forma esa 469 piksel — faqat shu qism surildi, sarlavha va tugmalar joyida qoldi; - ochilganda fokus
autofocusli "Ismingiz" maydonida; - pardaga bosish oynani yopdi (
closedby="any"), fokus "Tezkor bron" tugmasiga qaytdi; - telefon enida oyna pastki cheti natija oynasining pastki chetiga to'liq tegdi.
4.3 Kodni o'qiymiz
Tuzilish va skroll:
dialog[open] { display: flex; flex-direction: column; }— faqat ochiq holatda (Top layer darsidagidisplaytuzog'i). Yopiq oynagadisplaybersangiz, u sahifada ko'rinib qoladi.max-block-size: calc(100% - 2rem)— top layer'da "100%" — ekran balandligi. Oyna ekrandan hech qachon chiqmaydi.dvhham ishlatsa bo'ladi, lekin%bu yerda yetarli va telefon manzil qatorining o'zgarishiga ham moslashadi.formva ichkidivdamin-block-size: 0,overflow-y: auto— flex elementi kontentidan kichrayishi uchunmin-block-size: 0shart (min-width: 0). Busiz forma oynani cho'zib yuborardi va oyna o'zi surilardi — sarlavha va tugmalar bilan birga.overscroll-behavior: containikki joyda: oynaning o'zida va surilayotgan qismda — zanjir qayerdan boshlanmasin, sahifaga yetmaydi.header role="none"vafooter role="none"—dialogHTML'da alohida bo'lim hisoblanmaydi. Shuning uchun ichidagiheadersahifa header'i kabi ikkinchibannerlandmark'iga,footeresa ikkinchicontentinfoga aylanadi — Malika ekran o'quvchisida ikkita "banner" eshitadi (Sahifa landmarklari).role="none"landmark rolini o'chiradi; element ham,dialog :is(header, footer)selektori ham qoladi.
Fokus va yopish:
autofocus— oyna ochilganda fokus qayerga tushishini aytadi. Busiz brauzer birinchi fokuslanadigan elementni tanlaydi — Chrome'da tekshirdik: bu "" tugmasi bo'ldi. Forma oynasida mehmon darhol yozishni boshlasin; tasdiqlash oynasida esa — xavfsiz tugma (1-mashq).- "" —
aria-label="Yopish"bilan (Tugmalar tizimi dagi faqat ikonkali tugma), 44 × 44 piksel. closedby="any"— pardaga bosganda yopish. U Baseline emas: Baseline ma'lumotiga ko'ra Chrome, Edge va Firefox'da bor, Safari'da yo'q. Safari atributni e'tiborsiz qoldiradi — "", "Bekor qilish" vaEscbaribir ishlaydi. Forma oynasida ehtiyot bo'ling: mehmon yarim yozilgan formani tasodifan pardaga bosib yo'qotishi mumkin. Uzun forma uchunclosedby="closerequest"(faqatEscva tugmalar) xavfsizroq.
Telefon va harakat:
- Sheet —
margin-block-end: 0: brauzer oynanimargin: autobilan markazlaydi (Top layer), pastki margin'ni nolga tushirsak, oyna pastga yopishadi. Pastki burchaklar to'g'ri — ekran cheti bilan qo'shiladi. Bosh barmoq yetadigan joy (UI/UX asoslari). --_kirish— bitta ichki o'zgaruvchi harakat yo'nalishini beradi: kompyuterda0 1.5rem(biroz pastdan), telefonda0 100%(to'liq pastdan). Animatsiya qoidasi bitta.- Harakat kamaytirilganda faqat
opacityqoladi:translateno-preferenceichida (harakat etikasi). @starting-style— yangi Baseline (2024-yil avgust);overlay— faqat Chrome va Edge'da (Top layer). Ishlamagan joyda oyna animatsiyasiz ochiladi. Pardadagibackdrop-filterham yangi Baseline (2024-yil sentabr) — «Bahor» faylida u@supportsichida: xiralash bo'lmasa, rangli parda qoladi.
Maslahat: iPhone'da sheet'ning pastida "uy chizig'i" bor. Tugmalar unga yopishmasligi uchun:
dialog footer { padding-block-end: max(0.75rem, env(safe-area-inset-bottom)); }(Mobil qurilma tuzoqlari).
Tekshirib ko'ring: Stajyor
dialog form > divdanmin-block-size: 0ni o'chirdi (formdagisini ham). Uzun formali oynada nima o'zgaradi?
Javob
Endi butun oyna suriladi: sarlavha va "Yuborish" tugmasi forma bilan birga ko'rinmay ketadi. Flex elementi sukut bo'yicha kontentidan kichraymaydi — forma o'z balandligicha (469 piksel) cho'ziladi. min-block-size: 0 unga oyna balandligiga sig'ib, ortiqchasini ichida surishga ruxsat beradi.
5. Modal yoki popover?
Ikkalasi ham top layer'da, ikkalasi ham JavaScript'siz ochiladi. Farq — orqa sahifa bilan munosabatda:
Modal dialog |
Popover | |
|---|---|---|
| Orqa sahifa | Inert — bosib ham, Tab bilan ham bo'lmaydi |
Ishlayveradi |
| Tashqariga bosish | Sukut bo'yicha yopmaydi (closedby="any" bilan — yopadi) |
Yopadi (auto turida) |
| Skroll | Bloklash kerak (shu dars) | Bloklanmaydi |
| Qachon | Mehmon javob berishi kerak: forma, tasdiqlash | Yordamchi ma'lumot: menyu, maslahat, "Ish vaqti" |
Qoida: agar oyna ochiqligida mehmon sahifaning boshqa joyi bilan ishlasa ham mantiqiy bo'lsa — popover. Agar avval oynadagi savolga javob berish kerak bo'lsa — modal. Navbar darsidagi mobil menyu — popover: mehmon uni yopmasdan ham orqaga qaytishi mumkin.
Tekshirib ko'ring: "Ish vaqti" haqidagi qisqa ma'lumot oynasi va "Buyurtmani tasdiqlaysizmi?" oynasi. Qaysi biri modal, qaysi biri popover?
Javob
"Ish vaqti" — popover: mehmon uni o'qib turib ham sahifa bilan ishlashi mumkin, hech narsaga javob berishi shart emas. "Buyurtmani tasdiqlaysizmi?" — modal: mehmon avval "ha" yoki "yo'q" deyishi kerak, orqa sahifa shu paytda to'xtab turadi.
6. «Bahor»ning komponentlar.css iga
/* === Modal oyna (Modal oyna va skroll bloklash darsi) === */
dialog {
--_kirish: 0 1.5rem;
box-sizing: border-box;
inline-size: min(100% - 2rem, 30rem);
max-block-size: calc(100% - 2rem);
padding: 0;
border: 0;
border-radius: var(--burchak-m);
background: var(--rang-yuza);
color: var(--rang-matn);
box-shadow: var(--soya-3);
overscroll-behavior: contain;
}
dialog[open] {
display: flex;
flex-direction: column;
}
dialog::backdrop {
background: color-mix(in oklch, var(--rang-matn) 60%, transparent);
}
@supports (backdrop-filter: blur(1px)) {
dialog::backdrop {
backdrop-filter: blur(3px);
}
}
dialog form {
display: flex;
flex-direction: column;
min-block-size: 0;
}
dialog :is(header, footer) {
display: flex;
align-items: center;
gap: var(--masofa-xs);
padding: var(--masofa-s) var(--masofa-m);
}
dialog header {
justify-content: space-between;
border-block-end: 1px solid
color-mix(in oklch, var(--rang-asosiy) 25%, var(--rang-yuza));
}
dialog footer {
justify-content: end;
padding-block-end:
max(var(--masofa-s), env(safe-area-inset-bottom));
border-block-start: 1px solid
color-mix(in oklch, var(--rang-asosiy) 25%, var(--rang-yuza));
}
dialog form > div {
display: grid;
gap: var(--masofa-m);
padding: var(--masofa-m);
overflow-y: auto;
overscroll-behavior: contain;
}
@media (width < 40rem) {
dialog {
--_kirish: 0 100%;
inline-size: 100%;
max-inline-size: 100%;
margin-block-end: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
}
/* Halqaning ichki chizig'i — oyna fonida */
dialog :focus-visible {
box-shadow: 0 0 0 2px var(--rang-yuza);
}
/* Ochilish/yopilish: shaffoflik doim, siljish — ruxsat bo'lsa */
dialog,
dialog::backdrop {
opacity: 0;
transition:
opacity 0.25s ease-out,
display 0.25s allow-discrete,
overlay 0.25s allow-discrete;
}
dialog[open],
dialog[open]::backdrop {
opacity: 1;
}
@starting-style {
dialog[open],
dialog[open]::backdrop {
opacity: 0;
}
}
@media (prefers-reduced-motion: no-preference) {
dialog {
translate: var(--_kirish);
transition:
opacity 0.25s ease-out,
translate 0.25s ease-out,
display 0.25s allow-discrete,
overlay 0.25s allow-discrete;
}
dialog[open] {
translate: 0 0;
}
@starting-style {
dialog[open] {
translate: var(--_kirish);
}
}
}asos.css ga — sahifa darajasidagi ikki qoida:
html {
scrollbar-gutter: stable;
}
html:has(dialog:modal) {
overflow: hidden;
}- Tugmalar — Tugmalar tizimi dan: "" —
class="tugma shaffof belgi"(ichida.ikonkabilani-yopishbelgisi, SVG va ikonka tizimi), "Bekor qilish" —tugma shaffof, "Yuborish" —tugma. Oyna ichidagiheadervafooter—role="none"bilan (ikkinchi landmark bo'lmasin). Maydonlar — Forma komponentlari dan. Fokus halqasi —asos.cssdagi umumiy ikki rangli qoida (CSS'da accessibility). - Top layer darsidagi «Bahor» oynasi ("Bronni bekor qilasizmi?") shu qoidalarni oladi — uning eski alohida stillari endi keraksiz.
- Shaffoflik qoidalarida
dialog, dialog::backdrop— oddiy vergulli ro'yxat,:is()emas. Sababi — «Ko'p uchraydigan xatolar» dagi birinchi tuzoq. dialog :focus-visible— umumiy halqaning ichki chizig'i--rang-fon(qaymoq), oyna foni esa--rang-yuza(oq). Oyna ichida chiziq oyna foniga mos bo'lsin.
7. Ko'p uchraydigan xatolar
7.1 :is() ichida psevdo-element
:is(dialog, dialog::backdrop) { opacity: 0; } — oyna shaffof bo'ladi, parda esa yo'q. :is() ichida psevdo-element yozilmaydi, lekin :is() "kechirimli" ro'yxat: yaroqsiz qismni jimgina tashlaydi, qolgani ishlaydi (Mantiqiy pseudo-klasslar). Chrome'da tekshirdik: dialog qoidani oldi, ::backdrop — yo'q. Xato xabari ham chiqmaydi. Tuzatish: oddiy vergulli ro'yxat — dialog, dialog::backdrop { ... }.
7.2 dialog { display: flex; }
Yopiq oyna sahifada ko'rinib qoladi. Tuzatish: dialog[open].
7.3 Skroll bloklandi, sahifa "sakradi"
overflow: hidden skrollbarni olib tashladi. Tuzatish: html { scrollbar-gutter: stable; }.
7.4 Butun oyna suriladi, tugmalar ko'rinmay qoladi
Oynada overflow: auto, ichki qism esa cho'ziladi. Tuzatish: oyna — flex ustun, ichki qism — min-block-size: 0; overflow-y: auto.
7.5 Sahifa doim bloklangan
html:has(dialog[open]) — sahifadagi oddiy (modal emas) ochiq oyna ham sahifani to'xtatadi. Tuzatish: :modal.
7.6 Oyna ichida tugmalarning fokusi ko'rinmaydi
Oq oyna foni ustida brauzerning ko'k halqasi yoki fon rangidagi ichki chiziq ko'rinmaydi. Tuzatish: halqaning ichki chizig'i oynaning foniga mos bo'lsin — «Bahor»da --rang-yuza: dialog :focus-visible { box-shadow: 0 0 0 2px var(--rang-yuza); }.
8. Mashqlar
Mashqlarni kurs/mashqlar/06/ papkasida bajaring.
1-mashq (oson): "Bronni bekor qilasizmi?"
Vizual ierarxiya darsidagi va'da: bron sahifasida "Bronni bekor qilish" — qizg'ish matnli havola ko'rinishidagi tugma. U tasdiqlash oynasini ochsin: sarlavha, bir gap, ikki tugma — "Yo'q, qolsin" (xavfsiz, fokus shunda) va "Ha, bekor qilish" (xavfli variant). Pardaga bosish oynani yopmasin.
Ishora: xavfli tugma — Tugmalar tizimi dagi .xavfli g'oyasi. Fokus — autofocus. Pardaga bosish — closedby atributi yo'q bo'lsa, modal oyna faqat Esc va tugmalar bilan yopiladi.
Yechim
<style>
body { font-family: system-ui, sans-serif; background: #fffaf0; }
button {
min-block-size: 2.75rem;
padding-inline: 1.25em;
border: 2px solid transparent;
border-radius: 0.5rem;
font: inherit;
font-weight: 600;
}
.bekor-havola {
background: transparent;
color: oklch(52% 0.19 27);
text-decoration: underline;
}
dialog {
inline-size: min(100% - 2rem, 24rem);
padding: 1.5rem;
border: 0;
border-radius: 1rem;
background: white;
}
dialog::backdrop { background: #1f2a2499; }
dialog h2 { margin-block-start: 0; }
dialog form {
display: flex;
flex-wrap: wrap;
justify-content: end;
gap: 0.5rem;
}
button[value="yoq"] {
border-color: #2f6b3a;
background: transparent;
color: #2f6b3a;
}
button[value="ha"] {
background: oklch(52% 0.19 27);
color: #fffaf0;
}
:focus-visible {
outline: 3px solid #1a5d1a;
outline-offset: 2px;
box-shadow: 0 0 0 2px white;
}
</style>
<p>Bron: 4-stol, bugun 19:00.</p>
<button class="bekor-havola" type="button" commandfor="bekor"
command="show-modal">Bronni bekor qilish</button>
<dialog id="bekor" aria-labelledby="bekor-sarlavha">
<h2 id="bekor-sarlavha">Bronni bekor qilasizmi?</h2>
<p>4-stol bugun 19:00 ga band qilingan edi.</p>
<form method="dialog">
<button type="submit" value="yoq" autofocus>
Yo'q, qolsin</button>
<button type="submit" value="ha">Ha, bekor qilish</button>
</form>
</dialog>Oynani oching: fokus "Yo'q, qolsin" da — tasodifiy Enter bronni saqlab qoladi. Pardaga bosing — oyna yopilmaydi: qaytarib bo'lmaydigan amalda mehmon aniq javob bersin. .bekor-havola — namoyish class'i. Halqaning ichki chizig'i oq — oyna foni oq bo'lgani uchun («Oyna ichida tugmalarning fokusi ko'rinmaydi» xatosi). Yuborilgan qiymatni (ha/yoq) o'qib, bronni haqiqatan bekor qilish — server yoki JavaScript ishi.
2-mashq (o'rta): rasm to'liq ekranda
Menyu sahifasida taom rasmini bosganda u to'liq ekranli modal oynada ochilsin: qora parda, rasm ekranga sig'adigan eng katta o'lchamda (nisbati buzilmasin), yuqori o'ng burchakda "". Sahifa orqada surilmasin.
Ishora: oynaning o'zi butun ekran: inline-size: 100%; block-size: 100%; max-inline-size: none; max-block-size: none; margin: 0. Rasm — object-fit: contain (Rasm va media); natijada rasm o'rniga SVG.
Yechim
<style>
html:has(dialog:modal) { overflow: hidden; }
body { font-family: system-ui, sans-serif; background: #fffaf0; }
.kichik { inline-size: 10rem; border-radius: 0.5rem; }
#rasm {
box-sizing: border-box;
inline-size: 100%;
block-size: 100%;
max-inline-size: none;
max-block-size: none;
margin: 0;
padding: 3.5rem 1rem 1rem;
border: 0;
background: #000000e6;
}
#rasm[open] { display: grid; place-items: center; }
#rasm::backdrop { background: transparent; }
#rasm svg {
inline-size: 100%;
block-size: 100%;
}
#rasm button {
position: absolute;
inset-block-start: 0.5rem;
inset-inline-end: 0.5rem;
inline-size: 2.75rem;
block-size: 2.75rem;
border: 2px solid #fffaf0;
border-radius: 50%;
background: transparent;
color: #fffaf0;
font: inherit;
}
:focus-visible {
outline: 3px solid #8fd19e;
outline-offset: 2px;
}
</style>
<button type="button" commandfor="rasm" command="show-modal"
aria-label="Osh rasmini kattalashtirish">
<svg class="kichik" viewBox="0 0 160 100" aria-hidden="true">
<rect width="160" height="100" fill="#e9dcc3" />
<ellipse cx="80" cy="52" rx="44" ry="20" fill="#f2c46d" />
</svg>
</button>
<dialog id="rasm" aria-label="Osh rasmi">
<button type="button" commandfor="rasm" command="close"
aria-label="Yopish">✕</button>
<svg viewBox="0 0 160 100" role="img" aria-label="Osh">
<rect width="160" height="100" fill="#e9dcc3" />
<ellipse cx="80" cy="52" rx="44" ry="20" fill="#f2c46d" />
</svg>
</dialog>Kichik rasmni bosing: butun natija oynasi qora, o'rtada rasm — eni yoki bo'yi bo'yicha sig'adigan eng katta o'lchamda, nisbati o'zgarmagan (SVG'ning viewBox i object-fit: contain kabi ishlaydi). "" yuqori o'ng burchakda, qora fonda och halqa bilan. Pardani shaffof qildik — oynaning o'zi butun ekranni qoplaydi. #rasm — id selektori, faqat mashq uchun.
3-mashq (qiyin): o'ngdan chiqadigan filtr paneli
Menyu sahifasida "Filtr" tugmasi o'ngdan chiqadigan modal panel ochsin: "Achchiq", "Vegetarian", "Yangi" checkbox'lari va pastda "Qo'llash" tugmasi. Navbar darsidagi popover panelidan farqi: panel ochiqligida orqa sahifa inert va surilmaydi.
- Panel butun balandlikda, eni
min(20rem, 85%), o'ng chetda. - Kompyuterda ham, telefonda ham bir xil.
- Harakat kamaytirilganda siljish yo'q.
Ishora: margin-inline-end: 0, margin-block: 0, block-size: 100%, max-block-size: none — markazlashni bekor qilish (Top layer). Siljish — --_kirish: 100% 0.
Yechim
<style>
html:has(dialog:modal) { overflow: hidden; }
body { font-family: system-ui, sans-serif; background: #fffaf0; }
#filtr {
box-sizing: border-box;
inline-size: min(20rem, 85%);
block-size: 100%;
max-block-size: none;
margin-block: 0;
margin-inline-end: 0;
padding: 1rem;
border: 0;
background: white;
overscroll-behavior: contain;
}
#filtr[open] {
display: flex;
flex-direction: column;
gap: 1rem;
}
#filtr::backdrop { background: #1f2a2466; }
#filtr fieldset { border: 0; padding: 0; }
#filtr form {
display: flex;
flex: 1;
flex-direction: column;
}
#filtr button[type="submit"] { margin-block-start: auto; }
@media (prefers-reduced-motion: no-preference) {
#filtr {
translate: 100% 0;
transition:
translate 0.25s ease-out,
display 0.25s allow-discrete,
overlay 0.25s allow-discrete;
}
#filtr[open] { translate: 0 0; }
@starting-style {
#filtr[open] { translate: 100% 0; }
}
}
button { min-block-size: 2.75rem; font: inherit; }
</style>
<button type="button" commandfor="filtr" command="show-modal">
Filtr
</button>
<dialog id="filtr" aria-labelledby="filtr-sarlavha">
<h2 id="filtr-sarlavha">Filtr</h2>
<form method="dialog">
<fieldset>
<legend>Taomlar</legend>
<p><input id="f1" type="checkbox"><label for="f1">
Achchiq</label></p>
<p><input id="f2" type="checkbox"><label for="f2">
Vegetarian</label></p>
<p><input id="f3" type="checkbox"><label for="f3">
Yangi</label></p>
</fieldset>
<button type="submit">Qo'llash</button>
</form>
</dialog>"Filtr" ni bosing: oq panel o'ngdan suzib kiradi, qolgan qismi qoraygan parda, "Qo'llash" panelning eng pastida (margin-block-start: auto — Auto margin). Chrome'da tekshirdik: panel ochiqligida orqadagi "Filtr" tugmasiga Tab bilan borib bo'lmaydi — modal orqa sahifani inert qildi. Popover panelida esa Tab panel tugagach sahifaga qaytadi. "Qo'llash" form method="dialog" ichida — oynani yopadi. Filtrni haqiqatan qo'llash (taomlarni yashirish) — JavaScript ishi, 09-qismda. #filtr — mashq uchun id selektori.
4-mashq: Portfolio qadami — "Ma'lumotlaringiz qanday ishlatiladi?"
Portfolio'da Top layer darsidan beri dialog uchun umumiy uslub bor, lekin haqiqiy oyna yo'q. Aloqa sahifasiga birinchi modal oynani qo'shamiz: forma ostida "Ma'lumotlaringiz qanday ishlatiladi?" tugmasi qisqa izohli oynani ochadi.
aloqa/index.htmlda: tugma vadialog(sarlavha, 2–3 gaplik matn, "Tushunarli" tugmasi). JavaScript'siz.- Oyna ochiqligida sahifa surilmasin va skrollbar yo'qolganda sakramasin.
- Telefonda (40rem gacha) oyna pastdan chiqadigan sheet bo'lsin.
- Fokus "Tushunarli" tugmasida bo'lsin, pardaga bosish oynani yopsin (bu oynada yo'qotiladigan narsa yo'q).
Ishora: portfolio'dagi [popover], dialog uslubi max-inline-size, padding, ramka va shaffoflik o'tishini allaqachon beradi — faqat yetishmaganini qo'shing. Tugma ikkinchi variantda (Tugmalar tizimi, footer'dagi tugma kabi).
Yechim
aloqa/index.html da, formadan keyin:
<p>
<button type="button" commandfor="maxfiylik"
command="show-modal">Ma'lumotlaringiz qanday ishlatiladi?</button>
</p>
<dialog id="maxfiylik" aria-labelledby="maxfiylik-sarlavha"
closedby="any">
<h2 id="maxfiylik-sarlavha">Ma'lumotlaringiz haqida</h2>
<p>Ismingiz va email'ingiz faqat menga keladi va faqat javob
yozish uchun ishlatiladi. Ular hech kimga berilmaydi.</p>
<form method="dialog">
<button type="submit" autofocus>Tushunarli</button>
</form>
</dialog>asos.css ga (html dagi scrollbar-gutter: stable portfolio'da Overflow darsidan beri bor — skrollbar yo'qolganda sahifa sakramaydi):
/* Modal ochiqligida sahifa surilmaydi (Modal oyna darsi) */
html:has(dialog:modal) {
overflow: hidden;
}komponentlar.css dagi top layer qismiga:
/* Ma'lumot tugmasi — ikkinchi variant */
main > p > button[command="show-modal"] {
--_fon: transparent;
--_matn: var(--rang-asosiy);
--_chegara: var(--rang-asosiy);
}
dialog {
overscroll-behavior: contain;
}
dialog h2 {
margin-block-start: 0;
}
dialog form {
display: flex;
justify-content: end;
}
@media (width < 40rem) {
dialog {
inline-size: 100%;
max-inline-size: 100%;
margin-block-end: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
}- Oynaning
max-inline-size: min(100% - 2rem, 28rem)i (Top layer darsidagi portfolio uslubi) telefonda100%ga almashadi — sheet butun enda. - Ochilish animatsiyasi o'zgarmadi: portfolio oynasi faqat shaffoflik bilan ochiladi. Sheet'ni pastdan chiqarmoqchi bo'lsangiz — «Bahor»dagi
--_kirishusulinino-preferenceichida qo'shing. aloqa/index.htmldagiformformaningdialogichidagisi bilan chalkashmasin:dialog formselektori faqat oyna ichidagisiga tegadi. Aloqa formasiningdisplay: gridqoidasi (formteg selektori) ham oyna formasiga tushadi —dialog formog'irroq,flexyutadi.closedby="any"— Safari'da ishlamaydi, u yerda "Tushunarli" vaEscqoladi. Bu oynada yo'qotiladigan narsa yo'q, shuning uchun parda bilan yopish qulay.html-validate(11.16.1 da tekshirdik)command,commandforvaclosedbyni taniydi. Faqat bitta talab: oyna formasidagi tugmada hamtype="submit"yozilsin — aks holdano-implicit-button-typexatosi.
Tekshiring: tugmani bosing — fokus "Tushunarli" da; pardada g'ildirakni aylantiring — sahifa qimirlamaydi; 1280 pikselda ochib-yopganda forma chetlari joyidan siljimaydi. So'ng npm run check.
9. Real ishda
- Deyarli har mahsulotda modal bor: kirish oynasi, tasdiqlash, savat, rasm ko'rish. Ilgari ular
div+z-index: 9999+ JavaScript'dagi fokus tuzog'i bilan yozilardi — yuzlab qator. Radix, Headless UI kabi kutubxonalar (kursda keyinroq) bugun ichkaridadialogga o'tmoqda. - Mobil ilovalardagi sheet — Telegram'da xabar menyusi, taksi ilovasida manzil tanlash — veb'da ham shu naqsh: pastdan chiqadi, bosh barmoq yetadi.
- Skroll bloklash — kod ko'rigida tez-tez uchraydigan izoh: "Modal ochiq, lekin orqa sahifa suriladi" va "Modal ochilganda sahifa 15 piksel sakraydi".
- Intervyuda so'raladi: "Modal ochilganda orqa sahifa skrollini qanday bloklaysiz?", "
scrollbar-gutternima uchun?", "overscroll-behaviorqanday muammoni hal qiladi?", "Modal va popover farqi?".
Xulosa
- Modal —
dialog+command="show-modal"(JavaScript'dashowModal()): top layer, orqa sahifa inert,Escyopadi, fokus ichkariga kiradi va qaytadi. - Skroll bloklash — uch qator:
html:has(dialog:modal) { overflow: hidden },html { scrollbar-gutter: stable },dialog { overscroll-behavior: contain }. :modal,[open]emas: non-modal ochiq oyna sahifani bloklamasin.- Uzun kontent: oyna — flex ustun, ichki qism —
min-block-size: 0; overflow-y: auto; sarlavha va tugmalar joyida. - Telefonda — sheet:
margin-block-end: 0, butun en, tepa burchaklar yumaloq; harakat bitta--_kirisho'zgaruvchisidan. closedby="any"vaoverscroll-behavior— Baseline emas,scrollbar-gutter, invoker commands va@starting-style— yangi Baseline; hammasi ishlamagan joyda ham oyna ishlaydi.
Keyingi dars: Tooltip, dropdown va toast — oynadan kichikroq, lekin tez-tez uchraydigan "suzuvchi" komponentlar: tugma yonidagi izoh, ochiladigan ro'yxat va "Bron qabul qilindi" xabari.
Manbalar
- MDN: "
<dialog>", "Invoker Commands API", ":modal", "::backdrop", "scrollbar-gutter", "overscroll-behavior", "closedby", "@starting-style", "overlay" — developer.mozilla.org - HTML Standard: "The dialog element" — html.spec.whatwg.org
- WAI-ARIA Authoring Practices: "Dialog (Modal) pattern" — w3.org/WAI/ARIA/apg
- web-features (Baseline ma'lumotlari): "dialog", "invoker-commands", "dialog-closedby", "scrollbar-gutter", "overscroll-behavior", "starting-style", "overlay", "has" — github.com/web-platform-dx/web-features
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!