IlmHamroh
JavaScript Full-stack/6-qism. CSS: harakat, zamonaviy CSS va dizayn57/57-dars39 daqiqa
Mundarija (37)

Yakuniy loyiha: Figma'dan dizayn tizimi va responsive landing

Qisqacha: Bitta real biznes uchun bir sahifali landing quriladi. Figma maketidagi ranglar, shriftlar va masofalar tokenlarga aylanadi. CSS olti faylga va @layer qatlamlariga bo'linadi, sahifa 9 ta tayyor komponentdan yig'iladi. Natija Stylelint va html-validate'dan xatosiz o'tadi, Lighthouse'da Accessibility 100 va Performance 99–100 oladi va GitHub Pages'da ishlaydi.

Bu darsda

  • Figma maketidan tokenlar yozasiz va holat ranglarining qorong'i qiymatlarini kontrast bilan tanlaysiz.
  • 06-qism komponentlaridan landing yig'asiz — qatlamlar, container query va qorong'i rejim bilan.
  • Sahifani enlar, klaviatura, lint va Lighthouse bilan tekshirasiz.
  • Landing'ni yangi landing reposiga joylab, portfolio'ingizga qo'shasiz.

Oldin bilishingiz kerak: Yakuniy loyiha: portfolio'ni CSS bilan bezash, Dizayn tokenlari, Qorong'i rejim va temalash, Figma dasturchi uchun 2, Tugmalar tizimi, Modal oyna, Stylelint va Prettier.

1. Nega bu kerak?

06-qismda har dars bitta bo'lakni o'rgatdi: tugma, karta, menyu, oyna, qorong'i rejim, animatsiya. Real ishda ular bir sahifada uchrashadi.

Bu dars — birinchi "buyurtma". Tanishingizning kichik biznesiga (non do'koni, sartaroshxona, o'quv markazi) bitta sahifa kerak: nima qiladi, qancha turadi, qanday bog'lanish mumkin. Bunday sahifa landing (landing page — "qo'nish sahifasi") deyiladi: mehmon reklama yoki qidiruvdan shu sahifaga "qo'nadi" va bitta harakat qiladi — qo'ng'iroq, bron yoki buyurtma.

Usta uyni chizmadan boshlaydi, keyin g'isht va derazalarni tayyor o'lchamda buyuradi. Sizda chizma — Figma maketi, o'lchamlar — tokenlar, g'isht va derazalar — komponentlar.

2. Loyiha talablari

2.1 Nima quriladi

Biznesni o'zingiz tanlaysiz. Namunaviy yechim — «Bahor» choyxonasi landing'i: tuzilishi va fayllari siznikiga mos, faqat matn va ranglar boshqa.

Bo'lim Nima bor Komponent
Header Nom, menyu (telefonda yon panel) Navbar, tugma, ikonka
Bosh blok Sarlavha, 2 ta harakat, 3 ta afzallik, "kun taomi" Tugma, ikonka, karta
Menyu 4 ta taom kartasi Karta
Fikrlar Gorizontal lenta Lenta
Savollar 3 ta savol-javob Akkordeon
Aloqa va footer Manzil, telefon, bron, mavzu tanlash Tugmalar guruhi
Bron oynasi 5 maydonli forma Modal oyna, forma

Jami 9 ta komponent — hammasi 06-qism darslaridan tayyor keladi, faqat tokenlarga ulanadi.

2.2 Talablar

Bu ro'yxat — baholash jadvalining asosi ham ("Baholash" bo'limi).

  • Tokenlar — Figma maketidan, uch daraja: primitiv → semantik → komponent (Dizayn tokenlari). Komponentlarda xom rang yo'q.
  • Olti fayl va qatlamlar: tokenlar, reset, asos, layout, komponentlar, yordamchi (Kaskadni boshqarish). HTML'da bitta link.
  • Sarlavhalar shkaladan: h1 — --step-3, h2 — --step-2, h3 — --step-1 (Tipografik shkala).
  • Holat ranglari (--rang-xato va boshqalar) — ikkala rejimda fonda kamida 4.5:1.
  • 320, 360, 800 va 1280 pikselda gorizontal skroll yo'q; kamida bitta komponent konteynerga qarab o'zgaradi.
  • Qorong'i rejim tizimga ergashadi, foydalanuvchi o'zi ham tanlay oladi (data-tema).
  • Har animatsiya prefers-reduced-motion: reduce da o'chadi.
  • npm run check — 0 xato; Lighthouse (Mobile): Accessibility 100, qolgan uchtasi ≥ 95.

Cheklovlar:

  • style atributi va <style> tegi yo'q — CSP ularni to'sadi (HTML darajasidagi xavfsizlik).
  • JavaScript — faqat uchta kichik fayl (mavzu uchun 2 ta, menyu uchun 1 ta), aynan ko'chiriladi.
  • class — faqat kanonik nomlar: tugma (+ modifikatorlar), tugma-guruh, ikonka, taom-karta, narx, otish, korinmas. Qolgani — teg, atribut va id selektorlari.

Tekshirib ko'ring: Sahifa JavaScript'siz ham o'qilishi va bron oynasi ochilishi kerak. Nega, axir hamma brauzerda JavaScript yoqilgan-ku?

Javob

Skript yuklanmay qolishi mumkin: sekin internet, xato, bloklovchi kengaytma. Landing'ning asosiy ishi esa — mehmonni bronga olib kelish. Oyna command="show-modal" bilan (Modal oyna), menyu popover bilan ochiladi — bular HTML imkoniyati. Skriptlar faqat qulaylik qo'shadi. Bu — Brauzer mosligi darsidagi progressive enhancement.

3. Ish tartibi: olti bosqich

# Bosqich Natija
1 Maketdan tokenlar Token jadvali, tokenlar.css
2 Loyiha tuzilmasi Papkalar, sozlamalar, bo'sh fayllar
3 Yig'ish HTML skelet, 6 ta CSS fayl, 3 ta skript
4 Tekshirish 4 ta en, klaviatura, qorong'i rejim, lint, Lighthouse
5 Deploy login.github.io/landing/
6 Portfolio Loyiha kartasi va qayta yuklash
flowchart LR
  F["Figma<br/>Variables"] --> T["tokenlar.css"]
  T --> K["komponentlar.css"]
  K --> S["index.html"]
  S --> L["Lint va<br/>Lighthouse"]
  L --> D["GitHub Pages"]

Nimaga qarang: zanjir bir yo'nalishda. Maketda rang o'zgarsa, faqat tokenlar.css o'zgaradi — komponentlar va HTML tegilmaydi.

4. 1-bosqich: maketdan tokenlarga

4.1 Maket

Landing maketi Figma dasturchi uchun 2 darsidagi faylda davom etadi:

  • Ikki frame: Phone (360) va Desktop (1280). O'rtadagi enlarni kod hal qiladi (fluid shkala, RAM to'r, container query).
  • Ikki kolleksiya: Primitivlar (yashil/600, qaymoq/50...) va Semantik (rang/asosiy → yashil/600), Web code syntax bilan.
  • Komponentlar: Tugma (to'rt tur, uch o'lcham) va Taom karta (tik va yotiq).

Bepul tarifda Figma rejim (Yorug'/Qorong'i) qo'shishga yo'l bermaydi. Shuning uchun maketda faqat yorug' variant, qorong'i qiymatlar kodda — light-dark() ichida.

4.2 Pikseldan tokenga

Har qiymatni Figma dasturchi uchun 1 darsidagidek o'qing: piksel → rem → token. Namunaviy maketdan bir qismi:

Maketda (Phone) Qiymat Token yoki qoida
Bosh sarlavha 28 px, Lora 700 h1 → --step-3
Bo'lim sarlavhasi 23 px h2 → --step-2
Asosiy tugma balandligi 52 .tugma.katta (3.25rem)
Bo'limlar orasi 48 --masofa-2xl
Karta burchagi 8 --burchak-m
Narx rangi #B4461A --rang-urgu

Maketda 28, kodda 27.6 piksel: --step-3 telefonda 16 × 1.2³ = 27.65. Yarim piksel farq — bu shkala, xato emas. Dizaynerga savol faqat shkalaga umuman tushmaydigan son uchun kerak (Dizayn tokenlari). Tokenlar 60 taga yetmaydi, shuning uchun tokenlar.css qo'lda yoziladi; Figma'dan JSON eksport faqat solishtirish uchun.

4.3 Holat ranglarining qorong'i qiymatlari

«Bahor» tokenlarida bitta bo'shliq qolgan edi: --rang-xato, --rang-muvaffaqiyat, --rang-ogohlantirish, --rang-malumot faqat yorug' rejimda (Rang nazariyasi). Yorug' qiymat oklch(52% 0.19 27) to'q yashil fonda qiyin o'qiladi — qorong'i matn qorong'i fonda.

Qorong'i rang yorqinroq va kamroq to'yingan bo'lsin (Qorong'i rejim). To'rttasiga bir xil ravshanlik berdik: yorug'da L = 52% edi, qorong'ida 75%, to'yinganlik 0.10–0.12. Keyin har rangni ikki fonda tekshirdik: sahifa foni (--rang-fon, #16301b) va karta/oyna foni (--rang-yuza). Yuza fondan yorqinroq — kontrast unda pastroq, og'irroq sinov shu.

Yuzaning hisoblangan rangi Chrome'da — #2a3c24. Kontrastni WCAG formulasi bilan (CSS'da accessibility) colorjs.io kutubxonasida hisobladik:

Token Qorong'i qiymat Fonda Yuzada
--rang-xato oklch(75% 0.12 27) 6.1 5.1
--rang-muvaffaqiyat oklch(75% 0.12 150) 6.7 5.6
--rang-ogohlantirish oklch(75% 0.11 70) 6.3 5.3
--rang-malumot oklch(75% 0.1 250) 6.4 5.4

