ТРИ НАВЧАЛЬНІ ПРОЄКТИ · ПОРТФОЛІО ВИПУСКНИКА

Роботи учнів: живі демонстрації, вихідний код і критерії перевірки

Планова ціль пілотної групи з 10 ветеранів — 10 особистих сайтів, 10 вебзастосунків «Мій день» і 10 пакетів контенту. Кожен учень демонструє власні роботи та самостійно вносить контрольну правку під час фінального захисту (Урок 33).

Examples_UA.zip Examples_EN.zip
РОБОТА 01 · ЧАСТИНА 05 (УРОКИ 11–14) Відкрити живий сайт у новій вкладці ↗

Особистий сайт («Олена: мої маленькі проєкти»)

Сторінка складається з двох файлів у папці personal-site: розмітки index.html та таблиці стилів styles.css. Вона коректно перебудовується між широким екраном комп’ютера (960 px) та екраном смартфона (360 px) і має чітке виділення елементів під час навігації клавішею Tab.

Одна сторінка у двох вікнах
960 px · Широкий екран комп’ютера
960px preview
360 px · Екран телефона
360px preview
Справжні знімки готового прикладу в Microsoft Edge, 30.10.2026. Ширина вікон — 960 і 360 px; показано фрагменти сторінки.
personal-site/index.html
<!doctype html>
<html lang="uk">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Олена: мої маленькі проєкти">
  <title>Олена: мої маленькі проєкти</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="page">
  <header><nav aria-label="Розділи сторінки">
    <a href="#about">Про мене</a>
    <a href="#projects">Проєкти</a>
    <a href="#contact">Контакт</a>
  </nav></header>
  <main>
    <h1>Олена</h1>
    <p class="intro">Люблю книги, прогулянки й порядок удома. Вчуся перетворювати невеликі задуми на готові речі.</p>
    <section id="about">
      <h2>Про мене</h2>
      <p>На цій сторінці збираю свої перші цифрові роботи. Починаю з простого: зрозуміла сторінка, три щоденні справи й коротке відео.</p>
    </section>
    <section id="projects">
      <h2>Проєкти</h2>
      <ul class="projects">
      <li><h3>Особистий сайт</h3><p>Розповідаю про свої інтереси й навчальні роботи.</p></li>
      <li><h3>Мій день</h3><p>Створюю список із трьох справ і лічильником виконаного.</p></li>
      <li><h3>Порядок на столі</h3><p>Планую відео на 30–45 секунд про підготовку робочого місця.</p></li>
      </ul>
    </section>
    <section id="contact" class="contact">
      <h2>Контакт</h2>
      <p><a href="mailto:[email protected]">Написати Олені</a></p>
      <p>Навчальна адреса: [email protected]. Перед публікацією замініть її власною або приберіть посилання.</p>
    </section>
  </main>
  <footer>Вигаданий навчальний приклад. Ви можете замінити текст своїм.</footer>
</div>
</body>
</html>
personal-site/styles.css
* { box-sizing: border-box; }
:root { color-scheme: light; }
body { margin: 0; color: #20362f; background: #fff;
  font: 18px/1.65 "Segoe UI", sans-serif; }
