Chwile Ulotne – jak zbudować prostą stronę w jednym pliku (i dlaczego czasem to wystarcza)

DEMO strony

„Nie bogactwo, a prostota szacunek budzi.”

Tą myślą otwiera się moja mini-strona Chwile Ulotne. Znajdziesz tam jedno zdjęcie, kilka zdań i dedykację. Zero „fajerwerków”, za to intencja: zatrzymać na moment to, co ważne.

Co tu właściwie jest?

Na stronie widać trzy rzeczy:

  1. Sentencja/tytuł – krótkie zdanie prowadzące odbiorcę.
  2. Obraz – jeden plik together.jpg z tekstem osadzonym bezpośrednio na grafice (poemat/kaption stanowi część obrazu).
  3. Dedykacja – „Z dedykacją ku pamięci Dziadka oraz zainspirowane wspomnieniami o pewnej dziewczynie.”

Technicznie to idealny przykład „one-file website”: wystarczy pojedynczy index.html z kilkoma stylami w <style> i minimalnym skryptem w <script> (albo nawet bez niego). Zero AI w kodzie – wszystko ręcznie, od człowieka. Siła tkwi w wyborze i kompozycji, nie w ilości bibliotek.

Dlaczego jeden plik?

  • Szybkość i czytelność: otwierasz edytor, zapisujesz, gotowe.
  • Kontrola: rozumiesz każdą linijkę.
  • Archiwizacja: jedna paczka, łatwo zachować i przenieść.

To nie jest rozwiązanie „na wszystko”, ale gdy treści jest mało, a przekaz ma być szczery i skupiony – to strzał w dziesiątkę.

Minimalny szablon „one-file” (HTML + CSS + JS)

Poniżej przykład, który odtwarza układ: tytuł, obraz, podpis/dedykacja. Zadbane typografia, responsywność i drobiazgowy „dotyk” w postaci płynnego pojawiania się treści. Bez bibliotek, bez frameworków, zero AI – czysty HTML/CSS/JS.

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chwile Ulotne</title>
    <style>
        body {
            background-color: black;
        }
        header, figcaption, footer{
            color: white;
        }
        .video-list {
            list-style-type: none;
            padding: 0;
            margin: 0;
        }
        header, main, li, figure, footer {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 20px;
        }
        header {
            position: absolute;
            left: 0;
            right: 0;
        }
        .fullscreen-list {
            margin: 0;
            padding: 0;
            list-style-type: none;
        }
        .fullscreen-list li {
            height: 100vh;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            box-sizing: border-box;
            padding: 20px;
            position: relative;
        }
        @media (max-width: 768px) {
            .fullscreen-list li {
                font-size: 1.5rem;
                padding: 10px;
            }
        }
        .fullscreen-list li:nth-child(1)::after,
        .fullscreen-list li:nth-child(2)::after{    
            color: white;
            content: "↓";
            position: absolute;
            bottom: 150px;
            left: 50%;
            transform: translateX(-50%);
            font-size: 2rem;
            animation: bounce 2s infinite;
        }
        @keyframes bounce {
            0%, 20%, 50%, 80%, 100% {
                transform: translateX(-50%) translateY(0);
            }
            40% {
                transform: translateX(-50%) translateY(-10px);
            }
            60% {
                transform: translateX(-50%) translateY(-5px);
            }
        }
        p{
            color: white;
            text-align: center;
        }
        .spaceY{
            padding-top: 40px;
            padding-bottom: 10px;
        }
    </style>
</head>
<body>
    <header>
        <h1>„Nie bogactwo, a prostota szacunek budzi.”</h1>
    </header>
    <main>
        <ul class="video-list fullscreen-list">
            <li onclick="scrollToNext()">
                <iframe width="560" height="315" src="https://www.youtube.com/embed/o1UXBC7whrQ?si=jdZUw3Fkq_UBTUo1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
            </li>
            <li onclick="scrollToNext()">
                <iframe width="560" height="315" src="https://www.youtube.com/embed/kOzQ7yR7w74?si=nfySpFHXSyI2ESnl" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
            </li>
            <li>
                <figure>
                <img width="460" src="together.jpg" alt="Together" />
                <figcaption>„Requiescat in pace” (R.I.P.).</figcaption>
                </figure>
                <div class="spaceY"></div>
                <p>„Z dedykacją ku pamięci Dziadka<br>
                    oraz zainspirowane wspomnieniami o pewnej dziewczynie.”
                <div class="spaceY"></div>
            </li>
               
        </ul>
    </main>
    <footer>
        
    </footer>
    <script>
        function scrollToNext() {
            window.scrollBy({
                top: window.innerHeight,
                behavior: 'smooth'
            });
        }

    </script>