Hammasi 4.5:1 dan yuqori. L = 72% da xato yuzada 4.56 — chegarada, shuning uchun 75%.

Xavfli tugma (Tugmalar tizimi) teskari juftlik: matn --rang-fon, fon --rang-xato. Yorug'da — 5.8:1, qorong'ida — 6.1:1, qo'shimcha qoida kerak emas.

html
<style>
  .panel {
    --oq: oklch(100% 0 0);
    --rang-fon: light-dark(#fffaf0, #16301b);
    --rang-yuza: light-dark(var(--oq),
      color-mix(in oklch, #16301b, var(--oq) 7%));
    --rang-matn: light-dark(#1f2a24, #fffaf0);
    --rang-xato: light-dark(oklch(52% 0.19 27),
      oklch(75% 0.12 27));
    --rang-muvaffaqiyat: light-dark(oklch(52% 0.13 150),
      oklch(75% 0.12 150));
    --rang-ogohlantirish: light-dark(oklch(52% 0.11 70),
      oklch(75% 0.11 70));
    --rang-malumot: light-dark(oklch(52% 0.13 250),
      oklch(75% 0.1 250));
    margin-block: 0.5rem;
    padding: 0.75rem;
    border-radius: 0.5rem;
    font-family: system-ui, sans-serif;
    color: var(--rang-matn);
    background: var(--rang-fon);
  }
  .yorug { color-scheme: light; }
  .qorongi { color-scheme: dark; }
  .panel p { margin: 0.25rem 0; }
  .yuza {
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    background: var(--rang-yuza);
  }
  .ok { color: var(--rang-muvaffaqiyat); }
  .xato { color: var(--rang-xato); }
  .ogoh { color: var(--rang-ogohlantirish); }
  .info { color: var(--rang-malumot); }
  button {
    margin-block-start: 0.5rem;
    min-block-size: 2.75rem;
    padding-inline: 1.25em;
    border: 0;
    border-radius: 0.5rem;
    font: inherit;
    font-weight: 600;
    background: var(--rang-xato);
    color: var(--rang-fon);
  }
</style>
<div class="panel yorug">
  <p class="ok"><strong>✔ Bron qabul qilindi.</strong></p>
  <p class="ogoh"><strong>▲ Shanbaga 2 ta stol qoldi.</strong></p>
  <div class="yuza">
    <p class="xato"><strong>✖ Telefon noto'g'ri.</strong></p>
    <p class="info"><strong>ℹ Bron 2 soat oldin.</strong></p>
  </div>
  <button type="button">Bronni bekor qilish</button>
</div>
<div class="panel qorongi">
  <p class="ok"><strong>✔ Bron qabul qilindi.</strong></p>
  <p class="ogoh"><strong>▲ Shanbaga 2 ta stol qoldi.</strong></p>
  <div class="yuza">
    <p class="xato"><strong>✖ Telefon noto'g'ri.</strong></p>
    <p class="info"><strong>ℹ Bron 2 soat oldin.</strong></p>
  </div>
  <button type="button">Bronni bekor qilish</button>
</div>

Nimaga qarang: ikkala panelda CSS bir xil. Yuqorida to'q ranglar qaymoq fonda, pastda och ranglar to'q yashil fonda. Ichki blok — yuza: qorong'ida fondan biroz yorqinroq, ranglar unda ham o'qiladi. .yorug, .qorongi va rang class'lari — faqat namoyish; landing'da rejimni data-tema hal qiladi.

Tekshirib ko'ring: Nega kontrastni --rang-yuza bilan tekshirish muhimroq?

Javob

Xato matni ko'pincha forma ichida — bron oynasida, ya'ni yuzada. Qorong'ida yuza fondan yorqinroq, och matn bilan farqi kamroq: fonda 6.1, yuzada 5.1. Eng og'ir sharoitda o'tgan rang yengilida ham o'tadi.

5. 2-bosqich: loyiha tuzilmasi

Landing — alohida loyiha, portfolio'ning ichida emas. kurs/ papkangizda yangi papka oching:

text
landing/
├── package.json
├── .prettierrc
├── .htmlvalidate.json
├── .stylelintrc.json
├── tekshiruv/
│   └── lighthouse.mjs
└── sayt/                ← internetga faqat shu papka ichi
    ├── index.html
    ├── favicon.svg
    └── assets/
        ├── css/   asosiy.css + 6 ta fayl
        ├── js/    tema.js, tema-tanlash.js, menyu.js
        ├── fonts/ bahor.woff2, OFL.txt
        └── images/ osh.svg

.prettierrc, .htmlvalidate.json va .stylelintrc.json ni portfolio'dan ko'chiring (Yakuniy loyiha: portfolio). Stylelint sozlamasiga keyin ikki belgi qo'shiladi — sababi "Tekshirish" bosqichida.

Terminalda landing/ ichida buyruqlarni aynan takrorlang (Stylelint va Prettier darsidagidek):

bash
npm init -y
npm install --save-dev prettier html-validate lighthouse
npm install --save-dev stylelint stylelint-config-standard

package.json ga "type": "module" qatorini qo'shing. "scripts" — portfolio'dagidek, bitta yangisi (audit) bilan:

json
{
  "format": "prettier --write sayt",
  "lint": "html-validate \"sayt/**/*.html\"",
  "lint:css": "stylelint \"sayt/**/*.css\"",
  "check": "prettier --check sayt && npm run lint && npm run lint:css",
  "audit": "node tekshiruv/lighthouse.mjs"
}

"type": "module" — lighthouse.mjs dagi import qatorlari uchun (JavaScript, 09-qismda).

6. 3-bosqich: yig'ish — yechim bo'laklarda

Avval o'zingiz yozing, keyin solishtiring. Qatorlar telefon uchun 70 belgida bo'lingan — npm run format ularni qayta tekislaydi.

6.1 HTML skelet

Talab: head da tema skripti CSS'dan oldin (Qorong'i rejim), CSS va shrift uchun preload (CSS yuklash samaradorligi); body da skip link, ikonka sprite'i, header, main (bosh blok va 4 bo'lim), footer va oyna.

Yechim: index.html (qisqartirilgan)
html
<!doctype html>
<html lang="uz">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="color-scheme" content="light dark">
  <meta http-equiv="Content-Security-Policy"
    content="default-src 'self'; img-src 'self' data:;
      object-src 'none'; base-uri 'self';
      form-action https://example.com">
  <title>Choyxona «Bahor» — Chilonzorda oilaviy osh</title>
  <meta name="description" content="Choyxona «Bahor»: har kuni
    7:00 dan 23:00 gacha osh, lag'mon va manti. 20 kishigacha
    stolni bir daqiqada band qiling.">
  <link rel="icon" href="favicon.svg" type="image/svg+xml">
  <script src="assets/js/tema.js"></script>
  <link rel="preload" href="assets/css/tokenlar.css" as="style">
  <!-- ... reset, asos, layout, komponentlar, yordamchi ... -->
  <link rel="preload" href="assets/fonts/bahor.woff2" as="font"
    type="font/woff2" crossorigin>
  <link rel="stylesheet" href="assets/css/asosiy.css">
  <script src="assets/js/menyu.js" defer></script>
  <script src="assets/js/tema-tanlash.js" defer></script>
</head>
<body>
  <a class="otish" href="#asosiy">Asosiy qismga o'tish</a>

  <svg hidden>
    <symbol id="i-menyu" viewBox="0 0 24 24">
      <path d="M4 7h16M4 12h16M4 17h16"/>
    </symbol>
    <!-- i-yopish, i-soat, i-joy, i-odam, i-telefon -->
  </svg>

  <header>
    <a href="./">Choyxona «Bahor»</a>
    <button class="tugma ikkinchi" type="button"
      popovertarget="asosiy-menyu">
      <svg class="ikonka" aria-hidden="true">
        <use href="#i-menyu"/></svg>
      Menyu
    </button>
    <nav id="asosiy-menyu" aria-label="Asosiy menyu" popover>
      <button class="tugma shaffof" type="button"
        popovertarget="asosiy-menyu" popovertargetaction="hide">
        <svg class="ikonka" aria-hidden="true">
          <use href="#i-yopish"/></svg>
        Yopish
      </button>
      <!-- [html-validate-disable-next no-redundant-role] -->
      <ul role="list">
        <li><a href="#menyu">Menyu</a></li>
        <li><a href="#fikrlar">Fikrlar</a></li>
        <li><a href="#savollar">Savollar</a></li>
        <li><a href="#aloqa">Aloqa</a></li>
      </ul>
    </nav>
  </header>

  <main id="asosiy">
    <header>
      <div>
        <svg width="56" height="56" viewBox="0 0 56 56" fill="none"
          stroke="currentColor" stroke-width="3"
          stroke-linecap="round" aria-hidden="true">
          <path d="M10 30h32v4a16 16 0 0 1-32 0Z"/>
          <path d="M42 32h3a5 5 0 0 1 0 10h-5"/>
          <path d="M20 22c-3-4 3-6 0-10" pathLength="1"/>
          <path d="M28 22c-3-4 3-6 0-10" pathLength="1"/>
          <path d="M36 22c-3-4 3-6 0-10" pathLength="1"/>
        </svg>
        <h1>Rustam akaning oshi — har kuni soat 7:00 dan</h1>
        <p>Chilonzordagi oilaviy choyxona: katta qozonda osh,
          qo'lda cho'zilgan lag'mon va issiq ko'k choy.</p>
        <p>
          <button class="tugma katta" type="button"
            commandfor="bron-oyna" command="show-modal">
            Stolni band qilish
          </button>
          <a class="tugma katta ikkinchi" href="#menyu">
            Menyuni ko'rish</a>
        </p>
        <!-- [html-validate-disable-next no-redundant-role] -->
        <ul role="list">
          <li><svg class="ikonka" aria-hidden="true">
            <use href="#i-soat"/></svg> Har kuni 7:00–23:00</li>
          <!-- i-joy: Chilonzor, i-odam: 20 kishigacha stol -->
        </ul>
      </div>
      <aside aria-labelledby="kun-taomi">
        <article class="taom-karta">
          <img src="assets/images/osh.svg" alt="Lagandagi osh:
            sabzi, go'sht va bedana tuxumi" width="480"
            height="300" fetchpriority="high">
          <h2 id="kun-taomi">Kun taomi: to'y oshi</h2>
          <p class="narx">35 000 so'm</p>
          <p>Devzira guruch, qo'y go'shti, sariq sabzi va bedana
            tuxumi. Tushlikkacha tayyor.</p>
        </article>
      </aside>
    </header>

    <section id="menyu" aria-labelledby="menyu-sarlavha">
      <h2 id="menyu-sarlavha">Bugungi menyu</h2>
      <!-- [html-validate-disable-next no-redundant-role] -->
      <ul role="list">
        <li>
          <article class="taom-karta">
            <h3>Osh</h3>
            <p>Qazonda, o'tin olovida — har kuni ertalab.</p>
            <p class="narx">35 000 so'm</p>
          </article>
        </li>
        <!-- Lag'mon 28 000, Manti 30 000, Ko'k choy 5 000 -->
      </ul>
    </section>

    <section id="fikrlar" aria-labelledby="fikrlar-sarlavha">
      <h2 id="fikrlar-sarlavha">Mehmonlar fikri</h2>
      <!-- [html-validate-disable-next no-redundant-role] -->
      <ul role="list" aria-label="Mehmonlar fikri" tabindex="0">
        <li>
          <figure>
            <blockquote><p>Osh har safar bir xil mazali. Bolalar
              uchun alohida stol ham berishdi.</p></blockquote>
            <figcaption>Malika, Yunusobod</figcaption>
          </figure>
        </li>
        <!-- yana 3 ta fikr -->
      </ul>
    </section>

    <section id="savollar" aria-labelledby="savollar-sarlavha">
      <h2 id="savollar-sarlavha">Ko'p so'raladigan savollar</h2>
      <details name="savollar" open>
        <summary>Bron qanday qilinadi?</summary>
        <p>"Stolni band qilish" tugmasini bosing: ism, telefon,
          sana va vaqtni yozing. Biz qo'ng'iroq qilib
          tasdiqlaymiz.</p>
      </details>
      <!-- yana 2 ta savol -->
    </section>

    <section id="aloqa" aria-labelledby="aloqa-sarlavha">
      <h2 id="aloqa-sarlavha">Aloqa</h2>
      <!-- [html-validate-disable-next no-redundant-role] -->
      <ul role="list">
        <li><svg class="ikonka" aria-hidden="true">
          <use href="#i-telefon"/></svg>
          <a href="tel:+998900000000"
            >+998&nbsp;90&nbsp;000&nbsp;00&nbsp;00</a></li>
        <!-- manzil va ish vaqti -->
      </ul>
      <button class="tugma" type="button"
        commandfor="bron-oyna" command="show-modal">
        Stolni band qilish
      </button>
    </section>
  </main>

  <footer>
    <p>© 2026 Choyxona «Bahor». O'quv loyihasi — ma'lumotlar
      namuna.</p>
    <div class="tugma-guruh" role="group"
      aria-label="Sayt mavzusi" hidden>
      <button class="tugma ikkinchi kichik" id="tema-tizim"
        type="button" aria-pressed="true">Tizim</button>
      <button class="tugma ikkinchi kichik" id="tema-yorug"
        type="button" aria-pressed="false">Yorug'</button>
      <button class="tugma ikkinchi kichik" id="tema-qorongi"
        type="button" aria-pressed="false">Qorong'i</button>
    </div>
  </footer>

  <dialog id="bron-oyna" aria-labelledby="bron-sarlavha">
    <form action="https://example.com/bron" method="post">
      <header role="none">
        <h2 id="bron-sarlavha">Stolni band qilish</h2>
        <button class="tugma shaffof belgi" type="button"
          commandfor="bron-oyna" command="close"
          aria-label="Yopish">
          <svg class="ikonka" aria-hidden="true">
            <use href="#i-yopish"/></svg>
        </button>
      </header>
      <div>
        <div>
          <label for="ism">Ismingiz</label>
          <input id="ism" name="ism" type="text"
            autocomplete="name" required>
        </div>
        <div>
          <label for="telefon">Telefon raqamingiz</label>
          <input id="telefon" name="telefon" type="tel"
            autocomplete="tel" required
            pattern="\+?998 ?[0-9]{2} ?[0-9]{3} ?[0-9]{2} ?[0-9]{2}"
            aria-describedby="telefon-yordam">
          <span id="telefon-yordam">Namuna: +998 90 000 00 00</span>
        </div>
        <!-- sana (date), vaqt (time, 07:00–22:00),
             odam (number, 1–20) — xuddi shunday -->
      </div>
      <footer role="none">
        <button class="tugma shaffof" type="button"
          commandfor="bron-oyna" command="close">
          Bekor qilish
        </button>
        <button class="tugma" type="submit">Yuborish</button>
      </footer>
    </form>
  </dialog>
</body>
</html>
  • main > header — bosh blok, body > header — sayt header'i. Navbar darsidagi selektor shuning uchun body > header: bosh blok yopishqoq bo'lib qolmaydi.
  • role="list" — Safari tuzog'i (Ro'yxatlar stili); izohdagi direktiva html-validate'ning no-redundant-role xabarini shu qatorda o'chiradi.
  • Oyna ichidagi role="none" — sababini "Tekshirish" bosqichida html-validate topadi.
  • form-action https://example.com — forma faqat shu manzilga yuboriladi. O'zingizda bron qabul qiladigan xizmat manzilini yozasiz.

6.2 asosiy.css va tokenlar.css

asosiy.css — qatlamlar e'loni va olti import. tokenlar.css — primitivlar, semantik ranglar, shrift, --step-*, masofa, burchak, soya, qatlam va @font-face.

Yechim: asosiy.css
css
/* «Bahor» landing — kirish fayli: qatlamlar va importlar */
@layer tokenlar, reset, asos, layout, komponentlar, yordamchi;

@import url("tokenlar.css") layer(tokenlar);
@import url("reset.css") layer(reset);
@import url("asos.css") layer(asos);
@import url("layout.css") layer(layout);
@import url("komponentlar.css") layer(komponentlar);
@import url("yordamchi.css") layer(yordamchi);
Yechim: tokenlar.css
css
/* tokenlar.css — faqat qiymatlar: hech narsa chizmaydi.
   Manba: Figma "Bahor landing", Variables. Breakpointlar token
   bo'la olmaydi: 40rem va 64rem — qo'lda yoziladi. */

:root {
  /* 1. Primitivlar */
  --oq: oklch(100% 0 0deg);
  --yashil-200: oklch(88% 0.08 150.8deg); /* #b2e7bd */
  --yashil-300: oklch(80.3% 0.098 150.8deg); /* #8fd19e */
  --yashil-600: oklch(47.5% 0.1 147.7deg); /* #2f6b3a */
  --yashil-700: oklch(42.1% 0.12 143.1deg); /* #1a5d1a */
  --yashil-900: oklch(28.2% 0.05 148.6deg); /* #16301b */
  --qaymoq-50: oklch(98.6% 0.014 84.6deg); /* #fffaf0 */
  --gisht-300: oklch(75% 0.13 39.8deg);
  --gisht-600: oklch(53.7% 0.153 39.8deg); /* #b4461a */
  --kul-300: oklch(80% 0.016 162deg);
  --kul-600: oklch(49.9% 0.016 162deg); /* #5b6660 */
  --kul-900: oklch(27.2% 0.019 161deg); /* #1f2a24 */

  /* 2. Semantik ranglar — zaxira: light-dark() yo'q brauzer
     faqat yorug' rejimni ko'radi */
  color-scheme: light;

  --rang-fon: var(--qaymoq-50);
  --rang-yuza: var(--oq);
  --rang-matn: var(--kul-900);
  --rang-asosiy: var(--yashil-600);
  --rang-asosiy-toq: var(--yashil-700);
  --rang-urgu: var(--gisht-600);
  --rang-ikkinchi: var(--kul-600);
  --rasm-filtr: none;

  /* Holat ranglari (Rang nazariyasi darsi): L 52% */
  --rang-xato: oklch(52% 0.19 27deg);
  --rang-muvaffaqiyat: oklch(52% 0.13 150deg);
  --rang-ogohlantirish: oklch(52% 0.11 70deg);
  --rang-malumot: oklch(52% 0.13 250deg);

  /* 3. Shrift */
  --shrift-matn: system-ui, sans-serif;
  --shrift-sarlavha: "Bahor", "Bahor zaxira", serif;
  --qator-matn: 1.5;
  --qator-sarlavha: 1.2;

  /* O'lcham shkalasi: 360 da 16 × 1.2, 1280 da 18 × 1.25 */
  --step--1: clamp(0.8333rem, 0.8072rem + 0.1159vw, 0.9rem);
  --step-0: clamp(1rem, 0.9511rem + 0.2174vw, 1.125rem);
  --step-1: clamp(1.2rem, 1.1193rem + 0.3587vw, 1.4063rem);
  --step-2: clamp(1.44rem, 1.3156rem + 0.5527vw, 1.7578rem);
  --step-3: clamp(1.728rem, 1.5444rem + 0.8161vw, 2.1973rem);

  /* 4. Masofa: asos 16 → 18 px */
  --masofa-2xs: clamp(0.25rem, 0.2378rem + 0.0543vw, 0.2813rem);
  --masofa-xs: clamp(0.5rem, 0.4755rem + 0.1087vw, 0.5625rem);
  --masofa-s: clamp(0.75rem, 0.7133rem + 0.163vw, 0.8438rem);
  --masofa-m: clamp(1rem, 0.9511rem + 0.2174vw, 1.125rem);
  --masofa-l: clamp(1.5rem, 1.4266rem + 0.3261vw, 1.6875rem);
  --masofa-xl: clamp(2rem, 1.9022rem + 0.4348vw, 2.25rem);
  --masofa-2xl: clamp(3rem, 2.8533rem + 0.6522vw, 3.375rem);
  --masofa-s-l: clamp(0.75rem, 0.3832rem + 1.6304vw, 1.6875rem);

  /* 5. Burchak, soya, qatlam */
  --burchak-s: 0.25rem;
  --burchak-m: 0.5rem;
  --burchak-toliq: 999px;
  --soya-1: 0 1px 2px oklch(0% 0 0deg / 12%);
  --soya-2:
    0 1px 2px oklch(0% 0 0deg / 12%),
    0 4px 12px oklch(0% 0 0deg / 12%);
  --soya-3:
    0 2px 4px oklch(0% 0 0deg / 12%),
    0 12px 32px oklch(0% 0 0deg / 18%);
  --qatlam-yopishqoq: 10;
  --qatlam-menyu: 20;
  --qatlam-ustki: 30;
  --qatlam-otish: 40;
}

@supports (color: light-dark(white, black)) {
  :root {
    color-scheme: light dark;

    --rang-fon: light-dark(var(--qaymoq-50), var(--yashil-900));
    --rang-yuza: light-dark(
      var(--oq),
      color-mix(in oklch, var(--yashil-900), var(--oq) 7%)
    );
    --rang-matn: light-dark(var(--kul-900), var(--qaymoq-50));
    --rang-asosiy: light-dark(var(--yashil-600), var(--yashil-300));
    --rang-asosiy-toq: light-dark(
      var(--yashil-700),
      var(--yashil-200)
    );
    --rang-urgu: light-dark(var(--gisht-600), var(--gisht-300));
    --rang-ikkinchi: light-dark(var(--kul-600), var(--kul-300));

    /* Holat ranglari: qorong'ida L 75%, C pastroq */
    --rang-xato: light-dark(
      oklch(52% 0.19 27deg),
      oklch(75% 0.12 27deg)
    );
    --rang-muvaffaqiyat: light-dark(
      oklch(52% 0.13 150deg),
      oklch(75% 0.12 150deg)
    );
    --rang-ogohlantirish: light-dark(
      oklch(52% 0.11 70deg),
      oklch(75% 0.11 70deg)
    );
    --rang-malumot: light-dark(
      oklch(52% 0.13 250deg),
      oklch(75% 0.1 250deg)
    );

    /* Soyalar qorong'ida shaffof — balandlikni yuza beradi */
    --soya-1:
      0 1px 2px light-dark(oklch(0% 0 0deg / 12%), transparent);
    --soya-2:
      0 1px 2px light-dark(oklch(0% 0 0deg / 12%), transparent),
      0 4px 12px light-dark(oklch(0% 0 0deg / 12%), transparent);
    --soya-3:
      0 2px 4px light-dark(oklch(0% 0 0deg / 12%), transparent),
      0 12px 32px light-dark(oklch(0% 0 0deg / 18%), transparent);
  }

  /* Foydalanuvchi tanlovi (assets/js/tema.js qo'yadi) */
  :root[data-tema="yorug"] {
    color-scheme: light;
  }

  :root[data-tema="qorongi"] {
    color-scheme: dark;

    --rasm-filtr: brightness(0.85);
  }

  @media screen and (prefers-color-scheme: dark) {
    :root:not([data-tema="yorug"]) {
      --rasm-filtr: brightness(0.85);
    }
  }
}

/* Sarlavha shrifti — Lora, SIL OFL 1.1 (Web shriftlar darsi) */
@font-face {
  font-family: Bahor;
  src: url("../fonts/bahor.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
}

@font-face {
  font-family: "Bahor zaxira";
  src: local("Times New Roman");
  size-adjust: 115.2%;
  ascent-override: 87.33%;
  descent-override: 23.78%;
  line-gap-override: 0%;
}
  • Qiymatlar — Dizayn tokenlari va Qorong'i rejim darslaridagi «Bahor» tokenlari. Yangisi — holat ranglarining light-dark() qatorlari.
  • Tuslardagi deg va bo'sh qatorlar — Stylelint talablari (Stylelint va Prettier). Token ichidagi "Bahor" esa qo'shtirnoq bilan: aks holda Stylelint uni kalit so'z deb, "kichik harf bilan yozing" deydi.

6.3 reset.css, asos.css, layout.css

reset.css — CSS reset darsidagi «Bahor» reseti o'zgarishsiz. asos.css — teglar, fokus halqasi, oyna ochiqligida skroll bloki. layout.css — ustun, bo'limlar, bosh blok va menyu to'ri, karta konteyneri.

Yechim: asos.css
css
/* asos.css — teglar: sahifa, sarlavha, havola, fokus. */

html {
  scroll-padding-block-start: 5rem; /* sticky header */
  scrollbar-gutter: stable;
}

html:has(dialog:modal) {
  overflow: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  font-family: var(--shrift-matn);
  font-size: var(--step-0);
  line-height: var(--qator-matn);
  color: var(--rang-matn);
  background: var(--rang-fon);
}

h1,
h2,
h3 {
  font-family: var(--shrift-sarlavha);
  line-height: var(--qator-sarlavha);
  text-wrap: balance;
  color: var(--rang-asosiy);
}

h1 {
  font-size: var(--step-3);
}

h2 {
  font-size: var(--step-2);
}

h3 {
  font-size: var(--step-1);
}

p,
li,
dd {
  text-wrap: pretty;
}

a {
  color: var(--rang-asosiy);
  text-decoration-thickness: max(1px, 0.0625em);
  text-underline-offset: 0.15em;
}

img {
  filter: var(--rasm-filtr);
}

/* Fokus: tashqi to'q halqa + ichki fon rangidagi chiziq */
:focus-visible {
  outline: 3px solid var(--rang-asosiy-toq);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--rang-fon);
}

::selection {
  background: var(--rang-asosiy);
  color: var(--rang-fon);
}

scroll-behavior: smooth faqat no-preference ichida: harakatni kamaytirgan odamda sahifa darhol sakraydi (prefers-reduced-motion).

Yechim: layout.css
css
/* layout.css — sahifa skeleti. Komponent ichi bu yerda yo'q. */

main > *,
body > footer {
  inline-size: min(100% - 2 * var(--masofa-m), 72rem);
  margin-inline: auto;
}

main > section {
  padding-block-start: var(--masofa-2xl);
}

main > section > h2 {
  margin-block: 0 var(--masofa-m);
}

/* Bosh blok: telefonda bitta ustun, 64rem dan ikki ustun */
main > header {
  display: grid;
  gap: var(--masofa-l);
  padding-block-start: var(--masofa-l);
}

@media (width >= 64rem) {
  main > header {
    grid-template-columns: 3fr 2fr;
    align-items: center;
    column-gap: var(--masofa-2xl);
  }
}

/* Karta konteyneri: karta o'z ustuniga qaraydi, ekranga emas */
:has(> .taom-karta) {
  container: kartalar / inline-size;
}

/* Menyu to'ri — RAM (Intrinsic layout darsi) */
#menyu > ul {
  display: grid;
  grid-template-columns:
    repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  gap: var(--masofa-m);
  margin: 0;
}