.page { width: min(100% - 40px, 860px); margin: auto; }
header { padding: 24px 0; border-bottom: 1px solid #ccd8d1; }
nav { display: flex; flex-wrap: wrap; gap: 12px 28px; }
a { color: #174f87; text-underline-offset: 4px; }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 3px solid #174f87; outline-offset: 5px;
}
main { padding: 36px 0 20px; }
h1 { font: 700 clamp(36px, 6vw, 56px)/1.1 Georgia, serif;
  margin: 0 0 20px; letter-spacing: -1px; }
h2 { font-size: 26px; margin: 0 0 12px; }
h3 { font-size: 21px; margin: 0 0 4px; }
p { max-width: 65ch; margin: 0 0 16px; }
.intro { max-width: 620px; font-size: 21px; }
section { padding: 26px 0; scroll-margin-top: 16px; }
.projects { padding: 0; list-style: none; }
.projects li { padding: 18px 0; border-top: 1px solid #ccd8d1; }
.projects p { margin-bottom: 0; }
.contact { background: #edf5ef; padding: 22px; }
footer { color: #52645d; font-size: 15px; padding: 24px 0; }
@media (max-width: 480px) {
  .page { width: calc(100% - 32px); }
  main { padding-top: 28px; }
}
РОБОТА 02 · ЧАСТИНА 06 (УРОКИ 15–18) Запустити PWA «Мій день» окремо ↗

Вебзастосунок «Мій день» (PWA з підтримкою офлайн)

Застосунок навчає розділяти розмітку (index.html), оформлення (styles.css), логіку збереження стану (app.js), паспорт встановлення на телефон (manifest.webmanifest) і фоновий кеш для роботи без інтернету (sw.js).

Мій день

Три невеликі справи на сьогодні.

Виконано 0 з 3

my-day/app.js
"use strict";
const TEXT = {
  "progress": "Виконано {n} з 3",
  "save": "Позначки збережено в цьому браузері.",
  "blocked": "Позначки працюють, але не збереглися. Дозвольте сайту зберігати дані й спробуйте ще раз.",
  "invalid": "Збережені дані пошкоджені. Починаємо з порожнього списку.",
  "preview": "Попередній перегляд файлу. Збереження й офлайн перевіряйте за вебадресою.",
  "preparing": "Готуємо офлайн-копію…",
  "ready": "Готово до роботи офлайн.",
  "off": "Ви офлайн. Справи доступні.",
  "unavailable": "Офлайн-копія не готова. Підключіться до інтернету й відкрийте сторінку знову."
};
const boxes = [...document.querySelectorAll("input[type=checkbox]")];
const counter = document.querySelector("#counter");
const saved = document.querySelector("#saved");
const connection = document.querySelector("#connection");
// Різні папки на одному сайті мають окремі позначки.
const folder = new URL("./", location.href).pathname;
const key = "my-day:v1:" + folder;
const webAddress = ["http:", "https:"].includes(location.protocol);
let offlineReady = false;

function showCount() {
  const count = boxes.filter(box => box.checked).length;
  counter.textContent = TEXT.progress.replace("{n}", count);
}

function saveChoices() {
  if (!webAddress) { saved.textContent = TEXT.preview; return; }
  try {
    const done = boxes.map(box => box.checked);
    localStorage.setItem(key, JSON.stringify(done));
    saved.textContent = TEXT.save;
    saved.classList.remove("warning");
  } catch {
    saved.textContent = TEXT.blocked;
    saved.classList.add("warning");
  }
}

function loadChoices() {
  if (!webAddress) { saved.textContent = TEXT.preview; return; }
  try {
    const raw = localStorage.getItem(key);
    if (raw !== null) {
      const done = JSON.parse(raw);
      if (!Array.isArray(done) || done.length !== 3 ||
          !done.every(value => typeof value === "boolean")) {
        throw new Error("Invalid saved choices");
      }
      boxes.forEach((box, index) => { box.checked = done[index]; });
    }
    saved.textContent = TEXT.save;
  } catch (error) {
    saved.textContent = error.name === "SecurityError"
      ? TEXT.blocked : TEXT.invalid;
    saved.classList.add("warning");
  }
}

boxes.forEach(box => box.addEventListener("change", () => {
  showCount();
  saveChoices();
}));
document.querySelector("#reset").addEventListener("click", () => {
  boxes.forEach(box => { box.checked = false; });
  showCount();
  saveChoices();
});

function showConnection() {
  connection.textContent = offlineReady
    ? (navigator.onLine ? TEXT.ready : TEXT.off)
    : (webAddress ? TEXT.preparing : TEXT.preview);
}

async function prepareOffline() {
  if (!webAddress || !("serviceWorker" in navigator)) {
    connection.textContent = webAddress ? TEXT.unavailable : TEXT.preview;
    return;
  }
  try {
    // Наявний обробник може віддавати файли без інтернету.
    const previous = await navigator.serviceWorker.getRegistration();
    if (!previous) await navigator.serviceWorker.register("./sw.js");
    else navigator.serviceWorker.register("./sw.js").catch(() => {});
    await navigator.serviceWorker.ready;
    offlineReady = true;
    showConnection();
  } catch {
    connection.textContent = TEXT.unavailable;
  }
}

loadChoices();
showCount();
showConnection();
window.addEventListener("online", showConnection);
window.addEventListener("offline", showConnection);
prepareOffline();
my-day/sw.js (Service Worker)
"use strict";
// Після зміни файлів перед публікацією змініть v1 на v2.
const prefix = "my-day:" + self.registration.scope + ":";
const cacheName = prefix + "v1";
const files = ["./", "./index.html", "./styles.css", "./app.js",
  "./manifest.webmanifest", "./icon-192.png", "./icon-512.png"];

self.addEventListener("install", event => {
  event.waitUntil(caches.open(cacheName).then(cache => cache.addAll(files)));
});

self.addEventListener("activate", event => {
  event.waitUntil((async () => {
    const names = await caches.keys();
    await Promise.all(names.filter(name => name.startsWith(prefix) &&
      name !== cacheName).map(name => caches.delete(name)));
    await self.clients.claim();
  })());
});

self.addEventListener("fetch", event => {
  const request = event.request;
  if (request.method !== "GET" ||
      !request.url.startsWith(self.registration.scope)) return;
  event.respondWith((async () => {
    const cache = await caches.open(cacheName);
    const cached = await cache.match(request);
    if (cached) return cached;
    try { return await fetch(request); }
    catch (error) {
      if (request.mode === "navigate") return cache.match("./index.html");
      throw error;
    }
  })());
});
РОБОТА 03 · ЧАСТИНИ 07–09 (УРОКИ 19–28) Відкрити конспект Уроку 24

Пакет медіаконтенту: YouTube, карусель Instagram та TikTok

Приклад першого слайда
Навчальний макет, не знімок Canva. У папці content/carousel архіву прикладів є всі п’ять PNG розміром 1080 × 1080 px.

КОД ОПОРУ (Shionika Academy) — Відповіді на ключові запитання

Для кого створена гібридна IT/AI-школа «КОД ОПОРУ»?

Програма та книга «ШІ на практиці: Перший сайт, застосунок і контент» розроблені для ветеранів України та дорослих початківців без попереднього досвіду програмування. Навчання поєднує практику в обладнаному класі на 10 місць із покроковим друкованим та онлайн-конспектом.

Які три роботи входять у випускне портфоліо кожного учасника?

Кожен із 10 учасників пілота створює й захищає: 1) власний адаптивний сайт-візитку на GitHub Pages (уроки 11–14), 2) мобільний вебзастосунок «Мій день» (PWA з підтримкою офлайн-роботи, уроки 15–18), 3) пакет контенту з субтитрами для YouTube, Instagram і TikTok (уроки 19–28).

З чого складається кошторис пілота $37,377.24 на 4 місяці?

Кошторис (перевірено 01.11.2026) охоплює: обладнання 10 учнівських і 2 службових місць та резервне живлення 5.12 кВт·год ($14,280), команду з менеджера й викладача та оренду класу на 4 місяці ($12,960), іменні доступи ChatGPT Plus/Codex, Claude Pro, Google AI Pro/Antigravity та хостинг ($3,258.76), ліцензії Windows 11 FPP і практикум публікації ($2,780), податковий запас і резерв 10% ($4,098.48).