Przejdź do głównej treści
INF.03-02-25.01-SGStyczeń 2025 · 50 kroków · ok. 99 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Strona firmy montażu paneli (Nowoczesny kod (grid, osobny skrypt))

To drugi sposób rozwiązania tego samego arkusza: baza przewozy z czterema zapytaniami, grafika z napisem na przezroczystym tle, trzy strony - index.html z rodzajami paneli, oferta.html z tabelą o scalonych komórkach i koszty.html z kalkulatorem - oraz style w style.css. Obie wersje spełniają wszystkie 40 kryteriów klucza - różni je sposób pisania kodu, nie wynik.

Czym ta wersja różni się od podstawowej:

  • kolory z arkusza w zmiennych CSS - przy okazji widać, że #A0522D z gradientu to po prostu kolor Sienna, a #F5DEB3 to Wheat,
  • tabela z częścią nagłówkową <thead> i komórkami nagłówkowymi <th> w dwóch pierwszych wierszach,
  • pola opcji ułożone siatką CSS zamiast znaczników <br>, każde pole razem z etykietą w znaczniku <label>, grupa opisana dla czytnika ekranu,
  • skrypt w osobnym pliku koszty.js z defer i addEventListener - w HTML nie ma onclick,
  • stawki w obiekcie zamiast drabinki warunków, sprawdzenie wymiarów i wybranego panelu od pierwszej linii, liczby w polskim formacie,
  • zapytanie 3 z LEFT JOIN - w wyniku są też osoby bez zadań, z liczbą 0 (klucz uznaje obie wersje).

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Baza i zdjęcia pochodzą z prawdziwego archiwum pliki12, zapytania są sprawdzone na MariaDB, a kalkulator działa w podglądzie i jest sprawdzony w prawdziwej przeglądarce Chrome.

Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych przewozy

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki12.7z ma hasło P@neLePodLoGowe!+ (z wykrzyknikiem i plusem na końcu); zawiera baza.sql i trzy zdjęcia paneli. W phpMyAdmin: Nowa, nazwa przewozy, porównywanie z polskimi znakami - np. utf8mb4_polish_ci (utf8mb4 mieści każdy znak Unicode) albo utf8_unicode_ci z przykładu w arkuszu.

    Kolumny tekstowe w baza.sql mają porównywanie zapisane we własnej definicji (utf8_polish_ci), więc wybór dla bazy ich nie zmienia.

  2. Krok 2: Import tabel z pliku baza.sql

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum.

    Zaznaczona baza przewozy, Import → baza.sql → Wykonaj. Dwie tabele z ilustracji 1: osoby (6 osób) i zadania (7 zadań). Relacja jeden do wielu: zadania.osoba_id → osoby.id_osoba.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych przewozy, co jest udokumentowane w pliku 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.

    Print Screen i zapis jako import.png - w tym arkuszu wszystkie zrzuty są w PNG.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: osoby z imieniem na literę A

    Kryteria: ,

    R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie imiona i nazwiska osób, których imiona rozpoczynają się od litery A. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT imie, nazwisko FROM osoby WHERE imie LIKE "A%"; oraz wynik działania udokumentowany zrzutem, na którym widoczne są dokładnie 2 wiersze z danymi Anna A. oraz Arkadiusz G.

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie imiona i nazwiska osób, których imiona rozpoczynają się od litery A.

    Każda klauzula w osobnej linii. Napis we wzorcu w apostrofach 'A%' - to zapis ze standardu SQL; cudzysłów "A%" MariaDB też przyjmie, ale w innych bazach (np. PostgreSQL) cudzysłów oznacza nazwę kolumny.

    • WHERE imie LIKE 'A%' - A na początku, % zastępuje dowolny ciąg znaków.

    Wynik: Anna A. i Arkadiusz G. Zapytanie trafia do kwerendy.txt, zrzut do kw1.png.

    Typowe błędy

    WHERE imie = 'A%' szuka imienia dosłownie „A%” - % działa jako wzorzec tylko z LIKE.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE imie LIKE 'A%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: zadania z meblami według daty

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwy zadań (pole zadanie ) i daty, w których nazwie zadania zawiera się słowo „mebli”, posortowane rosnąco według daty. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT zadanie, data FROM zadania WHERE zadanie LIKE "%mebli%" ORDER BY data; (opcjonalnie ASC ) oraz wynik działania udokumentowany zrzutem, na którym widoczne są dokładnie 2 rekordy z datami 2019-11-06 i 2021-01-26

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwy zadań (pole zadanie) i daty, w których w nazwie zadania zawiera się słowo „mebli”, posortowane rosnąco według daty.

    • WHERE zadanie LIKE '%mebli%' - słowo w dowolnym miejscu nazwy,
    • ORDER BY data - rosnąco jest domyślnie.

    Wynik: dwa przewozy mebli, 2019-11-06 i 2021-01-26. Zrzut: kw2.png.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE imie LIKE 'A%';
    
    -- Zapytanie 2
    SELECT zadanie, data
    FROM zadania
    WHERE zadanie LIKE '%mebli%'
    ORDER BY data;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: liczba zadań każdej osoby, także zera

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie pogrupowane nazwiska osób i odpowiadającą im liczbę zadań. Kolumna z liczbą zadań jest nazwana „Liczba zadań” (alias). W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT nazwisko, COUNT(zadania.id_zadania) AS "Liczba zadań" FROM osoby JOIN zadania ON id_osoba = osoba_id GROUP BY nazwisko; (możliwe również LEFTJOIN oraz porównanie kluczy po WHERE ) oraz wynik działania udokumentowany zrzutem, na którym są widoczne dokładnie 4 rekordy z danymi: A. 2; G. 2; K. 1; W. 2 lub 6 rekordów A. 2; C. 0; G. 2; K. 1; L. 0; W. 2

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pogrupowane nazwiska osób i odpowiadającą im liczbę zadań. Kolumnie z liczbą zadań należy nadać nazwę (alias) „Liczba zadań”. Należy posłużyć się relacją.

    • FROM osoby AS o LEFT JOIN zadania AS z ON z.osoba_id = o.id_osoba - LEFT JOIN bierze każdą osobę z lewej tabeli, także taką, która nie ma żadnego zadania (wtedy kolumny z zadania są puste, NULL),
    • GROUP BY o.nazwisko - jeden wiersz na nazwisko,
    • COUNT(z.id_zadania) - liczy tylko niepuste identyfikatory zadań, więc osoba bez zadań dostaje 0,
    • AS "Liczba zadań" - alias ze spacją i polską literą, w cudzysłowie.

    Wynik: sześć wierszy - A. 2, C. 0, G. 2, K. 1, L. 0, W. 2. Klucz uznaje ten wynik i wersję ze zwykłym JOIN (cztery wiersze, bez osób bez zadań). Która jest lepsza, zależy od pytania: zestawienie obciążenia pracowników powinno pokazać też tych, którzy nie mają nic. Zrzut: kw3.png.

    Typowe błędy

    COUNT(*) przy LEFT JOIN liczy wiersz osoby bez zadań - C. i L. dostałyby 1 zamiast 0. Trzeba liczyć kolumnę z tabeli zadania.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE imie LIKE 'A%';
    
    -- Zapytanie 2
    SELECT zadanie, data
    FROM zadania
    WHERE zadanie LIKE '%mebli%'
    ORDER BY data;
    
    -- Zapytanie 3
    SELECT o.nazwisko, COUNT(z.id_zadania) AS "Liczba zadań"
    FROM osoby AS o
    LEFT JOIN zadania AS z ON z.osoba_id = o.id_osoba
    GROUP BY o.nazwisko;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: usunięcie kolumny telefon

    Kryteria:

    R.1.6 Wykonane zapytanie 4 usuwające kolumnę telefon z tabeli osoby . W pliku z kwerendami lub na zrzucie istnieje zapis np. ALTER TABLE osoby DROP COLUMN telefon; oraz wynik działania udokumentowany zrzutem, na którym jest widoczny komunikat o poprawnie wykonanym zapytaniu lub tabela osoby bez kolumny telefon

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: usuwające kolumnę telefon z tabeli osoby.

    Zmiana budowy tabeli: ALTER TABLE osoby DROP COLUMN telefon. Kolumna znika razem z danymi i nie da się jej przywrócić bez kopii zapasowej - dlatego to zapytanie stoi na końcu, a w pracy z prawdziwą bazą robi się wcześniej eksport. Zrzut: kw4.png.

    Typowe błędy

    DELETE usuwa wiersze, nie kolumny - kolumny zmienia się tylko przez ALTER TABLE.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE imie LIKE 'A%';
    
    -- Zapytanie 2
    SELECT zadanie, data
    FROM zadania
    WHERE zadanie LIKE '%mebli%'
    ORDER BY data;
    
    -- Zapytanie 3
    SELECT o.nazwisko, COUNT(z.id_zadania) AS "Liczba zadań"
    FROM osoby AS o
    LEFT JOIN zadania AS z ON z.osoba_id = o.id_osoba
    GROUP BY o.nazwisko;
    
    -- Zapytanie 4
    ALTER TABLE osoby
    DROP COLUMN telefon;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strony HTML

    Kroki 8-32 · po tym etapie masz 19 z 40 kryteriów (pkt)

    Krok 8: Kolor napisu pobrany z laminowane.png

    Kryteria:

    R.2.2 Na grafice znajduje się napis "Montaż paneli podłogowych" zapisany czcionką bezszeryfową o dowolnym odcieniu koloru brązowego

    ok. 3 min
    Polecenie z arkusza

    Kolor wypełnienia liter obrazu otrzymany z grafiki laminowane.png. Czynność tą udokumentuj zrzutem ekranu o nazwie kolor.png.

    W GIMP-ie laminowane.png i narzędzie Pipeta (klawisz O): kliknięcie w brązowe pole deski ustawia kolor pierwszoplanowy. Print Screen z obrazem i oknem koloru - kolor.png. Klucz uzna dowolny odcień brązu (R.2.2).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Grafika montaz_paneli.png

    Kryteria: ,

    R.2.1 Grafika z pliku montaz_paneli.png ma wymiary dokładnie: szerokość 400 px i wysokość 50 px oraz przezroczyste tło

    R.2.2 Na grafice znajduje się napis "Montaż paneli podłogowych" zapisany czcionką bezszeryfową o dowolnym odcieniu koloru brązowego

    ok. 4 min
    Polecenie z arkusza

    Przygotowanie grafiki o nazwie montaz_paneli.png: szerokość obrazu 400 px, wysokość 50 px, tło przezroczyste, napis "Montaż paneli podłogowych", pogrubioną czcionką bezszeryfową (sans-serif), w rozmiarze 27.

    Plik → Nowy: 400 × 50 px, w opcjach zaawansowanych Wypełnienie: Przezroczystość. Narzędzie Tekst: bezszeryfowa pogrubiona (np. Arial Bold), 27 px, kolor z pipety, napis wyrównany do środka. Eksportuj jako montaz_paneli.png - PNG zachowuje przezroczystość (R.2.1, R.2.2).

    Typowe błędy

    Nowy obraz z białym wypełnieniem nie ma przezroczystego tła - na gradiencie widać biały prostokąt i R.2.1 przepada.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Trzy zdjęcia paneli: dokładnie 500 × 70 px

    Kryteria:

    R.2.3 Grafiki laminowane.png , winylowe.png, deska.png mają wymiary dokładnie: szerokość 500 px i wysokość 70 px

    ok. 4 min
    Polecenie z arkusza

    Przygotowanie grafik laminowane.png, winylowe.png, deska.png: Obrazy należy przeskalować dokładnie do szerokości 500 px i wysokości 70 px.

    Obraz → Skaluj obraz, rozpięty łańcuch proporcji, 500 i 70 - zdjęcie się spłaszczy, ale arkusz wymaga dokładnie tych wymiarów. Eksport pod tą samą nazwą, trzy razy (R.2.3).

    Typowe błędy

    Przy zamkniętym łańcuchu szerokość 500 daje wysokość 313 - złe wymiary i R.2.3 przepada.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 10
  11. Krok 11: Plik index.html w HTML5

    Kryteria:

    R.2.4 Witryna internetowa zapisana w plikach index.html, oferta.html i koszty.html oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "Montaż paneli podłogowych". Strona zawiera sekcje head i body

    ok. 2 min
    Polecenie z arkusza

    Cechy witryny: Składa się z podstron o nazwach index.html, oferta.html i koszty.html. Cechy wspólne dla stron: Zapisane w języku HTML5.

    Najpierw cała strona index.html; oferta.html i koszty.html będą jej kopiami z innym blokiem głównym.

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

    Kryteria:

    R.2.4 Witryna internetowa zapisana w plikach index.html, oferta.html i koszty.html oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "Montaż paneli podłogowych". Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    lang="pl" w <html> - czytnik ekranu czyta stronę polską wymową.

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  13. Krok 13: Kodowanie polskich znaków

    Kryteria:

    R.2.4 Witryna internetowa zapisana w plikach index.html, oferta.html i koszty.html oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "Montaż paneli podłogowych". Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <meta charset="UTF-8"> jako pierwszy element <head>.

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  14. Krok 14: Tytuł strony i viewport

    Kryteria:

    R.2.4 Witryna internetowa zapisana w plikach index.html, oferta.html i koszty.html oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "Montaż paneli podłogowych". Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Montaż paneli podłogowych”.

    <title> to napis na karcie. <meta name="viewport"> każe telefonowi pokazać stronę w jego prawdziwej szerokości - arkusz tego nie wymaga.

    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>Montaż paneli podłogowych</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  15. Krok 15: Podpięcie arkusza stylów

    Kryteria:

    R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie style.css formatowanie CSS pochodzi jedynie z tego arkusza. Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

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

    <link> bez ukośnika /> (HTML5). Nazwa: style.css.

    Typowe błędy

    styl.css zamiast style.css odcina wszystkie style - R.3.5, a rezultat R.4 jest wtedy oceniany tylko z kodu.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  16. Krok 16: Bloki: nagłówek, menu, blok główny, stopka

    Kryteria:

    R.2.5 Zdefiniowany układ strony dzielący ją na bloki: nagłówkowy, menu, główny i stopki przy pomocy semantycznych znaczników sekcji języka HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, do stopki - footer. Dopuszcza się dodatkowe użycie bloków div lub section

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, poniżej menu, poniżej główny, na dole stopka. Podział zrealizowany za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z ilustracją 3.

    <header>, <nav>, <main>, <footer> (R.2.5). Bloki stoją jeden pod drugim - to zwykły układ dokumentu, więc strona nie potrzebuje ani siatki, ani float. Siatka przyda się w kalkulatorze.

    index.html HTML zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
        </header>
        <nav>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  17. Krok 17: Nagłówek: grafika z napisem

    Kryteria:

    R.2.7 W bloku nagłówkowym umieszczony obraz z pliku montaz_paneli.png z tekstem alternatywnym "firma usługowa"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: obraz montaz_paneli.png z tekstem alternatywnym "firma usługowa".

    <img> z tekstem alternatywnym z arkusza (R.2.7).

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  18. Krok 18: Menu: trzy odnośniki

    Kryteria:

    R.3.2 Wybranie odnośnika "Strona Główna" otwiera stronę index.html , "Oferta" - oferta.html , "Kalkulator kosztów" - koszty.html

    ok. 2 min
    Polecenie z arkusza

    Zawartość Menu: Odnośnik "Strona Główna" prowadzący do strony index.html. Odnośnik "Oferta" prowadzący do strony oferta.html. Odnośnik "Kalkulator kosztów" prowadzący do strony koszty.html.

    Trzy odnośniki z adresami względnymi (R.3.2). aria-current="page" przy bieżącej stronie mówi czytnikowi ekranu „jesteś tutaj” - na kolejnych stronach przechodzi na ich odnośnik.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  19. Krok 19: Stopka

    Kryteria:

    R.2.6 W bloku głównym umieszczony przynajmniej jeden <h1>: "Panele podłogowe" (główna) lub "Koszt montażu paneli podłogowych" (oferta), lub "Kalkulator kosztów montażu" (koszty); w stopce umieszczony <h5> z numerem zdającego oraz umieszczone 3 paragrafy na stronie głównej w bloku głównym

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek piątego stopnia o treści "Autor strony: ", dalej wstawiony numer zdającego.

    Tym razem nagłówek <h5>, nie akapit. Na egzaminie po dwukropku wpisuje się swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  20. Krok 20: Blok główny: nagłówek pierwszego stopnia

    Kryteria:

    R.2.6 W bloku głównym umieszczony przynajmniej jeden <h1>: "Panele podłogowe" (główna) lub "Koszt montażu paneli podłogowych" (oferta), lub "Kalkulator kosztów montażu" (koszty); w stopce umieszczony <h5> z numerem zdającego oraz umieszczone 3 paragrafy na stronie głównej w bloku głównym

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego strony index.html: Nagłówek pierwszego stopnia o treści "Panele podłogowe".

    <h1> w <main>.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Panele podłogowe</h1>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  21. Krok 21: Panele laminowane: obraz i akapit

    Kryteria:

    R.2.8 W bloku głównym strony głównej umieszczone 3 obrazy z plików laminowane.png , winylowe.png i deska.png , każdy z nich zawiera tekst alternatywny

    ok. 1 min
    Polecenie z arkusza

    Obraz laminowane.png, z tekstem alternatywnym „Laminowane”. Akapit o treści "Panele laminowane".

    Obraz i pod nim akapit. Atrybuty width="500" i height="70" podają wymiary pliku: przeglądarka rezerwuje miejsce, zanim obraz się wczyta, i treść pod nim nie podskakuje w trakcie ładowania strony.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Panele podłogowe</h1>
            <img src="laminowane.png" alt="Laminowane" width="500" height="70">
            <p>Panele laminowane</p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  22. Krok 22: Panele winylowe: obraz i akapit

    Kryteria:

    R.2.8 W bloku głównym strony głównej umieszczone 3 obrazy z plików laminowane.png , winylowe.png i deska.png , każdy z nich zawiera tekst alternatywny

    ok. 1 min
    Polecenie z arkusza

    Obraz winylowe.png, z tekstem alternatywnym „Winylowe”. Akapit o treści "Panele winylowe".

    Ta sama para: obraz z wymiarami i akapit z podpisem.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Panele podłogowe</h1>
            <img src="laminowane.png" alt="Laminowane" width="500" height="70">
            <p>Panele laminowane</p>
            <img src="winylowe.png" alt="Winylowe" width="500" height="70">
            <p>Panele winylowe</p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  23. Krok 23: Deska podłogowa: obraz i akapit

    Kryteria: ,

    R.2.6 W bloku głównym umieszczony przynajmniej jeden <h1>: "Panele podłogowe" (główna) lub "Koszt montażu paneli podłogowych" (oferta), lub "Kalkulator kosztów montażu" (koszty); w stopce umieszczony <h5> z numerem zdającego oraz umieszczone 3 paragrafy na stronie głównej w bloku głównym

    R.2.8 W bloku głównym strony głównej umieszczone 3 obrazy z plików laminowane.png , winylowe.png i deska.png , każdy z nich zawiera tekst alternatywny

    ok. 1 min
    Polecenie z arkusza

    Obraz deska.png, z tekstem alternatywnym „Deska”. Akapit o treści "Deska podłogowa".

    Trzecia para. Klucz liczy trzy obrazy z tekstami alternatywnymi i trzy akapity w bloku głównym (R.2.6, R.2.8). Akapit zostaje akapitem - podpis w <figure> i <figcaption> byłby semantycznie zgrabniejszy, ale arkusz wymaga paragrafów.

    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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html" aria-current="page">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Panele podłogowe</h1>
            <img src="laminowane.png" alt="Laminowane" width="500" height="70">
            <p>Panele laminowane</p>
            <img src="winylowe.png" alt="Winylowe" width="500" height="70">
            <p>Panele winylowe</p>
            <img src="deska.png" alt="Deska" width="500" height="70">
            <p>Deska podłogowa</p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  24. Krok 24: Strona oferta.html

    Kryteria:

    R.2.6 W bloku głównym umieszczony przynajmniej jeden <h1>: "Panele podłogowe" (główna) lub "Koszt montażu paneli podłogowych" (oferta), lub "Kalkulator kosztów montażu" (koszty); w stopce umieszczony <h5> z numerem zdającego oraz umieszczone 3 paragrafy na stronie głównej w bloku głównym

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego strony oferta.html: Nagłówek pierwszego stopnia o treści "Koszt montażu paneli podłogowych".

    Kopia strony jako oferta.html z nowym <h1>.

    oferta.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html" aria-current="page">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Koszt montażu paneli podłogowych</h1>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (oferta.html)
  25. Krok 25: Tabela: dwa wiersze nagłówka ze scalonymi komórkami

    Kryteria:

    R.2.9 W bloku głównym strony z ofertą umieszczona tabela o 4 kolumnach i 5 wierszach, zastosowane znaczniki <table>, <tr>, <td> oraz układ tabeli jest zgodny z ilustracją 4 arkusza egzaminacyjnego, zastosowane atrybuty rowspan i colspan

    ok. 4 min
    Polecenie z arkusza

    Tabela o 4 kolumnach i 5 wierszach, z czego niektóre wiersze / kolumny są scalone. Treść i wygląd tabeli przedstawiony jest na ilustracji 4.

    Dwa wiersze nagłówka trafiają do <thead> i są komórkami nagłówkowymi <th> - bo to są opisy kolumn, a nie dane:

    • „Rodzaj paneli” i „Koszt montażu” sięgają przez oba wiersze - rowspan="2",
    • „Rozmiar” zajmuje dwie kolumny - colspan="2"; scope="colgroup" mówi czytnikowi ekranu, że opisuje całą grupę kolumn,
    • drugi wiersz ma tylko „Szerokość” i „Długość” - resztę miejsc zajmują komórki scalone.

    Na ilustracji 4 napisy nagłówka nie są pogrubione - przeglądarka domyślnie pogrubia <th>, więc styl to wyrówna. Klucz sprawdza rowspan, colspan i układ (R.2.9).

    Typowe błędy

    Cztery komórki w drugim wierszu mimo scalenia wypychają tabelę o kolumnę w prawo - komórka z rowspan już zajmuje swoje miejsce.

    oferta.html HTML zmienione: 13 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html" aria-current="page">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Koszt montażu paneli podłogowych</h1>
            <table>
                <thead>
                    <tr>
                        <th rowspan="2" scope="col">Rodzaj paneli</th>
                        <th colspan="2" scope="colgroup">Rozmiar</th>
                        <th rowspan="2" scope="col">Koszt montażu</th>
                    </tr>
                    <tr>
                        <th scope="col">Szerokość</th>
                        <th scope="col">Długość</th>
                    </tr>
                </thead>
            </table>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (oferta.html)
  26. Krok 26: Tabela: trzy wiersze z danymi

    Kryteria:

    R.2.9 W bloku głównym strony z ofertą umieszczona tabela o 4 kolumnach i 5 wierszach, zastosowane znaczniki <table>, <tr>, <td> oraz układ tabeli jest zgodny z ilustracją 4 arkusza egzaminacyjnego, zastosowane atrybuty rowspan i colspan

    ok. 3 min
    Polecenie z arkusza

    Treść i wygląd tabeli przedstawiony jest na ilustracji 4.

    Trzy wiersze danych w <tbody>, zwykłe komórki <td> (R.2.9). Razem 5 wierszy i 4 kolumny. Ostatnia kolumna to stawki za m² - 12, 14 i 18 zł - z których liczy kalkulator.

    oferta.html HTML zmienione: 20 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html" aria-current="page">Oferta</a>
            <a href="koszty.html">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Koszt montażu paneli podłogowych</h1>
            <table>
                <thead>
                    <tr>
                        <th rowspan="2" scope="col">Rodzaj paneli</th>
                        <th colspan="2" scope="colgroup">Rozmiar</th>
                        <th rowspan="2" scope="col">Koszt montażu</th>
                    </tr>
                    <tr>
                        <th scope="col">Szerokość</th>
                        <th scope="col">Długość</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>Laminowane</td>
                        <td>30</td>
                        <td>200</td>
                        <td>12</td>
                    </tr>
                    <tr>
                        <td>Winylowe</td>
                        <td>20</td>
                        <td>200</td>
                        <td>14</td>
                    </tr>
                    <tr>
                        <td>Deska</td>
                        <td>10</td>
                        <td>100</td>
                        <td>18</td>
                    </tr>
                </tbody>
            </table>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (oferta.html)
  27. Krok 27: Strona koszty.html

    Kryteria:

    R.2.6 W bloku głównym umieszczony przynajmniej jeden <h1>: "Panele podłogowe" (główna) lub "Koszt montażu paneli podłogowych" (oferta), lub "Kalkulator kosztów montażu" (koszty); w stopce umieszczony <h5> z numerem zdającego oraz umieszczone 3 paragrafy na stronie głównej w bloku głównym

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego strony koszty.html: Nagłówek pierwszego stopnia o treści "Kalkulator kosztów montażu".

    Trzecia kopia, koszty.html, z nowym <h1>.

    koszty.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  28. Krok 28: Dwa pola liczbowe obok siebie

    Kryteria: ,

    R.2.10 W bloku głównym strony z kosztami umieszczone dwa pola edycyjne, trzy pola opcji i przycisk "Oblicz". Przynajmniej z jednym polem powiązana etykieta <label>

    R.3.4 Na stronie koszty pola edycyjne są typu number, a pola opcji typu radio. W jednym momencie może być zaznaczona tylko jedna opcja (np. mają ustawioną tę samą wartość atrybutu name )

    ok. 2 min
    Polecenie z arkusza

    Obok siebie dwa pola edycyjne typu numerycznego poprzedzone etykietami: „Szerokość pomieszczenia [m]: ", "Długość pomieszczenia [m]: ".

    Etykieta <label for> i pole type="number" - dwa razy, w jednej linii, bo to elementy liniowe (R.2.10, R.3.4). step="0.01" pozwala wpisać wymiar z dokładnością do centymetra (domyślny krok to 1 i przeglądarka oznaczyłaby 4,25 m jako błędne), a min="0" blokuje strzałkami wartości ujemne.

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  29. Krok 29: Akapit „Typ panelu”

    ok. 1 min
    Polecenie z arkusza

    Paragraf o treści "Typ panelu".

    Akapit z identyfikatorem panel-type - za chwilę posłuży za opis grupy pól opcji.

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  30. Krok 30: Trzy pola opcji w jednej grupie

    Kryteria: ,

    R.2.10 W bloku głównym strony z kosztami umieszczone dwa pola edycyjne, trzy pola opcji i przycisk "Oblicz". Przynajmniej z jednym polem powiązana etykieta <label>

    R.3.4 Na stronie koszty pola edycyjne są typu number, a pola opcji typu radio. W jednym momencie może być zaznaczona tylko jedna opcja (np. mają ustawioną tę samą wartość atrybutu name )

    ok. 3 min
    Polecenie z arkusza

    Jedno pod drugim trzy pola opcji z etykietami o treści: "Panele laminowane", "Panele winylowe", "Deska podłogowa". Pola są zgrupowane tak, że w jednym momencie można zaznaczyć tylko jedno pole.

    • wspólne name="panel" tworzy grupę - zaznaczenie jednego pola odznacza pozostałe (R.3.4),
    • value każdego pola to nazwa rodzaju panelu (laminated, vinyl, board) - skrypt odczyta wybrany rodzaj wprost z pola, bez osobnych identyfikatorów,
    • pole siedzi wewnątrz <label> - to samo wiąże etykietę z polem i cały napis jest klikalny,
    • role="radiogroup" z aria-labelledby="panel-type" mówi czytnikowi ekranu, że to grupa opisana akapitem „Typ panelu”.

    Bez <br> pola stoją na razie w jednej linii - ułoży je siatka CSS.

    Typowe błędy

    Różne wartości name robią z pól trzy niezależne przełączniki - da się zaznaczyć wszystkie i R.3.4 przepada.

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  31. Krok 31: Przycisk „Oblicz”

    Kryteria:

    R.2.10 W bloku głównym strony z kosztami umieszczone dwa pola edycyjne, trzy pola opcji i przycisk "Oblicz". Przynajmniej z jednym polem powiązana etykieta <label>

    ok. 1 min
    Polecenie z arkusza

    Poniżej przycisk o treści "Oblicz", którego wybranie uruchamia skrypt.

    <button type="button"> z identyfikatorem calculate, bez onclick - obsługę kliknięcia podepnie skrypt (klucz R.3.3 wymienia addEventListener).

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  32. Krok 32: Pusty akapit na wynik

    ok. 1 min
    Polecenie z arkusza

    Pusty paragraf, przeznaczony do wyświetlenia wyników działania skryptu.

    Pusty <p id="result">. aria-live="polite" każe czytnikowi ekranu przeczytać wynik, gdy skrypt go wpisze.

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (koszty.html)
  33. Etap 3: Styl CSS

    Kroki 33-42 · po tym etapie masz 30 z 40 kryteriów (pkt)

    Krok 33: Zmienne z kolorami i krój czcionki

    Kryteria:

    R.4.1 Ustawiony styl domyślny dla wszystkich selektorów (*): krój czcionki Georgia

    ok. 2 min
    Polecenie z arkusza

    Domyślnie, dla wszystkich selektorów: krój czcionki Georgia.

    Na początku style.css zmienne z dwoma kolorami. Arkusz podaje je raz nazwą (Wheat, Sienna), raz zapisem szesnastkowym w gradientach (#F5DEB3, #A0522D) - ale to te same kolory: #A0522D to dokładnie Sienna, a #F5DEB3 to Wheat. Strona ma więc tylko dwie barwy i obie siedzą w zmiennych. Selektor * ustawia krój Georgia (R.4.1).

    style.css CSS nowy plik
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    Podgląd strony po tym kroku (index.html)
  34. Krok 34: Tekst wyśrodkowany

    Kryteria:

    R.4.8 Ustawione wyrównanie tekstu do środka dla selektora body

    ok. 1 min
    Polecenie z arkusza

    Dla selektora ciała strony: wyrównanie tekstu do środka.

    Właściwość dziedziczą wszystkie bloki (R.4.8).

    style.css CSS zmienione: 3 linie
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    Podgląd strony po tym kroku (index.html)
  35. Krok 35: Nagłówek: gradient pionowy

    Kryteria: ,

    R.4.3 Ustawione gradienty liniowe pionowe: dla bloku nagłówkowego od koloru #A0522D do #F5DEB3 np. linear-gradient(#A0522D, #F5DEB3) oraz dla stopki od koloru #F5DEB3 do #A0522D np. linear-gradient(#F5DEB3, #A0522D)

    R.4.6 Ustawione marginesy wewnętrzne (padding) 30px dla bloków nagłówkowego i stopki, 14px dla bloku menu, 50px dla bloku głównego, 10px dla odnośnika i 5px dla selektora td

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nagłówkowego: marginesy wewnętrzne 30 px, gradient pionowy od koloru #A0522D do koloru #F5DEB3.

    Gradient to obraz tła: background-image: linear-gradient(...), bez kierunku - z góry na dół (R.4.3, R.4.6). Kolory ze zmiennych, czyli od #A0522D do #F5DEB3.

    Typowe błędy

    background-color: linear-gradient(...) nie działa - gradient należy do background-image albo skrótu background.

    style.css CSS zmienione: 4 linie
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    Podgląd strony po tym kroku (index.html)
  36. Krok 36: Menu i blok główny

    Kryteria: ,

    R.4.2 Ustawiony kolor tła Wheat dla bloku menu i głównego oraz kolor tła Sienna i kolor czcionki biały dla selektora a

    R.4.6 Ustawione marginesy wewnętrzne (padding) 30px dla bloków nagłówkowego i stopki, 14px dla bloku menu, 50px dla bloku głównego, 10px dla odnośnika i 5px dla selektora td

    ok. 1 min
    Polecenie z arkusza

    Dla bloku menu: kolor tła Wheat, marginesy wewnętrzne 14 px. Dla bloku głównego: kolor tła Wheat, marginesy wewnętrzne 50 px.

    To samo tło, różne marginesy wewnętrzne - dwie reguły (R.4.2, R.4.6).

    style.css CSS zmienione: 8 linii
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    Podgląd strony po tym kroku (index.html)
  37. Krok 37: Stopka: gradient w drugą stronę

    Kryteria: ,

    R.4.3 Ustawione gradienty liniowe pionowe: dla bloku nagłówkowego od koloru #A0522D do #F5DEB3 np. linear-gradient(#A0522D, #F5DEB3) oraz dla stopki od koloru #F5DEB3 do #A0522D np. linear-gradient(#F5DEB3, #A0522D)

    R.4.6 Ustawione marginesy wewnętrzne (padding) 30px dla bloków nagłówkowego i stopki, 14px dla bloku menu, 50px dla bloku głównego, 10px dla odnośnika i 5px dla selektora td

    ok. 1 min
    Polecenie z arkusza

    Dla stopki: marginesy wewnętrzne 30 px, gradient pionowy od koloru #F5DEB3 do koloru #A0522D.

    Te same zmienne w odwrotnej kolejności (R.4.3, R.4.6).

    style.css CSS zmienione: 4 linie
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    footer {
        padding: 30px;
        background-image: linear-gradient(var(--color-wheat), var(--color-sienna));
    }
    Podgląd strony po tym kroku (index.html)
  38. Krok 38: Odnośniki

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła Wheat dla bloku menu i głównego oraz kolor tła Sienna i kolor czcionki biały dla selektora a

    R.4.5 Ustawione cechy czcionki dla odnośnika: czcionka pogrubiona, bez podkreślenia

    R.4.6 Ustawione marginesy wewnętrzne (padding) 30px dla bloków nagłówkowego i stopki, 14px dla bloku menu, 50px dla bloku głównego, 10px dla odnośnika i 5px dla selektora td

    R.4.7 Ustawione marginesy zewnętrzne (margin) auto dla tabeli, 20px dla odnośnika

    ok. 2 min
    Polecenie z arkusza

    Dla selektora odnośnika: kolor tła Sienna, biały kolor czcionki, czcionka pogrubiona, bez podkreślenia, marginesy wewnętrzne 10 px, zewnętrzne 20 px.

    Selektor znacznika a, jak wymaga arkusz. „Bez podkreślenia” to text-decoration: none (R.4.2, R.4.5, R.4.6, R.4.7).

    style.css CSS zmienione: 8 linii
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    footer {
        padding: 30px;
        background-image: linear-gradient(var(--color-wheat), var(--color-sienna));
    }
    a {
        background-color: var(--color-sienna);
        color: white;
        font-weight: bold;
        text-decoration: none;
        padding: 10px;
        margin: 20px;
    }
    Podgląd strony po tym kroku (index.html)
  39. Krok 39: Odnośnik pod kursorem i z klawiatury

    Kryteria:

    R.4.10 Gdy kursor myszy znajdzie się na odnośniku pojawia się podkreślenie tekstu

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor myszy znajdzie się na odnośniku pojawia się podkreślenie tekstu.

    :hover przywraca podkreślenie pod kursorem (R.4.10). :focus-visible daje ten sam efekt odnośnikowi wybranemu klawiszem Tab - bez tego osoba poruszająca się klawiaturą nie widzi, gdzie jest.

    style.css CSS zmienione: 4 linie
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    footer {
        padding: 30px;
        background-image: linear-gradient(var(--color-wheat), var(--color-sienna));
    }
    a {
        background-color: var(--color-sienna);
        color: white;
        font-weight: bold;
        text-decoration: none;
        padding: 10px;
        margin: 20px;
    }
    a:hover,
    a:focus-visible {
        text-decoration: underline;
    }
    Podgląd strony po tym kroku (index.html)
  40. Krok 40: Tabela: ramki, szerokość, wyśrodkowanie

    Kryteria: , , ,

    R.4.4 Ustawiona szerokość 70% dla selektora table

    R.4.6 Ustawione marginesy wewnętrzne (padding) 30px dla bloków nagłówkowego i stopki, 14px dla bloku menu, 50px dla bloku głównego, 10px dla odnośnika i 5px dla selektora td

    R.4.7 Ustawione marginesy zewnętrzne (margin) auto dla tabeli, 20px dla odnośnika

    R.4.9 Ustawione dla tabeli i komórki tabeli obramowanie 1px solid Sienna oraz dla tabeli obramowanie połączone border-collapse: collapse;

    ok. 3 min
    Polecenie z arkusza

    Wspólne dla selektora tabeli i selektora komórki tabeli: obramowanie o szerokości 1px linią ciągłą koloru Sienna, obramowanie połączone w jedną linię. Dodatkowo dla selektora tabeli: szerokość 70%, marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę. Dla selektora komórki tabeli: marginesy wewnętrzne 5 px.

    Komórki są dwóch rodzajów - <th> i <td> - więc reguły komórek obejmują oba selektory:

    • table, th, td - ramka 1px solid i border-collapse: collapse, które łączy ramki sąsiednich komórek w jedną linię (R.4.9),
    • table - szerokość 70% i margin: auto: przy ustalonej szerokości wolne miejsce dzieli się po równo na boki (R.4.4, R.4.7),
    • th, td - odstęp tekstu od ramki (R.4.6),
    • th { font-weight: normal; } - nagłówki bez pogrubienia, jak na ilustracji 4.

    Arkusz każe stylować tabelę i komórki „wyłącznie selektorem znacznika” - th też jest selektorem znacznika, a selektor td stoi w każdej regule komórek.

    style.css CSS zmienione: 14 linii
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    footer {
        padding: 30px;
        background-image: linear-gradient(var(--color-wheat), var(--color-sienna));
    }
    a {
        background-color: var(--color-sienna);
        color: white;
        font-weight: bold;
        text-decoration: none;
        padding: 10px;
        margin: 20px;
    }
    a:hover,
    a:focus-visible {
        text-decoration: underline;
    }
    table, th, td {
        border: 1px solid var(--color-sienna);
        border-collapse: collapse;
    }
    table {
        width: 70%;
        margin: auto;
    }
    th, td {
        padding: 5px;
    }
    th {
        font-weight: normal;
    }
    Podgląd strony po tym kroku (oferta.html)
  41. Krok 41: Pola opcji jedno pod drugim: siatka

    ok. 2 min
    Polecenie z arkusza

    Jedno pod drugim trzy pola opcji z etykietami.

    display: grid na grupie pól opcji: każda etykieta z polem to osobny wiersz siatki, więc stają jedno pod drugim bez <br>. Dwie właściwości ustawiają je jak na ilustracji 5:

    • justify-content: center - kolumna siatki staje na środku bloku głównego,
    • justify-items: start - w tej kolumnie etykiety są dosunięte do lewej, więc kółka pól opcji stoją równo jedno pod drugim, mimo różnej długości napisów.
    style.css CSS zmienione: 5 linii
    :root {
        --color-sienna: Sienna;    /* #A0522D */
        --color-wheat: Wheat;      /* #F5DEB3 */
    }
    * {
        font-family: Georgia;
    }
    body {
        text-align: center;
    }
    header {
        padding: 30px;
        background-image: linear-gradient(var(--color-sienna), var(--color-wheat));
    }
    nav {
        background-color: var(--color-wheat);
        padding: 14px;
    }
    main {
        background-color: var(--color-wheat);
        padding: 50px;
    }
    footer {
        padding: 30px;
        background-image: linear-gradient(var(--color-wheat), var(--color-sienna));
    }
    a {
        background-color: var(--color-sienna);
        color: white;
        font-weight: bold;
        text-decoration: none;
        padding: 10px;
        margin: 20px;
    }
    a:hover,
    a:focus-visible {
        text-decoration: underline;
    }
    table, th, td {
        border: 1px solid var(--color-sienna);
        border-collapse: collapse;
    }
    table {
        width: 70%;
        margin: auto;
    }
    th, td {
        padding: 5px;
    }
    th {
        font-weight: normal;
    }
    .panel-types {
        display: grid;
        justify-content: center;
        justify-items: start;
    }
    Podgląd strony po tym kroku (koszty.html)
  42. Krok 42: Układ bloków zgodny z ilustracją 3

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym

    ok. 2 min
    Polecenie z arkusza

    Uwaga: styl CSS ciała strony, odnośnika, tabeli i komórki tabeli należy zdefiniować wyłącznie przy pomocy selektora tych znaczników. Jest to uwarunkowane projektem późniejszej rozbudowy witryny.

    Na index.html: nagłówek z gradientem, menu i blok główny w kolorze Wheat, stopka z odwróconym gradientem - bloki jeden pod drugim, jak na ilustracji 3 (R.3.1). Style body, a, table i td są zapisane selektorami znaczników - klucz uznaje R.4.2, R.4.4, R.4.6 i R.4.8 tylko w tej postaci. Zmienne var(...) stoją po prawej stronie deklaracji i niczego tu nie zmieniają.

    Podgląd strony po tym kroku (index.html)
  43. Etap 4: Skrypt JavaScript

    Kroki 43-47 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 43: Plik koszty.js z defer i obsługa kliknięcia

    Kryteria: ,

    R.3.3 Na stronie koszty zdefiniowane dla przycisku zdarzenie związane z jego wciśnięciem, uruchamiające funkcję skryptu (obsługa zdarzenia zdefiniowana w HTML np. onClick lub w skrypcie np.addEventListener zmienna.onClick = …)

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

    ok. 3 min
    Polecenie z arkusza

    Wykonywany po stronie klienta, na stronie koszty.html po kliknięciu przycisku „Oblicz”.

    Skrypt w pliku koszty.js, dołączonym w <head> z defer - uruchamia się dopiero po wczytaniu całego HTML. Na początku pliku:

    • obiekt RATES ze stawkami z tabeli 1 arkusza - klucz to value pola opcji, wartość to stawka za m²,
    • stałe z odnośnikami do pól, grupy pól opcji (querySelectorAll z selektorem atrybutu name) i akapitu wyniku,
    • addEventListener('click', calculateCost) podpina funkcję pod przycisk (R.3.3).

    Funkcja jest jeszcze pusta (R.5.1).

    koszty.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>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
        <script src="koszty.js" defer></script>
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    koszty.js JavaScript nowy plik
    const RATES = { laminated: 12, vinyl: 14, board: 18 };
    
    const widthInput = document.querySelector('#width');
    const lengthInput = document.querySelector('#length');
    const panelInputs = document.querySelectorAll('input[name="panel"]');
    const result = document.querySelector('#result');
    
    function calculateCost() {
    }
    
    document.querySelector('#calculate').addEventListener('click', calculateCost);
    Podgląd strony po tym kroku (koszty.html)
  44. Krok 44: Odczyt wymiarów i wybranego panelu

    Kryteria: ,

    R.5.2 Skrypt pobiera dane z przynajmniej jednego pola edycyjnego oraz wykorzystuje pole .value obiektu

    R.5.3 Skrypt pobiera dane z przynajmniej jednego pola opcji oraz wykorzystuje pole .checked obiektu

    ok. 3 min
    Polecenie z arkusza

    Skrypt pobiera dane z pól edycyjnych i pól opcji.

    • .value pola to wpisany tekst, a Number(...) zamienia go na liczbę; puste pole daje 0 (R.5.2),
    • [...panelInputs] zamienia listę pól opcji na tablicę, a find(input => input.checked) zwraca pierwsze pole, którego właściwość checked jest prawdziwa - czyli zaznaczone; gdy żadne nie jest zaznaczone, find zwraca undefined (R.5.3).
    koszty.html HTML zmienione: 0 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
        <script src="koszty.js" defer></script>
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    koszty.js JavaScript zmienione: 3 linie
    const RATES = { laminated: 12, vinyl: 14, board: 18 };
    
    const widthInput = document.querySelector('#width');
    const lengthInput = document.querySelector('#length');
    const panelInputs = document.querySelectorAll('input[name="panel"]');
    const result = document.querySelector('#result');
    
    function calculateCost() {
        const width = Number(widthInput.value);
        const length = Number(lengthInput.value);
        const selected = [...panelInputs].find(input => input.checked);
    }
    
    document.querySelector('#calculate').addEventListener('click', calculateCost);
    Podgląd strony po tym kroku (koszty.html)
  45. Krok 45: Warunek: dane poprawne, inaczej komunikat

    Kryteria: ,

    R.5.6 Jeżeli jedno z pól edycyjnych jest puste, skrypt wyświetla dowolną metodą tekst: "Wprowadź poprawne dane."

    R.5.8 Przynajmniej jeden tekst jest wyświetlany w paragrafie pod przyciskiem

    ok. 2 min
    Polecenie z arkusza

    Jeżeli oba pola edycyjne zostały wypełnione, skrypt oblicza pole powierzchni pomieszczenia i koszt montażu. W przeciwnym wypadku skrypt wyświetla komunikat "Wprowadź poprawne dane."

    Jeden warunek obejmuje wszystko, czego arkusz wymaga, i trochę więcej: oba wymiary większe od zera (puste pole daje 0, więc też odpada) i zaznaczony rodzaj panelu. W przeciwnym wypadku komunikat z arkusza trafia do akapitu przez textContent (zwykły tekst, nie HTML), a return kończy funkcję - dalsza część nie potrzebuje już else (R.5.6, R.5.8).

    Wersja podstawowa sprawdza tylko puste pola, więc wymiar -4 albo brak wybranego panelu dawały koszt ujemny albo 0. „Wprowadź poprawne dane” pasuje do każdego z tych przypadków.

    koszty.html HTML zmienione: 0 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
        <script src="koszty.js" defer></script>
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    koszty.js JavaScript zmienione: 4 linie
    const RATES = { laminated: 12, vinyl: 14, board: 18 };
    
    const widthInput = document.querySelector('#width');
    const lengthInput = document.querySelector('#length');
    const panelInputs = document.querySelectorAll('input[name="panel"]');
    const result = document.querySelector('#result');
    
    function calculateCost() {
        const width = Number(widthInput.value);
        const length = Number(lengthInput.value);
        const selected = [...panelInputs].find(input => input.checked);
        if (!(width > 0 && length > 0 && selected)) {
            result.textContent = 'Wprowadź poprawne dane.';
            return;
        }
    }
    
    document.querySelector('#calculate').addEventListener('click', calculateCost);
    Podgląd strony po tym kroku (koszty.html)
  46. Krok 46: Pole powierzchni i koszt według stawki

    Kryteria: ,

    R.5.4 Pole powierzchni policzone jako wynik mnożenia podanych w kontrolkach wartości szerokości i długości

    R.5.5 Koszt montażu policzony jako wynik mnożenia pola powierzchni przez 12, jeśli wybrano panel laminowany lub przez 14, jeśli wybrano panel winylowy, lub przez 18, jeśli wybrano deskę podłogową

    ok. 2 min
    Polecenie z arkusza

    Oblicza pole powierzchni pomieszczenia. Oblicza koszt montażu uwzględniając pole powierzchni oraz typ panelu i koszt z tabeli 1.

    Pole to szerokość razy długość (R.5.4). Stawka to RATES[selected.value]: wartość zaznaczonego pola (board) jest kluczem w obiekcie, więc dla deski wychodzi 18 (R.5.5). Zamiast trzech warunków if - jeden odczyt z obiektu; nowy rodzaj panelu to jedna linia w RATES i jedno pole w HTML.

    koszty.html HTML zmienione: 0 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
        <script src="koszty.js" defer></script>
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    koszty.js JavaScript zmienione: 2 linie
    const RATES = { laminated: 12, vinyl: 14, board: 18 };
    
    const widthInput = document.querySelector('#width');
    const lengthInput = document.querySelector('#length');
    const panelInputs = document.querySelectorAll('input[name="panel"]');
    const result = document.querySelector('#result');
    
    function calculateCost() {
        const width = Number(widthInput.value);
        const length = Number(lengthInput.value);
        const selected = [...panelInputs].find(input => input.checked);
        if (!(width > 0 && length > 0 && selected)) {
            result.textContent = 'Wprowadź poprawne dane.';
            return;
        }
        const area = width * length;
        const cost = area * RATES[selected.value];
    }
    
    document.querySelector('#calculate').addEventListener('click', calculateCost);
    Podgląd strony po tym kroku (koszty.html)
  47. Krok 47: Wynik pod przyciskiem

    Kryteria: , ,

    R.5.7 Jeżeli oba pola edycyjne są wypełnione, skrypt wyświetla tekst: "Pole powierzchni pomieszczenia: <pole>, koszt montażu <koszt>”, gdzie pola <> oznaczają wartości obliczone skryptem

    R.5.8 Przynajmniej jeden tekst jest wyświetlany w paragrafie pod przyciskiem

    R.5.9 W skryptach zastosowana przynajmniej jedna zmienna lub funkcja oraz nazwy wszystkich funkcji i zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 2 min
    Polecenie z arkusza

    W paragrafie pod przyciskiem wyświetla napis: „Pole powierzchni pomieszczenia: <pole>, koszt montażu <koszt>”, gdzie pola <> oznaczają wartości obliczone skryptem.

    Szablon tekstowy z wartościami w ${...}. Intl.NumberFormat('pl-PL') zapisuje liczby po polsku i obcina ogon dziesiętny: 3,5 × 4,2 to „14,7”, a nie „14.700000000000001”, który wyszedłby z samego mnożenia liczb zmiennoprzecinkowych.

    Wpisz w podglądzie 4 i 3, zaznacz „Deska podłogowa” i kliknij „Oblicz”: „Pole powierzchni pomieszczenia: 12, koszt montażu 216” - jak na ilustracji 5 (R.5.7, R.5.8). Nazwy mówią, co przechowują: RATES, width, length, selected, area, cost, calculateCost (R.5.9).

    koszty.html HTML zmienione: 0 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Montaż paneli podłogowych</title>
        <link rel="stylesheet" href="style.css">
        <script src="koszty.js" defer></script>
    </head>
    <body>
        <header>
            <img src="montaz_paneli.png" alt="firma usługowa">
        </header>
        <nav>
            <a href="index.html">Strona Główna</a>
            <a href="oferta.html">Oferta</a>
            <a href="koszty.html" aria-current="page">Kalkulator kosztów</a>
        </nav>
        <main>
            <h1>Kalkulator kosztów montażu</h1>
            <label for="width">Szerokość pomieszczenia [m]: </label>
            <input type="number" id="width" min="0" step="0.01">
            <label for="length">Długość pomieszczenia [m]: </label>
            <input type="number" id="length" min="0" step="0.01">
            <p id="panel-type">Typ panelu</p>
            <div class="panel-types" role="radiogroup" aria-labelledby="panel-type">
                <label><input type="radio" name="panel" value="laminated"> Panele laminowane</label>
                <label><input type="radio" name="panel" value="vinyl"> Panele winylowe</label>
                <label><input type="radio" name="panel" value="board"> Deska podłogowa</label>
            </div>
            <button type="button" id="calculate">Oblicz</button>
            <p id="result" aria-live="polite"></p>
        </main>
        <footer>
            <h5>Autor strony: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    koszty.js JavaScript zmienione: 2 linie
    const RATES = { laminated: 12, vinyl: 14, board: 18 };
    
    const widthInput = document.querySelector('#width');
    const lengthInput = document.querySelector('#length');
    const panelInputs = document.querySelectorAll('input[name="panel"]');
    const result = document.querySelector('#result');
    const number = new Intl.NumberFormat('pl-PL', { maximumFractionDigits: 2 });
    
    function calculateCost() {
        const width = Number(widthInput.value);
        const length = Number(lengthInput.value);
        const selected = [...panelInputs].find(input => input.checked);
        if (!(width > 0 && length > 0 && selected)) {
            result.textContent = 'Wprowadź poprawne dane.';
            return;
        }
        const area = width * length;
        const cost = area * RATES[selected.value];
        result.textContent = `Pole powierzchni pomieszczenia: ${number.format(area)}, koszt montażu ${number.format(cost)}`;
    }
    
    document.querySelector('#calculate').addEventListener('click', calculateCost);
    Podgląd strony po tym kroku (koszty.html)
  48. Etap 5: Porównanie z wersją podstawową

    Kroki 48-49 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 48: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

    Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt.

    Obie wersje dają te same punkty. Różnice:

    wersja podstawowata wersja
    zapytanie 3JOIN - 4 osobyLEFT JOIN - 6 osób, także z zerem zadań
    koloryWheat, Sienna i ich zapis szesnastkowy w regułachdwie zmienne w :root
    tabelasame <td><thead> z <th>, <tbody> z <td>
    pola opcji<br> w HTML, id każdego polasiatka CSS, value pól, <label> wokół pola
    zdarzenieonclick w HTMLaddEventListener w koszty.js z defer
    stawkidrabinka if ... else ifobiekt RATES
    złe danesprawdzane tylko puste polawymiary większe od zera i wybrany panel
    liczby14.700000000000001Intl.NumberFormat: 14,7

    Skrypt tej wersji jest sprawdzony w prawdziwym Chrome: wynik 12 i 216 z ilustracji 5, laminowane (144), wymiary z ułamkiem, brak panelu, puste i ujemne pole.

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

  49. Krok 49: Którą wersję wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    • Wersja podstawowa, gdy liczy się czas: onclick, getElementById i innerHTML są w tabeli 2 arkusza, a skrypt to kilkanaście linii.
    • Ta wersja, gdy addEventListener, szablony tekstowe i metody tablic (find) pisze się z pamięci. Plik koszty.js trzeba nagrać na płytę razem z resztą.

    Mieszanie też daje pełne punkty - na przykład zmienne CSS i siatka z tej wersji, skrypt z podstawowej.

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

  50. Etap 6: Oddanie pracy

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

    Krok 50: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

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

    W przeglądarka.txt nazwa przeglądarki, w której strony były sprawdzane.

    W folderze z numerem zdającego: trzy strony HTML, style.css, koszty.js, kwerendy.txt, zrzuty import.png, kw1.png - kw4.png i kolor.png, grafika montaz_paneli.png i trzy przeskalowane zdjęcia paneli. Po nagraniu trzeba sprawdzić odczyt płyty i opisać ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.