Przejdź do głównej treści
INF.03-08-24.06-SGCzerwiec 2024 · 52 kroki · ok. 88 min · 43 kryteria z klucza

Rozwiązanie krok po kroku: Rejestracja użytkownika sklepu (Nowoczesny kod (grid))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza klienci z czterema zapytaniami, plakietka z przezroczystym tłem i strona rejestracja.html z trzema zakładkami formularza, paskiem postępu i danymi wypisywanymi w konsoli - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • siatka CSS na całej stronie: grid-template-areas stawia blok boczny obok banera, bloku głównego i paska postępu, a szerokości 25% i 73% stoją w grid-template-columns (klucz R.4.5 przyjmuje je wprost). Kod HTML ma kolejność treści - baner, formularz, pasek, promocje, stopka,
  • kolory w zmiennych CSS i zakładka widoczna dzięki klasie active: skrypt przestawia klasę, a o wyglądzie decyduje arkusz stylów,
  • podpisy pól w znacznikach <label> wewnątrz akapitów z arkusza i pasek postępu opisany dla czytników ekranu,
  • skrypt bez atrybutów w HTML-u: zdarzenia podpina pętla w skrypcie, const, querySelectorAll, stałe zamiast „magicznych liczb”,
  • SQL czytelniej: aliasy tabel, JOIN ... ON, warunek na datę jako zakres i nieujemny typ kolumny.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i grafika pochodzą z prawdziwego archiwum pliki8, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a skrypt jest sprawdzony w prawdziwej przeglądarce Chrome: przełączanie zakładek, szerokości paska po kolejnych polach, napis w konsoli i położenie bloków.