#menyu > ul > li {
  display: grid;
}

/* Narx doim kartaning pastida — qo'shni kartalar bir chiziqda */
#menyu .taom-karta {
  grid-template-rows: auto 1fr auto;
}

body > footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--masofa-s) var(--masofa-l);
  margin-block-start: var(--masofa-2xl);
  padding-block: var(--masofa-l);
  border-block-start: 1px solid
    color-mix(in oklch, var(--rang-asosiy) 25%, var(--rang-fon));
  color: var(--rang-ikkinchi);
  font-size: var(--step--1);
}
  • :has(> .taom-karta) — "ichida karta bor element": bosh blokdagi aside ham, menyudagi har li ham class'siz konteyner bo'ladi (Container queries).
  • Breakpoint bitta — 64rem, bosh blok uchun. Qolgani media query'siz moslashadi (Intrinsic layout).

6.4 komponentlar.css

Komponentlarning ko'pi o'z darsidagi «Bahor» blokidan o'zgarishsiz ko'chadi — dizayn tizimining asosiy foydasi shu:

Blok Qayerdan O'zgarish
Tugma va guruh Tugmalar tizimi Yuklanish holati olib tashlandi, aria-pressed qo'shildi
Header va navbar Navbar va mobil menyu Brend havolasi, ul margin'i
Taom kartasi Kartalar Stretched link yo'q, rasmsiz karta
Forma Forma komponentlari Faqat matn maydonlari (checkbox yo'q)
Modal oyna Modal oyna O'zgarishsiz
Akkordeon Tabs va accordion details[name="savollar"] selektori
Lenta Karusel #fikrlar > ul selektori

Tartib: skip link → ikonka → tugma → header → bosh blok → karta → lenta → akkordeon → aloqa → forma → oyna → animatsiya. Tugma, navbar, forma va oyna bloklarini o'sha darslardan ko'chiring; quyida — faqat yangi va o'zgargan qismlar.

Yechim: skip link, ikonka, tugmaga qo'shimcha, header
css
/* === Skip link (CSS'da accessibility darsi) === */
.otish:not(:focus) {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.otish:focus {
  position: fixed;
  inset-block-start: var(--masofa-xs);
  inset-inline-start: var(--masofa-xs);
  z-index: var(--qatlam-otish);
  padding: var(--masofa-xs) var(--masofa-m);
  border-radius: var(--burchak-m);
  background: var(--rang-asosiy);
  color: var(--rang-fon);
}

@keyframes otish-kirish {
  from {
    translate: 0 -150%;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .otish:focus {
    animation: otish-kirish 0.2s ease-out;
  }
}

/* === Ikonka (SVG va ikonka tizimi darsi) — chiziqli to'plam === */
.ikonka {
  flex: none;
  inline-size: 1.25em;
  block-size: 1.25em;
  fill: none;
  stroke: currentcolor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* === Tugma: .tugma bloklaridan keyin (Tugmalar tizimi) === */
.tugma[aria-pressed="true"] {
  --_fon: var(--rang-asosiy);
  --_matn: var(--rang-fon);
  --_chegara: var(--rang-asosiy);
}

/* === Header: navbar blokidan keyin (Navbar darsi) === */
body > header > a {
  padding-block: var(--masofa-2xs);
  font-family: var(--shrift-sarlavha);
  font-size: var(--step-1);
  font-weight: 700;
  text-decoration: none;
}
  • Skip link fixed (portfolio'da absolute): landing uzun, havola qayerda bo'lsangiz ham ekran burchagida chiqsin.
  • ul margin'i — navbar blokining o'zida margin: 0 bor (Navbar va mobil menyu). Usiz brauzerning 16 piksellik margin'i qolib, header 1280 pikselda 100 piksel bo'lib ketadi.
  • aria-pressed — tanlangan mavzu tugmasi. Holat HTML'da, CSS faqat uni o'qiydi (Tugmalar tizimi).
Yechim: bosh blok va taom kartasi
css
/* === Bosh blok === */
main > header h1 {
  margin-block: var(--masofa-xs) var(--masofa-s);
}

main > header > div > p {
  margin-block: 0 var(--masofa-l);
  font-size: var(--step-1);
}

main > header > div > p:has(> .tugma) {
  display: flex;
  flex-wrap: wrap;
  gap: var(--masofa-s);
}

main > header > div > ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--masofa-xs) var(--masofa-l);
  margin: 0;
  color: var(--rang-ikkinchi);
}

main > header li,
#aloqa li {
  display: flex;
  align-items: center;
  gap: var(--masofa-xs);
}

main > header li .ikonka,
#aloqa li .ikonka {
  color: var(--rang-asosiy);
}

/* === Taom kartasi (Kartalar darsi) === */
.taom-karta {
  position: relative;
  isolation: isolate;
  display: grid;
  align-content: start;
  gap: var(--masofa-xs);
  padding: var(--masofa-xs) var(--masofa-xs) var(--masofa-m);
  border-radius: var(--burchak-m);
  background: var(--rang-yuza);
  box-shadow: var(--soya-1);
}

.taom-karta > :not(img) {
  margin: 0;
  margin-inline: var(--masofa-xs);
}

.taom-karta img {
  inline-size: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--burchak-s);
}

