From e2daab03e94f646ca0a0e1948dfe6e52125320d8 Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 18 Oct 2026 22:45:59 +0200 Subject: [PATCH 1/4] Add self-hosted services as project cards. The homelab apps that stand on their own now show up on the Projects page, with a Services filter. Co-authored-by: Cursor --- llms.txt | 1 + projects.html | 285 ++++++++++++++++++++++++++++++++++++++++++++++++-- style.css | 274 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 550 insertions(+), 10 deletions(-) diff --git a/llms.txt b/llms.txt index 5015f65..3a9894a 100644 --- a/llms.txt +++ b/llms.txt @@ -30,6 +30,7 @@ Local 10.0.0.x addresses work only on the home network or with Josef’s Tailsca - BatchGym - Graduately — https://github.com/pepaskopec-blip/Graduately - Terra +- Jellyfin, Navidrome, Ollama, SearXNG, ConvertX, Pi-hole, Joplin, Immich — services from the homelab, also listed on the Projects page ## Friends - Martin Dvořák — https://dvorakmortonxd.dev diff --git a/projects.html b/projects.html index 8de43cf..4c5fa04 100644 --- a/projects.html +++ b/projects.html @@ -3,11 +3,11 @@ - - Projects — websites & games · Josef + + Projects — websites, services & games · Josef - + @@ -15,7 +15,7 @@ - + @@ -34,7 +34,7 @@ } })(); - + - + + diff --git a/script.js b/script.js index baa064c..a8e0ba6 100644 --- a/script.js +++ b/script.js @@ -218,11 +218,29 @@ } }; + const snakePalette = () => { + const light = document.documentElement.dataset.theme === "light"; + return light + ? { + grid: "rgba(17, 21, 28, 0.08)", + food: "#dc3c11", + head: "#11151c", + body: "179, 47, 8", + } + : { + grid: "rgba(238, 242, 246, 0.04)", + food: "#ff4a1c", + head: "#eef2f6", + body: "255, 122, 82", + }; + }; + const drawSnake = () => { if (!ctx) return; + const palette = snakePalette(); ctx.clearRect(0, 0, snakeCanvas.width, snakeCanvas.height); - ctx.strokeStyle = "rgba(238, 242, 246, 0.04)"; + ctx.strokeStyle = palette.grid; ctx.lineWidth = 1; for (let x = 0; x <= cols; x++) { ctx.beginPath(); @@ -237,14 +255,14 @@ ctx.stroke(); } - ctx.fillStyle = "#ff4a1c"; + ctx.fillStyle = palette.food; ctx.beginPath(); ctx.arc(food.x * cell + cell / 2, food.y * cell + cell / 2, 4.5, 0, Math.PI * 2); ctx.fill(); snake.forEach((seg, idx) => { const alpha = Math.max(0.28, 1 - idx * 0.09); - ctx.fillStyle = idx === 0 ? "#eef2f6" : `rgba(255, 122, 82, ${alpha})`; + ctx.fillStyle = idx === 0 ? palette.head : `rgba(${palette.body}, ${alpha})`; ctx.fillRect(seg.x * cell + 2, seg.y * cell + 2, cell - 4, cell - 4); }); }; @@ -296,6 +314,10 @@ if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) { setInterval(stepSnake, 180); } + new MutationObserver(drawSnake).observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"], + }); } const revealItems = document.querySelectorAll(".reveal"); diff --git a/style.css b/style.css index 6f1a4dc..a9d0802 100644 --- a/style.css +++ b/style.css @@ -1590,6 +1590,17 @@ a:hover { color: var(--accent-ink); } +:root[data-theme="light"] .mock-arcade { + background: #f4f6f8; + box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08); +} + +:root[data-theme="light"] .mock-arcade-hud { + color: var(--ink-soft); + background: rgba(17, 21, 28, 0.04); + border-bottom-color: var(--line); +} + .snake-canvas { display: block; width: 100%; @@ -1658,6 +1669,30 @@ a:hover { background: var(--accent); } +:root[data-theme="light"] .mock-terminal { + background: #f7f8fa; + box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08); +} + +:root[data-theme="light"] .mock-terminal-bar { + color: var(--muted); + background: #eef1f4; + border-bottom-color: var(--line); +} + +:root[data-theme="light"] .mock-terminal-body { + color: var(--ink-soft); +} + +:root[data-theme="light"] .term-stats, +:root[data-theme="light"] .term-log { + color: var(--muted); +} + +:root[data-theme="light"] .term-prompt { + color: var(--ink); +} + /* Terra challenge preview */ .mock-terra { width: 100%; From 61a644174f50fe45233ef6f085ca626da5c5647a Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 18 Oct 2026 23:03:41 +0200 Subject: [PATCH 3/4] Add all services to projects.html and some info about it. --- hardware.html | 4 ++-- projects.html | 4 ++-- sitemap.xml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hardware.html b/hardware.html index af98765..8510336 100644 --- a/hardware.html +++ b/hardware.html @@ -136,7 +136,7 @@ Artix Linux · SwayFX

