Przejdź do głównej treści
INF.03-09-24.06-SGCzerwiec 2024 · 37 kroków · ok. 72 min · 34 kryteria z klucza

Rozwiązanie krok po kroku: Animowana galeria wycieczek (Nowoczesny kod (grid))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza wycieczki z czterema zapytaniami, zdjęcie wykadrowane między kreskami i strona galeria.html z dużym zdjęciem między strzałkami, paskiem wjeżdżających miniatur i powiększeniem pod kursorem - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • siatka CSS na całej stronie: grid-template-areas układa baner, trzy bloki, pasek miniatur i stopkę, a szerokości 15%, 70% i 15% stoją w grid-template-columns (klucz R.4.5 przyjmuje je wprost) - bez float i clear,
  • kolory w zmiennych CSS, duże zdjęcie w znaczniku <main>, pasek miniatur na flex,
  • animacje w skróconym zapisie animation i wyłączane u osób, które w systemie ograniczyły ruch na ekranie,
  • galeria prowadzona przez listę miniatur: skrypt bierze pliki z miniatur, a jedna funkcja zawija numerację w obie strony resztą z dzielenia - bez liczby 7 wpisanej w warunki,
  • zdarzenia podpięte w skrypcie (addEventListener) i obsługa strzałek klawiatury,
  • SQL czytelniej: aliasy tabel, zaokrąglona i posortowana średnia, a użytkownik Ewa dostaje prawo odczytu bazy, czyli naprawdę jest „dla bazy wycieczki”.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcia pochodzą z prawdziwego archiwum pliki9, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a (także logowanie kontem Ewa), a skrypt i animacje są sprawdzone w prawdziwej przeglądarce Chrome.

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

    Krok 1: Baza danych wycieczki

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki9.zip rozpakuj hasłem G@leri@Animowan@ (trzy małpy w miejscu liter „a”) do folderu z numerem zdającego. W phpMyAdmin Nowa → wycieczki, porównywanie utf8_unicode_ci, albo zapytaniem:

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

    ok. 2 min
    Polecenie z arkusza

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

    Zaznaczona baza wycieczki, Import → wycieczki.sql. Dwie tabele z obrazu 1: wycieczki (7 wycieczek: miejsce, cena, liczba dni) i zdjecia (pliki 1.jpg - 7.jpg, każde z kluczem obcym Wycieczki_id).

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych wycieczki 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: wycieczki tańsze niż 1000 zł

    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 miejsce i liczbę dni dla wycieczek, których cena jest mniejsza od 1000 zł. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miejsce, liczbaDni FROM wycieczki WHERE cena < 1000; oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 5 rekordów z danymi: Gdańsk 2; Kraków 3; Niedzica 1; Szklarska Poręba 4; Wrocław 2

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie miejsce i liczbę dni dla wycieczek, których cena jest mniejsza od 1000 zł

    Dwa pola i warunek na trzecie - kolumna w WHERE nie musi być na liście SELECT. Każda klauzula w osobnej linii: przy dłuższych zapytaniach od razu widać, co jest listą pól, a co warunkiem. Wynik: 5 wierszy - Gdańsk 2, Kraków 3, Niedzica 1, Szklarska Poręba 4, Wrocław 2, jak w kluczu. Zapytania zapisz w kwerendy.txt, zrzut jako kw1.jpg.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT miejsce, liczbaDni
    FROM wycieczki
    WHERE cena < 1000;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: średnia cena według liczby dni

    Kryteria:

    R.1.4 Wykonano zapytanie 2 liczące średnią cenę dla wycieczek pogrupowanych ze względu na liczbę dni. Kwerenda wybiera jedynie liczbę dni oraz średnią cenę, której kolumna nosi nazwę sredniaCena . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT liczbaDni, AVG(cena) AS sredniaCena FROM wycieczki GROUP BY liczbaDni; oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 5 rekordów, kolumna ze średnią nazwana jest sredniaCena , wyświetlono dane z dowolną dokładnością: 1 150; 2 350; 3 500; 4 540; 7: 1350

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: liczące średnią cenę dla wycieczek pogrupowanych ze względu na liczbę dni (czyli średnia cena wycieczek jednodniowych, dwudniowych itd.). Kwerenda wybiera jedynie liczbę dni oraz średnią cenę, której kolumnę należy nazwać (alias) sredniaCena

    • GROUP BY liczbaDni - jedna grupa na każdą liczbę dni, AVG(cena) liczy średnią w grupie, AS sredniaCena nadaje kolumnie nazwę z arkusza,
    • ROUND(..., 2) - cena ma grosze, więc dwa miejsca po przecinku (350.00) zamiast czterech, które daje samo AVG (350.0000). Klucz przyjmuje dowolną dokładność,
    • ORDER BY liczbaDni - wiersze od jednodniowych do najdłuższych. MariaDB często zwraca grupy w tej kolejności i bez sortowania, ale to szczegół wykonania, nie gwarancja.

    Wynik: 1 - 150, 2 - 350, 3 - 500, 4 - 540, 7 - 1350, jak w kluczu. Zrzut: kw2.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT miejsce, liczbaDni
    FROM wycieczki
    WHERE cena < 1000;
    
    -- Zapytanie 2
    SELECT liczbaDni, ROUND(AVG(cena), 2) AS sredniaCena
    FROM wycieczki
    GROUP BY liczbaDni
    ORDER BY liczbaDni;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: miejsca droższe niż 500 zł ze zdjęciem

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie miejsce wycieczki i odpowiadającą mu nazwę zdjęcia dla wycieczek których cena jest wyższa od 500 zł. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miejsce, nazwa FROM Wycieczki JOIN Zdjecia ON Wycieczki.id = Wycieczki_id WHERE cena > 500; (możliwe również INNER JOIN lub porównanie kluczy po WHERE, poprawne również porównanie kluczy głównych) oraz wynik działania udokumentowano zrzutem, na którym widoczne są dokładnie 3 rekordy z danymi: Pieniny 4.jpg; Szklarska Poręba 5.jpg; Tatry 6.jpg

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie miejsce wycieczki i odpowiadającą mu nazwę zdjęcia dla wycieczek, których cena jest wyższa od 500 zł. Należy posłużyć się relacją

    Aliasy w (wycieczki) i z (zdjecia): przy każdej kolumnie widać tabelę, a ON z.Wycieczki_id = w.id pokazuje parę kluczy relacji. w.cena > 500 - ostro większa, więc Kraków za dokładnie 500 zł odpada. Wynik: Pieniny 4.jpg, Szklarska Poręba 5.jpg, Tatry 6.jpg. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT miejsce, liczbaDni
    FROM wycieczki
    WHERE cena < 1000;
    
    -- Zapytanie 2
    SELECT liczbaDni, ROUND(AVG(cena), 2) AS sredniaCena
    FROM wycieczki
    GROUP BY liczbaDni
    ORDER BY liczbaDni;
    
    -- Zapytanie 3
    SELECT w.miejsce, z.nazwa
    FROM wycieczki AS w
    JOIN zdjecia AS z ON z.Wycieczki_id = w.id
    WHERE w.cena > 500;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: użytkownik Ewa z dostępem do bazy

    Kryteria:

    R.1.6 Wykonano zapytanie 4 tworzące dla bazy wycieczki użytkownika Ewa o haśle Ewa!Ewa na serwerze localhost . W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE USER 'Ewa'@'localhost' IDENTIFIED BY 'Ewa!Ewa'; oraz wynik działania udokumentowano zrzutem, na którym jest widoczne poprawnie wykonane zapytanie; wybrano bazę wycieczki, a zapytanie nie zwraca błędów

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: Tworzące użytkownika Ewa o haśle Ewa!Ewa dla bazy wycieczki na serwerze localhost

    • CREATE USER 'Ewa'@'localhost' IDENTIFIED BY 'Ewa!Ewa' - konto serwera: nazwa, host, z którego wolno się logować, i hasło w apostrofach (to jest zapis z klucza, R.1.6),
    • GRANT SELECT ON wycieczki.* TO 'Ewa'@'localhost' - dodatek, który robi z konta użytkownika „dla bazy wycieczki”. Samo CREATE USER daje tylko prawo zalogowania się (USAGE), bez dostępu do żadnej bazy.

    Jakie prawa? Arkusz ich nie podaje, więc najmniejsze sensowne: odczyt (zasada najmniejszych uprawnień). Sprawdzone: Ewa odczytuje 7 wycieczek, a DELETE kończy się odmową dostępu. Na zrzucie kw4.jpg komunikat o wykonaniu przy zaznaczonej bazie wycieczki i wynik SHOW GRANTS.

    ⚠️ Rozbieżność arkusz-klucz: klucz CKE przyjmuje samo CREATE USER, które nie wiąże konta z bazą. GRANT niczego nie odbiera - kryterium ocenia zapis z pierwszej linii.

    Typowe błędy

    Hasło bez apostrofów (IDENTIFIED BY Ewa!Ewa) to błąd składni; bez @'localhost' konto dostaje host % (logowanie z dowolnego komputera).

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT miejsce, liczbaDni
    FROM wycieczki
    WHERE cena < 1000;
    
    -- Zapytanie 2
    SELECT liczbaDni, ROUND(AVG(cena), 2) AS sredniaCena
    FROM wycieczki
    GROUP BY liczbaDni
    ORDER BY liczbaDni;
    
    -- Zapytanie 3
    SELECT w.miejsce, z.nazwa
    FROM wycieczki AS w
    JOIN zdjecia AS z ON z.Wycieczki_id = w.id
    WHERE w.cena > 500;
    
    -- Zapytanie 4
    CREATE USER 'Ewa'@'localhost' IDENTIFIED BY 'Ewa!Ewa';
    GRANT SELECT ON wycieczki.* TO 'Ewa'@'localhost';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: 6.jpg: kadr między czarnymi kreskami

    Kryteria:

    R.2.1 Plik 6.jpg przycięto do czarnych kresek (nie są widoczne) i rozmiar obrazu wynosi dokładnie 640 px szerokości i 427 px wysokości

    ok. 3 min
    Polecenie z arkusza

    Plik 6.jpg, wypakowany z archiwum, należy skadrować, tak aby była widoczna tylko część zdjęcia pomiędzy czarnymi kreskami (bez widocznych kresek). Odpowiednio wycięte zdjęcie powinno mieć rozmiary 640 px na 427 px. W przypadku drobnych rozbieżności rozmiaru spowodowanych niedokładnym przycięciem należy przeskalować obraz do dokładnego wymiaru 640 px na 427 px

    Grafika jest taka sama jak w wersji podstawowej. W GIMP-ie narzędzie Kadrowanie, w jego opcjach pozycja 239, 0 i rozmiar 640 × 427 - zaznaczenie zaczyna się w pierwszej kolumnie za lewą kreską i kończy tuż przed prawą, więc skalowanie nie jest potrzebne. Plik → Nadpisz 6.jpg. Podgląd: oryginał z zaznaczonym kadrem.

    Typowe błędy

    Kadr z jedną kolumną kreski - przy krawędzi zostaje ciemny pasek, a klucz wymaga, żeby kreski nie były widoczne.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik galeria.html: HTML5, język, kodowanie

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku galeria.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. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie galeria.html zapisanej w języku HTML5. Ustawiony język zawartości strony na polski. Jawnie zastosowany właściwy standard kodowania polskich znaków

    <!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head> - razem jedno kryterium (R.2.2). Strona nie ma PHP, otwiera się ją prosto z folderu.

    galeria.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  10. Krok 10: Tytuł strony

    Kryteria:

    R.2.3 Nadano tytuł stronie: Biuro turystyczne

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Biuro turystyczne”

    <title> i przy okazji viewport - telefon nie pomniejsza strony do rozmiaru ekranu komputera.

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

    Kryteria:

    R.3.4 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

    Całe formatowanie, także obie animacje, pochodzi ze styl.css.

    Typowe błędy

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

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  12. Krok 12: Bloki: baner, trzy bloki, miniatury, stopka

    Kryteria:

    R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: banera, lewy, środkowy, prawy, miniatur 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 bloki: baner, poniżej trzy bloki: lewy, środkowy i prawy, poniżej blok miniatur, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Baner <header>, stopka <footer> (klucz R.2.4 wymaga ich wprost). Duże zdjęcie to główna treść strony - <main>, bloki ze strzałkami i pasek miniatur to <section>. Identyfikatory po angielsku (left, middle, right, thumbnails) posłużą siatce CSS jako nazwy pól. aria-label nazywa pasek miniatur dla czytnika ekranu - sekcja bez nagłówka nie ma innej nazwy.

    galeria.html HTML zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <section id="left">
        </section>
        <main id="middle">
        </main>
        <section id="right">
        </section>
        <section id="thumbnails" aria-label="Miniatury">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  13. Krok 13: Baner: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „Zwiedzamy Polskę”

    <h1> w banerze.

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
        </section>
        <main id="middle">
        </main>
        <section id="right">
        </section>
        <section id="thumbnails" aria-label="Miniatury">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  14. Krok 14: Blok lewy: przycisk „<”

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku lewego: przycisk o treści „<” (znak mniejszości – patrz tabela 2), którego wybranie powoduje wywołanie funkcji wyświetlającej poprzednie zdjęcie

    • treść &lt; - encja z tabeli 2: goły „<” zaczynałby znacznik,
    • type="button" - przycisk niczego nie wysyła, tylko uruchamia skrypt,
    • aria-label="Poprzednie zdjęcie" - czytnik ekranu przeczyta, do czego służy przycisk, zamiast „mniejsze niż”. Na ekranie dalej widać strzałkę,
    • identyfikator previous - po nim skrypt podepnie funkcję.
    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
        </main>
        <section id="right">
        </section>
        <section id="thumbnails" aria-label="Miniatury">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  15. Krok 15: Blok środkowy: aktywne zdjęcie

    Kryteria:

    R.2.7 W bloku środkowym umieszczono obraz 1.jpg z tekstem alternatywnym "Aktywne zdjęcie"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku środkowego: obraz o nazwie 1.jpg i tekście alternatywnym „Aktywne zdjęcie”

    Obraz bez identyfikatora - to jedyny obraz w <main>, więc CSS i skrypt znajdą go selektorem #middle img.

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
        </section>
        <section id="thumbnails" aria-label="Miniatury">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  16. Krok 16: Blok prawy: przycisk „>”

    Kryteria:

    R.2.6 W bloku lewym umieszczono przycisk o treści "<" oraz w bloku prawym umieszczono przycisk o treści „>”

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: przycisk o treści „>” (znak większości – patrz tabela 2), którego wybranie powoduje wywołanie funkcji wyświetlającej następne zdjęcie

    Lustrzany przycisk: encja &gt;, type="button", aria-label="Następne zdjęcie", identyfikator next (R.2.6).

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  17. Krok 17: Blok miniatur: siedem zdjęć

    Kryteria:

    R.2.8 W bloku miniatur umieszczono 7 obrazów od 1.jpg do 7.jpg . Teksty alternatywne kolejno: Gdańsk, Kraków, Niedzica, Pieniny, Szklarska Poręba, Tatry, Wrocław

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku miniatur: Siedem obrazów od 1.jpg do 7.jpg. Teksty alternatywne kolejno: Gdańsk, Kraków, Niedzica, Pieniny, Szklarska Poręba, Tatry, Wrocław

    Siedem <img> w kolejności z arkusza (zgodnej z bazą). Ta lista będzie dla skryptu źródłem zdjęć galerii: kolejność miniatur wyznaczy kolejność przewijania, więc nowe zdjęcie w galerii to jedna linia HTML-u, bez zmian w skrypcie. Na razie zdjęcia są w pełnym rozmiarze.

    galeria.html HTML zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  18. Krok 18: Stopka: nagłówek i autor

    Kryteria:

    R.2.5 W banerze zapisano nagłówek h1: "Zwiedzamy Polskę" , w bloku stopki nagłówek h3: "Autorem galerii jest:" oraz paragraf z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: Nagłówek trzeciego stopnia o treści: „Autorem galerii jest:”. Paragraf z wstawionym numerem zdającego

    <h3> i akapit. Na egzaminie w akapicie wpisujesz swój numer zdającego (PESEL), tu stoi autor rozwiązania. Komplet nagłówków i akapitu to R.2.5.

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  19. Krok 19: Stopka: odnośnik w nowej karcie

    Kryteria:

    R.3.2 Odnośnik o treści „Więcej zdjęć” inicjuje otwarcie strony http://pixabay.com, strona otwiera się w nowej zakładce przeglądarki

    ok. 1 min
    Polecenie z arkusza

    Odnośnik o treści „Więcej zdjęć” do strony http://pixabay.com otwierający się w nowej karcie przeglądarki

    target="_blank" otwiera nową kartę (R.3.2). rel="noopener" odcina otwartą stronę od karty galerii: obca strona nie może przez window.opener podmienić adresu galerii. Nowe przeglądarki robią to same dla _blank, ale zapis jawny działa wszędzie. Adres zostaje dosłownie z arkusza (http://); pixabay sam przekieruje na https://.

    Typowe błędy

    target="blank" bez podkreślenia otwiera kartę nazwaną „blank” - kolejne kliknięcie trafia do tej samej karty. Poprawnie: _blank.

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  20. Etap 3: Styl CSS

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

    Krok 20: Plik styl.css: zmienne, krój Georgia, biały tekst

    Kryteria:

    R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Georgia, biały kolor czcionki

    ok. 1 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Georgia, biały kolor czcionki

    Dwa kolory z arkusza występują w kilku regułach (Maroon: baner, miniatury, stopka, strzałki; LightSalmon: bloki i przyciski), więc trafiają do zmiennych na :root. Selektor * z Georgią (i zapasowym krojem szeryfowym) oraz bielą - biel dostaje każdy element, także odnośnik (R.4.1). Na białym tle strony tekst chwilowo znika.

    styl.css CSS nowy plik
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    Podgląd strony po tym kroku (galeria.html)
  21. Krok 21: 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 (prawidłowo zastosowano właściwości CSS decydujące o układzie strony, np. float i clear albo display flex albo grid)

    R.4.5 Ustawiono szerokość dla bloków lewego i prawego 15% oraz dla bloku środkowego 70% (width lub grid-template-columns lub flex)

    ok. 3 min
    Polecenie z arkusza

    Podział strony na bloki: baner, poniżej trzy bloki: lewy, środkowy i prawy, poniżej blok miniatur, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    grid-template-areas rysuje obraz 2 słowami: baner przez trzy kolumny, potem trzy bloki obok siebie, pod nimi pasek miniatur i stopka, oba na całą szerokość. grid-template-columns: 15% 70% 15% to od razu szerokości bloków z arkusza (klucz R.4.5 przyjmuje je w tej postaci). Każdy blok dostaje nazwę pola przez grid-area.

    W porównaniu z float: nie ma clear - pasek miniatur stoi pod blokami, bo siatka ma dla niego osobny wiersz (R.3.1).

    styl.css CSS zmienione: 34 linie
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (galeria.html)
  22. Krok 22: Baner i stopka

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla banera i stopki

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku banera i stopki: kolor tła Maroon, wyrównanie tekstu do środka, marginesy wewnętrzne 2 px

    Jedna reguła na dwa selektory, kolor ze zmiennej (R.4.6).

    styl.css CSS zmienione: 6 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    Podgląd strony po tym kroku (galeria.html)
  23. Krok 23: Trzy bloki: tło i wysokość

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloków lewego, środkowego i prawego: kolor tła LightSalmon, wysokość 527 px

    Trzy identyfikatory po przecinku. Selektor section nie wystarczyłby - złapałby pasek miniatur, a pominął <main>.

    styl.css CSS zmienione: 5 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    Podgląd strony po tym kroku (galeria.html)
  24. Krok 24: Szerokości bloków lewego, prawego i środkowego

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla bloku lewego i prawego: szerokość 15%. Dodatkowo dla bloku środkowego: szerokość 70%

    Te szerokości są już w grid-template-columns - nie dopisuje się width. Procentowa szerokość elementu siatki liczyłaby się od jego kolumny, więc width: 70% w kolumnie 70% dałoby blok szeroki na 49% strony.

    Typowe błędy

    width: 15% i width: 70% dopisane do bloków na siatce 15% 70% 15% zwężają je do 2,25% i 49% strony.

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

  25. Krok 25: Blok miniatur: tło, wysokość, miniatury w rzędzie

    ok. 2 min
    Polecenie z arkusza

    Dla bloku miniatur: kolor tła Maroon, wysokość 70 px

    Tło i wysokość z arkusza, a do tego:

    • display: flex - miniatury stają w rzędzie jako elementy elastyczne, a nie jako tekst w linii. Obraz w linii tekstu siedzi na linii bazowej i zostawia pod sobą kilka pikseli na „ogonki” liter; w flex tego odstępu nie ma i 70-pikselowe miniatury dokładnie wypełniają 70-pikselowy pasek,
    • gap: 4px - stały odstęp zamiast spacji z kodu HTML (jej szerokość zależy od kroju),
    • overflow: hidden - na początku animacji miniatury są rozsunięte i na wąskim ekranie wyszłyby poza pasek; tak „wjeżdżają” zza jego krawędzi, a strona nie dostaje poziomego paska przewijania.
    styl.css CSS zmienione: 8 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    Podgląd strony po tym kroku (galeria.html)
  26. Krok 26: Przyciski strzałek

    Kryteria: ,

    R.4.2 Ustawiono kolor tła Maroon dla bloków: banera, miniatur i stopki oraz LightSalmon dla bloków lewego, środkowego i prawego oraz dla przycisków

    R.4.3 Ustawiono kolor czcionki Maroon dla przycisku

    ok. 2 min
    Polecenie z arkusza

    Dla obu przycisków: kolor tła LightSalmon, kolor czcionki Maroon, bez obramowania, rozmiar czcionki 400%, blokowy sposób wyświetlania elementu, marginesy zewnętrzne automatycznie obliczane przez przeglądarkę, margines wewnętrzny górny 210 px

    Selektor wskazuje przyciski w blokach ze strzałkami - gdyby na stronie pojawił się inny przycisk, nie dostanie 400%. display: block + margin: auto wyśrodkowuje przycisk w poziomie, padding-top: 210px spycha strzałkę mniej więcej na środek wysokości bloku. Kolor Maroon z tej reguły wygrywa z bielą z * (R.4.3). Ta reguła domyka kolory tła z R.4.2: Maroon banera, miniatur i stopki, LightSalmon bloków i przycisków. cursor: pointer (dodatek) - rączka nad przyciskiem bez ramki.

    styl.css CSS zmienione: 12 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    
    #left button,
    #right button {
        background-color: var(--salmon);
        color: var(--maroon);
        border: none;
        font-size: 400%;
        display: block;
        margin: auto;
        padding-top: 210px;
        cursor: pointer;
    }
    Podgląd strony po tym kroku (galeria.html)
  27. Krok 27: Miniatury: wysokość i animacja wjazdu

    Kryteria: ,

    R.4.4 Ustawiono wysokość bloków lewego, środkowego i prawego 527px oraz bloku miniatur i obrazu miniatur 70px

    R.4.9 Dla obrazów miniatur ustawiono animację trwającą 4 sekundy polegającą na zmianie cechy padding-left z 50 px na początku do 0 px na końcu. Po odświeżeniu strony miniatury przesuwają się od prawej do lewej strony okna przeglądarki

    ok. 3 min
    Polecenie z arkusza

    Dla obrazów miniatur: Styl miniatury: wysokość 70 px, animacja trwa 4 sekundy. Stan początkowy animacji: lewy margines wewnętrzny: 50 px. Stan końcowy animacji: lewy margines wewnętrzny: 0 px

    • @keyframes slide-in - klatki: from (stan początkowy) każda miniatura ma 50 px dopełnienia z lewej, to (stan końcowy) - 0. Klatki pośrednie liczy przeglądarka,
    • animation: slide-in 4s - skrócony zapis animation-name i animation-duration w jednej linii (kolejne wartości skrótu to m.in. funkcja czasu i liczba powtórzeń). Nazwa z myślnikiem, jak nazwy właściwości CSS.

    Po wczytaniu strony miniatury stoją rozsunięte i przez 4 sekundy dosuwają się w lewo - „od prawej do lewej strony okna”, jak opisuje klucz. Wysokość miniatur 70 px domyka wysokości z R.4.4 (R.4.4, R.4.9).

    Typowe błędy

    Same klatki @keyframes bez animation w regule obrazu - animacja nigdy się nie uruchamia.

    styl.css CSS zmienione: 14 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    
    #left button,
    #right button {
        background-color: var(--salmon);
        color: var(--maroon);
        border: none;
        font-size: 400%;
        display: block;
        margin: auto;
        padding-top: 210px;
        cursor: pointer;
    }
    
    #thumbnails img {
        height: 70px;
        animation: slide-in 4s;
    }
    
    @keyframes slide-in {
        from {
            padding-left: 50px;
        }
        to {
            padding-left: 0;
        }
    }
    Podgląd strony po tym kroku (galeria.html)
  28. Krok 28: Duże zdjęcie: wyśrodkowane

    Kryteria: ,

    R.4.7 Ustawiono marginesy wewnętrzne (padding) dla banera i stopki 2px, górny 210px dla obu przycisków, górny 45px dla obrazu z bloku środkowego oraz margin: auto; dla obu przycisków oraz obrazu wyświetlanego w bloku środkowym

    R.4.8 Ustawiono dla obrazu z bloku środkowego i obu przycisków blokowy sposób wyświetlania oraz dla obu przycisków rozmiar czcionki 400% i brak obramowania

    ok. 1 min
    Polecenie z arkusza

    Dla obrazu z bloku środkowego: blokowy sposób wyświetlania elementu, marginesy zewnętrzne automatycznie obliczane przez przeglądarkę, górny margines wewnętrzny 45 px

    To samo wyśrodkowanie co przy przyciskach: display: block + margin: auto. Ten krok domyka marginesy z R.4.7 i sposoby wyświetlania z R.4.8 (przyciski i zdjęcie blokowe, 400% i brak ramki przycisków).

    styl.css CSS zmienione: 6 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    
    #left button,
    #right button {
        background-color: var(--salmon);
        color: var(--maroon);
        border: none;
        font-size: 400%;
        display: block;
        margin: auto;
        padding-top: 210px;
        cursor: pointer;
    }
    
    #thumbnails img {
        height: 70px;
        animation: slide-in 4s;
    }
    
    @keyframes slide-in {
        from {
            padding-left: 50px;
        }
        to {
            padding-left: 0;
        }
    }
    
    #middle img {
        display: block;
        margin: auto;
        padding-top: 45px;
    }
    Podgląd strony po tym kroku (galeria.html)
  29. Krok 29: Duże zdjęcie: powiększanie pod kursorem

    Kryteria:

    R.4.10 Obraz powiększa się po umieszczeniu na nim kursora myszy. Dla pseudoklasy hover obrazu z bloku środkowego ustawiono skalę 120% oraz animację trwającą 5 sekund, polegającą na zmianie skali obrazu ze 100% na początku do 120% na końcu

    ok. 3 min
    Polecenie z arkusza

    Gdy kursor znajduje się na obrazie z bloku środkowego wykonana jest animacja: Stan początkowy animacji: skala 100%. Stan końcowy animacji: skala 120%. Styl obrazu: Skala 120%, czas trwania animacji 5 sekund

    :hover - reguła działa, gdy kursor jest na zdjęciu. Klatki zoom-in prowadzą od scale(1) (100%) do scale(1.2) (120%) przez 5 sekund. transform: scale(1.2) w samej regule („Styl obrazu: Skala 120%”) trzyma powiększenie po końcu animacji - bez niego zdjęcie po 5 sekundach wróciłoby do 100%, choć kursor stoi na nim dalej (R.4.10). Po zjechaniu kursorem powiększenie znika od razu: animacja z @keyframes działa tylko w jedną stronę, a klucz wymaga właśnie jej.

    styl.css CSS zmienione: 14 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    
    #left button,
    #right button {
        background-color: var(--salmon);
        color: var(--maroon);
        border: none;
        font-size: 400%;
        display: block;
        margin: auto;
        padding-top: 210px;
        cursor: pointer;
    }
    
    #thumbnails img {
        height: 70px;
        animation: slide-in 4s;
    }
    
    @keyframes slide-in {
        from {
            padding-left: 50px;
        }
        to {
            padding-left: 0;
        }
    }
    
    #middle img {
        display: block;
        margin: auto;
        padding-top: 45px;
    }
    
    #middle img:hover {
        transform: scale(1.2);
        animation: zoom-in 5s;
    }
    
    @keyframes zoom-in {
        from {
            transform: scale(1);
        }
        to {
            transform: scale(1.2);
        }
    }
    Podgląd strony po tym kroku (galeria.html)
  30. Krok 30: Mniej ruchu dla osób, które o to proszą

    ok. 2 min

    Dodatek spoza arkusza - klucz go nie ocenia. Zapytanie @media (prefers-reduced-motion: reduce) jest prawdziwe u osób, które w systemie zaznaczyły ograniczenie animacji (ruch na ekranie bywa dla nich męczący, a nawet wywołuje zawroty głowy). Dla nich obie animacje są wyłączone: miniatury stoją od razu na miejscu, a zdjęcie powiększa się bez płynnego przejścia. U pozostałych strona działa dokładnie jak w arkuszu.

    styl.css CSS zmienione: 7 linii
    :root {
        --maroon: Maroon;
        --salmon: LightSalmon;
    }
    
    * {
        font-family: Georgia, serif;
        color: white;
    }
    
    body {
        display: grid;
        grid-template-columns: 15% 70% 15%;
        grid-template-areas:
            "header     header     header"
            "left       middle     right"
            "thumbnails thumbnails thumbnails"
            "footer     footer     footer";
    }
    
    header {
        grid-area: header;
    }
    
    #left {
        grid-area: left;
    }
    
    #middle {
        grid-area: middle;
    }
    
    #right {
        grid-area: right;
    }
    
    #thumbnails {
        grid-area: thumbnails;
    }
    
    footer {
        grid-area: footer;
    }
    
    header, footer {
        background-color: var(--maroon);
        text-align: center;
        padding: 2px;
    }
    
    #left, #middle, #right {
        background-color: var(--salmon);
        height: 527px;
    }
    
    #thumbnails {
        background-color: var(--maroon);
        height: 70px;
        display: flex;
        gap: 4px;
        overflow: hidden;
    }
    
    #left button,
    #right button {
        background-color: var(--salmon);
        color: var(--maroon);
        border: none;
        font-size: 400%;
        display: block;
        margin: auto;
        padding-top: 210px;
        cursor: pointer;
    }
    
    #thumbnails img {
        height: 70px;
        animation: slide-in 4s;
    }
    
    @keyframes slide-in {
        from {
            padding-left: 50px;
        }
        to {
            padding-left: 0;
        }
    }
    
    #middle img {
        display: block;
        margin: auto;
        padding-top: 45px;
    }
    
    #middle img:hover {
        transform: scale(1.2);
        animation: zoom-in 5s;
    }
    
    @keyframes zoom-in {
        from {
            transform: scale(1);
        }
        to {
            transform: scale(1.2);
        }
    }
    
    @media (prefers-reduced-motion: reduce) {
        #thumbnails img,
        #middle img:hover {
            animation: none;
        }
    }
    Podgląd strony po tym kroku (galeria.html)
  31. Etap 4: JavaScript

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

    Krok 31: Następne zdjęcie

    Kryteria: , ,

    R.5.1 Skrypt zapisano w języku JavaScript i zawiera on przynajmniej jedną instrukcję zapisaną zgodnie ze składnią

    R.5.2 Po wybraniu przycisku ">" obraz w bloku środkowym zmienia się na następny, np. 1.jpg na 2.jpg

    R.3.3 Wybranie przynajmniej jednego przycisku powoduje wywołanie funkcji skryptu

    ok. 3 min
    Polecenie z arkusza

    Działanie funkcji wyświetlającej następne zdjęcie: Zmienia aktywne zdjęcie w bloku środkowym na następne (np. 1.jpg na 2.jpg)

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

    • const - stałe na duże zdjęcie i listę siedmiu miniatur; obie znajduje querySelector po selektorach CSS,
    • photoIndex - pozycja wyświetlanego zdjęcia na liście miniatur, liczona od 0 (0 = 1.jpg). let, bo się zmienia,
    • showPhoto(index) - jedno miejsce, które zmienia zdjęcie: plik bierze z miniatury o tym indeksie,
    • nextPhoto prosi o pozycję o jeden dalej, a addEventListener("click", ...) podpina ją do przycisku „>” (R.3.3).

    Skrypt nie składa nazwy pliku z liczby - działa dla dowolnych nazw zdjęć. W podglądzie „>” już przewija galerię, ale za siódmym zdjęciem nie ma ósmej miniatury.

    galeria.html HTML zmienione: 16 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
        <script>
            const activePhoto = document.querySelector("#middle img");
            const thumbnails = document.querySelectorAll("#thumbnails img");
            let photoIndex = 0;
    
            function showPhoto(index) {
                photoIndex = index;
                activePhoto.src = thumbnails[photoIndex].getAttribute("src");
            }
    
            function nextPhoto() {
                showPhoto(photoIndex + 1);
            }
    
            document.querySelector("#next").addEventListener("click", nextPhoto);
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  32. Krok 32: Za siódmym zdjęciem pierwsze

    Kryteria:

    R.5.3 Gdy w bloku środkowym jest wyświetlany obraz 7.jpg , wybranie przycisku ">" powoduje wyświetlenie obrazu 1.jpg

    ok. 2 min
    Polecenie z arkusza

    W przypadku, gdy jest wyświetlane ostatnie zdjęcie: 7.jpg zmienia zdjęcie na: 1.jpg

    Reszta z dzielenia przez liczbę miniatur zawija numerację: przy siedmiu zdjęciach 7 % 7 daje 0, czyli pierwsze zdjęcie. Liczba 7 nie jest wpisana w skrypt - thumbnails.length policzy miniatury sama (R.5.3).

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
        <script>
            const activePhoto = document.querySelector("#middle img");
            const thumbnails = document.querySelectorAll("#thumbnails img");
            let photoIndex = 0;
    
            function showPhoto(index) {
                photoIndex = index % thumbnails.length;
                activePhoto.src = thumbnails[photoIndex].getAttribute("src");
            }
    
            function nextPhoto() {
                showPhoto(photoIndex + 1);
            }
    
            document.querySelector("#next").addEventListener("click", nextPhoto);
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  33. Krok 33: Poprzednie zdjęcie

    Kryteria:

    R.5.4 Po wybraniu przycisku "<" obraz w bloku środkowym zmienia się na poprzedni, np. 3.jpg na 2.jpg

    ok. 2 min
    Polecenie z arkusza

    Działanie funkcji wyświetlającej poprzednie zdjęcie: Zmienia aktywne zdjęcie w bloku środkowym na poprzednie (np. 3.jpg na 2.jpg)

    Lustrzana funkcja previousPhoto prosi o pozycję o jeden wcześniej i dostaje własny nasłuchiwacz na przycisku „<”. Z 3.jpg (indeks 2) przechodzi na 2.jpg (R.5.4).

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
        <script>
            const activePhoto = document.querySelector("#middle img");
            const thumbnails = document.querySelectorAll("#thumbnails img");
            let photoIndex = 0;
    
            function showPhoto(index) {
                photoIndex = index % thumbnails.length;
                activePhoto.src = thumbnails[photoIndex].getAttribute("src");
            }
    
            function nextPhoto() {
                showPhoto(photoIndex + 1);
            }
    
            function previousPhoto() {
                showPhoto(photoIndex - 1);
            }
    
            document.querySelector("#next").addEventListener("click", nextPhoto);
            document.querySelector("#previous").addEventListener("click", previousPhoto);
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  34. Krok 34: Przed pierwszym zdjęciem siódme

    Kryteria:

    R.5.5 Gdy w bloku środkowym jest wyświetlany obraz 1.jpg , wybranie przycisku "<" powoduje wyświetlenie obrazu 7.jpg

    ok. 2 min
    Polecenie z arkusza

    W przypadku, gdy jest wyświetlane pierwsze zdjęcie: 1.jpg zmienia zdjęcie na ostatnie: 7.jpg

    Przy pierwszym zdjęciu previousPhoto prosi o indeks -1, a w JavaScripcie -1 % 7 daje -1 (reszta ma znak dzielnej), nie 6. Dlatego przed dzieleniem dodaje się liczbę miniatur: (-1 + 7) % 7 = 6, czyli 7.jpg, a dla indeksów dodatnich wynik się nie zmienia ((7 + 7) % 7 = 0). Jedna linia zawija numerację w obie strony (R.5.5).

    Typowe błędy

    Samo index % count przy pierwszym zdjęciu daje -1 - thumbnails[-1] nie istnieje i skrypt kończy się błędem.

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
        <script>
            const activePhoto = document.querySelector("#middle img");
            const thumbnails = document.querySelectorAll("#thumbnails img");
            let photoIndex = 0;
    
            function showPhoto(index) {
                const count = thumbnails.length;
                photoIndex = (index + count) % count;
                activePhoto.src = thumbnails[photoIndex].getAttribute("src");
            }
    
            function nextPhoto() {
                showPhoto(photoIndex + 1);
            }
    
            function previousPhoto() {
                showPhoto(photoIndex - 1);
            }
    
            document.querySelector("#next").addEventListener("click", nextPhoto);
            document.querySelector("#previous").addEventListener("click", previousPhoto);
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  35. Krok 35: Opis zdjęcia i strzałki klawiatury

    Kryteria:

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

    ok. 3 min

    Dodatki spoza arkusza - klucz ich nie ocenia:

    • tekst alternatywny dużego zdjęcia zmienia się razem z nim: „Aktywne zdjęcie: Wrocław”. Nazwę miejsca skrypt bierze z alt miniatury, szablonem tekstowym. Na starcie zostaje dokładnie „Aktywne zdjęcie” z arkusza,
    • nasłuchiwacz keydown na całym dokumencie: strzałka w lewo i w prawo wywołują te same funkcje co przyciski. === porównuje bez zamiany typów.

    Nazwy mówią, co robią i co przechowują: activePhoto, thumbnails, photoIndex, showPhoto, nextPhoto, previousPhoto (R.5.6).

    galeria.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zwiedzamy Polskę</h1>
        </header>
        <section id="left">
            <button type="button" id="previous" aria-label="Poprzednie zdjęcie">&lt;</button>
        </section>
        <main id="middle">
            <img src="1.jpg" alt="Aktywne zdjęcie">
        </main>
        <section id="right">
            <button type="button" id="next" aria-label="Następne zdjęcie">&gt;</button>
        </section>
        <section id="thumbnails" aria-label="Miniatury">
            <img src="1.jpg" alt="Gdańsk">
            <img src="2.jpg" alt="Kraków">
            <img src="3.jpg" alt="Niedzica">
            <img src="4.jpg" alt="Pieniny">
            <img src="5.jpg" alt="Szklarska Poręba">
            <img src="6.jpg" alt="Tatry">
            <img src="7.jpg" alt="Wrocław">
        </section>
        <footer>
            <h3>Autorem galerii jest:</h3>
            <p>Bartosz Bryniarski</p>
            <a href="http://pixabay.com" target="_blank" rel="noopener">Więcej zdjęć</a>
        </footer>
        <script>
            const activePhoto = document.querySelector("#middle img");
            const thumbnails = document.querySelectorAll("#thumbnails img");
            let photoIndex = 0;
    
            function showPhoto(index) {
                const count = thumbnails.length;
                photoIndex = (index + count) % count;
                activePhoto.src = thumbnails[photoIndex].getAttribute("src");
                activePhoto.alt = `Aktywne zdjęcie: ${thumbnails[photoIndex].alt}`;
            }
    
            function nextPhoto() {
                showPhoto(photoIndex + 1);
            }
    
            function previousPhoto() {
                showPhoto(photoIndex - 1);
            }
    
            document.querySelector("#next").addEventListener("click", nextPhoto);
            document.querySelector("#previous").addEventListener("click", previousPhoto);
    
            document.addEventListener("keydown", (event) => {
                if (event.key === "ArrowLeft") {
                    previousPhoto();
                } else if (event.key === "ArrowRight") {
                    nextPhoto();
                }
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  36. Etap 5: Porównanie z wersją podstawową

    Krok 36 · po tym etapie masz 34 z 34 kryteriów (pkt)

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left trzech bloków, clear paska miniaturgrid-template-areas na <body>, szerokości w grid-template-columns
    duże zdjęcie<section><main> - główna treść strony
    pasek miniaturobrazy w linii tekstuflex z gap i overflow: hidden
    animacjeanimation-name + animation-durationskrót animation, wyłączane przy prefers-reduced-motion
    numer zdjęciaphotoNumber sklejany z „.jpg”, dwa warunki z liczbą 7indeks na liście miniatur, zawijanie (index + count) % count
    zdarzeniaatrybuty onclickaddEventListener, do tego strzałki klawiatury
    zapytanie 4samo CREATE USER (konto bez dostępu do bazy)CREATE USER + GRANT SELECT ON wycieczki.*

    Na egzaminie wersja podstawowa jest prostsza do napisania z pamięci i trzyma się podpowiedzi z tabel arkusza. Nowoczesna działa dla dowolnej liczby i dowolnych nazw zdjęć - galerię rozbudowuje się w HTML-u, bez dotykania skryptu. Przy siatce pamiętaj, żeby szerokości bloków nie powtarzać w width.

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

  37. Etap 6: Oddanie pracy

    Krok 37 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 37: 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 i animacje. W folderze z numerem zdającego: 1.jpg - 7.jpg (6.jpg wykadrowany), galeria.html, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, 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 35.

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.