Przejdź do głównej treści
INF.03-12-26.06-SGCzerwiec 2026 · 40 kroków · ok. 90 min · 45 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja salonu rowerowego (Nowoczesny kod (zdarzenia, walidacja))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza rowery z czterema zapytaniami, odwrócone i przeskalowane zdjęcie oraz strona index.html z kalkulatorem rozmiaru ramy - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • skrypt w osobnym pliku skrypt.js z atrybutem defer i nasłuchiwaczami zdarzeń zamiast atrybutów oninput, onclick i onload,
  • suwak i pole liczbowe zsynchronizowane w obie strony, wzrost spoza zakresu 100-220 cm daje komunikat zamiast bzdurnego wyniku, a współczynniki stoją w jednym obiekcie zamiast drabinki if,
  • data z toLocaleDateString('pl-PL') zamiast ręcznego dopisywania zer, w znaczniku <time>,
  • zmienne CSS, kontrolki wzrostu w osobnym wierszu bloku zamiast <br>, nazwy pól dla czytnika ekranu,
  • zapytania z aliasami i złączeniem JOIN ... ON.

Układ bloków zostaje na flexboksie - dlaczego nie siatka, wyjaśnia krok z układem. Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcia pochodzą z prawdziwego archiwum pliki22, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a skrypty sprawdzone w prawdziwej przeglądarce. Strona w podglądzie działa - przesuń suwak, wybierz typ roweru i kliknij „Sprawdź”.