.taom-karta:not(:has(img)) {
  padding-block-start: var(--masofa-m);
}

.taom-karta .narx {
  color: var(--rang-urgu);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Hover soyasi — faqat shaffoflik o'zgaradi */
.taom-karta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow: var(--soya-3);
  opacity: 0;
}

@media (hover: hover) {
  .taom-karta:hover::before {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .taom-karta {
    transition: translate 0.2s ease-out;
  }

  .taom-karta::before {
    transition: opacity 0.2s ease-out;
  }

  @media (hover: hover) {
    .taom-karta:hover {
      translate: 0 -4px;
    }
  }
}

/* Keng konteynerda — media object */
@container kartalar (width >= 28rem) {
  .taom-karta {
    grid-template-columns: minmax(8rem, 1fr) 2fr;
    grid-template-rows: auto auto 1fr;
    padding: var(--masofa-xs);
  }

  .taom-karta img {
    grid-row: span 3;
    block-size: 100%;
  }
}
  • Stretched link yo'q — kartalar hech qayerga olib bormaydi.
  • main > header > div > p — faqat bosh blok matni. Avval main > header p yozgan edik: kun taomi kartasidagi narx ham --step-1 bo'lib kattalashib ketdi.
Yechim: lenta, akkordeon, aloqa
css
/* === Fikrlar lentasi (Karusel darsi) === */
#fikrlar > ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(80%, 20rem);
  gap: var(--masofa-s);
  margin: 0;
  padding-block-end: var(--masofa-s);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  scrollbar-color: var(--rang-asosiy) transparent;
}