Jak liczą się punkty. Klucz ma 43 kryteria (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania to 75%, czyli 33 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 43 kryteriów (pkt)

    Krok 1: Baza danych klienci

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki8.zip rozpakuj hasłem _Rejestr@cja_ (podkreślenia na obu końcach, małpa zamiast „a”) do folderu z numerem zdającego. W phpMyAdmin Nowa → klienci, porównywanie utf8_unicode_ci, albo zapytaniem:

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

    ok. 2 min
    Polecenie z arkusza

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

    Zaznaczona baza klienci, Import → baza.sql. Trzy tabele z obrazu 1: osoby (7 osób z datą urodzenia dataUr) oraz adresy i telefony - obie z kluczem obcym Osoby_id, który wskazuje właściciela. Jedna osoba może mieć kilka adresów i kilka telefonów, dlatego są to osobne tabele (relacja jeden do wielu).

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych klienci czynność udokumentowano plikiem o nazwie import w formacie PNG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG i nazwij 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ń, w PNG. Zrzuty zapytań idą później do JPEG (kw1.jpg - kw4.jpg).

    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 urodzone po 2000 roku

    Kryteria: ,

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

    R.1.3 Wykonano zapytanie 1 wybierające jedynie imiona i nazwiska osób urodzonych po 2000 roku. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko FROM osoby WHERE dataUr >="2001-01-01"; (lub dataUr > "2000-12-31") oraz wynik działania udokumentowano zrzutem, na którym są widoczne jedynie dane: Anna Kowalewska, Ewa Kowalska, Andrzej Nowak

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie imiona i nazwiska osób urodzonych po 2000 roku

    • SELECT imie, nazwisko - tylko dwa pola, jak każe polecenie,
    • WHERE dataUr >= '2001-01-01' - „po 2000 roku” to od 1 stycznia 2001. Warunek zapisany jako początek zakresu czyta się wprost („od tego dnia”) i działa na samej kolumnie, więc baza może użyć indeksu na dataUr. YEAR(dataUr) > 2000 daje ten sam wynik, ale liczy funkcję dla każdego wiersza.

    Każda klauzula w osobnej linii - przy dłuższych zapytaniach od razu widać, co jest warunkiem, a co listą pól. Wynik: Anna Kowalewska, Ewa Kowalska, Andrzej Nowak, jak w kluczu. Zapytania zapisz w kwerendy.txt, zrzut jako kw1.jpg.

    Typowe błędy

    dataUr >= '2000-01-01' łapie też osoby urodzone w 2000 roku - a te nie są urodzone „po 2000 roku”.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE dataUr >= '2001-01-01';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: miasta bez powtórzeń, alfabetycznie

    Kryteria:

    R.1.4 Wykonano zapytanie 2 wybierające nazwy miast, z których pochodzą klienci posortowane alfabetycznie rosnąco. Nazwy miast nie mogą się powtarzać. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT miasto FROM `adresy` ORDER BY miasto; oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 4 rekordy w kolejności: Kraków, Poznań, Warszawa, Wrocław

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające nazwy miast, z których pochodzą klienci posortowane alfabetycznie rosnąco. Nazwy miast nie mogą się powtarzać

    DISTINCT usuwa powtórzenia (Poznań i Warszawa są w adresach po dwa razy), ORDER BY miasto ASC sortuje rosnąco - ASC jest domyślne, ale zapisane wprost mówi czytającemu, że kierunek jest zamierzony. Polskie litery sortują się poprawnie dzięki porównywaniu utf8_unicode_ci tabeli. Wynik: Kraków, Poznań, Warszawa, Wrocław. Zrzut: kw2.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE dataUr >= '2001-01-01';
    
    -- Zapytanie 2
    SELECT DISTINCT miasto
    FROM adresy
    ORDER BY miasto ASC;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: osoby z numerami telefonów

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie imiona i nazwiska osób oraz odpowiadające im numery telefonów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, telefony.numer FROM osoby JOIN telefony ON osoby.id = telefony.Osoby_id; (możliwe również INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 9 rekordów z czego dla Anny Kowalewskiej i Anny Nowak są dwa numery telefonu

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie imiona i nazwiska osób oraz odpowiadające im numery telefonów. Należy posłużyć się relacją

    • aliasy tabel o (osoby) i t (telefony) - przy każdej kolumnie widać, skąd pochodzi. Przy numer to konieczne: kolumna o tej nazwie jest też w adresy,
    • JOIN telefony AS t ON t.Osoby_id = o.id - relacja: do osoby dołączany jest każdy jej telefon; warunek złączenia stoi przy złączeniu, a nie w WHERE,
    • ORDER BY o.nazwisko, o.imie - dodatek: oba numery jednej osoby stoją obok siebie. Bez ORDER BY kolejność wierszy zależy od planu zapytania i może się zmienić.

    Wynik: 9 wierszy - Anna Kowalewska i Anna Nowak po dwa razy, bo mają po dwa numery (jak w kluczu). Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE dataUr >= '2001-01-01';
    
    -- Zapytanie 2
    SELECT DISTINCT miasto
    FROM adresy
    ORDER BY miasto ASC;
    
    -- Zapytanie 3
    SELECT o.imie, o.nazwisko, t.numer
    FROM osoby AS o
    JOIN telefony AS t ON t.Osoby_id = o.id
    ORDER BY o.nazwisko, o.imie;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowa kolumna numerMieszkania

    Kryteria:

    R.1.6 Wykonano zapytanie 4 dodające do tabeli adresy kolumnę numerMieszkania typu całkowitego, po kolumnie numer. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE adresy ADD COLUMN numerMieszkania INT AFTER numer; (dowolny typ całkowity, poprawnie również bez słowa COLUMN) oraz wynik działania udokumentowano zrzutem, na którym widoczne jest poprawnie wykonane zapytanie lub struktura tabeli z dodaną kolumną

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: Dodające kolumnę numerMieszkania typu całkowitego, do tabeli Adresy po kolumnie numer

    • ALTER TABLE adresy - zmiana struktury tabeli (w bazie nazwa jest małą literą, arkusz pisze „Adresy”),
    • ADD COLUMN numerMieszkania SMALLINT UNSIGNED NULL - typ całkowity dobrany do danych: numer mieszkania nie bywa ujemny ani większy niż 65 535, więc SMALLINT UNSIGNED (2 bajty) wystarcza. NULL zapisane wprost: dom jednorodzinny nie ma numeru mieszkania, a istniejące adresy dostaną w tej kolumnie NULL,
    • AFTER numer - miejsce kolumny: zaraz po numer, a nie na końcu tabeli.

    Klucz przyjmuje dowolny typ całkowity - INT z wersji podstawowej jest tak samo poprawny. Na zrzucie kw4.jpg struktura tabeli: id, Osoby_id, ulica, numer, numerMieszkania, miasto.

    Typowe błędy

    Bez AFTER numer kolumna trafia na koniec tabeli, za miasto.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM osoby
    WHERE dataUr >= '2001-01-01';
    
    -- Zapytanie 2
    SELECT DISTINCT miasto
    FROM adresy
    ORDER BY miasto ASC;
    
    -- Zapytanie 3
    SELECT o.imie, o.nazwisko, t.numer
    FROM osoby AS o
    JOIN telefony AS t ON t.Osoby_id = o.id
    ORDER BY o.nazwisko, o.imie;
    
    -- Zapytanie 4
    ALTER TABLE adresy
    ADD COLUMN numerMieszkania SMALLINT UNSIGNED NULL AFTER numer;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-30 · po tym etapie masz 20 z 43 kryteriów (pkt)

    Krok 8: obraz.png: niebieska plakietka, przezroczyste tło, 200 px

    Kryteria:

    R.2.1 Plik obraz.png przeskalowano z zachowaniem proporcji do wysokości 200 px, jest on przezroczysty oraz widoczna jest na nim jedynie niebieska plakietka z pętelką

    ok. 5 min
    Polecenie z arkusza

    Plik obraz.jpg wypakowany z archiwum należy skadrować, tak aby była widoczna tylko niebieska plakietka z pętelką (patrz obraz 2), ustawić tło przezroczyste oraz przeskalować z zachowaniem proporcji do wysokości 200 px. Obraz zapisać pod tą samą nazwą w formacie PNG

    Grafika jest taka sama jak w wersji podstawowej. W GIMP-ie:

    1. kadr wokół prawej plakietki - do niebieskiej plakietki należy zielona nitka zaczepiona w jej otworze (tak jest na obrazie 2),
    2. przezroczystość: Warstwa → Przezroczystość → Dodaj kanał alfa, Zaznaczanie rozmyte na białym tle, z klawiszem Shift także białe pole wewnątrz pętelki i otwór na nitkę, potem Usuń. Białego procentu i koszyka nie zaznaczaj - są częścią plakietki,
    3. skala: Obraz → Skaluj obraz, łańcuch zamknięty, wysokość 200 px,
    4. Plik → Wyeksportuj jako obraz.png - JPEG nie ma przezroczystości.

    Podgląd: oryginał i gotowa plakietka na szachownicy, która pokazuje przezroczyste miejsca.

    Typowe błędy

    Zapis do JPEG - przezroczyste tło zamienia się na białe, a klucz wymaga przezroczystości.

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

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie rejestracja.html zapisanej w języku HTML5

    Deklaracja <!DOCTYPE html> i szkielet z sekcjami head i body. Strona nie ma PHP, więc otwiera się ją prosto z folderu.

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

    ok. 1 min
    Polecenie z arkusza

    Ustawiony język zawartości strony na polski

    lang="pl" w <html> - przeglądarka wie, jakiego słownika użyć do sprawdzania pisowni w polach, a czytnik ekranu - jak wymawiać tekst.

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

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku rejestracja.html oraz zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> i zadeklarowano język witryny polski np.<HTML lang="pl">. 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> (z deklaracją HTML5, językiem i nazwą pliku to R.2.2). Obok viewport - telefon nie pomniejsza wtedy strony do rozmiaru ekranu komputera.

    rejestracja.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">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  12. Krok 12: Tytuł strony

    Kryteria:

    R.2.3 Nadano tytuł stronie: Sklep - rejestracja

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Sklep - rejestracja”

    <title> w <head>.

    rejestracja.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>Sklep - rejestracja</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  13. Krok 13: Podpięcie arkusza stylów

    Kryteria:

    R.3.8 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.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 styl.css prawidłowo połączony z kodem strony

    <link rel="stylesheet" href="styl.css"> - całe formatowanie pochodzi z tego pliku, bez atrybutów style w HTML-u.

    Typowe błędy

    Klucz odbiera R.3.8 także wtedy, gdy choć jeden selektor nie trafia w blok (np. kropka zamiast #).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  14. Krok 14: Bloki semantyczne w kolejności treści

    Kryteria:

    R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: boczny, baner, główny, blok paska postępu oraz stopkę. Zastosowano semantyczne znaczniki sekcji HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, stopki - footer, dopuszcza się zagnieżdżenie bloków div w znacznikach semantycznych

    ok. 2 min
    Polecenie z arkusza

    Podział strony na blok boczny i obok niego bloki: baner, główny, blok paska postępu oraz na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML 5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Baner <header>, blok główny <main>, pasek postępu <section id="progress">, blok boczny z promocjami <aside> (treść poboczna wobec formularza), stopka <footer> (R.2.4). Kolejność w kodzie to kolejność treści: nagłówek strony, formularz, jego postęp, potem promocje. Tak czyta stronę czytnik ekranu i tak ułoży ją telefon. W wersji podstawowej <aside> musiał stać pierwszy, bo float układa bloki w kolejności z kodu; siatka CSS umieści każdy blok w jego miejscu niezależnie od kolejności.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <section id="progress">
        </section>
        <aside>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  15. Krok 15: Blok boczny: plakietka

    Kryteria:

    R.2.6 W bloku bocznym umieszczono obraz o nazwie "obraz" w formacie PNG lub JPEG z tekstem alternatywnym "promocje"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku bocznego: Obraz o nazwie obraz.png z tekstem alternatywnym „promocje”

    Obraz z przezroczystym tłem - na niebieskim bloku plakietka nie ma białego prostokąta wokół.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  16. Krok 16: Blok boczny: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Nagłówek drugiego stopnia o treści „Sprawdź promocje”

    <h2> pod obrazem.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  17. Krok 17: Blok boczny: tabela promocji

    Kryteria:

    R.2.7 W bloku bocznym umieszczono tabelę zgodną z Obrazem 3 arkusza egzaminacyjnego, której pierwszy wiersz ma komórki nagłówkowe, zastosowano znaczniki <table>, <tr>, <th>, <td>

    ok. 2 min
    Polecenie z arkusza

    Tabela, której pierwszy wiersz jest złożony z komórek nagłówkowych, wypełniona treściami widocznymi na obrazie 3

    Pierwszy wiersz z komórkami <th> („co?”, „ile taniej?”) w <thead>, wiersze z danymi w <tbody>. scope="col" mówi, że nagłówek opisuje kolumnę - czytnik ekranu przy „15%” przeczyta „ile taniej?”. Klucz wymaga table, tr, th i td - są wszystkie (R.2.7).

    rejestracja.html HTML zmienione: 18 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  18. Krok 18: Baner: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: Nagłówek pierwszego stopnia o treści „Zarejestruj się w sklepie”

    <h1> w banerze - pierwszy nagłówek w kodzie strony, bo baner stoi teraz na początku <body>.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  19. Krok 19: Blok główny: trzy przyciski zakładek

    Kryteria:

    R.2.8 W bloku głównym umieszczono trzy przyciski o treściach „Klient”, „Adres”, „Kontakt”

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego: Trzy przyciski o treściach: „Klient”, „Adres”, „Kontakt”, wciśnięcie dowolnego z nich wywołuje funkcję aktywującą kolejno blok pierwszy, drugi, trzeci

    Trzy <button> z type="button" - przycisk niczego nie wysyła, tylko przełącza. Atrybut data-tab niesie identyfikator zakładki, którą przycisk pokaże: skrypt odczyta go przez dataset.tab, więc w HTML-u nie ma wywołań funkcji. Klucz R.2.8 ocenia treść przycisków.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  20. Krok 20: Blok 1: imię z podpowiedzią

    ok. 2 min
    Polecenie z arkusza

    Blok pierwszy z kontrolkami: Paragraf „Imię”, następnie pole edycyjne z podpowiedzią „Wpisz dane...”

    • zakładka to <div> z klasą tab (wspólny wygląd) i identyfikatorem client. Druga klasa, active, oznacza zakładkę widoczną - pierwszą od początku. Którą klasę ukrywa arkusz stylów, a którą pokazuje, ustali CSS,
    • podpis stoi w akapicie, jak każe arkusz, ale w akapicie jest <label for="firstName"> - napis jest powiązany z polem: kliknięcie „Imię” ustawia kursor w polu, a czytnik ekranu przy wejściu w pole przeczyta jego podpis,
    • placeholder - szara podpowiedź znikająca po wpisaniu tekstu. Nie zastępuje podpisu, dlatego jest i jedno, i drugie.
    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  21. Krok 21: Blok 1: nazwisko

    ok. 1 min
    Polecenie z arkusza

    Paragraf „Nazwisko”, następnie pole edycyjne

    Ten sam wzór: akapit z <label> i pole tekstowe.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  22. Krok 22: Blok 1: data urodzenia

    Kryteria:

    R.3.2 Pierwszy blok z kontrolkami ma dwa pola edycyjne oraz jedno typu date, dla pola do wpisywania imienia zdefiniowano placeholder "Wpisz dane..."

    ok. 1 min
    Polecenie z arkusza

    Paragraf „Data urodzenia”, następnie pole edycyjne typu daty

    Pole type="date" - przeglądarka pokazuje maskę dd.mm.rrrr i kalendarz (obraz 2), a wartość pola zawsze ma postać RRRR-MM-DD, niezależnie od wyświetlania (widać to w konsoli na obrazie 6). Pierwszy blok ma komplet: dwa pola tekstowe, pole daty i podpowiedź (R.3.2).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  23. Krok 23: Blok 2: ulica

    ok. 1 min
    Polecenie z arkusza

    Blok drugi z kontrolkami: Paragraf „Ulica”, następnie pole edycyjne

    Druga zakładka (id="address") - bez klasy active. Na razie wszystkie bloki są widoczne jeden pod drugim, ukryje je arkusz stylów.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  24. Krok 24: Blok 2: numer domu

    ok. 1 min
    Polecenie z arkusza

    Paragraf „Numer”, następnie pole edycyjne typu numerycznego

    Pole type="number" ze strzałkami. min="1" - numer domu nie bywa zerem ani liczbą ujemną, więc strzałka w dół zatrzyma się na 1, a przeglądarka oznaczy niepoprawną wartość. Walidacji w skrypcie nie ma (arkusz każe ją pominąć) - to tylko podpowiedź w samym polu.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  25. Krok 25: Blok 2: miasto

    Kryteria:

    R.3.3 Drugi blok z kontrolkami ma dwa pola edycyjne oraz jedno typu number

    ok. 1 min
    Polecenie z arkusza

    Paragraf „Miasto”, następnie pole edycyjne

    Drugi blok ma komplet z R.3.3: dwa pola tekstowe i jedno liczbowe.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  26. Krok 26: Blok 3: numer komórkowy

    Kryteria:

    R.2.9 W bloku głównym znajdują się trzy bloki, w każdym z nich umieszczono przynajmniej jedno pole edycyjne oraz przynajmniej jeden podpis w paragrafie

    ok. 1 min
    Polecenie z arkusza

    Blok trzeci z kontrolkami: Paragraf „Numer komórkowy”, następnie pole edycyjne typu numeru telefonu

    Trzecia zakładka (id="contact"), pole type="tel" - telefon otwiera przy nim klawiaturę numeryczną, a autocomplete="tel" pozwala przeglądarce podpowiedzieć zapisany numer. Każdy z trzech bloków ma już pole i podpis w akapicie (R.2.9).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  27. Krok 27: Blok 3: zgoda RODO

    Kryteria:

    R.3.4 Trzeci blok z kontrolkami ma jedno pole typu tel i jedno typu checkbox

    ok. 1 min
    Polecenie z arkusza

    Pole wyboru podpisane „Akceptuję RODO”

    Pole wyboru type="checkbox" stoi wewnątrz <label> - podpis i pole są powiązane bez for/id, a klikalny jest cały napis. Akapit przenosi zgodę do nowej linii pod telefonem (obraz 5), bez <br>. Trzeci blok ma pole tel i pole wyboru (R.3.4).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  28. Krok 28: Blok 3: przycisk „Zatwierdź dane”

    Kryteria:

    R.2.10 W trzecim bloku umieszczono przycisk "Zatwierdź dane"

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Zatwierdź dane”, którego kliknięcie powoduje wywołanie funkcji zatwierdzającej

    Przycisk z identyfikatorem confirm - po nim skrypt podepnie funkcję zatwierdzającą. Też type="button": dane trafiają do konsoli, nie do serwera.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  29. Krok 29: Pasek postępu: pusty blok

    ok. 2 min
    Polecenie z arkusza

    Zawartość paska postępu: pusty blok (sekcja)

    W sekcji progress pusta sekcja bar - granatowy pasek, którego szerokość zmieni skrypt. Blok jest pusty, ale czytnik ekranu i tak go rozpozna: role="progressbar" z aria-valuemin, aria-valuemax i aria-valuenow mówi czytnikowi ekranu, że to wskaźnik postępu i ile wynosi (na start 4). Skrypt będzie aktualizował aria-valuenow razem z szerokością.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  30. Krok 30: Stopka

    Kryteria:

    R.2.5 W bloku bocznym zapisano nagłówek h2 "Sprawdź promocje" , w banerze zapisano nagłówek h1: "Zarejestruj się w sklepie" , w bloku stopki nagłówek h4: "Rejestrację do sklepu wykonał:" z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek czwartego stopnia o treści: „Rejestrację do sklepu wykonał: ”, dalej wstawiony numer zdającego

    <h4>. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL), tu stoi autor rozwiązania. Komplet nagłówków h1, h2 i h4 to R.2.5.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  31. Etap 3: Styl CSS

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

    Krok 31: Plik styl.css: zmienne i krój Cambria

    Kryteria:

    R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Cambria

    ok. 2 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Cambria

    Kolory, które powtarzają się w kilku regułach, trafiają do zmiennych CSS na :root: SteelBlue (bloki i przyciski), Navy (przycisk pod kursorem, pasek, ramka tabeli), LightBlue (zakładki). Zmiana koloru firmowego to wtedy jedna linia. Selektor * z Cambrią i zapasowym krojem szeryfowym (R.4.1).

    styl.css CSS nowy plik
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  32. Krok 32: Tło strony i układ bloków na siatce

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu egzaminacyjnym, w bloku głównym może być widoczny jeden blok z kontrolkami, wszystkie trzy lub żaden (prawidłowo zastosowano właściwości CSS decydujące o układzie strony, np. float i clear albo display flex albo grid)

    ok. 3 min
    Polecenie z arkusza

    Dla ciała dokumentu: kolor tła Linen

    Tło Linen dla body, a przy okazji całe rozmieszczenie bloków z obrazu 2 w jednym miejscu:

    • grid-template-columns: 25% 73% - kolumna bloku bocznego i kolumna banera, bloku głównego i paska; to od razu szerokości z arkusza (klucz R.4.5 przyjmuje je w tej postaci),
    • column-gap: 2% - odstęp między kolumnami, który w wersji podstawowej dawały marginesy bloku bocznego (25% + 2% + 73% = 100%),
    • grid-template-areas rysuje układ słowami: blok boczny w lewej kolumnie przez trzy wiersze, obok niego baner, blok główny i pasek, na dole stopka na całą szerokość,
    • align-items: start - bloki mają swoje wysokości z arkusza, zamiast rozciągać się do wysokości wiersza.

    Każdy blok dostaje nazwę pola przez grid-area. Kolejność w HTML-u nie ma znaczenia - <aside> stoi w kodzie przedostatni, a trafia na lewą stronę (R.3.1).

    styl.css CSS zmienione: 33 linie
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  33. Krok 33: Blok boczny, baner i stopka: wspólne cechy

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloków bocznego, banera i stopki: kolor tła SteelBlue, biały kolor czcionki, wyrównanie tekstu do środka

    Jedna reguła na trzy selektory, kolor ze zmiennej. Wyśrodkowanie dotyczy też obrazu w bloku bocznym, bo obraz jest elementem liniowym.

    styl.css CSS zmienione: 6 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  34. Krok 34: Blok boczny: wysokość, marginesy, cień

    Kryteria:

    R.4.8 Ustawiono dla bloku bocznego: box-shadow: 4px 4px 5px DimGray

    ok. 2 min
    Polecenie z arkusza

    Dodatkowo dla bloku bocznego: szerokość 25%, wysokość 510 px, margines zewnętrzny górny i dolny 0, lewy i prawy 1%, cień bloku o przesunięciu 4 px w obu osiach, rozmyciu 5 px i kolorze DimGray

    • szerokość 25% daje kolumna siatki - dopisane width: 25% liczyłoby się od kolumny, więc blok miałby ćwierć z 25%,
    • margin: 0 1% - góra i dół 0, boki po 1%, jak każe arkusz (R.4.7). Uwaga na siatkę: procentowy margines elementu siatki liczy się od szerokości jego pola (kolumny 25%), a nie całej strony, więc wychodzi drobniejszy niż w wersji podstawowej. Główny odstęp od formularza daje column-gap,
    • box-shadow: 4px 4px 5px DimGray - przesunięcie w poziomie, w pionie, rozmycie, kolor: cień pada w prawo i w dół (R.4.8).
    styl.css CSS zmienione: 3 linie
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  35. Krok 35: Baner: wysokość

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla banera: szerokość 73%, wysokość 80 px

    Szerokość 73% to druga kolumna siatki, w regule zostaje tylko wysokość.

    styl.css CSS zmienione: 1 linia
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  36. Krok 36: Blok główny: wysokość

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: szerokość 73%, wysokość 400 px

    Ta sama kolumna co baner - blok główny stoi pod nim.

    styl.css CSS zmienione: 1 linia
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  37. Krok 37: Przyciski

    Kryteria: ,

    R.4.3 Ustawiono biały kolor czcionki dla bloków bocznego, banera i stopki oraz przycisków

    R.4.6 Ustawiono wyrównanie tekstu do środka dla bloków bocznego, banera i stopki oraz rozmiar czcionki 130% dla przycisków

    ok. 2 min
    Polecenie z arkusza

    Dla wszystkich przycisków: kolor tła SteelBlue, biały kolor czcionki, szerokość 20%, marginesy wewnętrzne 15 px, margines zewnętrzny górny 30 px, rozmiar czcionki 130%, bez obramowania

    Selektor button obejmuje cztery przyciski. Szerokość 20% liczy się od rodzica: zakładek - od bloku głównego, „Zatwierdź dane” - od jego zakładki, dlatego jest węższy (obraz 5). cursor: pointer (dodatek) pokazuje rączkę - przycisk bez ramki wygląda wtedy na klikalny. Biel czcionki domyka R.4.3, rozmiar 130% razem z wyśrodkowaniem bloków - R.4.6.

    styl.css CSS zmienione: 11 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  38. Krok 38: Przycisk pod kursorem

    Kryteria:

    R.4.10 Gdy kursor znajduje się na przycisku jego kolor tła zmienia się na Navy

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajduje się na przycisku jego kolor tła zmienia się na Navy

    :hover - przycisk pod kursorem (obraz 2 pokazuje granatową zakładkę „Klient”). Ten sam kolor dostaje :focus-visible: przycisk wybrany klawiszem Tab - bez tego osoba bez myszy nie widzi, który przycisk jest aktywny, bo border: none zabrało ramkę (R.4.10).

    styl.css CSS zmienione: 5 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    
    button:hover,
    button:focus-visible {
        background-color: var(--accent-color);
    }
    Podgląd strony po tym kroku (rejestracja.html)
  39. Krok 39: Bloki z kontrolkami: wygląd i widoczna tylko zakładka aktywna

    Kryteria:

    R.5.5 W stanie początkowym jest pokazany pierwszy blok z imieniem i nazwiskiem, a pozostałe są utworzone i ukryte (w skrypcie lub CSS)

    ok. 2 min
    Polecenie z arkusza

    Dla trzech bloków z kontrolkami: kolor tła LightBlue, margines zewnętrzny prawy 80 px, marginesy wewnętrzne 10 px. W stanie początkowym jest widoczny tylko pierwszy blok z kontrolkami

    Klasa .tab - wspólny wygląd trzech bloków i display: none dla wszystkich. Druga reguła, .tab.active (oba warunki naraz), przywraca blok, który ma klasę active - w stanie początkowym tylko pierwszy (R.5.5). Pozostałe są w kodzie strony, tylko niewidoczne. Skrypt nie będzie już ustawiał stylów - przestawi klasę, a wygląd zostaje w CSS.

    styl.css CSS zmienione: 11 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    
    button:hover,
    button:focus-visible {
        background-color: var(--accent-color);
    }
    
    .tab {
        background-color: var(--tab-color);
        margin-right: 80px;
        padding: 10px;
        display: none;
    }
    
    .tab.active {
        display: block;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  40. Krok 40: Pasek postępu: tło i margines

    Kryteria: ,

    R.4.2 Ustawiono kolor tła Linen dla body, SteelBlue dla bloków bocznego, banera i stopki oraz przycisków, LightGrey dla bloku paska postępu oraz LightBlue dla bloków z kontrolkami

    R.4.7 Ustawiono marginesy wewnętrzne (padding) 15px dla przycisków, 10px dla bloków z kontrolkami oraz marginesy zewnętrzne (margin) dla bloku bocznego: jedynie lewy i prawy 1%, górny 30px dla przycisków, prawy 80px dla bloków z kontrolkami i dolny 10px dla paska postępu

    ok. 1 min
    Polecenie z arkusza

    Dla bloku paska postępu: kolor tła LightGrey, szerokość 73%, margines zewnętrzny dolny 10 px

    Szerokość 73% daje kolumna. Ta reguła domyka kolory tła (R.4.2: Linen, SteelBlue, LightGrey, LightBlue) i marginesy zewnętrzne z R.4.7.

    styl.css CSS zmienione: 2 linie
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
        background-color: LightGrey;
        margin-bottom: 10px;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    
    button:hover,
    button:focus-visible {
        background-color: var(--accent-color);
    }
    
    .tab {
        background-color: var(--tab-color);
        margin-right: 80px;
        padding: 10px;
        display: none;
    }
    
    .tab.active {
        display: block;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  41. Krok 41: Pusty blok paska: 4% na start

    Kryteria: ,

    R.5.2 W stanie początkowym pasek postępu ma szerokość 4% (w skrypcie lub CSS)

    R.4.4 Ustawiono wysokość bloku bocznego 510px, banera 80px, głównego 400px oraz 30px dla paska postępu

    ok. 1 min
    Polecenie z arkusza

    Dla pustego bloku w bloku paska postępu: kolor tła Navy, szerokość 4%, wysokość 30 px

    Granatowy pasek o szerokości 4% szarego tła - stan początkowy (R.5.2). Wysokość 30 px nadaje wysokość całemu paskowi. transition: width 0.3s (dodatek) sprawia, że pasek wydłuża się płynnie, a nie skokiem. Komplet wysokości z R.4.4: 510, 80, 400 i 30 px.

    styl.css CSS zmienione: 7 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
        background-color: LightGrey;
        margin-bottom: 10px;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    
    button:hover,
    button:focus-visible {
        background-color: var(--accent-color);
    }
    
    .tab {
        background-color: var(--tab-color);
        margin-right: 80px;
        padding: 10px;
        display: none;
    }
    
    .tab.active {
        display: block;
    }
    
    #bar {
        background-color: var(--accent-color);
        width: 4%;
        height: 30px;
        transition: width 0.3s;
    }
    Podgląd strony po tym kroku (rejestracja.html)
  42. Krok 42: Tabela promocji

    Kryteria: ,

    R.4.5 Ustawiono szerokość bloku bocznego 25%, banera, głównego oraz paska postępu 73% (width lub grid-template-columns lub flex) oraz 20% dla przycisków i 100% dla tabeli

    R.4.9 Ustawiono obramowanie tabeli linią kropkowaną o szerokości 1 px i kolorze Navy oraz brak obramowania dla przycisków

    ok. 1 min
    Polecenie z arkusza

    Dla tabeli: szerokość 100%, obramowanie linią kropkowaną o szerokości 1 px i kolorze Navy

    Linia kropkowana to dotted. Ta reguła domyka szerokości (R.4.5: kolumny 25% i 73%, przyciski 20%, tabela 100%) i obramowania (R.4.9: kropkowana tabela, przyciski bez ramki).

    styl.css CSS zmienione: 5 linii
    :root {
        --main-color: SteelBlue;
        --accent-color: Navy;
        --tab-color: LightBlue;
    }
    
    * {
        font-family: Cambria, serif;
    }
    
    body {
        background-color: Linen;
        display: grid;
        grid-template-columns: 25% 73%;
        column-gap: 2%;
        grid-template-areas:
            "aside  header"
            "aside  main"
            "aside  progress"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 400px;
    }
    
    #progress {
        grid-area: progress;
        background-color: LightGrey;
        margin-bottom: 10px;
    }
    
    aside {
        grid-area: aside;
        height: 510px;
        margin: 0 1%;
        box-shadow: 4px 4px 5px DimGray;
    }
    
    aside, header, footer {
        background-color: var(--main-color);
        color: white;
        text-align: center;
    }
    
    footer {
        grid-area: footer;
    }
    
    button {
        background-color: var(--main-color);
        color: white;
        width: 20%;
        padding: 15px;
        margin-top: 30px;
        font-size: 130%;
        border: none;
        cursor: pointer;
    }
    
    button:hover,
    button:focus-visible {
        background-color: var(--accent-color);
    }
    
    .tab {
        background-color: var(--tab-color);
        margin-right: 80px;
        padding: 10px;
        display: none;
    }
    
    .tab.active {
        display: block;
    }
    
    #bar {
        background-color: var(--accent-color);
        width: 4%;
        height: 30px;
        transition: width 0.3s;
    }
    
    table {
        width: 100%;
        border: 1px dotted var(--accent-color);
    }
    Podgląd strony po tym kroku (rejestracja.html)
  43. Etap 4: JavaScript

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

    Krok 43: Funkcja aktywująca: ukrycie wszystkich zakładek

    Kryteria:

    R.5.1 Skrypt zapisano w języku JavaScript i zawiera on przynajmniej jedną instrukcję

    ok. 2 min
    Polecenie z arkusza

    Funkcja aktywująca: ukrywa wszystkie zakładki

    Skrypt stoi na końcu <body> - gdy przeglądarka go czyta, wszystkie elementy strony już istnieją. Zostaje w pliku strony, bo klucz każe sprawdzać skrypt „w kodzie strony”.

    • const tabs - lista trzech zakładek znaleziona raz, selektorem CSS .tab. const, bo zmienna nie dostanie innej listy,
    • showTab(tabId) - pętla for ... of zdejmuje wszystkim klasę active. Ukrywa je reguła .tab z arkusza stylów, skrypt nie dotyka style.display.

    Funkcji nikt jeszcze nie wywołuje (R.5.1).

    rejestracja.html HTML zmienione: 9 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const tabs = document.querySelectorAll(".tab");
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  44. Krok 44: Funkcja aktywująca: pokazanie wybranej zakładki

    ok. 1 min
    Polecenie z arkusza

    Pokazuje jedynie tę zakładkę (blok), której przycisk wybrano

    Po zdjęciu klasy wszystkim jedna linia dodaje ją wybranej. Kolejność „najpierw wszystkim zdejmij, potem jednej dodaj” sprawia, że nie trzeba pamiętać, która zakładka była otwarta.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const tabs = document.querySelectorAll(".tab");
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  45. Krok 45: Przyciski zakładek wywołują funkcję

    Kryteria: ,

    R.3.6 Przynajmniej jeden przycisk zakładek ma zdefiniowane zdarzenie onClick z przypisaną funkcją

    R.5.6 Po kliknięciu w dowolny przycisk (nazwę bloku) jest pokazywany odpowiadający mu blok, a pozostałe są ukrywane

    ok. 3 min
    Polecenie z arkusza

    Funkcja aktywująca jest wywoływana przez wciśnięcie dowolnego przycisku zakładki w bloku głównym

    Pętla po przyciskach z atrybutem data-tab przypisuje każdemu obsługę kliknięcia: funkcję strzałkową, która wywołuje showTab z nazwą zakładki odczytaną z dataset.tab. Nowa zakładka w formularzu nie wymaga zmian w skrypcie - wystarczy przycisk z data-tab.

    Dlaczego właściwość onclick, a nie addEventListener("click", ...)? addEventListener jest ogólniejszy (kilka niezależnych obsług jednego zdarzenia), ale klucz w R.3.6 mówi wprost o zdarzeniu onClick z przypisaną funkcją - właściwość onclick jest dokładnie tym zapisem, tylko w skrypcie zamiast w HTML-u. Jedna obsługa na przycisk w zupełności tu wystarcza. W podglądzie zakładki już działają: „Adres” (obraz 4), „Kontakt” (obraz 5).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const tabs = document.querySelectorAll(".tab");
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  46. Krok 46: Funkcja paska postępu: +12%

    Kryteria:

    R.5.3 Po opuszczeniu przynajmniej jednego pola edycyjnego pasek postępu zwiększa swoją szerokość o 12%

    ok. 3 min
    Polecenie z arkusza

    Funkcja modyfikuje właściwość stylu dla pustego bloku wewnątrz bloku paska postępu, w ten sposób, że jego szerokość jest zwiększana o 12%, np. po utracie focusa za pierwszym razem wartość wynosi 16%; za drugim razem jest to 28%, trzecim 40% i tak dalej

    • stałe PROGRESS_START i PROGRESS_STEP na górze skryptu - liczby 4 i 12 mają nazwy, a zmiana kroku to jedna linia (wielkie litery to zwyczaj dla stałych konfiguracyjnych),
    • let progress stoi poza funkcją, więc pamięta wartość między wywołaniami; let, bo wartość się zmienia,
    • progress += PROGRESS_STEP - skrót od progress = progress + PROGRESS_STEP,
    • szablon tekstowy `${progress}%` składa „16%” bez sklejania plusami,
    • aria-valuenow dostaje tę samą liczbę - czytnik ekranu zna aktualny postęp (R.5.3).
    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const PROGRESS_START = 4;
            const PROGRESS_STEP = 12;
            const progressBar = document.querySelector("#bar");
            const tabs = document.querySelectorAll(".tab");
            let progress = PROGRESS_START;
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
    
            function increaseProgress() {
                progress += PROGRESS_STEP;
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute("aria-valuenow", progress);
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  47. Krok 47: Każde pole edycyjne wywołuje funkcję po utracie focusa

    Kryteria:

    R.3.5 Przynajmniej jedno pole edycyjne ma zdefiniowane zdarzenie onBlur lub onFocusOut z przypisaną funkcją

    ok. 2 min
    Polecenie z arkusza

    Dla wszystkich pól edycyjnych – utrata focusa powoduje wywołanie funkcji zmieniającej wartość paska postępu

    Utrata focusa to zdarzenie blur (kliknięcie obok albo Tab). Selektor .tab input:not([type=checkbox]) wybiera wszystkie pola w zakładkach oprócz pola wyboru - pole RODO nie jest polem edycyjnym. Wychodzi siedem pól: imię, nazwisko, data, ulica, numer, miasto, telefon.

    Pętla przypisuje każdemu właściwość onblur - z tego samego powodu co onclick przy zakładkach: klucz R.3.5 nazywa wprost zdarzenie onBlur. Uwaga: increaseProgress bez nawiasów - przypisana jest funkcja, a nie wynik jej wywołania. W podglądzie: kliknięcie w pole imienia i potem obok wydłuża pasek do 16%.

    Typowe błędy

    field.onblur = increaseProgress() (z nawiasami) wywołuje funkcję raz, przy wczytaniu strony, i przypisuje jej wynik undefined - pasek skacze na 16% i dalej już nie rośnie.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const PROGRESS_START = 4;
            const PROGRESS_STEP = 12;
            const progressBar = document.querySelector("#bar");
            const tabs = document.querySelectorAll(".tab");
            let progress = PROGRESS_START;
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
    
            function increaseProgress() {
                progress += PROGRESS_STEP;
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute("aria-valuenow", progress);
            }
    
            const editFields = document.querySelectorAll(".tab input:not([type=checkbox])");
            for (const field of editFields) {
                field.onblur = increaseProgress;
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  48. Krok 48: Pasek nigdy szerszy niż 100%

    Kryteria:

    R.5.4 Szerokość paska postępu nie może być większa niż 100%, zastosowano warunek

    ok. 2 min
    Polecenie z arkusza

    Należy zabezpieczyć funkcję, aby wartość szerokości nigdy nie była wyższa niż 100%

    4 + 8 · 12 = 100, więc po ósmej utracie focusa pasek jest pełny, a po dziewiątej miałby 112%. Warunek przycina wartość do stałej PROGRESS_MAX, zanim trafi do stylu. Krócej byłoby Math.min(progress + PROGRESS_STEP, PROGRESS_MAX), ale klucz R.5.4 mówi „zastosowano warunek” - jawny if nie zostawia wątpliwości.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const PROGRESS_START = 4;
            const PROGRESS_STEP = 12;
            const PROGRESS_MAX = 100;
            const progressBar = document.querySelector("#bar");
            const tabs = document.querySelectorAll(".tab");
            let progress = PROGRESS_START;
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
    
            function increaseProgress() {
                progress += PROGRESS_STEP;
                if (progress > PROGRESS_MAX) {
                    progress = PROGRESS_MAX;
                }
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute("aria-valuenow", progress);
            }
    
            const editFields = document.querySelectorAll(".tab input:not([type=checkbox])");
            for (const field of editFields) {
                field.onblur = increaseProgress;
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  49. Krok 49: Funkcja zatwierdzająca: wartości w konsoli

    Kryteria: ,

    R.5.7 Po kliknięciu w przycisk zatwierdzający wyświetlone są w konsoli przeglądarki wartości wpisane w pola edycyjne

    R.5.8 W funkcji zatwierdzającej zastosowano funkcję lub zestaw funkcji odwołujących się do wszystkich pól edycyjnych i pola wyboru na stronie

    ok. 3 min
    Polecenie z arkusza

    Funkcja zatwierdzająca pobiera wartość z każdego pola edycyjnego i pola wyboru. Wartości wyświetlane są w konsoli przeglądarki (obraz 6). Dla uproszczenia wszelką walidację należy pominąć

    Funkcja korzysta z listy editFields, którą skrypt już ma:

    • [...editFields] zamienia listę elementów w tablicę, a map robi z każdego pola jego wartość - siedem odczytów value jedną linią, w kolejności pól na stronie,
    • pole wyboru dokłada .checked (true albo false). Obraz 6 pokazuje przy zgodzie „on”, ale to .value pola wyboru, które wynosi „on” zawsze, także bez zaznaczenia - nic nie mówi o zgodzie,
    • join(", ") skleja wartości przecinkami w jeden napis, jak na obrazie 6.

    Funkcja odwołuje się do wszystkich pól edycyjnych i pola wyboru (R.5.8). Walidacji nie ma - tak każe arkusz.

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const PROGRESS_START = 4;
            const PROGRESS_STEP = 12;
            const PROGRESS_MAX = 100;
            const progressBar = document.querySelector("#bar");
            const tabs = document.querySelectorAll(".tab");
            let progress = PROGRESS_START;
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
    
            function increaseProgress() {
                progress += PROGRESS_STEP;
                if (progress > PROGRESS_MAX) {
                    progress = PROGRESS_MAX;
                }
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute("aria-valuenow", progress);
            }
    
            const editFields = document.querySelectorAll(".tab input:not([type=checkbox])");
            for (const field of editFields) {
                field.onblur = increaseProgress;
            }
    
            const consentBox = document.querySelector("#rodo");
    
            function confirmData() {
                const values = [...editFields].map((field) => field.value);
                values.push(consentBox.checked);
                console.log(values.join(", "));
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  50. Krok 50: Przycisk „Zatwierdź dane” wywołuje funkcję

    Kryteria: ,

    R.3.7 Przycisk do zatwierdzenia danych ma zdefiniowane zdarzenie onClick z przypisaną funkcją

    R.5.9 Zastosowano znaczące nazewnictwo dla wszystkich zmiennych i funkcji w języku polskim lub angielskim. Nazwy odzwierciedlają cel zastosowania

    ok. 1 min
    Polecenie z arkusza

    Funkcja zatwierdzająca jest wywoływana po wybraniu przycisku „Zatwierdź dane”

    Właściwość onclick przycisku #confirm dostaje funkcję confirmData - znowu bez nawiasów (R.3.7). Wynik widać w konsoli (F12, Console). Nazwy mówią, co robią i co przechowują: showTab, increaseProgress, confirmData, editFields, consentBox, PROGRESS_STEP (R.5.9).

    rejestracja.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>Sklep - rejestracja</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zarejestruj się w sklepie</h1>
        </header>
        <main>
            <button type="button" data-tab="client">Klient</button>
            <button type="button" data-tab="address">Adres</button>
            <button type="button" data-tab="contact">Kontakt</button>
            <div class="tab active" id="client">
                <p><label for="firstName">Imię</label></p>
                <input type="text" id="firstName" placeholder="Wpisz dane...">
                <p><label for="lastName">Nazwisko</label></p>
                <input type="text" id="lastName">
                <p><label for="birthDate">Data urodzenia</label></p>
                <input type="date" id="birthDate">
            </div>
            <div class="tab" id="address">
                <p><label for="street">Ulica</label></p>
                <input type="text" id="street">
                <p><label for="houseNumber">Numer</label></p>
                <input type="number" id="houseNumber" min="1">
                <p><label for="city">Miasto</label></p>
                <input type="text" id="city">
            </div>
            <div class="tab" id="contact">
                <p><label for="phone">Numer komórkowy</label></p>
                <input type="tel" id="phone" autocomplete="tel">
                <p>
                    <label>
                        <input type="checkbox" id="rodo">
                        Akceptuję RODO
                    </label>
                </p>
                <button type="button" id="confirm">Zatwierdź dane</button>
            </div>
        </main>
        <section id="progress">
            <section id="bar" role="progressbar" aria-label="Postęp rejestracji"
                     aria-valuemin="0" aria-valuemax="100" aria-valuenow="4"></section>
        </section>
        <aside>
            <img src="obraz.png" alt="promocje">
            <h2>Sprawdź promocje</h2>
            <table>
                <thead>
                    <tr>
                        <th scope="col">co?</th>
                        <th scope="col">ile taniej?</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>ubrania</td>
                        <td>15%</td>
                    </tr>
                    <tr>
                        <td>buty</td>
                        <td>25%</td>
                    </tr>
                </tbody>
            </table>
        </aside>
        <footer>
            <h4>Rejestrację do sklepu wykonał: Bartosz Bryniarski</h4>
        </footer>
        <script>
            const PROGRESS_START = 4;
            const PROGRESS_STEP = 12;
            const PROGRESS_MAX = 100;
            const progressBar = document.querySelector("#bar");
            const tabs = document.querySelectorAll(".tab");
            let progress = PROGRESS_START;
    
            function showTab(tabId) {
                for (const tab of tabs) {
                    tab.classList.remove("active");
                }
                document.getElementById(tabId).classList.add("active");
            }
    
            for (const button of document.querySelectorAll("button[data-tab]")) {
                button.onclick = () => showTab(button.dataset.tab);
            }
    
            function increaseProgress() {
                progress += PROGRESS_STEP;
                if (progress > PROGRESS_MAX) {
                    progress = PROGRESS_MAX;
                }
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute("aria-valuenow", progress);
            }
    
            const editFields = document.querySelectorAll(".tab input:not([type=checkbox])");
            for (const field of editFields) {
                field.onblur = increaseProgress;
            }
    
            const consentBox = document.querySelector("#rodo");
    
            function confirmData() {
                const values = [...editFields].map((field) => field.value);
                values.push(consentBox.checked);
                console.log(values.join(", "));
            }
    
            document.querySelector("#confirm").onclick = confirmData;
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (rejestracja.html)
  51. Etap 5: Porównanie z wersją podstawową

    Krok 51 · po tym etapie masz 43 z 43 kryteriów (pkt)

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left czterech bloków + clear stopkigrid-template-areas na <body>
    kolejność w HTML-u<aside> pierwszy (wymóg float)kolejność treści: baner, formularz, pasek, promocje
    szerokościwidth w każdej regulekolumny siatki 25% 73% i column-gap
    widoczna zakładkaskrypt ustawia style.displayskrypt przestawia klasę active, wygląd w CSS
    podpisy pólakapityakapity z <label> powiązanym z polem
    zdarzeniaatrybuty onclick i onblur w HTML-uwłaściwości onclick i onblur ustawiane pętlą w skrypcie
    odczyt pólsiedem getElementById(...).valuemap po liście pól i join
    SQL> '2000-12-31', pełne nazwy tabel>= '2001-01-01', aliasy, ORDER BY, SMALLINT UNSIGNED

    Na egzaminie wersja podstawowa jest szybsza do napisania i trzyma się funkcji z tabeli 1 arkusza. Nowoczesna jest krótsza tam, gdzie pola się powtarzają, i łatwiej ją rozbudować: nowe pole albo nowa zakładka nie wymagają zmian w skrypcie. Jeśli używasz siatki, pilnuj szerokości w grid-template-columns, a nie w width bloków.

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

  52. Etap 6: Oddanie pracy

    Krok 52 · po tym etapie masz 43 z 43 kryteriów (pkt)

    Krok 52: 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. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona. W folderze z numerem zdającego: import.png, kw1.jpg - kw4.jpg, kwerendy.txt, obraz.png, przeglądarka.txt, rejestracja.html, styl.css. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.