Jak liczą się punkty. Klucz ma 45 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania to 75%, czyli 34 punkty. Przy każdym kroku jest napisane, które kryteria spełnia.

  1. Etap 1: Baza danych

    Kroki 1-7 · po tym etapie masz 6 z 45 kryteriów (pkt)

    Krok 1: Baza danych rowery

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie rowery, z zestawem polskich znaków (np. utf8_unicode_ci)

    Archiwum pliki22.7z rozpakuj hasłem {Row3RY} (razem z nawiasami klamrowymi, cyfra 3 zamiast „e”) do folderu z numerem zdającego. W phpMyAdmin Nowa → rowery, porównywanie utf8_unicode_ci, albo:

    CREATE DATABASE rowery CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
  2. Krok 2: Import tabel z pliku rowery.sql

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku rowery.sql

    Import → rowery.sql. Dwie tabele: producenci i rowery - pole producent_id roweru wskazuje producenta (relacja jeden do wielu: producent ma wiele rowerów).

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych rowery - plik import.png

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Cały ekran z paskiem zadań, zapis jako import.png.

    Typowe błędy

    Wykadrowany zrzut nie jest oceniany.

  4. Krok 4: Zapytanie 1: górskie do 4500 zł

    Kryteria: ,

    R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie pola model, rozmiar_ramy, rozmiar_kol, kolor i cena z tabeli rowery, których typ to górski oraz cena <= 4500. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT model, rozmiar_ramy, rozmiar_kol, kolor, cena FROM rowery WHERE typ = "górski" AND cena <= 4500; oraz na zrzucie widocznych jest dokładnie 9 rekordów z modelami: Fuse Comp 22, Rockhopper Comp 22, ..., Marlin 5, Roscoe 6

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie model, rozmiar ramy, rozmiar kół, kolor i cenę z tabeli rowery dla rowerów w typie górski, których cena jest mniejsza lub równa 4500

    Pięć kolumn (nazwy z ilustracji 1: rozmiar_kol bez „ó”), dwa warunki połączone AND; „mniejsza lub równa” to <=. Wynik: 9 wierszy, od Fuse Comp 22 do Roscoe 6. Zapytania zapisuj w kwerendy.txt, zrzut jako kw1.png.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT model, rozmiar_ramy, rozmiar_kol, kolor, cena
    FROM rowery
    WHERE typ = 'górski'
      AND cena <= 4500;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: ceny według producenta

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwy producentów oraz minimalną, średnią i maksymalną cenę rowerów dla każdego producenta. Kolumny z cenami mają nazwy (aliasy): „najtańszy”, „średnia cena”, „najdroższy”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT producenci.nazwa, MIN(cena) AS 'najtańszy' , AVG(cena) AS 'średnia cena', MAX(cena) AS 'najdroższy' FROM rowery JOIN producenci ON producenci.id = rowery.producent_id GROUP BY producent_id; (poprawne również GROUP BY nazwa, możliwe też porównanie kluczy po WHERE, AS opcjonalnie) oraz na zrzucie widocznych jest dokładnie 6 wierszy z nazwami: Cup, Knife, Glass, Teaspoon, Spoon, Fork; z cenami z dowolną dokładnością

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwy producentów oraz minimalną, średnią i maksymalną cenę rowerów zgrupowane dla każdego producenta. Kolumnom z cenami należy nadać nazwy (aliasy): „najtańszy”, „średnia cena”, „najdroższy”. Należy posłużyć się relacją

    • aliasy tabel r i p, złączenie po kluczu w ON,
    • trzy funkcje agregujące z aliasami kolumn w odwrotnych apostrofach - w MySQL i MariaDB to znak dla nazw; spacja i polskie litery w aliasie „średnia cena” są wtedy bezpieczne,
    • GROUP BY p.id, p.nazwa - grupowanie po kluczu producenta (dwóch producentów o tej samej nazwie nie skleiłoby się w jeden wiersz) i po kolumnie, którą zapytanie wybiera. W MySQL wystarczyłoby p.id, ale wymienienie wszystkich wybieranych kolumn bez funkcji agregującej działa w każdej bazie SQL.

    Wynik: 6 wierszy - Cup, Knife, Glass, Teaspoon, Spoon, Fork. Siódmy producent nie ma rowerów, więc złączenie wewnętrzne go pomija. Zrzut: kw2.png.

    Typowe błędy

    Bez GROUP BY funkcje agregujące liczą całą tabelę - jeden wiersz zamiast sześciu.

    kwerendy.txt Tekst zmienione: 9 linii
    -- Zapytanie 1
    SELECT model, rozmiar_ramy, rozmiar_kol, kolor, cena
    FROM rowery
    WHERE typ = 'górski'
      AND cena <= 4500;
    
    -- Zapytanie 2
    SELECT p.nazwa,
           MIN(r.cena) AS `najtańszy`,
           AVG(r.cena) AS `średnia cena`,
           MAX(r.cena) AS `najdroższy`
    FROM rowery AS r
    JOIN producenci AS p ON p.id = r.producent_id
    GROUP BY p.id, p.nazwa;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: karbonowe w rozmiarze L

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie id, model, typ i rozmiar kół rowerów karbonowych w rozmiarze ramy L. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, model, typ, rozmiar_kol FROM rowery WHERE model LIKE "%carbon%" AND rozmiar_ramy = "L"; oraz na zrzucie widocznych jest dokładnie 7 wierszy z id: 5, 7, 13, 16, 18, 29, 52

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie id, model, typ i rozmiar kół rowerów karbonowych (w nazwie modelu zawierają słowo „Carbon”) w rozmiarze ramy L

    LIKE '%Carbon%' - „Carbon” w dowolnym miejscu nazwy (porównywanie utf8_unicode_ci nie rozróżnia wielkości liter, więc wzorzec pisany jak w arkuszu złapie też „carbon”). Wynik: 7 wierszy - id 5, 7, 13, 16, 18, 29, 52. Zrzut: kw3.png.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT model, rozmiar_ramy, rozmiar_kol, kolor, cena
    FROM rowery
    WHERE typ = 'górski'
      AND cena <= 4500;
    
    -- Zapytanie 2
    SELECT p.nazwa,
           MIN(r.cena) AS `najtańszy`,
           AVG(r.cena) AS `średnia cena`,
           MAX(r.cena) AS `najdroższy`
    FROM rowery AS r
    JOIN producenci AS p ON p.id = r.producent_id
    GROUP BY p.id, p.nazwa;
    
    -- Zapytanie 3
    SELECT id, model, typ, rozmiar_kol
    FROM rowery
    WHERE model LIKE '%Carbon%'
      AND rozmiar_ramy = 'L';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: rowery gravelowe tańsze o 25%

    Kryteria:

    R.1.6 Wykonane zapytanie 4 aktualizujące cenę rowerów gravelowych obniżając ją o 25%. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE rowery SET cena=cena*0.75 WHERE typ="gravel"; oraz na zrzucie widoczny jest komunikat o poprawnie wykonanym zapytaniu, zmienionych zostało 5 wierszy, lub tabela ze zmienionymi wartościami

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: aktualizujące cenę rowerów gravelowych (typ: gravel) obniżając ją o 25%

    Obniżka o 25% to 75% dawnej ceny: SET cena = cena * 0.75. Kolumna cena jest typu DECIMAL(10,2), więc wynik zostaje zapisany dokładnie do grosza (MySQL sam zaokrągla trzecie miejsce po przecinku). phpMyAdmin melduje 5 zmienionych wierszy. Zrzut z komunikatem albo z tabelą po zmianie: kw4.png.

    Przed UPDATE na prawdziwej bazie dobrze jest uruchomić SELECT z tym samym WHERE - widać, które wiersze się zmienią, zanim się zmienią.

    Typowe błędy

    UPDATE bez WHERE obniża ceny wszystkich rowerów, a każde kolejne uruchomienie zapytania obniża ceny gravelowych o następne 25%.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT model, rozmiar_ramy, rozmiar_kol, kolor, cena
    FROM rowery
    WHERE typ = 'górski'
      AND cena <= 4500;
    
    -- Zapytanie 2
    SELECT p.nazwa,
           MIN(r.cena) AS `najtańszy`,
           AVG(r.cena) AS `średnia cena`,
           MAX(r.cena) AS `najdroższy`
    FROM rowery AS r
    JOIN producenci AS p ON p.id = r.producent_id
    GROUP BY p.id, p.nazwa;
    
    -- Zapytanie 3
    SELECT id, model, typ, rozmiar_kol
    FROM rowery
    WHERE model LIKE '%Carbon%'
      AND rozmiar_ramy = 'L';
    
    -- Zapytanie 4
    UPDATE rowery
    SET cena = cena * 0.75
    WHERE typ = 'gravel';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-21 · po tym etapie masz 23 z 45 kryteriów (pkt)

    Krok 8: Grafika 1.jpg: odwrócenie i szerokość 400 px

    Kryteria:

    R.2.1 Obraz z pliku 1.jpg został obrócony wzdłuż osi pionowej oraz przeskalowany do szerokości 400 px, odpowiadająca temu wysokość wynosi 306 px (±5 px)

    ok. 3 min
    Polecenie z arkusza

    Plik 1.jpg należy obrócić wzdłuż osi pionowej oraz przeskalować z zachowaniem proporcji tak, aby jego szerokość wynosiła dokładnie 400 px. Zmodyfikowany obraz jest widoczny na ilustracji 2

    Zdjęcie w archiwum leży kołami do góry, z przednim kołem po prawej (lewa połowa obrazu obok). Na ilustracji 2 rower stoi na kołach, a przód nadal jest po prawej - to daje tylko odwrócenie góra-dół. W GIMP-ie: Obraz → Przekształć → Odbij pionowo, potem Obraz → Skaluj obraz: szerokość 400 px przy zamkniętym łańcuchu proporcji, wysokość wychodzi sama - 306 px (klucz R.2.1: 306 ±5).

    ⚠️ Sprzeczność w arkuszu: „obrócić wzdłuż osi pionowej” dosłownie oznacza odbicie lewo-prawo, a wtedy rower nadal leżałby kołami do góry, a przód przeszedłby na lewo. Arkusz każe trzymać się ilustracji („zmodyfikowany obraz jest widoczny na ilustracji 2”), a klucz sprawdza tylko wymiar - dlatego odwrócenie góra-dół.

    Typowe błędy

    Obróć o 180° też stawia rower na kołach, ale przenosi przód na lewą stronę - to lustrzane odbicie zdjęcia z ilustracji 2.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik index.html w HTML5

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie index.html. Zapisana w języku HTML5

    Pierwsza linia <!DOCTYPE html>. Strona nie korzysta z bazy (zapytania są tylko w phpMyAdmin), więc to zwykły plik .html.

    index.html HTML nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  10. Krok 10: Język strony

    Kryteria:

    R.2.2 W pliku index.html znajduje się deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny

    lang="pl" w <html> - razem z deklaracją HTML5 to R.2.2. Przyda się też skryptowi: to dzięki polskiemu językowi strony czytnik ekranu przeczyta datę po polsku.

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  11. Krok 11: Kodowanie i tytuł strony

    Kryteria:

    R.2.3 W sekcji head jawnie podany standard kodowania polskich znaków i tytuł strony "Salon rowerowy"

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Salon rowerowy”

    <meta charset="UTF-8"> jako pierwszy element <head>, pod nim viewport (telefon nie pomniejsza strony) i <title> (R.2.3).

    index.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  12. Krok 12: Podpięcie arkusza stylów

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza

    ok. 1 min
    Polecenie z arkusza

    Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony

    <link rel="stylesheet" href="styl.css"> - całe formatowanie z tego pliku, bez atrybutów style (R.3.2).

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  13. Krok 13: Bloki: nagłówek, główny, boczny, dwie stopki

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, main, aside, footer, footer (dopuszcza się stosowanie bloków pomocniczych)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, główny, boczny, dwa bloki stopki. Podział zrealizowany za pomocą semantycznych znaczników języka HTML 5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 3

    header, main, aside i dwa footer - dokładnie lista z klucza R.2.4. <footer> może wystąpić więcej niż raz: to stopka treści, w której stoi, a nie „jedyna stopka strony”.

    index.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  14. Krok 14: Nagłówek: pięć zdjęć rowerów

    Kryteria:

    R.2.7 W bloku nagłówkowym znajduje się pięć różnych obrazów z tekstem alternatywnym "rower"

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: Pięć obrazów od 1.jpg do 5.jpg. Wszystkie obrazy mają tekst alternatywny „rower”

    Pięć <img> z alt="rower" (R.2.7). Na razie mają po 400 px - zmniejszy je styl.

    index.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  15. Krok 15: Nagłówki i pole opcji „górski z kołami 29”

    Kryteria: , ,

    R.3.7 Co najmniej jedno pole ma powiązaną etykietę (label z for i id lub zagnieżdżenie kontrolki w label)

    R.3.8 Przynajmniej jedna etykieta ma podpowiedź w postaci "dymku", np.: atrybut title

    R.3.9 Przynajmniej jedna etykieta lub przycisk w bloku głównym mają przypisaną klasę klasa_etykiety

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek pierwszego stopnia o treści „Dobierz rozmiar ramy roweru do wzrostu”. Nagłówek drugiego stopnia o treści „Wybierz rodzaj roweru”. Pole opcji, domyślnie zaznaczone, z etykietą „górski z kołami 29”. Znacznik etykiety ma przypisaną klasę klasa_etykiety i podpowiedź „górski z kołami 29” w postaci "dymku"

    <h1> i <h2>, a pod nimi pole opcji z checked i etykieta powiązana przez for/id (R.3.7) - kliknięcie kafelka zaznacza pole. „Dymek” to atrybut title (R.3.8), a klasa klasa_etykiety zrobi z etykiety ciemny kafelek (R.3.9). Identyfikatory po angielsku (mountain, potem road, trekking) - skrypt użyje ich jako kluczy współczynników.

    index.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  16. Krok 16: Pola opcji „szosa” i „trekking”, jedno zaznaczenie naraz

    Kryteria:

    R.3.4 Pierwsze pole opcji jest domyślnie zaznaczone. W jednym momencie można zaznaczyć tylko jedno pole opcji

    ok. 2 min
    Polecenie z arkusza

    Pole opcji z etykietą „szosa”. Znacznik etykiety ma przypisaną klasę klasa_etykiety i podpowiedź „szosa” w postaci "dymku". Pole opcji z etykietą „trekking”. Znacznik etykiety ma przypisaną klasę klasa_etykiety i podpowiedź „trekking” w postaci "dymku". W jednym momencie można zaznaczyć tylko jedno pole opcji

    Dwa kolejne pola na tym samym wzorze. Wszystkie trzy mają ten sam name="bike" - przeglądarka traktuje je jak jedną grupę, więc zaznaczenie jednego odznacza pozostałe; z pierwszym zaznaczonym na starcie to R.3.4.

    Typowe błędy

    Różne name w polach opcji - da się zaznaczyć wszystkie trzy naraz.

    index.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  17. Krok 17: Suwak i pole liczbowe wzrostu

    Kryteria: ,

    R.3.5 Pole typu range ma ustawione wartości min=100 oraz max=220 oraz value=150

    R.3.6 Pole edycyjne jest typu number, z domyślną wartością 150

    ok. 3 min
    Polecenie z arkusza

    Nagłówek drugiego stopnia o treści „Podaj swój wzrost”. Pole zakresu liczbowego z ustawionymi wartościami: domyślną 150, minimalną 100, maksymalną 220, zmiana wartości na suwaku powoduje wywołanie skryptu 1. Pole edycyjne typu numerycznego z wartością domyślną 150

    • <input type="range"> z min, max i value z arkusza (R.3.5), <input type="number"> z value="150" (R.3.6),
    • pole liczbowe dostaje te same min i max - strzałki pola nie wyjdą poza zakres, a przeglądarka zaznaczy wpisaną wartość spoza niego jako błędną,
    • aria-labelledby="height-title" - oba pola nie mają własnej etykiety, a nagłówek „Podaj swój wzrost” ją zastępuje: czytnik ekranu przeczyta go przy wejściu w pole,
    • oba pola stoją w <div> - blok zajmuje cały wiersz, więc przycisk dopisany pod nim stanie „poniżej” bez znacznika <br>.
    index.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  18. Krok 18: Przycisk „Sprawdź” i akapit na wynik

    Kryteria:

    R.2.6 W bloku głównym znajdują się kontrolki: trzy pola opcji, trzy etykiety (label) , pole zakresu liczbowego (type=range), pole edycyjne i przycisk "Sprawdź"

    ok. 2 min
    Polecenie z arkusza

    Poniżej przycisk „Sprawdź” z przypisaną klasą klasa_etykiety, którego kliknięcie wywołuje skrypt 2. Paragraf z wynikiem działania skryptu 2

    type="button" - przycisk niczego nie wysyła. Pusty akapit result czeka na wynik; aria-live="polite" każe czytnikowi ekranu przeczytać wynik, gdy się pojawi. Na stronie są wszystkie kontrolki z R.2.6: trzy pola opcji, trzy etykiety, suwak, pole i przycisk.

    index.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
            <button type="button" id="check" class="klasa_etykiety">Sprawdź</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  19. Krok 19: Blok boczny: logotypy z dwiema klasami

    Kryteria:

    R.3.10 Przynajmniej jeden paragraf w bloku bocznym ma przypisane dwie klasy: logo1 i prawa lub logo1 i srodek lub logo2 i prawa (użycie dwa razy atrybutu class jest błędem)

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku bocznego: nagłówek pierwszego stopnia o treści „Polecane firmy”, paragraf z przypisanymi klasami logo1 i prawa z zawartością „cup”, paragraf z przypisaną klasą logo2 z zawartością „glass”, paragraf z przypisanymi klasami logo1 i srodek z zawartością „spoon”, paragraf z przypisanymi klasami logo2 i prawa z zawartością „teaspoon”, paragraf z przypisaną klasą logo1 z zawartością „fork”

    Kilka klas w jednym atrybucie, rozdzielonych spacją: class="logo1 prawa" - element dostaje style obu klas (R.3.10). Treść małymi literami jak w arkuszu; wielkie litery z ilustracji 2 da CSS.

    Typowe błędy

    Dwa atrybuty class w jednym znaczniku - przeglądarka bierze tylko pierwszy, a klucz mówi o tym wprost przy R.3.10.

    index.html HTML zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
            <button type="button" id="check" class="klasa_etykiety">Sprawdź</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <aside>
            <h1>Polecane firmy</h1>
            <p class="logo1 prawa">cup</p>
            <p class="logo2">glass</p>
            <p class="logo1 srodek">spoon</p>
            <p class="logo2 prawa">teaspoon</p>
            <p class="logo1">fork</p>
        </aside>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  20. Krok 20: Pierwsza stopka: lista zagnieżdżona

    Kryteria:

    R.2.8 W stopce znajdują się dwie listy punktowane: pierwszy element pierwszej listy zagnieżdża drugą listę, zastosowane znaczniki <ul>, <li>. Podlista <ul> zawarta wewnątrz pierwszego <li> listy głównej

    ok. 2 min
    Polecenie z arkusza

    Zawartość pierwszego bloku stopki: nagłówek czwartego stopnia o treści „Oferowane typy rowerów”, lista punktowana (nieuporządkowana) z elementami: „górskie”, „szosowe”, „miejskie”. Pierwszy element zagnieżdża drugą listę punktowaną z elementami „koła 29 cali”, „koła 26 cali”

    Podlista to cała druga <ul> wewnątrz pierwszego <li> - po słowie „górskie”, przed jego </li> (R.2.8).

    index.html HTML zmienione: 11 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
            <button type="button" id="check" class="klasa_etykiety">Sprawdź</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <aside>
            <h1>Polecane firmy</h1>
            <p class="logo1 prawa">cup</p>
            <p class="logo2">glass</p>
            <p class="logo1 srodek">spoon</p>
            <p class="logo2 prawa">teaspoon</p>
            <p class="logo1">fork</p>
        </aside>
        <footer>
            <h4>Oferowane typy rowerów</h4>
            <ul>
                <li>górskie
                    <ul>
                        <li>koła 29 cali</li>
                        <li>koła 26 cali</li>
                    </ul>
                </li>
                <li>szosowe</li>
                <li>miejskie</li>
            </ul>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  21. Krok 21: Druga stopka: tabela z autorem i datą

    Kryteria: ,

    R.2.5 W bloku głównym znajduje się <h1>: "Dobierz rozmiar ramy roweru do wzrostu", <h2>: "Wybierz rodzaj roweru" lub "Podaj swój wzrost", w bloku bocznym <h1>: "Polecane firmy" oraz przynajmniej jeden <p>, w stopce <h4>: "Oferowane typy rowerów" lub "Informacje"

    R.2.9 W stopce znajduje się tabela o 2 wierszach i 2 kolumnach: zastosowane znaczniki <table>, <tr>, <td>; w drugiej komórce pierwszego wiersza wpisany jest numer zdającego

    ok. 2 min
    Polecenie z arkusza

    Zawartość drugiego bloku stopki: nagłówek czwartego stopnia o treści „Informacje”, tabela o dwóch wierszach i dwóch kolumnach. W komórkach pierwszego wiersza wstawione: tekst „Autor: ”, numer zdającego; w komórkach drugiego wiersza: tekst „Aktualna data: ”, efekt działania skryptu 3

    Dwa wiersze <tr> po dwie komórki <td>. W drugiej komórce pierwszego wiersza na egzaminie wpisujesz swój numer zdającego (PESEL) - tu stoi imię i nazwisko autora rozwiązania (R.2.9). W ostatniej komórce <time id="date"> - znacznik daty; skrypt 3 wpisze do niego datę czytelną dla człowieka, a w atrybucie datetime tę samą datę w formacie dla maszyn. Z tym krokiem są wszystkie nagłówki i akapity z R.2.5.

    index.html HTML zmienione: 11 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
            <button type="button" id="check" class="klasa_etykiety">Sprawdź</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <aside>
            <h1>Polecane firmy</h1>
            <p class="logo1 prawa">cup</p>
            <p class="logo2">glass</p>
            <p class="logo1 srodek">spoon</p>
            <p class="logo2 prawa">teaspoon</p>
            <p class="logo1">fork</p>
        </aside>
        <footer>
            <h4>Oferowane typy rowerów</h4>
            <ul>
                <li>górskie
                    <ul>
                        <li>koła 29 cali</li>
                        <li>koła 26 cali</li>
                    </ul>
                </li>
                <li>szosowe</li>
                <li>miejskie</li>
            </ul>
        </footer>
        <footer>
            <h4>Informacje</h4>
            <table>
                <tr>
                    <td>Autor: </td>
                    <td>Bartosz Bryniarski</td>
                </tr>
                <tr>
                    <td>Aktualna data: </td>
                    <td><time id="date"></time></td>
                </tr>
            </table>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  22. Etap 3: Styl CSS

    Kroki 22-31 · po tym etapie masz 34 z 45 kryteriów (pkt)

    Krok 22: Zmienna z kolorem i ciało strony

    ok. 2 min
    Polecenie z arkusza

    Dla ciała strony: szerokość 80%, marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę

    Na górze zmienna --dark z ciemną zielenią #02220a - arkusz używa jej w czterech miejscach (stopki, cień bloku bocznego, kafelki). Ciało strony: 80% szerokości i margin: auto - wolne miejsce po równo z lewej i z prawej, strona staje na środku.

    styl.css CSS nowy plik
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    Podgląd strony po tym kroku (index.html)
  23. Krok 23: Nagłówek przyklejony do góry okna

    Kryteria:

    R.4.5 pozycjonowanie: blok nagłówkowy position: fixed; top: 0;

    ok. 3 min
    Polecenie z arkusza

    Dla bloku nagłówkowego: wysokość 200 px, biały kolor tła, wyrównanie tekstu do środka, blok położony zawsze w odległości 0 px od góry względem okna przeglądarki, pozycja ta nie zmienia się nawet po przewinięciu całej strony

    „Względem okna” i „nie zmienia się po przewinięciu” to position: fixed z top: 0 (R.4.5) - klucz wymienia tę parę wprost. position: sticky (blok przykleja się dopiero, gdy dojdzie do krawędzi przy przewijaniu) byłby tu wygodniejszy, bo nie wyjmuje bloku z układu - ale arkusz opisuje zachowanie fixed.

    Blok z fixed wypada z układu strony: reszta treści zachowuje się tak, jakby go nie było, i wjeżdża pod niego. Dlatego blok główny i boczny dostaną margines górny 200 px, a białe tło nie pozwala przewijanej treści prześwitywać.

    styl.css CSS zmienione: 8 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.html)
  24. Krok 24: Blok główny

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: szerokość 60%, wysokość 650 px, jedynie marginesy wewnętrzne prawy i lewy 2%, jedynie margines zewnętrzny górny 200 px

    padding-inline: 2% to skrót od padding-left i padding-right naraz: „inline” to kierunek, w którym płynie tekst (w polskim - poziomo). Górny i dolny margines wewnętrzny zostają bez zmian, jak chce arkusz („jedynie prawy i lewy”). W inspektorze widać dwie wartości: padding-left: 2% i padding-right: 2%. margin-top: 200px odsuwa treść spod przyklejonego nagłówka.

    Typowe błędy

    padding: 2% dałby 2% także u góry i u dołu - arkusz mówi „jedynie prawy i lewy”.

    styl.css CSS zmienione: 7 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    Podgląd strony po tym kroku (index.html)
  25. Krok 25: Blok boczny z rozmytym cieniem

    Kryteria:

    R.4.6 cień bloku: blok boczny 0px 0px 100px #02220a

    ok. 2 min
    Polecenie z arkusza

    Dla bloku bocznego: szerokość 20%, wysokość 650 px, cień bloku bez przesunięcia (przesunięcie 0 px), z rozmyciem 100 px w kolorze #02220a, jedynie margines zewnętrzny górny 200 px

    box-shadow: 0 0 100px var(--dark) - bez przesunięcia, rozmycie 100 px: cień rozlewa się równo dookoła, na ilustracji 2 to ciemna poświata po bokach bloku (R.4.6). Zero nie potrzebuje jednostki.

    styl.css CSS zmienione: 7 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    Podgląd strony po tym kroku (index.html)
  26. Krok 26: Bloki stopki

    Kryteria:

    R.4.1 kolor tła: blok nagłówkowy - biały, dwa bloki stopki - #02220a, kolor czcionki: dwa bloki stopki - biały

    ok. 1 min
    Polecenie z arkusza

    Dla obu bloków stopki: szerokość 50%, wysokość 170 px, kolor tła #02220a, biały kolor czcionki

    Selektor footer obejmuje obie stopki. Ta reguła domyka kolory tła i tekstu (R.4.1).

    styl.css CSS zmienione: 7 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    Podgląd strony po tym kroku (index.html)
  27. Krok 27: Układ bloków zgodny z ilustracją 3

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)

    ok. 3 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, główny, boczny, dwa bloki stopki. Podział zrealizowany za pomocą semantycznych znaczników języka HTML 5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 3

    display: flex z flex-wrap: wrap na ciele strony: blok główny (60% + 2 × 2% marginesu wewnętrznego = 64%) i boczny (20%) mieszczą się w jednym rzędzie, a dwie stopki po 50% zawijają się razem do drugiego (R.3.1). Nagłówek z position: fixed w tym układzie nie uczestniczy. align-items: flex-start - bloki nie rozciągają się do wysokości najwyższego w rzędzie.

    Dlaczego nie siatka? Arkusz i klucz (R.4.2) ustawiają szerokości na samych blokach: 60%, 20%, 50%. W siatce procent szerokości elementu liczy się od jego kolumny, nie od rodzica - blok główny z width: 60% w kolumnie zająłby 60% kolumny (sprawdzone w Chrome). Szerokości trzeba by przenieść do grid-template-columns, a w tym arkuszu klucz takiej formy nie wymienia. Flex liczy procenty od rodzica, więc szerokości z arkusza działają wprost - punkt z klucza ma pierwszeństwo przed zmianą techniki.

    Typowe błędy

    Bez marginesu górnego 200 px blok główny wjeżdża pod przyklejony nagłówek - w inspektorze bloki na siebie nachodzą i R.3.1 przepada.

    styl.css CSS zmienione: 6 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    
    body {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
    }
    Podgląd strony po tym kroku (index.html)
  28. Krok 28: Zdjęcia w nagłówku

    Kryteria: ,

    R.4.2 szerokość: ciało strony - 80%, blok główny - 60%, blok boczny - 20%, bloki stopki - 50%, selektor img - 16% wysokość: blok nagłówkowy - 200px, bloki główny i boczny - 650px, bloki stopki - 170px

    R.4.4 marginesy wewnętrzne (padding) : blok główny: jedynie prawy i lewy - 2%, obrazy - 3px, marginesy zewnętrzne (margin) : ciało strony - auto, obrazy - 8px, blok główny i boczny: jedynie górny - 200px

    ok. 2 min
    Polecenie z arkusza

    Dla selektora obrazu: szerokość 16%, marginesy zewnętrzne 8 px, marginesy wewnętrzne 3 px, zaokrąglenie rogów 50%

    Selektor znacznika img (uwaga arkusza). border-radius: 50% na prostokątnym zdjęciu daje elipsę - jak na ilustracji 2. Szerokości i wysokości (R.4.2) oraz marginesy (R.4.4) są już komplet.

    styl.css CSS zmienione: 7 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    
    body {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
    }
    
    img {
        width: 16%;
        margin: 8px;
        padding: 3px;
        border-radius: 50%;
    }
    Podgląd strony po tym kroku (index.html)
  29. Krok 29: Etykiety i przycisk jako kafelki, zieleń pod kursorem

    Kryteria: ,

    R.4.9 dla klasy .klasa_etykiety: szerokość - 18%, wysokość - 60px, wysokość wiersza - 60px, sposób wyświetlania liniowo-blokowy, kolor tła - #02220a, biały kolor czcionki, wyrównanie tekstu do środka, przepełnienie elementu – ukryte (wystarczy 5 cech z 8)

    R.4.10 gdy kursor znajdzie się na elemencie z przypisaną klasą .klasa_etykiety jego kolor tła zmienia się na ForestGreen

    ok. 3 min
    Polecenie z arkusza

    Dla klasy klasa_etykiety: szerokość 18%, wysokość 60 px, wysokość wiersza (interlinia) 60 px, sposób wyświetlania liniowo-blokowy, kolor tła #02220a, biały kolor czcionki, wyrównanie tekstu do środka, obsłużone przepełnienie elementu – ukryte. W momencie gdy kursor znajdzie się na elemencie z przypisaną klasą klasa_etykiety jego kolor tła zmienia się na ForestGreen

    Etykieta jest elementem liniowym i nie przyjmuje wymiarów - display: inline-block pozwala je nadać, a kafelki nadal stoją w jednej linii. line-height równe wysokości stawia jedną linię tekstu na środku w pionie, overflow: hidden ucina to, co się nie mieści (R.4.9). Pod kursorem :hover - zieleń ForestGreen (R.4.10).

    Właściwości w kolejności: sposób wyświetlania i wymiary, potem przepełnienie, na końcu kolory i tekst - w każdej regule tak samo, więc łatwo znaleźć brakującą.

    styl.css CSS zmienione: 15 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    
    body {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
    }
    
    img {
        width: 16%;
        margin: 8px;
        padding: 3px;
        border-radius: 50%;
    }
    
    .klasa_etykiety {
        display: inline-block;
        width: 18%;
        height: 60px;
        line-height: 60px;
        overflow: hidden;
        background-color: var(--dark);
        color: white;
        text-align: center;
    }
    
    .klasa_etykiety:hover {
        background-color: ForestGreen;
    }
    Podgląd strony po tym kroku (index.html)
  30. Krok 30: Logotypy

    Kryteria:

    R.4.8 dla klasy .logo1 : wielkość czcionki - 50px, tekst przekształcony na wielkie litery, pochylenie czcionki dla klasy .logo2 : wielkość czcionki - 30px, podkreślenie, krój czcionki Impact

    ok. 2 min
    Polecenie z arkusza

    Dla klasy logo1: wielkość czcionki 50 px, tekst przekształcony na wielkie litery, czcionka pochylona. Dla klasy logo2: wielkość czcionki 30 px, podkreślenie, krój czcionki Impact

    text-transform: uppercase pokazuje „CUP”, choć w HTML-u jest „cup”; pochylenie to font-style: italic, podkreślenie - text-decoration: underline (R.4.8).

    styl.css CSS zmienione: 12 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    
    body {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
    }
    
    img {
        width: 16%;
        margin: 8px;
        padding: 3px;
        border-radius: 50%;
    }
    
    .klasa_etykiety {
        display: inline-block;
        width: 18%;
        height: 60px;
        line-height: 60px;
        overflow: hidden;
        background-color: var(--dark);
        color: white;
        text-align: center;
    }
    
    .klasa_etykiety:hover {
        background-color: ForestGreen;
    }
    
    .logo1 {
        font-size: 50px;
        font-style: italic;
        text-transform: uppercase;
    }
    
    .logo2 {
        font-family: Impact;
        font-size: 30px;
        text-decoration: underline;
    }
    Podgląd strony po tym kroku (index.html)
  31. Krok 31: Klasy wyrównania i listy bez punktorów

    Kryteria: ,

    R.4.3 wyrównanie tekstu do środka: blok nagłówkowy, klasa .srodek wyrównanie tekstu do prawej strony: klasa .prawa

    R.4.7 zaokrąglenie rogów: obrazy - 50%, styl listy: selektor ul - brak punktora

    ok. 1 min
    Polecenie z arkusza

    Dla klasy prawa: wyrównanie tekstu do prawej. Dla klasy srodek: wyrównanie tekstu do środka. Dla selektora listy punktowanej: brak punktora

    Dwie małe klasy wyrównania (R.4.3) - „cup” bierze rozmiar z logo1, a wyrównanie z prawa. ul (selektor znacznika, jak wymaga uwaga) bez punktorów, także w podliście (R.4.7).

    styl.css CSS zmienione: 12 linii
    :root {
        --dark: #02220a;
    }
    
    body {
        width: 80%;
        margin: auto;
    }
    
    header {
        position: fixed;
        top: 0;
        height: 200px;
        background-color: white;
        text-align: center;
    }
    
    main {
        width: 60%;
        height: 650px;
        padding-inline: 2%;
        margin-top: 200px;
    }
    
    aside {
        width: 20%;
        height: 650px;
        margin-top: 200px;
        box-shadow: 0 0 100px var(--dark);
    }
    
    footer {
        width: 50%;
        height: 170px;
        background-color: var(--dark);
        color: white;
    }
    
    body {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
    }
    
    img {
        width: 16%;
        margin: 8px;
        padding: 3px;
        border-radius: 50%;
    }
    
    .klasa_etykiety {
        display: inline-block;
        width: 18%;
        height: 60px;
        line-height: 60px;
        overflow: hidden;
        background-color: var(--dark);
        color: white;
        text-align: center;
    }
    
    .klasa_etykiety:hover {
        background-color: ForestGreen;
    }
    
    .logo1 {
        font-size: 50px;
        font-style: italic;
        text-transform: uppercase;
    }
    
    .logo2 {
        font-family: Impact;
        font-size: 30px;
        text-decoration: underline;
    }
    
    .prawa {
        text-align: right;
    }
    
    .srodek {
        text-align: center;
    }
    
    ul {
        list-style-type: none;
    }
    Podgląd strony po tym kroku (index.html)
  32. Etap 4: JavaScript

    Kroki 32-38 · po tym etapie masz 45 z 45 kryteriów (pkt)

    Krok 32: Skrypt 1: suwak przepisuje wzrost do pola

    Kryteria: , ,

    R.3.3 Zdefiniowano dla przycisku zdarzenie związane z jego wciśnięciem lub dla pola zakresu zdarzenie związane ze zmianą jego wartości, które uruchamia funkcję skryptu

    R.5.3 Skrypt pobiera dane z przynajmniej jednej kontrolki

    R.5.4 Skrypt 1 wyświetla w polu edycyjnym wartość pobraną z suwaka

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1: wykonywany po zmianie wartości pola zakresu liczbowego (po przesunięciu suwaka), pobiera wartość z pola zakresu liczbowego i wpisuje ją do pola edycyjnego

    Skrypt w osobnym pliku skrypt.js, w <head> z defer - wykona się po wczytaniu całego HTML-u. Dwie stałe z polami wzrostu i nasłuchiwacz zdarzenia input na suwaku: zachodzi przy każdym ruchu, więc liczba w polu zmienia się na bieżąco (change zaszłoby dopiero po puszczeniu suwaka). Funkcja strzałkowa przepisuje value suwaka do pola (R.3.3, R.5.3, R.5.4). Przesuń suwak w podglądzie.

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Salon rowerowy</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <img src="1.jpg" alt="rower">
            <img src="2.jpg" alt="rower">
            <img src="3.jpg" alt="rower">
            <img src="4.jpg" alt="rower">
            <img src="5.jpg" alt="rower">
        </header>
        <main>
            <h1>Dobierz rozmiar ramy roweru do wzrostu</h1>
            <h2>Wybierz rodzaj roweru</h2>
            <input type="radio" name="bike" id="mountain" checked>
            <label for="mountain" class="klasa_etykiety" title="górski z kołami 29">górski z kołami 29</label>
            <input type="radio" name="bike" id="road">
            <label for="road" class="klasa_etykiety" title="szosa">szosa</label>
            <input type="radio" name="bike" id="trekking">
            <label for="trekking" class="klasa_etykiety" title="trekking">trekking</label>
            <h2 id="height-title">Podaj swój wzrost</h2>
            <div>
                <input type="range" id="height-range" min="100" max="220" value="150" aria-labelledby="height-title">
                <input type="number" id="height" min="100" max="220" value="150" aria-labelledby="height-title">
            </div>
            <button type="button" id="check" class="klasa_etykiety">Sprawdź</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <aside>
            <h1>Polecane firmy</h1>
            <p class="logo1 prawa">cup</p>
            <p class="logo2">glass</p>
            <p class="logo1 srodek">spoon</p>
            <p class="logo2 prawa">teaspoon</p>
            <p class="logo1">fork</p>
        </aside>
        <footer>
            <h4>Oferowane typy rowerów</h4>
            <ul>
                <li>górskie
                    <ul>
                        <li>koła 29 cali</li>
                        <li>koła 26 cali</li>
                    </ul>
                </li>
                <li>szosowe</li>
                <li>miejskie</li>
            </ul>
        </footer>
        <footer>
            <h4>Informacje</h4>
            <table>
                <tr>
                    <td>Autor: </td>
                    <td>Bartosz Bryniarski</td>
                </tr>
                <tr>
                    <td>Aktualna data: </td>
                    <td><time id="date"></time></td>
                </tr>
            </table>
        </footer>
    </body>
    </html>
    skrypt.js JavaScript nowy plik
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    Podgląd strony po tym kroku (index.html)
  33. Krok 33: Pole przesuwa suwak

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: wylicza rozmiar ramy i wypisuje informacje na ten temat. Pobiera wartość z pola edycyjnego lub pola zakresu liczbowego (wzrost)

    Arkusz pozwala skryptowi 2 czytać pole albo suwak - i właśnie na tej dowolności przewraca się ilustracja 4: wzrost 189 wpisany w pole, a wynik policzony z suwaka stojącego w innym miejscu (szczegóły w wersji podstawowej). Drugi nasłuchiwacz na polu przesuwa suwak przy każdej zmianie liczby - oba pola zawsze pokazują to samo i nie ma znaczenia, które z nich czyta skrypt.

    skrypt.js JavaScript zmienione: 4 linie
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    Podgląd strony po tym kroku (index.html)
  34. Krok 34: Skrypt 2: wzrost z pola po kliknięciu „Sprawdź”

    ok. 3 min
    Polecenie z arkusza

    Wykonywany po kliknięciu na przycisk „Sprawdź”. Pobiera wartość z pola edycyjnego lub pola zakresu liczbowego (wzrost)

    • FACTORS - obiekt ze współczynnikami z arkusza, kluczem jest id pola opcji. Nazwa wielkimi literami to zwyczaj dla stałych konfiguracyjnych: wartości, których program nie zmienia,
    • checkFrame uruchamia nasłuchiwacz kliknięcia przycisku,
    • heightField.valueAsNumber daje liczbę od razu (zamiast tekstu z value), a dla pustego pola - NaN (nie-liczbę), co za chwilę pozwoli wykryć brak wzrostu.
    skrypt.js JavaScript zmienione: 13 linii
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    
    const FACTORS = {
        mountain: 0.26,
        road: 0.3,
        trekking: 0.28,
    };
    const result = document.querySelector('#result');
    
    function checkFrame() {
        const height = heightField.valueAsNumber;
    }
    
    document.querySelector('#check').addEventListener('click', checkFrame);
    Podgląd strony po tym kroku (index.html)
  35. Krok 35: Skrypt 2: współczynnik z zaznaczonego pola i obliczenie

    Kryteria: , ,

    R.5.5 Sprawdzone przynajmniej dla jednego pola radio czy jest wybrane (np.: w warunku zapisane sprawdzenie wartości checked dla elementu)

    R.5.6 Skrypt 2 oblicza rozmiar ramy - mnoży wzrost przez współczynnik 0.26 dla zaznaczonego pierwszego radio; 0.3 - drugiego; 0.28 trzeciego, a następnie dzieli przez 2.54

    R.5.7 Skrypt 2 zaokrągla obliczony rozmiar ramy do liczby całkowitej

    ok. 4 min
    Polecenie z arkusza

    Oblicza rozmiar ramy roweru, w ten sposób, że mnoży wzrost przez współczynnik zależny od typu roweru. Typ roweru pobiera z pól opcji: gdy zaznaczono pole „górski z kołami 29” wartość współczynnika wynosi 0,26, gdy zaznaczono pole „szosa” wartość współczynnika wynosi 0,3, gdy zaznaczono pole „trekking” wartość współczynnika wynosi 0,28. Następnie dzieli wynik przez 2,54 i zaokrągla do liczby całkowitej

    • querySelectorAll('input[name="bike"]') - wszystkie trzy pola opcji; [...] zamienia wynik na zwykłą tablicę,
    • find((radio) => radio.checked) zwraca pierwsze pole, którego właściwość checked jest prawdziwa, czyli zaznaczone (R.5.5),
    • FACTORS[selected.id] - współczynnik po identyfikatorze pola: mountain → 0.26, road → 0.3, trekking → 0.28. Jedna linia zamiast trzech gałęzi if - nowy typ roweru to nowe pole opcji i jedna pozycja w obiekcie (R.5.6),
    • wzrost razy współczynnik to długość ramy w cm, dzielenie przez 2,54 zamienia ją na cale, Math.round zaokrągla do całości (R.5.7). Ułamki w JavaScripcie z kropką: 0.26, 2.54.
    skrypt.js JavaScript zmienione: 2 linie
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    
    const FACTORS = {
        mountain: 0.26,
        road: 0.3,
        trekking: 0.28,
    };
    const result = document.querySelector('#result');
    
    function checkFrame() {
        const height = heightField.valueAsNumber;
        const selected = [...document.querySelectorAll('input[name="bike"]')].find((radio) => radio.checked);
        const frame = Math.round(height * FACTORS[selected.id] / 2.54);
    }
    
    document.querySelector('#check').addEventListener('click', checkFrame);
    Podgląd strony po tym kroku (index.html)
  36. Krok 36: Skrypt 2: wynik pod przyciskiem

    Kryteria:

    R.5.8 Skrypt 2 wyświetla pod przyciskiem „Sprawdź” wyliczony rozmiar ramy w formacie: „Zalecany rozmiar ramy to: <rozmiar> cali", gdzie <rozmiar> oznacza obliczoną wartość

    ok. 2 min
    Polecenie z arkusza

    Wyliczony rozmiar ramy wypisuje pod przyciskiem „Sprawdź” w formacie: „Zalecany rozmiar ramy to: <rozmiar> cali”, gdzie <rozmiar> oznacza obliczoną wartość

    Szablon tekstowy w odwrotnych apostrofach: ${frame} wstawia liczbę w środek napisu - bez sklejania plusami i pilnowania spacji. textContent, bo to zwykły tekst (R.5.8). Sprawdzone w przeglądarce: szosa i 189 cm - „22 cali”, górski i 150 cm - „15 cali”, trekking i 180 cm - „20 cali”.

    skrypt.js JavaScript zmienione: 1 linia
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    
    const FACTORS = {
        mountain: 0.26,
        road: 0.3,
        trekking: 0.28,
    };
    const result = document.querySelector('#result');
    
    function checkFrame() {
        const height = heightField.valueAsNumber;
        const selected = [...document.querySelectorAll('input[name="bike"]')].find((radio) => radio.checked);
        const frame = Math.round(height * FACTORS[selected.id] / 2.54);
        result.textContent = `Zalecany rozmiar ramy to: ${frame} cali`;
    }
    
    document.querySelector('#check').addEventListener('click', checkFrame);
    Podgląd strony po tym kroku (index.html)
  37. Krok 37: Skrypt 2: wzrost spoza zakresu

    ok. 3 min
    Polecenie z arkusza

    Pobiera wartość z pola edycyjnego lub pola zakresu liczbowego (wzrost)

    Wersja podstawowa przy pustym polu liczy „0 cali”, a przy wzroście 40 cm - „4 cali”. Tu na początku funkcji jest strażnik: jeśli wzrost nie jest w zakresie suwaka (100-220 cm), skrypt pisze, czego oczekuje, i kończy działanie (return).

    Warunek jest zapisany odwrotnie - !(height >= 100 && height <= 220) zamiast height < 100 || height > 220 - bo NaN z pustego pola nie spełnia żadnego porównania: NaN < 100 i NaN > 220 są fałszywe, więc druga postać przepuściłaby puste pole. Sprawdzone w przeglądarce: puste pole i 40 cm dają komunikat.

    skrypt.js JavaScript zmienione: 4 linie
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    
    const FACTORS = {
        mountain: 0.26,
        road: 0.3,
        trekking: 0.28,
    };
    const result = document.querySelector('#result');
    
    function checkFrame() {
        const height = heightField.valueAsNumber;
        if (!(height >= 100 && height <= 220)) {
            result.textContent = 'Podaj wzrost od 100 do 220 cm.';
            return;
        }
        const selected = [...document.querySelectorAll('input[name="bike"]')].find((radio) => radio.checked);
        const frame = Math.round(height * FACTORS[selected.id] / 2.54);
        result.textContent = `Zalecany rozmiar ramy to: ${frame} cali`;
    }
    
    document.querySelector('#check').addEventListener('click', checkFrame);
    Podgląd strony po tym kroku (index.html)
  38. Krok 38: Skrypt 3: dzisiejsza data DD.MM.RRRR po załadowaniu strony

    Kryteria: , , ,

    R.5.1 Skrypt napisany w języku JavaScript i zawiera on przynajmniej jedną poprawną składniowo instrukcję wynikającą z treści zadania

    R.5.2 Zastosowana przynajmniej jedna zmienna oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania

    R.5.9 Skrypt 3 tworzy obiekt typu Date

    R.5.10 Skrypt 3 wyświetla w stopce aktualną datę

    ok. 3 min
    Polecenie z arkusza

    Skrypt 3: wykonywany po załadowaniu strony, wyświetla aktualną (dzisiejszą) datę w formacie DD.MM.RRRR

    • new Date() - obiekt z bieżącą datą i godziną (R.5.9),
    • toLocaleDateString('pl-PL', {...}) zapisuje datę według polskich zwyczajów: day: '2-digit' i month: '2-digit' dokładają zero przed dniem i miesiącem, a polski format rozdziela je kropkami - wychodzi DD.MM.RRRR bez ręcznego + 1 przy miesiącu i bez if dla zer (R.5.10),
    • date.dateTime ustawia atrybut datetime znacznika <time> na datę w formacie ISO (RRRR-MM-DD) - tak datę czytają wyszukiwarki i programy. Format ISO daje tu toLocaleDateString('sv-SE'): szwedzki zapis daty to właśnie RRRR-MM-DD, liczony w czasie lokalnym,
    • window.addEventListener('load', showDate) - „po załadowaniu strony” dosłownie: zdarzenie load zachodzi, gdy wczytane są wszystkie zasoby, także zdjęcia. Sam defer uruchamia skrypt wcześniej, zaraz po przeczytaniu HTML-u.

    Nazwy (heightRange, heightField, FACTORS, result, checkFrame, selected, frame, showDate, today) mówią, czemu służą (R.5.1, R.5.2).

    Typowe błędy

    Kusi toISOString().slice(0, 10), ale ta metoda podaje czas UTC - między północą a 1:00 (latem 2:00) polskiego czasu to jeszcze wczorajsza data.

    skrypt.js JavaScript zmienione: 9 linii
    const heightRange = document.querySelector('#height-range');
    const heightField = document.querySelector('#height');
    
    heightRange.addEventListener('input', () => {
        heightField.value = heightRange.value;
    });
    
    heightField.addEventListener('input', () => {
        heightRange.value = heightField.value;
    });
    
    const FACTORS = {
        mountain: 0.26,
        road: 0.3,
        trekking: 0.28,
    };
    const result = document.querySelector('#result');
    
    function checkFrame() {
        const height = heightField.valueAsNumber;
        if (!(height >= 100 && height <= 220)) {
            result.textContent = 'Podaj wzrost od 100 do 220 cm.';
            return;
        }
        const selected = [...document.querySelectorAll('input[name="bike"]')].find((radio) => radio.checked);
        const frame = Math.round(height * FACTORS[selected.id] / 2.54);
        result.textContent = `Zalecany rozmiar ramy to: ${frame} cali`;
    }
    
    document.querySelector('#check').addEventListener('click', checkFrame);
    
    function showDate() {
        const today = new Date();
        const date = document.querySelector('#date');
        date.textContent = today.toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit', year: 'numeric' });
        date.dateTime = today.toLocaleDateString('sv-SE');
    }
    
    window.addEventListener('load', showDate);
    Podgląd strony po tym kroku (index.html)
  39. Etap 5: Porównanie z wersją podstawową

    Krok 39 · po tym etapie masz 45 z 45 kryteriów (pkt)

    Krok 39: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 2 min

    Obie wersje dają komplet 45 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    zdarzeniaoninput, onclick, <body onload> w HTMLaddEventListener w skrypt.js z defer
    suwak i poletylko suwak → polew obie strony
    zaznaczone pole opcjiif / else if / else z checkedfind(radio => radio.checked) + obiekt FACTORS
    zły wzrost„0 cali”, „4 cali”komunikat o zakresie 100-220 cm
    datagetDate, getMonth() + 1, dopisywanie zertoLocaleDateString('pl-PL'), <time datetime>
    wynikinnerHTML i plusytextContent i szablon tekstowy
    przycisk pod polami<br>pola w <div>
    CSSkolory wpisane w regułyzmienna --dark, padding-inline

    Układ bloków jest w obu wersjach na flexboksie - tu szerokości bloków z klucza nie dają się przenieść do siatki bez zmiany formy, którą klucz ocenia. Na egzaminie wersja podstawowa trzyma się dosłownie tabel arkusza; z nowoczesnej najbardziej opłaca się przenieść synchronizację pola z suwakiem - to jedna funkcja, a usuwa rozjazd widoczny nawet na ilustracji 4.

    Podgląd strony bez zmian - taki jak po kroku 38.

  40. Etap 6: Oddanie pracy

    Krok 40 · po tym etapie masz 45 z 45 kryteriów (pkt)

    Krok 40: Plik przegladarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przegladarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W tym arkuszu plik nazywa się przegladarka.txt - bez „ą”, jak w treści zadania. W folderze z numerem zdającego: 1.jpg … 5.jpg (1.jpg po przeróbce), index.html, skrypt.js, styl.css, kwerendy.txt, import.png, kw1.png … kw4.png, przegladarka.txt. Płytę sprawdź po nagraniu.

    przegladarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 38.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.