#fikrlar > ul > li {
  display: grid;
  scroll-snap-align: start;
}

#fikrlar figure {
  display: grid;
  align-content: space-between;
  gap: var(--masofa-s);
  margin: 0;
  padding: var(--masofa-m);
  border-inline-start: 4px solid var(--rang-asosiy);
  border-radius: var(--burchak-m);
  background: var(--rang-yuza);
}

#fikrlar blockquote,
#fikrlar blockquote p {
  margin: 0;
}

#fikrlar figcaption {
  color: var(--rang-ikkinchi);
  font-size: var(--step--1);
}

/* === Savol-javob (Tabs va accordion darsi) === */
details[name="savollar"] {
  max-inline-size: 48rem;
  border: 1px solid
    color-mix(in oklch, var(--rang-asosiy) 25%, var(--rang-fon));
  border-radius: var(--burchak-m);
  background: var(--rang-yuza);
}

details[name="savollar"] + details[name="savollar"] {
  margin-block-start: var(--masofa-xs);
}

details[name="savollar"][open] {
  border-color: var(--rang-asosiy);
}

details[name="savollar"] summary {
  display: flex;
  justify-content: space-between;
  gap: var(--masofa-s);
  padding: var(--masofa-s) var(--masofa-m);
  border-radius: inherit;
  font-weight: 700;
  cursor: pointer;
}

details[name="savollar"] summary::-webkit-details-marker {
  display: none;
}

details[name="savollar"] summary::after {
  content: "+" / "";
  color: var(--rang-asosiy);
  font-size: var(--step-1);
  line-height: 1;
}

details[name="savollar"][open] summary::after {
  rotate: 45deg;
}

details[name="savollar"] p {
  margin: 0;
  padding: 0 var(--masofa-m) var(--masofa-s);
}

@media (prefers-reduced-motion: no-preference) {
  details[name="savollar"] {
    interpolate-size: allow-keywords;
  }

  details[name="savollar"] summary::after {
    transition: rotate 0.2s;
  }

  details[name="savollar"]::details-content {
    block-size: 0;
    overflow-y: clip;
    transition:
      block-size 0.25s ease-out,
      content-visibility 0.25s allow-discrete;
  }

  details[name="savollar"][open]::details-content {
    block-size: auto;
  }
}

/* === Aloqa ro'yxati === */
#aloqa ul {
  display: grid;
  gap: var(--masofa-xs);
  margin: 0 0 var(--masofa-l);
}

Lentada havola yo'q, shuning uchun ul da tabindex="0": Bobur Tab bilan kelib, → bilan suradi (Karusel).

Yechim: forma xatosi va bug' animatsiyasi