</body>
</html>

Co warto zauważyć:

  • Wszystkie style w <style>, zero zewnętrznych arkuszy.
  • Responsywność dzięki szerokościom procentowym.
  • Mały skrypt tylko do ustawienia animacji wejścia – można go usunąć, jeśli wolisz absolutne minimum.

Warstwa emocji

Strona nie jest „portfolio” ani „landingiem” do sprzedaży. To przystań. Zdjęcie opowiada o dwóch osobach – o pamięci, która mówi szeptem, i o drodze, która dopiero się rysuje. Proste słowa, jeden obraz, krótka dedykacja… i wystarczy.

Kiedy prostota wygrywa?

  • Gdy treść udźwignie ciszę (nie potrzebuje wielu sekcji i efektów).
  • Gdy chcesz, by odbiorca skupił się na jednym komunikacie.
  • Gdy liczysz na lekkość publikacji i łatwość przeniesienia.

Jeśli kiedyś ta strona urośnie – dołożysz nawigację, więcej zdjęć, osobne pliki CSS/JS. Dziś jednak niech wygra to, co najważniejsze: przesłanie.


PS: „One-file website” to także świetne ćwiczenie rzemiosła. Każdy znak ma znaczenie. Każda linia jest decyzją. I może właśnie dlatego – choć tak prosta – taka strona bywa bardziej osobista niż niejedna wielka witryna.

Jak Stworzyć Własny Plik Nagłówkowy w C – Kompletny Przewodnik

Wprowadzenie

Pliki nagłówkowe (.h) to fundament programowania w języku C. Pozwalają na organizację kodu, reużywalność funkcji i profesjonalną strukturę projektu. W tym wpisie nauczysz się, jak tworzyć własne pliki nagłówkowe od podstaw.

Co to są Pliki Nagłówkowe?

Pliki .h to pliki zawierające:

  • Deklaracje funkcji (bez implementacji)
  • Definicje struktur i typów
  • Makrodefinicje i stałe
  • Instrukcje preprocessora

Struktura Pliku Nagłówkowego

Każdy plik nagłówkowy powinien mieć następującą strukturę:

// nazwa_pliku.h

#ifndef NAZWA_PLIKU_H    // Guard - chroni przed wielokrotnym includowaniem

#define NAZWA_PLIKU_H

// Tutaj umieszczamy deklaracje

#endif // NAZWA_PLIKU_H

Przykład Praktyczny – funkcja Print

1. Plik Nagłówkowy (print.h)

#ifndef PRINT_H
#define PRINT_H

#include <stdio.h>

void print(char *str);

#endif


2. Implementacja (print.c)

#include "print.h"

void print(char *str)
{
    printf("%s", str);
}

3. Główny Program (main.c)

#include "print.h"

int main(void)
{
    print("Hello, World!\n");
    return 0;
}

Kompilacja Projektu

gcc -o program main.c print.c

Docker – Wprowadzenie do konteneryzacji (cz. 5)

Persistencja danych z użyciem Bind Mounts (Dev Mode)

W poprzednim wpisie poznaliśmy named volumes, które pozwalają przechowywać dane aplikacji niezależnie od kontenera. Teraz przyjrzymy się innej technice – bind mounts – szczególnie przydatnej podczas pracy deweloperskiej.


Czym są Bind Mounts?

Bind mount to sposób, w jaki możemy podłączyć katalog z naszego hosta (komputera) bezpośrednio do kontenera Dockera. Dzięki temu kontener ma dostęp do bieżących plików źródłowych i reaguje natychmiast na ich zmiany – bez konieczności ponownego budowania obrazu.


1. Przejście do katalogu aplikacji

Otwórz terminal i przejdź do katalogu, w którym znajduje się Twoja aplikacja:

cd /path/to/getting-started/app


2. Uruchomienie kontenera w trybie deweloperskim

Wykonaj polecenie:

docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
node:18-alpine \
sh -c "yarn install && yarn run dev"

Co oznaczają parametry?

  • -w /app – ustawia katalog roboczy kontenera na /app.
  • -v "$(pwd):/app" – tworzy bind mount, który łączy bieżący katalog hosta ($(pwd)) z katalogiem /app w kontenerze.
  • node:18-alpine – lekki obraz Node.js, na którym uruchamiamy aplikację.
  • yarn install && yarn run dev – instalacja zależności i start w trybie developerskim.

3. Podgląd logów kontenera

Aby zobaczyć logi aplikacji w czasie rzeczywistym:

docker logs -f <container-id>

<container-id> uzyskasz poleceniem:

docker ps


4. Wprowadzenie zmian w kodzie

Otwórz plik:

src/static/js/app.js

Na linii 109 zmień:

{Submitting ? 'Adding...' : 'Add item'}

na:

{Submitting ? 'Adding...' : 'Add'}


5. Podgląd zmian w przeglądarce

Dzięki bind mountowi zmiana w kodzie zostanie automatycznie odzwierciedlona w aplikacji – wystarczy odświeżyć stronę w przeglądarce na http://localhost:3000.

Docker – Wprowadzenie do konteneryzacji (cz. 4)

Persystencja danych z użyciem named volumes

Do tej pory nasza aplikacja działała w kontenerze, ale miała jedną istotną wadę: po usunięciu kontenera traciliśmy wszystkie dane. To normalne, ponieważ kontener jest tymczasowy – kiedy go zatrzymasz i usuniesz, znika także jego system plików.

Rozwiązaniem tego problemu są wolumeny (volumes) w Dockerze. Dzięki nim możemy przechowywać dane w sposób trwały, niezależnie od cyklu życia kontenera.

1. Tworzenie wolumenu

Na początek stwórzmy wolumen o nazwie todo-db:

docker volume create todo-db

Możesz sprawdzić listę wolumenów poleceniem:

docker volume ls

2. Uruchomienie aplikacji z wolumenem

Teraz uruchomimy naszą aplikację Todo App i zamontujemy wolumen w katalogu /etc/todos:

docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started

➡️ Dzięki temu wszystkie dane aplikacji (np. zadania na liście) będą zapisywane w wolumenie, a nie tylko w kontenerze.

Dodaj teraz kilka nowych zadań do aplikacji Todo – np. przez interfejs w przeglądarce na http://localhost:3000.

3. Usuwanie kontenera

Zatrzymajmy i usuńmy kontener, aby sprawdzić, co stanie się z danymi.

Najpierw sprawdź uruchomione kontenery:

docker ps

Następnie usuń kontener (najpierw zatrzymując go):

docker stop CONTAINER_ID

docker rm CONTAINER_ID

lub jednym poleceniem:

docker rm -f CONTAINER_ID


4. Uruchomienie nowego kontenera z tym samym wolumenem

Teraz uruchommy aplikację ponownie – z tym samym wolumenem:

docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started

Wejdź do aplikacji w przeglądarce. Zauważysz, że Twoje zadania nadal tam są 🎉 – mimo że stary kontener został usunięty.

Docker – Wprowadzenie do konteneryzacji (cz. 3)

Publikacja obrazu w Docker Hub i uruchamianie na zewnętrznej instancji

W poprzednich częściach serii nauczyliśmy się:

  • jak stworzyć aplikację i uruchomić ją w kontenerze Dockera,
  • jak aktualizować kod i ponownie budować obraz.

Teraz pójdziemy krok dalej – opublikujemy nasz obraz w Docker Hub, aby można go było uruchomić na dowolnym komputerze lub w chmurze.

1. Tworzenie repozytorium w Docker Hub

  1. Zaloguj się na Docker Hub.
  2. Kliknij Create Repository.
  3. Podaj nazwę (np. getting-started) i ustaw repozytorium jako Public, aby inni mogli je pobrać.

2. Pushowanie obrazu do Docker Hub

a) Próba pushowania

Jeśli spróbujesz:

docker push docker/getting-started

prawdopodobnie zobaczysz błąd:

denied: requested access to the resource is denied

Dlaczego? Bo taki obraz lokalnie nie istnieje pod tą nazwą. Musimy go przygotować.


b) Logowanie do Docker Hub

Najpierw zaloguj się w terminalu:

docker login -u YOUR-USER-NAME

Podaj swoje hasło do Docker Hub.


c) Tagowanie obrazu

Musimy nadać obrazowi właściwą nazwę w formacie:

YOUR-USER-NAME/repository-name

W naszym przypadku:

docker tag getting-started YOUR-USER-NAME/getting-started

Teraz mamy lokalny obraz o nowej nazwie, zgodnej z repozytorium w Docker Hub.


d) Pushowanie obrazu

Wystarczy teraz wykonać:

docker push YOUR-USER-NAME/getting-started

Jeśli wszystko pójdzie dobrze, obraz zostanie przesłany i będzie dostępny w Twoim repozytorium.

3. Uruchamianie obrazu na nowej instancji

Spróbujmy teraz uruchomić naszą aplikację w zewnętrznym środowisku – np. w Play with Docker.

a) Otwórz Play with Docker