Custom build · 2022

-

Main PC running Artix Linux with SwayFX wm — assembled in 2022.

+

Main PC running Artix Linux with SwayFX wm/(wayland) compositor, also I use Waybar as my top bar (you can customize it with css), Wofi for application opener — assembled in 2022.

  • Ryzen 5 5600
  • RX 6700 10 GB
  • @@ -216,7 +216,7 @@ macOS · Daily driver

    MacBook Air 13″ · M5 · base

    -

    Portable machine for school and on-the-go work.

    +

    Portable machine for school and on-the-go work. Now I use it more than my "main" PC, because I bought usb hub with HDMI port, so I keep it plugged in to my monitor.

    • Apple M5 (10C)
    • 16 GB Unified
    • diff --git a/projects.html b/projects.html index 6bcb144..a43bb52 100644 --- a/projects.html +++ b/projects.html @@ -439,7 +439,7 @@

      Ollama

      -

      Local AI on the Proxmox box. Models run on my hardware, so a prompt stays on the network instead of going out to a hosted API.

      +

      Local AI on the Proxmox box. Models run on my hardware, so a prompt stays on the network instead of going out to a hosted API. I do not have dedicated GPU, so all of the llms run on CPU, which limits me for only some weaker models under 15B parameters.

      • Self-hosted
      • Local LLM
      • @@ -542,7 +542,7 @@

        Pi-hole

        -

        DNS for the network, with ad blocking in front of it. Devices that use this resolver drop a lot of ads and trackers before the page loads.

        +

        DNS for the network, with ad blocking in front of it. Probably the most useful service on my homelab, it is able to block ads even in apps on mobile, also it blocks malware and dangerous content. Not a long time ago, I have set up DNS over HTTPS for maximum privacy.

        • DNS
        • Ad blocking
        • diff --git a/sitemap.xml b/sitemap.xml index d2fa02f..3cac36b 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,19 +2,19 @@ https://skopec.dev/ - 2026-10-12 + 2026-10-18 monthly 1.0 https://skopec.dev/projects.html - 2026-10-12 + 2026-10-18 monthly 0.8 https://skopec.dev/hardware.html - 2026-10-12 + 2026-10-18 monthly 0.8 From a1db8ae3e4c5286fb44c2510b60e353ecc152933 Mon Sep 17 00:00:00 2001 From: pepaskopec-blip Date: Sun, 18 Oct 2026 23:16:54 +0200 Subject: [PATCH 4/4] Fix copy, thread count, and the light-theme hardware card. FAQ data now matches the page, filters announce the active chip, and the mobile menu closes when you click outside it. Co-authored-by: Cursor --- 404.html | 4 ++-- hardware.html | 6 +++--- index.html | 22 +++++++++++----------- privacy.html | 4 ++-- projects.html | 8 ++++---- script.js | 20 ++++++++++++++++++-- style.css | 27 ++++++++++++++++++++++++++- 7 files changed, 66 insertions(+), 25 deletions(-) diff --git a/404.html b/404.html index 26b5e37..6072f53 100644 --- a/404.html +++ b/404.html @@ -28,7 +28,7 @@ } })(); - + @@ -82,6 +82,6 @@ - + diff --git a/hardware.html b/hardware.html index 8510336..9ae26f8 100644 --- a/hardware.html +++ b/hardware.html @@ -34,7 +34,7 @@ } })(); - + + diff --git a/index.html b/index.html index 7bc27aa..e552f0b 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -34,7 +34,7 @@ } })(); - + + diff --git a/privacy.html b/privacy.html index 72ab9b6..6735614 100644 --- a/privacy.html +++ b/privacy.html @@ -33,7 +33,7 @@ } })(); - + + diff --git a/projects.html b/projects.html index a43bb52..a448ebd 100644 --- a/projects.html +++ b/projects.html @@ -34,7 +34,7 @@ } })(); - + + diff --git a/script.js b/script.js index a8e0ba6..e36dedb 100644 --- a/script.js +++ b/script.js @@ -57,6 +57,12 @@ document.addEventListener("keydown", (event) => { if (event.key === "Escape") setOpen(false); }); + + document.addEventListener("click", (event) => { + if (!nav.classList.contains("is-open")) return; + if (nav.contains(event.target) || toggle.contains(event.target)) return; + setOpen(false); + }); } const radar = document.querySelector("[data-radar]"); @@ -157,9 +163,14 @@ const projectCards = document.querySelectorAll(".project-card[data-category]"); if (filterChips.length && projectCards.length) { filterChips.forEach((chip) => { + chip.setAttribute("aria-pressed", chip.classList.contains("is-active") ? "true" : "false"); chip.addEventListener("click", () => { const filter = chip.dataset.filter; - filterChips.forEach((c) => c.classList.toggle("is-active", c === chip)); + filterChips.forEach((c) => { + const on = c === chip; + c.classList.toggle("is-active", on); + c.setAttribute("aria-pressed", on ? "true" : "false"); + }); projectCards.forEach((card) => { const match = filter === "all" || card.dataset.category === filter; card.hidden = !match; @@ -172,9 +183,14 @@ const hwCards = document.querySelectorAll(".hw-card[data-hw-category]"); if (hwChips.length && hwCards.length) { hwChips.forEach((chip) => { + chip.setAttribute("aria-pressed", chip.classList.contains("is-active") ? "true" : "false"); chip.addEventListener("click", () => { const filter = chip.dataset.hwFilter; - hwChips.forEach((c) => c.classList.toggle("is-active", c === chip)); + hwChips.forEach((c) => { + const on = c === chip; + c.classList.toggle("is-active", on); + c.setAttribute("aria-pressed", on ? "true" : "false"); + }); hwCards.forEach((card) => { const match = filter === "all" || card.dataset.hwCategory === filter; card.hidden = !match; diff --git a/style.css b/style.css index a9d0802..43c6ce7 100644 --- a/style.css +++ b/style.css @@ -2306,6 +2306,25 @@ a:hover { .fetch-palette i:nth-child(5) { background: #818cf8; } .fetch-palette i:nth-child(6) { background: #e2e8f0; } +:root[data-theme="light"] .mock-fetch { + background: #f7f8fa; + box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08); +} + +:root[data-theme="light"] .mock-fetch-bar { + color: var(--muted); + background: #eef1f4; + border-bottom-color: var(--line); +} + +:root[data-theme="light"] .mock-fetch-body { + color: var(--ink-soft); +} + +:root[data-theme="light"] .fetch-palette i:nth-child(6) { + box-shadow: inset 0 0 0 1px rgba(17, 21, 28, 0.28); +} + /* Laptop Apple M5 SoC widget */ .mock-soc { width: 100%; @@ -2432,11 +2451,17 @@ a:hover { .server-bays { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); + grid-template-columns: 1fr; gap: 0.45rem; margin-bottom: 0.6rem; } +@media (min-width: 640px) { + .server-bays { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + .server-bay { display: flex; flex-direction: column;