Forma bloki Forma komponentlari darsidan (checkbox va radio'siz), bitta qoida qo'shiladi — xato bo'lsa yordam matni ham qizaradi:

css
form div:has(> :user-invalid) span[id$="-yordam"] {
  color: var(--rang-xato);
}

Fayl oxiriga — bosh blokdagi piyola bug'i (SVG va ikonka tizimi darsidagi chizilish animatsiyasi):

css
/* === Bug' chizilishi — bosh blok rasmi === */
main > header > div > svg {
  color: var(--rang-asosiy);
}

main > header svg path[pathLength] {
  stroke-dasharray: 1;
}

@keyframes bug-chiz {
  from {
    stroke-dashoffset: 1;
  }
}

@media (prefers-reduced-motion: no-preference) {
  main > header svg path[pathLength] {
    animation: bug-chiz 1.2s ease-out 0.3s backwards;
  }
}
  • Harakatsiz holat — birinchi. stroke-dasharray: 1 va pathLength="1" bilan chiziq to'liq ko'rinadi. Animatsiya faqat no-preference da: bug' 0.3 soniyadan keyin 1.2 soniyada "chiziladi". backwards kutish vaqtida chiziqsiz kadrni ushlab turadi (keyframes).
  • Piyola inline SVG: <use> ichidagi path ga tashqi selektor yetmaydi (SVG va ikonka tizimi).
  • Sarlavhani animatsiya qilmadik. h1 yoki kun taomi rasmi shaffoflikdan chiqsa, LCP (eng katta element chizilgan vaqt) kechikadi — birinchi ekran "sekin" ko'rinadi.

6.5 yordamchi.css va skriptlar

Yechim: yordamchi.css
css
/* yordamchi.css — yordamchi class'lar va chop etish. */

.korinmas {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media print {
  :root,
  :root[data-tema] {
    color-scheme: light;
  }

  body > header,
  dialog,
  main > header > div > p:has(> .tugma),
  body > footer [role="group"] {
    display: none;
  }

  #fikrlar > ul {
    grid-auto-flow: row;
    overflow: visible;
  }

  section {
    break-inside: avoid;
  }
}

@page {
  size: a4;
  margin: 15mm;
}

Qog'ozda tugmalar va menyu keraksiz, lenta esa ustun bo'lib to'liq chiqadi — qog'ozni surib bo'lmaydi.

Skriptlarni aynan ko'chiring (JavaScript — 09-qismda). tema.js — Qorong'i rejim darsidagi fayl o'zgarishsiz. Qolgan ikkitasi:

Yechim: tema-tanlash.js va menyu.js

assets/js/tema-tanlash.js:

js
// Mavzu tugmalari: guruhni ochadi, tanlovni belgilaydi va saqlaydi
const guruh = document.getElementById("tema-tizim")?.parentElement;
const tugmalar = guruh ? guruh.querySelectorAll("button") : [];

function belgila(tema) {
  for (const tugma of tugmalar) {
    const tanlandi = tugma.id === "tema-" + tema;
    tugma.setAttribute("aria-pressed", String(tanlandi));
  }
}

for (const tugma of tugmalar) {
  tugma.addEventListener("click", () => {
    const tema = tugma.id.replace("tema-", "");
    if (tema === "tizim") {
      delete document.documentElement.dataset.tema;
    } else {
      document.documentElement.dataset.tema = tema;
    }
    try {
      localStorage.setItem("tema", tema);
    } catch {
      // saqlab bo'lmasa — tanlov shu sahifada ishlaydi
    }
    belgila(tema);
  });
}

if (guruh) {
  belgila(document.documentElement.dataset.tema ?? "tizim");
  guruh.hidden = false;
}

Mazmuni: "guruhni ko'rsat; tugma bosilsa, <html> ga data-tema qo'y (yoki "Tizim" da olib tashla), tanlovni saqla va tugmani belgila". To'liq tushuntirish — matchMedia va foydalanuvchi sozlamalari darsida.

assets/js/menyu.js:

js
// Telefon menyusi: havola bosilganda panel yopilsin
const menyu = document.getElementById("asosiy-menyu");

menyu?.addEventListener("click", (hodisa) => {
  if (hodisa.target.closest("a") && menyu.matches(":popover-open")) {
    menyu.hidePopover();
  }
});

Nega kerak? Menyu havolasi shu sahifaning bo'limiga olib boradi, popover esa faqat tashqariga bosilganda yoki Esc da yopiladi. Chrome'da skriptsiz "Savollar" bosilgach sahifa surildi, panel ochiq qoldi; skript bilan yopildi.

7. Natija: bosh blok va container query

Eng qiziq joy — kun taomi kartasi: bitta karta uch enda uch xil turadi. Quyida bosh blok (tokenlar o'rniga qiymatlar, shrift Google Fonts'dan, rasm data: SVG — natija oynasi nisbiy yo'lni ochmaydi):

html
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Lora:wght@400..700">
<style>
  :root {
    --rang-fon: #fffaf0;
    --rang-yuza: #fff;
    --rang-matn: #1f2a24;
    --rang-asosiy: #2f6b3a;
    --rang-urgu: #b4461a;
  }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    font-family: system-ui, sans-serif;
    line-height: 1.5;
    color: var(--rang-matn);
    background: var(--rang-fon);
  }
  h1, h2 {
    font-family: Lora, serif;
    line-height: 1.2;
    color: var(--rang-asosiy);
  }
  h1 {
    margin: 0 0 0.75rem;
    font-size: clamp(1.728rem, 1.5444rem + 0.8161vw, 2.1973rem);
  }
  h2 { font-size: 1.3rem; }
  main > header {
    display: grid;
    gap: 1.5rem;
    inline-size: min(100% - 2rem, 72rem);
    margin-inline: auto;
    padding-block: 1.5rem;
  }
  @media (width >= 64rem) {
    main > header {
      grid-template-columns: 3fr 2fr;
      align-items: center;
      column-gap: 3rem;
    }
  }
  main > header > div > p { margin: 0 0 1.5rem; }
  main > header > div > p:has(> .tugma) {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
  }
  .tugma {
    display: inline-flex;
    align-items: center;
    min-block-size: 3.25rem;
    padding: 0.25em 1.25em;
    border: 2px solid var(--rang-asosiy);
    border-radius: 0.5rem;
    background: var(--rang-asosiy);
    color: var(--rang-fon);
    font: inherit;
    font-size: 1.2rem;
    font-weight: 600;
    text-decoration: none;
  }
  .tugma.ikkinchi {
    background: transparent;
    color: var(--rang-asosiy);
  }
  :has(> .taom-karta) { container: kartalar / inline-size; }
  .taom-karta {
    display: grid;
    align-content: start;
    gap: 0.5rem;
    padding: 0.5rem 0.5rem 1rem;
    border-radius: 0.5rem;
    background: var(--rang-yuza);
    box-shadow: 0 1px 2px #0000001f;
  }
  .taom-karta > :not(img) { margin: 0 0.5rem; }
  .taom-karta img {
    inline-size: 100%;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    border-radius: 0.25rem;
  }
  .narx { color: var(--rang-urgu); font-weight: 700; }
  @container kartalar (width >= 28rem) {
    .taom-karta {
      grid-template-columns: minmax(8rem, 1fr) 2fr;
      grid-template-rows: auto auto 1fr;
      padding: 0.5rem;
    }
    .taom-karta img { grid-row: span 3; block-size: 100%; }
  }
</style>
<main>
  <header>
    <div>
      <h1>Rustam akaning oshi — har kuni soat 7:00 dan</h1>
      <p>Chilonzordagi oilaviy choyxona: katta qozonda osh,
        qo'lda cho'zilgan lag'mon va issiq ko'k choy.</p>
      <p>
        <button class="tugma" type="button">
          Stolni band qilish</button>
        <a class="tugma ikkinchi" href="#menyu">Menyuni ko'rish</a>
      </p>
    </div>
    <aside aria-labelledby="kun-taomi">
      <article class="taom-karta">
        <img alt="Lagandagi osh" width="480" height="300"
          src="data:image/svg+xml,%3Csvg
          xmlns='http://www.w3.org/2000/svg'
          viewBox='0 0 160 100'%3E%3Crect width='160' height='100'
          fill='%23e9dcc3'/%3E%3Cellipse cx='80' cy='52' rx='62'
          ry='38' fill='%23fffaf0'/%3E%3Cellipse cx='80' cy='52'
          rx='50' ry='29' fill='%232f6b3a'/%3E%3Cellipse cx='80'
          cy='51' rx='43' ry='23' fill='%23f3d9a4'/%3E%3C/svg%3E">
        <h2 id="kun-taomi">Kun taomi: to'y oshi</h2>
        <p class="narx">35 000 so'm</p>
        <p>Devzira guruch, qo'y go'shti, sariq sabzi va bedana
          tuxumi.</p>
      </article>
    </aside>
  </header>
</main>

Nimaga qarang:

  • 360: bitta ustun, karta tik — rasm tepada.
  • 768: hali bitta ustun, lekin karta yotiq: konteyneri 28rem dan oshdi.
  • 1280: bosh blok ikki ustunli, karta o'ng ustunda va yana tik — ekran keng, konteyner esa tor.

Media query uchinchi holatni bera olmasdi: ekran kengaygan sari karta yotiq qolardi. Container query kartaga ekranni emas, o'z joyini so'ratadi.

8. 4-bosqich: tekshirish

Sonlar — namunaviy yechimni Chrome'da (Windows) o'lchaganimiz; telefon enlari — mobil qurilma emulyatsiyasida.

8.1 Enlar va layout

En Header Kun taomi kartasi Menyu to'ri
320 2 qator, 106 px tik, 288 1 ustun
360 1 qator, "Menyu" tugmasi tik, 328 1 ustun
800 Havolalar qatorda yotiq, 751 2 ustun
1280 Havolalar qatorda tik, 439 4 ustun

To'rtala enda scrollWidth - clientWidth musbat emas (Yakuniy loyiha: portfolio darsidagi Console tekshiruvi). 320 pikselda header ikki qatorga o'tadi — buni 2-mashqda tuzatasiz.

Sarlavhalar: h1 360 da 27.6, 1280 da 35.2 piksel; h2 — 23.0 va 28.1. Tugmalar: katta — 52, kichik (mavzu) — 32 piksel, 24×24 dan katta (O'lchamlar).

8.2 Klaviatura, oyna, harakat va qorong'i rejim

  1. Tab — chap yuqorida skip link (197×45 piksel) tushib keladi.
  2. "Stolni band qilish" — oyna ochiladi, orqa sahifa surilmaydi. 360 pikselda oyna ekran pastiga yopishadi.
  3. Telefon maydoniga 12345 yozib Tab bosing: yorliq, chegara va yordam matni xato rangida, oldida "".
  4. DevTools → Rendering → prefers-reduced-motion: reduce: bug' chizilmaydi, menyu va oyna siljimasdan chiqadi, havola sahifani darhol o'tkazadi.
  5. Footer'da "Qorong'i": <html data-tema="qorongi">, fon #16301b. Sahifani yangilang — tanlov saqlangan, oq "chaqnash" yo'q (tema.js CSS'dan oldin ishlaydi). Oynadagi xato rangi — oklch(0.75 0.12 27), jadvaldagi qiymat. "Tizim" — sayt yana tizimga ergashadi.

8.3 html-validate: ikkita yangi tuzoq

npm run lint birinchi urinishda 9 ta xato berdi:

text
   55:6   error  Landmarks must have a non-empty and unique accessible name (aria-label or aria-labelledby)  unique-landmark
  219:45  error  " " should be replaced with "&nbsp;" (non-breaking space) in telephone number               tel-non-breaking
  230:6   error  Landmarks must have a non-empty and unique accessible name (aria-label or aria-labelledby)  unique-landmark
  245:10  error  Landmarks must have a non-empty and unique accessible name (aria-label or aria-labelledby)  unique-landmark
  256:14  error  <input> is missing recommended "type" attribute                                             no-implicit-input-type
  284:10  error  Landmarks must have a non-empty and unique accessible name (aria-label or aria-labelledby)  unique-landmark

✖ 9 problems (9 errors, 0 warnings)

(tel-non-breaking to'rt marta — telefon raqamidagi har bo'sh joyga; bu yerda bittasi qoldirildi.)

  • unique-landmark — "landmark nomlari takrorlanmasin". To'rtta qator: sahifa header/footer'i va oyna ichidagi header/footer. dialog HTML'ga ko'ra "bo'lim" emas, shuning uchun Chrome'ning accessibility daraxtida oynadagi header ikkinchi banner, footer ikkinchi contentinfo bo'lib chiqdi — Malika ekran o'quvchisida ikkita "banner" eshitadi. Tuzatish — role="none": element va CSS selektori qoladi, landmark roli o'chadi.
  • tel-non-breaking — telefon raqami bo'linmasin: bo'sh joylar o'rniga &nbsp; (Bo'sh joylar va qator uzish).
  • no-implicit-input-type — type="text" ni aniq yozing.

Tuzatishlardan keyin — 0 xato.

8.4 Stylelint: kanonik nomlar va standart to'plam

Sozlama — portfolio'dagi .stylelintrc.json (Stylelint va Prettier). Undagi custom-property-pattern namunasi _? bilan boshlanadi — shu muhim: «Bahor» komponentlarida pastki chiziq bilan boshlangan ichki o'zgaruvchilar ko'p (--_fon, --_matn, --_chegara — CSS o'zgaruvchilari 2).