Zaloguj się przy użyciu swojego konta Docker Hub.

b) Utwórz instancję

Kliknij +ADD NEW INSTANCE, aby uruchomić nową maszynę z Dockerem.

c) Uruchom aplikację

W terminalu instancji wpisz:

docker run -dp 3000:3000 YOUR-USER-NAME/getting-started

Docker pobierze Twój obraz z Docker Hub i uruchomi aplikację.

d) Otwórz port

Kliknij przycisk Open Port, wpisz 3000 i potwierdź.
Twoja aplikacja powinna się otworzyć w nowej zakładce 🎉

Docker – Wprowadzenie do konteneryzacji (cz. 2)

Aktualizacja aplikacji i ponowne uruchomienie kontenera

W poprzednim wpisie pokazaliśmy, jak z prostą aplikacją, zbudować jej obraz przy użyciu Dockera i uruchomić ją w kontenerze. Teraz zajmiemy się tym, co zrobić, gdy zmienimy coś w kodzie źródłowym aplikacji – czyli typowym scenariuszem pracy deweloperskiej.

1. Aktualizacja kodu źródłowego

Załóżmy, że edytujesz plik src/index.js.
Chcesz, żeby po tej zmianie aplikacja zastosowała zmiany – ale aktualnie działający kontener nadal pokazuje starą aplikację.

2. Budowanie nowego obrazu

Po dokonaniu zmian w kodzie musisz zbudować nowy obraz, który zawiera zaktualizowaną wersję aplikacji:

docker build -t getting-started .

To nadpisze poprzedni obraz o nazwie getting-started.

3. Uruchomienie nowej wersji kontenera

Spróbuj uruchomić nowy kontener:

docker run -dp 3000:3000 getting-started

⚠️ Błąd! Prawdopodobnie otrzymasz komunikat, że port 3000 jest już zajęty. To dlatego, że stary kontener nadal działa.

4. Sprawdzenie i usunięcie starego kontenera

a) Sprawdź, które kontenery są uruchomione:

docker ps

Zobaczysz coś takiego:

CONTAINER ID IMAGE ... PORTS NAMES
abc123456789 getting-started ... 0.0.0.0:3000->3000/tcp inspiring_shaw

b) Zatrzymaj i usuń kontener:

Możesz to zrobić na dwa sposoby:

Metoda 1: Ręczne zatrzymanie i usunięcie

docker stop abc123456789

docker rm abc123456789

Metoda 2: Jednym poleceniem

docker rm -f abc123456789

Metoda 3: GUI – Docker Desktop

Jeśli korzystasz z Docker Desktop, możesz wejść do interfejsu graficznego, znaleźć uruchomiony kontener, kliknąć „Stop” i potem „Remove”.

5. Uruchomienie nowej wersji aplikacji

Po usunięciu starego kontenera uruchom aplikację ponownie:

docker run -dp 3000:3000 getting-started

Wejdź na http://localhost:3000 i zobacz nowy komunikat 🎉

Docker – Wprowadzenie do konteneryzacji

W dzisiejszym świecie tworzenia aplikacji coraz częściej spotykamy się z pojęciem konteneryzacji. Dzięki niej możemy łatwo pakować aplikacje wraz z całym środowiskiem uruchomieniowym, co upraszcza wdrażanie i eliminuje problem „u mnie działa”. Najpopularniejszym narzędziem do konteneryzacji jest Docker.

W tym wpisie pokażę Ci, jak można  w obrazie kontenerowym zbudować aplikację i uruchomić ją lokalnie.

I. Tworzenie aplikacji w kontenerze

1. Stworzenie aplikacji

Na początek potrzebujemy aplikacji. Będzie to aplikacja napisana w Node.js. i jest to przykładowa lista rzeczy do zrobienia:

Przykład zaciągnięty z: https://www.docker.com/101-tutorial/

2. Budowanie obrazu aplikacji

Aby uruchomić aplikację w kontenerze, musimy stworzyć Dockerfile – plik, który opisuje, jak zbudować obraz naszej aplikacji.

a) Tworzenie pliku Dockerfile

Utwórz plik Dockerfile w katalogu głównym projektu i dodaj do niego następującą treść:

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]

Opis poszczególnych poleceń:

  • FROM node:18-alpine – bazujemy na oficjalnym obrazie Node.js.

  • WORKDIR /app – ustawiamy katalog roboczy.

  • COPY . . – kopiujemy wszystkie pliki do kontenera.

  • RUN yarn install --production – instalujemy zależności.

  • CMD ["node", "src/index.js"] – komenda uruchamiająca aplikację.