Namunada _? bo'lmasa nima bo'lishini sinab ko'rdik (stylelint 17.15, stylelint-config-standard 40.0) — 32 ta xato, hammasi bitta qoida:

text
sayt/assets/css/komponentlar.css
   52:3   ✖  Nom: kichik harf, raqam, chiziqcha  custom-property-pattern
   53:3   ✖  Nom: kichik harf, raqam, chiziqcha  custom-property-pattern
   54:3   ✖  Nom: kichik harf, raqam, chiziqcha  custom-property-pattern

✖ 32 problems (32 errors, 0 warnings)

Shunday xatoni ko'rsangiz, sozlamangiz eski — namunani yangilang:

json
"custom-property-pattern": [
  "^_?([a-z][a-z0-9]*)(--?[a-z0-9]+)*$",
  { "message": "Nom: kichik harf, raqam, chiziqcha" }
]

Boshida ixtiyoriy _, qolgani o'zgarishsiz: --Fon yoki --rang_fon baribir ushlanadi. To'g'ri sozlama bilan:

text
> landing@1.0.0 check
> prettier --check sayt && npm run lint && npm run lint:css

Checking formatting...
All matched files use Prettier code style!

> landing@1.0.0 lint
> html-validate "sayt/**/*.html"


> landing@1.0.0 lint:css
> stylelint "sayt/**/*.css"

Uchala tekshiruv jim — 0 xato.

8.5 Lighthouse — Node API orqali

CSS yuklash samaradorligi darsida Lighthouse'ni brauzerda ishlatgan edik; loyihada uni bitta buyruq qilamiz. tekshiruv/lighthouse.mjs ni aynan ko'chiring:

js
// Lighthouse — Node API orqali (Mobile, standart sozlama)
// Avval Live Server'ni ishga tushiring: 127.0.0.1:5500
import lighthouse from "lighthouse";
import * as chromeLauncher from "chrome-launcher";

const manzil = process.argv[2] ?? "http://127.0.0.1:5500/";
const chrome = await chromeLauncher.launch({
  chromeFlags: ["--headless=new"],
});
const natija = await lighthouse(manzil, {
  port: chrome.port,
  onlyCategories: [
    "performance", "accessibility", "best-practices", "seo",
  ],
});
const { categories, audits } = natija.lhr;
for (const bolim of Object.values(categories)) {
  console.log(bolim.title.padEnd(15), Math.round(bolim.score * 100));
}
console.log(
  "FCP", audits["first-contentful-paint"].displayValue,
  "| LCP", audits["largest-contentful-paint"].displayValue,
  "| CLS", audits["cumulative-layout-shift"].displayValue,
);
chrome.kill();

chrome-launcher Lighthouse bilan birga o'rnatiladi. Chrome topilmasa, CHROME_PATH muhit o'zgaruvchisida uning yo'lini bering. npm run audit natijasi (Lighthouse 12.8.2, Mobile, lokal server):

text
Performance     99
Accessibility   100
Best Practices  100
SEO             100
FCP 1.2 s | LCP 1.7 s | CLS 0

Ikkinchi ishga tushirishda Performance 100 chiqdi, qolgani o'sha.

  • LCP elementi — kun taomi rasmi. Birinchi o'lchovda "LCP request discovery" qizil edi; fetchpriority="high" uni yo'qotdi (Resurs maslahatlari).
  • CLS 0 — rasmda width/height, shriftda size-adjust li zaxira.
  • Sariq qatorlar — matnni siqish va kesh: bu serverning ishi, GitHub Pages ikkalasini o'zi qiladi.

8.6 Boshqa brauzerlar