b) Budowanie obrazu

Otwórz terminal w katalogu z projektem i uruchom:

docker build -t getting-started .

 

To polecenie zbuduje obraz kontenerowy i nazwie go getting-started.

3. Start kontenera aplikacji

Gdy obraz jest już gotowy, możemy uruchomić kontener:

docker run -dp 3000:3000 getting-started

 

  • -d – uruchamia kontener w tle (detached mode),

  • -p 3000:3000 – mapuje port lokalny 3000 na port 3000 w kontenerze.

Teraz wystarczy wejść w przeglądarkę i otworzyć:

http://localhost:3000

Powinieneś zobaczyć pustą listę rzeczy do zrobienia.  🎉

 

Szara Piechota – Odtwarzacz z Synchronizowanym Tekstem

Szara Piechota – Interaktywny Odtwarzacz Patriotycznej Pieśni

Cześć wszystkim! Chciałbym podzielić się z Wami moim najnowszym projektem – interaktywnym odtwarzaczem pieśni „Szara Piechota” z synchronizowanym tekstem.

🎵 O czym to jest?

Stworzyłem aplikację webową, która odtwarza patriotyczną pieśń „Szara Piechota” z tekstem pojawiającym się dokładnie w momencie, gdy jest śpiewany. To połączenie tradycji z nowoczesną technologią webową.

�� Live Demo

Możecie przetestować aplikację tutaj: https://szara-piechota-song.web.app/

✨ Funkcje

  • Synchronizacja tekstu – każda linia pojawia się w odpowiednim momencie muzyki
  • Patriotyczny design – kolory narodowe Polski (czerwony i biały)
  • Responsywność – działa na wszystkich urządzeniach
  • Intuicyjne sterowanie – play/pause, regulacja głośności, przewijanie

🛠️ Technologie

Projekt został zbudowany przy użyciu:

  • HTML5 – struktura strony
  • CSS3 – patriotyczny design z gradientami
  • JavaScript (ES6+) – synchronizacja tekstu z muzyką
  • Web Audio API – odtwarzanie audio
  • Firebase Hosting – wdrożenie aplikacji

🎯 Cel projektu

Chciałem stworzyć coś, co łączy polską tradycję wojskową z nowoczesną technologią. „Szara Piechota” to jedna z najważniejszych pieśni patriotycznych, a dzięki synchronizacji tekstu można lepiej zrozumieć i przeżyć jej treść.

📱 Jak używać

  1. Wejdź na stronę
  2. Kliknij przycisk „ODTWÓRZ”
  3. Ciesz się synchronizowanym tekstem!

🔗 Kod źródłowy

Projekt jest open-source i dostępny na GitHub. Możecie go znaleźć w moim repozytorium:

https://github.com/traininguniverse/szara-piechota

Kici World: Interaktywna strona dla miłośników kotów

Kici World – Magiczny świat kotów Rudego i Szarka.

Jesteś miłośnikiem kotów? Szukasz miejsca, gdzie znajdziesz nie tylko piękne zdjęcia, ale także inspirujące historie, praktyczne porady i ciekawostki ze świata kotów? Poznaj Kici World – wyjątkową, interaktywną stronę internetową poświęconą dwóm niezwykłym kotom: Rudemu i Szarkowi!

Na stronie Kici World czeka na Ciebie:

  • Galeria zdjęć – zobacz najpiękniejsze ujęcia Rudego i Szarka w różnych sytuacjach. Przeglądaj zdjęcia w wygodnym sliderze i poznaj ich codzienne życie.
  • Historie kotów – zanurz się w bajkowych opowieściach o przygodach naszych bohaterów. Każda historia to dawka uśmiechu i kociej magii!
  • Porady opieki – dowiedz się, jak najlepiej dbać o małe kocięta. Praktyczne wskazówki pomogą zarówno początkującym, jak i doświadczonym opiekunom.
  • Kocie ciekawostki – poznaj interesujące fakty i zabawne anegdoty ze świata kotów. Czy wiesz, że kocie mruczenie może działać leczniczo?

Strona została stworzona z myślą o wszystkich, którzy kochają koty i chcą dzielić się tą pasją z innymi. Przyjazny interfejs, kolorowa szata graficzna i mnóstwo kociej energii sprawiają, że Kici World to miejsce, do którego chce się wracać!

Zajrzyj na stronę: https://kici-world.web.app/

Kod do repozytorium strony/apki: https://github.com/traininguniverse/cat-world

Teraz też możesz zagrać w interaktywną grę z Kiciusiem – baw się, oglądaj animacje i przeżyj kocie przygody online!