Landing'ni Firefox va Safari'da (yoki iPhone'da) ham oching. Yangi imkoniyat bo'lmasa — oddiyroq ko'rinish (Brauzer mosligi):

Imkoniyat Holati (web-features) Ishlamagan joyda
light-dark(), @starting-style Yangi Baseline (2024) Faqat yorug' rejim; oyna va menyu animatsiyasiz
popover (2025-01), invoker commands (2025-12) Yangi Baseline Menyu oddiy ro'yxat, oyna ochilmaydi — "Aloqa"da telefon bor
scrollbar-gutter, scrollbar-width, text-wrap: balance, @page Yangi Baseline (2024) Oddiy skrollbar va o'rash
::details-content, scrollbar-color Yangi Baseline (2025) Akkordeon darhol ochiladi, skrollbar tizim rangida
interpolate-size, overlay Baseline emas (Chrome/Edge) Balandlik va parda animatsiyasi yo'q
text-wrap: pretty, ::selection, overscroll-behavior, ascent-override Baseline emas Oddiy o'rash va belgilash; zaxira shrift Lora'dan biroz farq qiladi

Birortasi yo'q bo'lsa ham sahifa o'qiladi va mehmon telefon qilib bron qila oladi.

9. 5-bosqich: deploy — yangi landing reposi

Git'ni hali bilmaysiz (07-qismda), shuning uchun Birinchi deploy darsidagi brauzer yo'li bilan yangi repo ochamiz:

  1. github.com → + → New repository. Nomi: landing. Public. Create repository.
  2. uploading an existing file → sayt/ papkasining ichidagi hamma narsani tashlang (index.html, favicon.svg, assets/). package.json, node_modules va sozlamalar yuklanmaydi — ular faqat kompyuteringizda kerak.
  3. Izoh: Landing: birinchi versiya → Commit changes (commit — o'zgarishni saqlash, Git atamasi).
  4. Settings → Pages → Deploy from a branch → main, / (root) → Save.

Bir necha daqiqadan keyin sayt https://login.github.io/landing/ da ochiladi. Bu — loyiha sayti: u landing/ papkasida yashaydi, shuning uchun / bilan boshlangan yo'l ishlamaydi (Birinchi deploy). Landing'da hamma yo'l nisbiy (assets/css/asosiy.css, ./, #menyu).

Deploy'dan keyingi tekshiruv:

  • Network: 7 ta CSS, 3 ta JS, bahor.woff2, osh.svg — hammasi 200; Console'da CSP xatosi yo'q.
  • Telefonda: "Menyu" → havola → panel yopiladi; bron oynasi pastdan chiqadi; qorong'i tizimda sayt ham qorong'i.
  • PageSpeed Insights (pagespeed.web.dev), Mobile: siqish va kesh tavsiyalari yo'qolgan.

10. Portfolio'ga qo'shing (login.github.io)

  1. Loyiha kartasi — loyihalar/index.html dagi .loyihalar ichiga yangi article. Unda sarlavha "«Bahor» landing", sana, tavsif ("Figma maketidan dizayn tizimi, 9 ta komponent, qorong'i rejim, Lighthouse 100") va ikki havola: "Saytni ochish" (https://login.github.io/landing/) va "Kodni ko'rish". Kartada to'rt bola (h2, ikki p, ul) — subgrid'dagi span 4 o'zgarmaydi (Yakuniy loyiha: portfolio).
  2. Stylelint — portfolio sozlamasida _? borligini tekshiring (Stylelint va Prettier); npm run check — 0 xato.
  3. Qayta yuklash — o'zgargan loyihalar/index.html ni login.github.io repoga Upload files bilan, izoh Loyihalar: landing kartasi.

11. Baholash va o'zingizni tekshirish

Majburiy shartlar: landing login.github.io/landing/ da ochiladi; npm run check — 0 xato; HTML'da style atributi va tegi yo'q.

Ballar (jami 100, o'tish — 80). Talablar — "Loyiha talablari" bo'limida; jadvalda — qanday tekshirish:

Bo'lim Qanday tekshiriladi Ball
Tokenlar Ctrl + F → #: rang faqat tokenlar.css da; Figma'da rang/asosiy o'zgarsa, CSS'da bitta qator o'zgaradi 15
Qatlamlar 6 fayl, @layer e'loni, !important faqat [hidden] da 10
Komponentlar 8+ komponent, kanonik class'lar, komponentlar.css da faqat var(--…) 15
Responsive 4 enda skroll yo'q; 800 da karta yotiq, 1280 da tik — sababini ayta olasiz 10
Qorong'i rejim data-tema="qorongi" da xato matni, soya va rasm filtri ham o'zgaradi 10
Harakat reduce emulyatsiyasida hech narsa siljimaydi, hammasi ko'rinadi 10
Accessibility Faqat klaviatura bilan bron formasi to'ldiriladi; oyna landmark'lari 10
Audit Lighthouse: A11y 100, qolgani ≥ 95 10
Deploy va portfolio Deploy ro'yxati bajarildi, portfolio'da karta 10

Har bo'limda: hammasi bajarilgan — to'liq ball, bitta kamchilik — yarmi, ko'p — 0.

12. Ko'p uchraydigan xatolar

  • Komponentda primitiv. .narx { color: var(--gisht-600); } — qorong'ida narx to'q qizg'ish qoladi. Tuzatish: faqat semantik token — var(--rang-urgu).
  • LCP elementiga kirish animatsiyasi. h1 yoki kun taomi rasmi opacity: 0 dan chiqsa, Performance tushadi. Tuzatish: birinchi ekranda faqat bezak (bug', ikonka) harakatlansin.
  • / bilan boshlangan yo'l. href="/assets/css/asosiy.css" lokal serverda ishlaydi, login.github.io/landing/ da CSS yuklanmaydi. Tuzatish: nisbiy yo'l.
  • node_modules ni yuklash. Butun landing/ tashlansa — minglab fayl, yuklash to'xtaydi. Tuzatish: faqat sayt/ ning ichi.

13. Mashqlar

1-mashq (oson): o'z brendingiz tokenlari

Biznesingiz rangini tanlang (masalan, non do'koni uchun jigarrang oklch(45% 0.08 55)), uni --rang-asosiy ga ulang. Yorug' fonda kontrast ≥ 4.5 bo'lsin, qorong'i variant ham qo'shing.

Ishora: Rang nazariyasi shkalasida 600-qadam — brend rangi, qorong'iga 300-qadam. Kontrast — DevTools rang tanlagichida.

Yechim

tokenlar.css da:

css
:root {
  --jigar-300: oklch(78% 0.07 55deg);
  --jigar-600: oklch(45% 0.08 55deg);
  --rang-asosiy: var(--jigar-600);
}

@supports (color: light-dark(white, black)) {
  :root {
    --rang-asosiy: light-dark(var(--jigar-600), var(--jigar-300));
  }
}

Formula bilan: oklch(45% 0.08 55) qaymoq fonda 7.3:1, oklch(78% 0.07 55) to'q yashil fonda 7.0:1 — ikkalasi o'tadi. Semantik nom o'sha, shuning uchun komponentlar o'zgarishni sezmaydi ham. Qorong'i fon ham yashil bo'lmasin desangiz, --yashil-900 o'rniga o'z rangingizni qo'shing va kontrastni qayta hisoblang.

2-mashq (o'rta): 320 pikselda header

320 pikselda header ikki qator (106 piksel) — scroll-padding-block-start: 5rem (80 piksel) dan baland. Menyu havolasi bosilganda bo'lim sarlavhasi header ostida qoladi. Header bir qatorga sig'sin.

Ishora: brend nomi --step-1 (19 piksel). 22.5rem (360 piksel) dan tor ekranda kichikroq qadam yetadimi?

Yechim

komponentlar.css dagi header qismiga:

css
@media (width < 22.5rem) {
  body > header > a {
    font-size: var(--step-0);
  }
}

Chrome'da 320 pikselda: header bir qatorda, 61 piksel — 360 dagidek. Kengroq ekranlarda hech narsa o'zgarmaydi. scroll-padding-block-start ni kattalashtirish ham mumkin edi, lekin keng ekranda sarlavha tepasida ortiqcha bo'shliq qolardi.

3-mashq (qiyin): "bron qabul qilindi" toast'i

Bron yuborilgach, xizmat mehmonni landing'ga qaytaradi deylik. Tooltip, dropdown va toast darsidagi bron-toast ni qo'shing: popover="manual", role="status", muvaffaqiyat rangi, qorong'ida ham o'qiladigan. Uni avtomatik ko'rsatish — JavaScript ishi (09-qism). Hozir tekshirish uchun HTML'ga vaqtincha popovertarget="bron-toast" li tugma qo'ying.

Ishora: faqat tokenlar — --rang-yuza, --rang-muvaffaqiyat, --soya-3, --masofa-*. Harakat no-preference ichida.

Yechim
html
<div id="bron-toast" popover="manual" role="status">
  <p><strong>✔ Bron qabul qilindi.</strong>
    Biz qo'ng'iroq qilib tasdiqlaymiz.</p>
  <button class="tugma shaffof belgi kichik" type="button"
    popovertarget="bron-toast" popovertargetaction="hide"
    aria-label="Xabarni yopish">
    <svg class="ikonka" aria-hidden="true">
      <use href="#i-yopish"/></svg>
  </button>
</div>
css
/* === Toast (Tooltip, dropdown va toast darsi) === */
#bron-toast {
  inset: auto var(--masofa-m) var(--masofa-m) auto;
  max-inline-size: min(100% - 2 * var(--masofa-m), 22rem);
  margin: 0;
  padding: var(--masofa-s) var(--masofa-xs) var(--masofa-s)
    var(--masofa-m);
  border: 0;
  border-inline-start: 4px solid var(--rang-muvaffaqiyat);
  border-radius: var(--burchak-m);
  background: var(--rang-yuza);
  color: var(--rang-matn);
  box-shadow: var(--soya-3);
}

#bron-toast:popover-open {
  display: flex;
  gap: var(--masofa-s);
  align-items: start;
}

#bron-toast p {
  margin: 0;
}

#bron-toast strong {
  color: var(--rang-muvaffaqiyat);
}

@media (prefers-reduced-motion: no-preference) {
  #bron-toast {
    opacity: 0;
    translate: 0 1rem;
    transition:
      opacity 0.25s,
      translate 0.25s,
      display 0.25s allow-discrete,
      overlay 0.25s allow-discrete;
  }

  #bron-toast:popover-open {
    opacity: 1;
    translate: 0 0;
  }

  @starting-style {
    #bron-toast:popover-open {
      opacity: 0;
      translate: 0 1rem;
    }
  }
}
  • display: flex faqat :popover-open da — yopiq popover'ga display bersangiz, u ko'rinib qoladi (Top layer).
  • reduce da toast shunchaki paydo bo'ladi.
  • Qorong'i rejimga qo'shimcha qoida kerak emas: tokenlar o'zi o'zgaradi, muvaffaqiyat rangi yuzada 5.6:1. Holat ranglarini tokenga qo'shganimizning foydasi shu.
  • .tugma.belgi.kichik — 32×32 piksel, 24×24 dan katta.

14. 06-qism yakuni: endi nimalarni qila olasiz

Tabriklaymiz — CSS'ning ikkinchi, eng katta qismi tugadi! Endi bilasiz:

  • Harakat: transform, transition, @keyframes, skroll va sahifa o'tishlari — prefers-reduced-motion ni hurmat qilib.
  • Zamonaviy CSS: nesting, @property, @layer, popover, anchor, container queries va Baseline.
  • Vositalar: Sass, PostCSS, Stylelint, Prettier, Tailwind.
  • Dizayn: tokenlar, shkalalar, rang nazariyasi, Figma'dan kod, komponentlar tizimi.

Eng muhimi — ish usuli: maket → token → komponent → sahifa → o'lchov.

Oldinda: hozir saytni yangilash uchun fayllarni qo'lda yuklaysiz. 07-qismda — Git: har o'zgarish tarixga yoziladi, xato bo'lsa ortga qaytasiz. Qism oxirida portfolio va landing'ni GitHub Actions o'zi joylaydi (GitHub Pages va Actions).

15. Real ishda

  • Frilans. Kichik biznes uchun landing — boshlovchi frontend dasturchining eng ko'p uchraydigan birinchi buyurtmasi: mijoz Figma havolasini yuboradi, "telefonda ham chiroyli bo'lsin" deydi.
  • Intervyuda: "Qorong'i rejimni tokenlar bilan qanday qildingiz?", "Container va media query farqi?" — o'z landing'ingiz bilan javob berasiz.

Xulosa

  • Zanjir: Figma Variables → tokenlar.css → komponentlar.css → HTML; o'zgarish bitta nuqtada.
  • Holat ranglarining qorong'i qiymatlari — L 75%: fonda 6.1–6.7, yuzada 5.1–5.6.
  • 9 ta komponent o'z darsidan deyarli o'zgarishsiz keldi; kartani konteyner, menyuni popover, oynani invoker commands boshqaradi.
  • Tekshiruv topgan tuzoqlar: oyna ichidagi landmark'lar, ichki o'zgaruvchi nomlari, LCP rasmining ustuvorligi.
  • Natija: npm run check — 0 xato, Lighthouse — Accessibility 100, Performance 99–100.

Keyingi dars: Versiya nazorati nima va nega aynan Git — 07-qism boshlanadi.

Manbalar

  • Figma Learn: "Guide to variables in Figma", "Export and import variables" — help.figma.com
  • Lighthouse: "Using programmatically" (Node moduli) — github.com/GoogleChrome/lighthouse
  • Stylelint: "custom-property-pattern" qoidasi — stylelint.io/user-guide/rules/custom-property-pattern
  • html-validate: "unique-landmark", "tel-non-breaking" — html-validate.org/rules
  • HTML-AAM: header va footer elementlarining roli — w3.org/TR/html-aam-1.0
  • web-features (Baseline ma'lumotlari) — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Yakuniy loyiha: Figma'dan dizayn tizimi va responsive landing — IlmHamroh