From a584a5252f1d29be1f7c0a938ab36f2d68652508 Mon Sep 17 00:00:00 2001 From: April John Date: Fri, 28 Aug 2026 15:39:56 +0200 Subject: [PATCH 1/2] adjust styling and bump dates etc --- README.md | 17 +- archetypes/datengarten.md | 6 +- assets/css/calendar.css | 7 +- assets/css/custom.css | 411 ++++++++++++++++++ assets/css/schemes/fira.css | 37 ++ assets/js/calendar.js | 34 +- assets/js/clubstatus.js | 17 +- assets/js/fairydust.js | 62 +++ build.sh | 2 - config/_default/hugo.toml | 1 - config/_default/params.toml | 10 +- content/_index.md | 2 +- content/post/Neujahresempfang-2026/index.md | 2 +- .../oma_paulsen_erzählt_von_früher.md | 130 ------ content/veranstaltungen/bastelabend.md | 32 -- content/veranstaltungen/chaosradio.md | 2 +- content/veranstaltungen/cms.md | 4 +- content/veranstaltungen/neujahresempfang.md | 2 +- content/veranstaltungen/plenum.md | 10 +- content/veranstaltungen/spieleabend.md | 4 +- content/veranstaltungen/subbotnik.md | 1 - dev.sh | 13 + flake.nix | 2 - layouts/_default/{list.xml.html => list.xml} | 4 +- layouts/_default/single.ics | 21 + layouts/datengarten/section.ics | 2 + layouts/datengarten/section.xml | 12 +- layouts/page/single.ics | 2 +- layouts/partials/extend-footer.html | 2 + layouts/partials/footer.html | 2 +- layouts/partials/talk-infobox.html | 10 +- layouts/shortcodes/foldergallery.html | 4 +- layouts/shortcodes/series.html | 2 +- layouts/veranstaltungen/list.ics | 23 +- requirements.txt | 4 +- static/fonts/PressStart2P.woff2 | Bin 0 -> 12512 bytes .../__pycache__/gen_upcoming.cpython-312.pyc | Bin 0 -> 5745 bytes tools/convert_page.sh | 10 +- tools/gen_upcoming.py | 55 ++- tools/merge_cals.py | 11 +- 40 files changed, 678 insertions(+), 294 deletions(-) create mode 100644 assets/css/custom.css create mode 100644 assets/css/schemes/fira.css create mode 100644 assets/js/fairydust.js delete mode 100644 content/sonstiges/oma_paulsen_erzählt_von_früher.md delete mode 100644 content/veranstaltungen/bastelabend.md create mode 100755 dev.sh rename layouts/_default/{list.xml.html => list.xml} (72%) create mode 100644 layouts/_default/single.ics create mode 100644 layouts/partials/extend-footer.html create mode 100644 static/fonts/PressStart2P.woff2 create mode 100644 tools/__pycache__/gen_upcoming.cpython-312.pyc diff --git a/README.md b/README.md index 3b0af0f..196fa4c 100644 --- a/README.md +++ b/README.md @@ -30,21 +30,20 @@ This is the website of the CCCB. Every change you make on the project will be reflected in your browser as long as `hugo serve` is running. -The *"Nächste Veranstaltungen"* table on the home page is generated by -post-processing in `./build.sh`, not by Hugo, so it is **not** visible under -`hugo serve`. To preview the fully-built site (including the home-page -calendar), or to ready the site for upload, run: +The *"Nächste Veranstaltungen"* calendar on the home page is rendered by +Hugo via the `calendar` shortcode, which fetches `all.ics` client-side. +`all.ics` is generated from the individual event pages by `./tools/merge_cals.py`. +To preview the fully-built site (including `all.ics`), or to ready the site for +upload, run: ```shell ./build.sh python3 -m http.server -d public 1313 ``` -`build.sh` generates `all.ics` and replaces the `CALENDAR` placeholder in -`index.html` with the upcoming-events table. It depends on Python with the -`icalendar`, `python-dateutil`, and `pytz` packages, plus a `de_DE.UTF-8` -locale (used to format weekday names). Inside `nix develop` these are -provided automatically. +`build.sh` runs `hugo`, generates `all.ics`, and copies it into `public/`. +It depends on Python with the `icalendar`, `python-dateutil`, and `pytz` +packages. Inside `nix develop` these are provided automatically. To build with *nix*: `nix build '.?submodules=1#production-content'` diff --git a/archetypes/datengarten.md b/archetypes/datengarten.md index 88fa12b..1c2e5ef 100644 --- a/archetypes/datengarten.md +++ b/archetypes/datengarten.md @@ -11,10 +11,10 @@ speaker: #speaker_url: date: {{ .Date }} event: - - start: {{ .Date }} - - end: {{ .Date }} + start: {{ .Date }} + end: {{ .Date }} location: CCCB -language: Englisch +language: de streaming: true #recording: https://media.ccc.de/v/dg-{{ .File.TranslationBaseName }} --- diff --git a/assets/css/calendar.css b/assets/css/calendar.css index 2806966..8cac5c1 100644 --- a/assets/css/calendar.css +++ b/assets/css/calendar.css @@ -36,15 +36,12 @@ border-collapse: collapse; } #calendar-table th, #calendar-table td { - border: 1px; + border: none; padding: 5px; text-align: center; vertical-align: top; height: 50px; width: 15%; - } - #calendar-table th - #calendar-table td { position: relative; cursor: pointer; } @@ -83,8 +80,6 @@ background-color: rgba(255, 255, 255, 0.1); box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); border-radius: 8px; - width: 20px; - height: 20px; border: 5px solid grey; font-weight: lighter; position: relative; diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..0355779 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,411 @@ +/* ── Self-hosted fonts ──────────────────────────────────────────────────── */ + +@font-face { + font-family: 'Press Start 2P'; + src: url('/fonts/PressStart2P.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +/* ── Modern micro-improvements ─────────────────────────────────────────── */ + +/* Smooth scrolling */ +html { + scroll-behavior: smooth; +} + +/* Thin, modern scrollbar on WebKit */ +::-webkit-scrollbar { width: 6px; height: 6px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { + background: rgba(var(--color-neutral-500), 0.5); + border-radius: 3px; +} +::-webkit-scrollbar-thumb:hover { + background: rgba(var(--color-primary-400), 0.7); +} + +/* ── Typography ─────────────────────────────────────────────────────────── */ + +/* Tighter, more confident headings */ +h1, h2, h3 { + letter-spacing: -0.02em; +} + +/* Slightly warmer body text contrast in dark mode */ +.dark .prose { + color: rgba(var(--color-neutral-200), 0.92); +} + +/* ── Cards ──────────────────────────────────────────────────────────────── */ + +/* Lift + glow on card hover */ +article.cursor-pointer, +.cursor-pointer[class*="backdrop"] { + transition: transform 0.2s ease, box-shadow 0.2s ease; +} +article.cursor-pointer:hover, +.cursor-pointer[class*="backdrop"]:hover { + transform: translateY(-3px); + box-shadow: 0 12px 32px rgba(var(--color-primary-400), 0.12); +} + +/* ── Links ──────────────────────────────────────────────────────────────── */ + +/* Animated underline: grows from left on hover */ +.prose a:not([class]) { + text-decoration: none; + background-image: linear-gradient( + rgba(var(--color-primary-400), 0.9), + rgba(var(--color-primary-400), 0.9) + ); + background-size: 0 1.5px; + background-position: 0 100%; + background-repeat: no-repeat; + transition: background-size 0.2s ease; +} +.prose a:not([class]):hover { + background-size: 100% 1.5px; +} + +/* ── Header ─────────────────────────────────────────────────────────────── */ + +/* Crisper blur on the fixed-fill-blur header */ +#menu-blur { + backdrop-filter: blur(20px) saturate(180%); + -webkit-backdrop-filter: blur(20px) saturate(180%); + background: rgba(var(--color-neutral-50), 0.85) !important; +} +.dark #menu-blur { + background: rgba(var(--color-neutral-900), 0.72) !important; +} + +/* ── Hero / Background ──────────────────────────────────────────────────── */ + +/* Subtle gradient vignette over hero images */ +.single_hero_background::after { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + transparent 40%, + rgba(var(--color-neutral-200), 0.5) 100% + ); + pointer-events: none; +} +.dark .single_hero_background::after { + background: linear-gradient( + to bottom, + transparent 40%, + rgba(var(--color-neutral-900), 0.6) 100% + ); +} + +/* ── Focus rings ────────────────────────────────────────────────────────── */ + +/* Modern, rounded focus ring */ +:focus-visible { + outline: 2px solid rgba(var(--color-primary-400), 0.9); + outline-offset: 3px; + border-radius: 4px; +} + +/* ── Buttons / Tags ─────────────────────────────────────────────────────── */ + +/* Pill-shaped tags with subtle glow on hover */ +a[href*="/tags/"], +a[href*="/categories/"] { + border-radius: 9999px; + transition: box-shadow 0.2s ease, background 0.2s ease; +} +a[href*="/tags/"]:hover, +a[href*="/categories/"]:hover { + box-shadow: 0 0 0 3px rgba(var(--color-primary-400), 0.2); +} + +/* ── Code blocks ────────────────────────────────────────────────────────── */ + +/* Rounded corners and a border on code blocks */ +.dark .highlight { + border: 1px solid rgba(var(--color-neutral-700), 1); + border-radius: 8px; + overflow: hidden; +} + +/* ── Retro-terminal layer ───────────────────────────────────────────────── */ + +/* Monospace headings + neon glow in dark mode */ +.dark .prose h1, +.dark .prose h2, +.dark .prose h3 { + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace; + text-shadow: + 0 0 8px rgba(var(--color-primary-400), 0.55), + 0 0 28px rgba(var(--color-primary-400), 0.2), + 0 0 2px rgba(192, 132, 252, 0.35); + letter-spacing: 0; +} + +/* Scanline texture (dark mode only) */ +.dark body::after { + content: ""; + position: fixed; + inset: 0; + z-index: 9998; + pointer-events: none; + background: repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.04) 0px, + rgba(0, 0, 0, 0.04) 1px, + transparent 1px, + transparent 4px + ); +} + +/* Terminal window chrome on code blocks (traffic-light dots via radial gradients) */ +.dark .highlight::before { + content: ""; + display: block; + height: 28px; + background-color: rgba(var(--color-neutral-800), 1); + background-image: + radial-gradient(circle 5px at 16px 14px, #ff5f56 0%, #ff5f56 100%), + radial-gradient(circle 5px at 32px 14px, #ffbd2e 0%, #ffbd2e 100%), + radial-gradient(circle 5px at 48px 14px, #27c93f 0%, #27c93f 100%); + background-repeat: no-repeat; + border-bottom: 1px solid rgba(var(--color-neutral-700), 0.8); +} + +/* Phosphor glow on hovered prose links in dark mode */ +.dark .prose a:not([class]):hover { + text-shadow: 0 0 8px rgba(var(--color-primary-400), 0.4); +} + +/* Cyan glow on card hover in dark mode */ +.dark article.cursor-pointer:hover, +.dark .cursor-pointer[class*="backdrop"]:hover { + box-shadow: 0 12px 40px rgba(var(--color-primary-400), 0.18), + 0 0 0 1px rgba(var(--color-primary-400), 0.1); +} + +/* Slide-in "> " prompt before nav items on hover (dark mode) */ +.dark .main-menu a p::before { + content: "> "; + display: inline-block; + max-width: 0; + overflow: hidden; + color: rgba(var(--color-primary-400), 0.65); + font-family: ui-monospace, monospace; + font-weight: normal; + transition: max-width 0.15s ease; + vertical-align: bottom; +} +.dark .main-menu a:hover p::before { + max-width: 1.5em; +} + +/* Blinking block cursor keyframe (reusable) */ +@keyframes cursor-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +/* ── Pixel font — site title ────────────────────────────────────────────── */ + +/* Navbar site title — only the first nav (space-x-3) holds the site title link; + the menu items live in the second nav (hidden md:flex) */ +.main-menu .flex-1 nav:first-of-type a { + font-family: 'Press Start 2P', ui-monospace, monospace !important; + font-size: 0.6rem !important; + transform: scaleY(1.4); + display: inline-block; + letter-spacing: 0 !important; + transition: color 0.2s ease, text-shadow 0.2s ease; +} + +/* Homepage hero h1 (font-extrabold is set only on the background layout h1) */ +h1.font-extrabold { + font-family: 'Press Start 2P', ui-monospace, monospace !important; + letter-spacing: 0 !important; + transition: color 0.2s ease, text-shadow 0.2s ease; +} + +/* Scale down the homepage h1 — pixel fonts read best smaller */ +h1.font-extrabold { + font-size: clamp(1rem, 2.5vw, 1.6rem) !important; + line-height: 1.8 !important; +} + +/* Purple glow on hover — navbar */ +.main-menu .flex-1 nav:first-of-type a:hover { + color: #c084fc !important; + text-shadow: + 0 0 6px rgba(192, 132, 252, 0.9), + 0 0 18px rgba(168, 85, 247, 0.6), + 0 0 40px rgba(139, 92, 246, 0.3); +} + +/* Purple glow on hover — homepage h1 */ +h1.font-extrabold:hover { + color: #c084fc !important; + text-shadow: + 0 0 8px rgba(192, 132, 252, 0.9), + 0 0 24px rgba(168, 85, 247, 0.6), + 0 0 50px rgba(139, 92, 246, 0.3); + cursor: default; +} + +/* ── Cyberpunk layer ─────────────────────────────────────────────────────── */ + +/* Text selection — neon cyan highlight */ +.dark ::selection { + background: rgba(34, 211, 238, 0.2); + color: rgba(var(--color-neutral-50), 1); +} + +/* Cyberpunk background: ambient glows + drifting grid (dark mode) */ +@keyframes grid-drift { + from { background-position: 0 0, 0 0, 0 0, 0 0; } + to { background-position: 0 0, 0 0, 48px 48px, 48px 48px; } +} + +.dark body::before { + content: ""; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: + radial-gradient(ellipse 55vw 55vh at 88% 4%, rgba(34, 211, 238, 0.07), transparent), + radial-gradient(ellipse 45vw 45vh at 8% 96%, rgba(192, 132, 252, 0.06), transparent), + linear-gradient(rgba(34, 211, 238, 0.018) 1px, transparent 1px), + linear-gradient(90deg, rgba(34, 211, 238, 0.018) 1px, transparent 1px); + background-size: 100% 100%, 100% 100%, 48px 48px, 48px 48px; + animation: grid-drift 28s linear infinite; +} + +/* Neon horizontal rule — cyan-to-magenta gradient */ +.dark .prose hr { + border: none; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + rgba(34, 211, 238, 0.6) 20%, + rgba(192, 132, 252, 0.6) 50%, + rgba(34, 211, 238, 0.6) 80%, + transparent + ); + box-shadow: 0 0 10px rgba(34, 211, 238, 0.25); + margin: 2em 0; +} + +/* Glitch keyframes for site title */ +@keyframes neon-glitch { + 0%, 88%, 100% { text-shadow: inherit; } + 89% { + text-shadow: + -2px 0 rgba(255, 0, 128, 0.85), + 2px 0 rgba(34, 211, 238, 0.85); + } + 91% { + text-shadow: + 2px 0 rgba(255, 0, 128, 0.85), + -2px 0 rgba(34, 211, 238, 0.85); + } + 93% { text-shadow: inherit; } + 94% { + text-shadow: + -1px 0 rgba(255, 0, 128, 0.6), + 1px 0 rgba(34, 211, 238, 0.6); + } + 96% { text-shadow: inherit; } +} + +/* Apply glitch to pixel-font site title in dark mode */ +.dark .main-menu .flex-1 nav:first-of-type a { + animation: neon-glitch 9s infinite; +} + +/* Neon card border glow — enhanced dual-color on hover */ +.dark article.cursor-pointer:hover, +.dark .cursor-pointer[class*="backdrop"]:hover { + box-shadow: + 0 12px 40px rgba(34, 211, 238, 0.22), + 0 0 0 1px rgba(34, 211, 238, 0.18), + 0 0 20px rgba(192, 132, 252, 0.08) inset; +} + +/* Neon code-block border */ +.dark .highlight { + border-color: rgba(34, 211, 238, 0.3); + box-shadow: 0 0 14px rgba(34, 211, 238, 0.07); +} + +/* ── Fairydust rocket ───────────────────────────────────────────────────── */ + +.fairydust-wrapper { + position: fixed; + z-index: 9997; + pointer-events: none; + width: 40px; + overflow: visible; +} + +.fairydust-wrapper.go-right { + left: -100px; + animation: fairydust-fly-right var(--fly-duration, 5s) linear forwards; +} +.fairydust-wrapper.go-left { + right: -100px; + animation: fairydust-fly-left var(--fly-duration, 5s) linear forwards; +} + +/* Comet trail */ +.fairydust-wrapper::after { + content: ""; + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 130px; + height: 6px; + border-radius: 3px; +} +.fairydust-wrapper.go-right::after { + right: 100%; + background: linear-gradient(to right, transparent, rgba(232, 121, 249, 0.5)); +} +.fairydust-wrapper.go-left::after { + left: 100%; + background: linear-gradient(to left, transparent, rgba(232, 121, 249, 0.5)); +} + +.fairydust-rocket { + width: 40px; + height: 80px; + animation: fairydust-glow 0.85s ease-in-out infinite alternate; +} + +@keyframes fairydust-glow { + from { + filter: + drop-shadow(0 0 5px rgba(232, 121, 249, 0.8)) + drop-shadow(0 0 14px rgba(232, 121, 249, 0.4)); + } + to { + filter: + drop-shadow(0 0 10px rgba(232, 121, 249, 1)) + drop-shadow(0 0 26px rgba(168, 85, 247, 0.65)); + } +} + +@keyframes fairydust-fly-right { + from { left: -100px; } + to { left: calc(100vw + 100px); } +} +@keyframes fairydust-fly-left { + from { right: -100px; } + to { right: calc(100vw + 100px); } +} diff --git a/assets/css/schemes/fira.css b/assets/css/schemes/fira.css new file mode 100644 index 0000000..0be9b55 --- /dev/null +++ b/assets/css/schemes/fira.css @@ -0,0 +1,37 @@ +/* Fira scheme — deep navy + cyan + emerald */ +:root { + --color-neutral: 255, 255, 255; + /* Deep cool-slate — richer than pure black in dark mode */ + --color-neutral-50: 247, 250, 252; + --color-neutral-100: 234, 241, 248; + --color-neutral-200: 210, 225, 238; + --color-neutral-300: 168, 196, 218; + --color-neutral-400: 120, 148, 178; + --color-neutral-500: 80, 105, 135; + --color-neutral-600: 50, 70, 100; + --color-neutral-700: 32, 48, 70; + --color-neutral-800: 20, 30, 48; + --color-neutral-900: 10, 15, 25; + /* Cyan — electric, technical */ + --color-primary-50: 236, 254, 255; + --color-primary-100: 207, 250, 254; + --color-primary-200: 165, 243, 252; + --color-primary-300: 103, 232, 249; + --color-primary-400: 34, 211, 238; + --color-primary-500: 6, 182, 212; + --color-primary-600: 8, 145, 178; + --color-primary-700: 14, 116, 144; + --color-primary-800: 21, 94, 117; + --color-primary-900: 22, 78, 99; + /* Emerald — nod to CCC's green */ + --color-secondary-50: 240, 253, 244; + --color-secondary-100: 220, 252, 231; + --color-secondary-200: 187, 247, 208; + --color-secondary-300: 134, 239, 172; + --color-secondary-400: 74, 222, 128; + --color-secondary-500: 34, 197, 94; + --color-secondary-600: 22, 163, 74; + --color-secondary-700: 21, 128, 61; + --color-secondary-800: 22, 101, 52; + --color-secondary-900: 20, 83, 45; +} diff --git a/assets/js/calendar.js b/assets/js/calendar.js index ffb3c7e..9bb4b61 100644 --- a/assets/js/calendar.js +++ b/assets/js/calendar.js @@ -35,6 +35,8 @@ document.addEventListener('DOMContentLoaded', function() { event.description = value; } else if (baseKey === "RRULE") { event.rrule = value; + } else if (baseKey === "URL") { + event.url = value; } } } @@ -281,13 +283,11 @@ document.addEventListener('DOMContentLoaded', function() { const events = eventsByDate[dateKey]; const hasMembersOnly = events.some(e => e.summary.toLowerCase().includes("members only")); const hasSubbotnik = events.some(e => e.summary.toLowerCase().includes("subbotnik")); - const hasBastelabend = events.some(e => e.summary.toLowerCase().includes("bastelabend")); const hasSpieleabend = events.some(e => e.summary.toLowerCase().includes("spieleabend")); const hasRegular = events.some(e => { const title = e.summary.toLowerCase(); return !title.includes("members only") && !title.includes("subbotnik") && - !title.includes("bastelabend") && !title.includes("spieleabend"); }); @@ -297,7 +297,7 @@ document.addEventListener('DOMContentLoaded', function() { dot.className = "event-dot event-dot-red"; dotsContainer.appendChild(dot); } - if (hasSubbotnik || hasBastelabend || hasSpieleabend) { + if (hasSubbotnik || hasSpieleabend) { let dot = document.createElement("div"); dot.className = "event-dot event-dot-orange"; dotsContainer.appendChild(dot); @@ -329,20 +329,6 @@ document.addEventListener('DOMContentLoaded', function() { calendarBody.appendChild(row); } - function createEventLink(eventTitle) { - if (eventTitle.startsWith("Datengarten")) { - // Extract the number after "Datengarten " - const match = eventTitle.match(/Datengarten\s+(\d+)/i); - if (match && match[1]) { - return `https://berlin.ccc.de/datengarten/${match[1]}/`; - } - } - - // For other titles, convert to lowercase and use as path - const slug = eventTitle.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, ''); - return `https://berlin.ccc.de/page/${slug}/`; - } - function showEventDetails(dateKey) { const events = eventsByDate[dateKey]; eventDateElem.textContent = formatDate(dateKey); @@ -357,10 +343,16 @@ document.addEventListener('DOMContentLoaded', function() { eventTitle.className = "event-title"; // Create a link for the event title - let titleLink = document.createElement("h"); - titleLink.textContent = ev.summary; - titleLink.target = "_blank"; - eventTitle.appendChild(titleLink); + if (ev.url) { + let titleLink = document.createElement("a"); + titleLink.textContent = ev.summary; + titleLink.href = ev.url; + titleLink.target = "_blank"; + titleLink.rel = "noopener noreferrer"; + eventTitle.appendChild(titleLink); + } else { + eventTitle.textContent = ev.summary; + } eventItem.appendChild(eventTitle); diff --git a/assets/js/clubstatus.js b/assets/js/clubstatus.js index d87eb04..2458d51 100644 --- a/assets/js/clubstatus.js +++ b/assets/js/clubstatus.js @@ -1,25 +1,21 @@ const spaceapiUrl = "https://spaceapi.hamburg.ccc.de" const interval_ms = 60000 -console.log("clubstatus.js geladen!"); /* TODO: ENTFERNEN */ - function timeDistanceDE(pastDate) { const seconds = Math.floor((Date.now() - pastDate.getTime()) / 1000) const minutes = Math.floor(seconds / 60) const hours = Math.floor(minutes / 60) const days = Math.floor(hours / 24) - if (days = 1) return `since ${days}d` - if (days > 1) return `since ${days}d` - if (hours = 1) return `since ${hours}h` - if (hours > 1) return `since ${hours}h` - if (minutes = 1) return `since ${minutes}min` - if (minutes > 1) return `since ${minutes}min` + if (days >= 1) return `seit ${days}d` + if (hours >= 1) return `seit ${hours}h` + if (minutes >= 1) return `seit ${minutes}min` return `gerade eben` } function updateRoomState(openState, lastChangeTimestamp) { const roomstate = document.querySelector('#clubstatus-badge') + if (!roomstate) return const statusItem = roomstate.querySelector(".state") const durationItem = roomstate.querySelector(".duration") @@ -30,17 +26,20 @@ function updateRoomState(openState, lastChangeTimestamp) { if (openState === null || lastChangeTimestamp === null) { statusItem.textContent = "unbekannt" + roomstate.classList.add("unknown") statusItem.classList.add("unknown") - durationItem.textContent = "(since unbekannt)" + durationItem.textContent = "(seit unbekannt)" } else { const changeDate = new Date(lastChangeTimestamp * 1000) const sinceText = timeDistanceDE(changeDate) if (openState) { statusItem.textContent = "open" + roomstate.classList.add("open") statusItem.classList.add("open") } else { statusItem.textContent = "closed" + roomstate.classList.add("closed") statusItem.classList.add("closed") } diff --git a/assets/js/fairydust.js b/assets/js/fairydust.js new file mode 100644 index 0000000..0565534 --- /dev/null +++ b/assets/js/fairydust.js @@ -0,0 +1,62 @@ +(function () { + 'use strict'; + + var SVG = [ + '', + // Exhaust flame + '', + '', + // Fins + '', + '', + // Body + '', + // Nose cone + '', + // Window + '', + '', + // Body highlight + '', + '' + ].join(''); + + function isDark() { + return document.documentElement.classList.contains('dark'); + } + + function launch() { + if (!isDark()) { scheduleNext(); return; } + + var goRight = Math.random() > 0.5; + var topPct = 6 + Math.random() * 72; + var duration = (3.5 + Math.random() * 4).toFixed(1); + + var wrapper = document.createElement('div'); + wrapper.className = 'fairydust-wrapper ' + (goRight ? 'go-right' : 'go-left'); + wrapper.style.top = topPct + 'vh'; + wrapper.style.setProperty('--fly-duration', duration + 's'); + + var rocket = document.createElement('div'); + rocket.className = 'fairydust-rocket'; + rocket.style.transform = 'rotate(' + (goRight ? 90 : -90) + 'deg)'; + rocket.innerHTML = SVG; + + wrapper.appendChild(rocket); + document.body.appendChild(wrapper); + + wrapper.addEventListener('animationend', function (e) { + if (e.animationName === 'fairydust-fly-right' || + e.animationName === 'fairydust-fly-left') { + wrapper.remove(); + } + }); + + scheduleNext(); + } + + function scheduleNext() { + setTimeout(launch, 250000 + Math.random() * 950000); // 250–1200 s + } + +})(); diff --git a/build.sh b/build.sh index 6bc707a..6537d21 100755 --- a/build.sh +++ b/build.sh @@ -7,6 +7,4 @@ rm -rf public/ # delete old generated files hugo $(cat .hugo-params) ./tools/merge_cals.py -upcoming="$(tools/gen_upcoming.py static/all.ics 20 5 | tr '\n' ' ')" cp static/all.ics public/all.ics -sed -i "s#CALENDAR#$upcoming#g" public/index.html diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index 13192d6..1e6e30b 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -1,7 +1,6 @@ theme = "blowfish" baseURL = "/" defaultContentLanguage = "de" -relativeURLs = true canonifyURLs = true pluralizeListTitles = false enableRobotsTXT = true diff --git a/config/_default/params.toml b/config/_default/params.toml index 15fd981..81045ad 100644 --- a/config/_default/params.toml +++ b/config/_default/params.toml @@ -28,7 +28,7 @@ giteaDefaultServer = "https://git.fsfe.org" forgejoDefaultServer = "https://git.berlin.ccc.de" [header] -layout = "basic" # valid options: basic, fixed, fixed-fill, fixed-gradient, fixed-fill-blur +layout = "fixed-fill-blur" # valid options: basic, fixed, fixed-fill, fixed-gradient, fixed-fill-blur [footer] showMenu = true @@ -46,7 +46,7 @@ showMoreLink = true showMoreLinkDest = "/post/" cardView = true cardViewScreenWidth = false -layoutBackgroundBlur = false # only used when layout equals background +layoutBackgroundBlur = true # only used when layout equals background [article] showDate = true @@ -85,14 +85,14 @@ showHero = false layoutBackgroundBlur = true # only used when heroStyle equals background or thumbAndBackground layoutBackgroundHeaderSpace = true # only used when heroStyle equals background showBreadcrumbs = false -showSummary = false +showSummary = true showViews = false showLikes = false showTableOfContents = false -showCards = false +showCards = true orderByWeight = false groupByYear = true -cardView = false +cardView = true cardViewScreenWidth = false constrainItemsWidth = false diff --git a/content/_index.md b/content/_index.md index 531fa85..67a6a3e 100644 --- a/content/_index.md +++ b/content/_index.md @@ -25,6 +25,6 @@ description: "Startseite CCCB mit Kurzkalender" ### Nächste Veranstaltungen -CALENDAR +{{< calendar >}} Weitere Termine findest du im [Veranstaltungskalender](/verein/calendar/). diff --git a/content/post/Neujahresempfang-2026/index.md b/content/post/Neujahresempfang-2026/index.md index 61844e1..dd0b3d8 100644 --- a/content/post/Neujahresempfang-2026/index.md +++ b/content/post/Neujahresempfang-2026/index.md @@ -1,6 +1,6 @@ --- title: "Der Neujahresempfang 2026 #NJE26" -date: 2026-03-05T12:00:00+01:00 +date: 2026-05-23T12:00:00+01:00 showHero: false tags: ["NJE", "Ankündigung"] diff --git a/content/sonstiges/oma_paulsen_erzählt_von_früher.md b/content/sonstiges/oma_paulsen_erzählt_von_früher.md deleted file mode 100644 index 01ce32a..0000000 --- a/content/sonstiges/oma_paulsen_erzählt_von_früher.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: "Oma Paulsen erzählt von früher" -date: 2018-05-20T23:16:30+02:00 -menu: - main: - parent: "sonstiges" -draft: true ---- - -Rundfunk --------- - -- [Bilder zur Geschichte des Rundfunks bis - 1945](http://www.dra.de/galerie.htm) Bilder DRA -- [Rundfunkwerbung der dreißiger Jahre von Elly - Heuss-Knapp](http://www.dra.de/dok_0504.htm) -- [Hans Fritsche. Rundfunk im totalen Krieg. (Reichsrundfunk 1944/45, - Heft 13, S - 135ff.)](http://www.mediaculture-online.de/fileadmin/bibliothek/fritzsche_total/fritzsche_total.pdf) - (PDF) -- [Verbot des Niggerjazz. Teil einer auf der Intendantentagung am 12. - Oktober 1935 gehaltenen - Rede.](http://www.mediaculture-online.de/fileadmin/bibliothek/hadamovsky_jazz/hadamovsky_jazz.pdf) - (PDF) -- [Zensur im Rundfunk. Niederschrift zur Sitzung des Programmrates des - deutschen Rundfunks aus dem Jahr - 1929](http://www.mediaculture-online.de/fileadmin/bibliothek/hardt_zensur/hardt_zensur.pdf) - (PDF) -- [Calling .... 2LO - Calling](http://www.sciencemuseum.org.uk/exhibitions/2lo/) The set - of equipment which transmitted the BBC’s first-ever programme - -Computer --------- - -- [Fördermaßnahmen zur Etablierung der Datenverarbeitung in - (West-)Deutschland. Von Konrad Zuse bis zum 1. DV-Programm des - Bundes 1967.](http://www.susas.de/computergeschichte_02.htm) -- [Informatik und Rechentechnik in der - DDR](http://waste.informatik.hu-berlin.de/robotron/studienarbeit/index.html) -- [EDV-Einführung in der DDR in der Epoche des "Neuen ökonomischen - Systems](http://www.susas.de/einfuehrung_edv_ddr_r300/index.htm) -- [Kleincomputer in der - DDR](http://waste.informatik.hu-berlin.de/robotron/diplom/texte/einleitung/einleitung.html) -- [Computersendungen im Rundfunk der - DDR](http://waste.informatik.hu-berlin.de/robotron/studienarbeit/files/literatur/rundfunk/rundfunk.html) -- [Computergeschichte und - Militär](http://www.susas.de/computer_militaer/index.htm) -- [Computer während Weltkrieg - zwei](http://robotron.informatik.hu-berlin.de/WW2/default.html) -- [Adam Thornton: A Brief History of the Rice Computer. - 1959-1971](http://www.princeton.edu/~adam/R1/r1rpt.html) -- [Ivan Sutherland. Sketchpad: A man-machine graphical communication - system. Massachusetts: Ph. D. Thesis, - 1963](http://www.cl.cam.ac.uk/TechReports/UCAM-CL-TR-574.pdf) - elektronische Version, 2003 (PDF) -- [Jeff Johnson, Teresa L. Roberts: The Xerox "Star": A - Retrospective](http://digibarn.com/friends/curbow/star/retrospect/index.html) -- [David Canfield Smith et al.Designing the Star User - Interface](http://www.aci.com.pl/mwichary/guidebook/articles/designingthestaruserinterface) -- [Alan C. Kay. The Early History of - Smalltalk](http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html) -- [Alan Kay: Doing with Images Makes Symbols Pt - 1](http://www.archive.org/movies/details-db.php?collection=opensource_movies&collectionid=AV_Geek_Skip_20021212061248) -- [Alan Kay: Doing with Images Makes Symbols Pt - 2](http://www.archive.org/movies/details-db.php?collection=opensource_movies&collectionid=AV_Geek_Skip_20021212071234) -- [Adele Goldberg, Alan C. Kay. Smalltalk72 Instruction - Manual](http://bitsavers.org/pdf/xerox/alto/Smalltalk72_Manual.pdf) - (PDF) -- [Licklider, J. C. R. Man-Computer Symbiosis. IRE Transactions on - Human Factors in Electronics, HFE-1, 4-11, - 1960](http://memex.org/licklider.pdf) (PDF) -- [Licklider, J. C. R., & Taylor, R. The Computer as a Communication - Device. Science and Technology (4), 21-41, - 1968](http://memex.org/licklider.pdf) -- [William Henry Gates. An Open Letter to Hobbyists. February 3, - 1976](http://www.blinkenlights.com/classiccmp/gateswhine.html) -- [Dennis Mocigemba. Ideengeschichte der Computernutzung. Metaphern - der Computernutzung und - Qualitätssicherungsstrategien](http://edocs.tu-berlin.de/diss/2003/mocigemba_dennis.htm) -- [Computer - Chronicles](http://www.archive.org/movies/computerchronicles.php) - - 618 episodes - -Roboter -------- - -- [Leave It to - Roll-Oh](http://www.archive.org/movies/details-db.php?collection=prelinger&collectionid=07898) - Tongue-in-cheek film showing a domestic robot freeing housewives of - their chores (and intimating that their work is hardly necessary); - actually a promo showing how relays and switches function in the - modern automobile. Shown at the New York World's Fair in 1940. - (1940) -- [Robots makes - radios](http://www.sciencemuseum.org.uk/on-line/ecme/) (1947) - -Vermischtes ------------ - -- [Der Schwarze Kanal 1960-1989](http://dra.orb.de/) digitalisierte - Sendemanuskripte -- [Technikgeschichte](http://fotothek.slub-dresden.de/index.html?/technik/technik.html) - Deutsche Fothothek, SUB Dresden -- [The Early History of Talking - Machines](http://www.haskins.yale.edu/haskins/HEADS/simulacra.html) -- ["Die telegraphischen Verbindungen Deutschlands mit seinen Kolonien" - von Ober Postpraktikant H. Thurn - Koblenz](http://www.jaduland.de/kolonien/text/televerbindung.html) -- [Die modernen Medien der Weimarer - Republik](http://www.ralf-hecht.de/weimar/) -- [120 Years of Electronic Music](http://www.obsolete.com/120_years/) - Electronic Musical Instrument 1870 - 1990 -- [Verbotene Bilder, manipulierte Filme - (I+II).](http://www.deutsches-filminstitut.de/dt2tai01.htm) Zur - Edition der Zensurentscheidungen der Berliner Film-Oberprüfstelle - aus den Jahren 1920 bis 1938 - -Filme aus dem Archiv der CBC (in behämmertem Format) - -- [A Network called - "Internet"](http://archives.cbc.ca/IDC-1-75-710-4205/science_technology/computers/) 8. - Oktober 1993 -- [Kids and their - Computers](http://archives.cbc.ca/400d.asp?id=1-75-710-4186) 26. - Oktober 1982 -- [IDA, der - Geldautomat](http://archives.cbc.ca/400d.asp?id=1-75-710-4201) 12. - April 1968 - diff --git a/content/veranstaltungen/bastelabend.md b/content/veranstaltungen/bastelabend.md deleted file mode 100644 index c787c17..0000000 --- a/content/veranstaltungen/bastelabend.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: "Bastelabend" -subtitle: "Der Bastelabend jeden 1. und 3. Samstag im Monat" -date: 2025-01-08T20:00:00+02:00 -dtstart: 20250118T170000 -dtend: 20250118T230000 -rrule: "FREQ=MONTHLY;BYDAY=1SA,3SA;WKST=MO" -menu: - main: - parent: "Veranstaltungen" -tag: ["Veranstaltung"] -draft: true ---- - - -{{< figure - src="/img/club/27481933907_f240f4232d.jpg" - title="Lötkolben im CCCB" - alt="Lötkolben im CCCB" - caption="Lötkolben im CCCB" ->}} - - -**Jeden 1. und 3. Samstag im Monat ist ab 17 Uhr Bastelabend im Club.** - - - -{{< alert "circle-info" >}} -Wenn ihr neu seid und den CCCB zum ersten Mal besuchen wollt, kommt am besten an einem Donnerstag zum [Club Discordia](/veranstaltungen/clubdiscordia/), da samstags nicht immer genug Leute da sind, um euch zu empfangen. - -Generell sind aber alle, die schon mal im Club waren, herzlich eingeladen, an den Bastelabenden vorbeizukommen. -{{< /alert >}} diff --git a/content/veranstaltungen/chaosradio.md b/content/veranstaltungen/chaosradio.md index f9357ac..0576910 100644 --- a/content/veranstaltungen/chaosradio.md +++ b/content/veranstaltungen/chaosradio.md @@ -19,7 +19,7 @@ tag: ["Veranstaltung"] caption="Chaosradio logo" >}} -[Chaosradio](https://chaosradio.ccc.de) ist der Podcast des CCCB. Seit 2020 zeichen +[Chaosradio](https://chaosradio.ccc.de) ist der Podcast des CCCB. Seit 2020 zeichnen wir ihn remote auf. Gelegentlich finden Sendungen vor Live-Publikum im Club, auf oder auf CCC-Veranstaltungen statt. Dies kündigen wir im Vorfeld separat an. diff --git a/content/veranstaltungen/cms.md b/content/veranstaltungen/cms.md index 2aa4048..c539d09 100644 --- a/content/veranstaltungen/cms.md +++ b/content/veranstaltungen/cms.md @@ -42,7 +42,7 @@ Wissen der Menschheit. All diese Ziele werden auch vom Gesamtkonzept Internet getragen, weshalb es dem CCC naheliegt, sich genau dort als öffentlicher Ansprechpartner einzufinden, wo das Internet als Werkzeug im Alltag überall Einzug hält. Von: -\[[München](http://schule.muc.ccc.de/%5DCCC) +[München – CCC Schule](http://schule.muc.ccc.de/) Regelmäßige Treffen ------------------- @@ -208,7 +208,7 @@ Mailingliste Die Mailingliste richtet sich an alle, die bei Chaos macht Schule in Berlin aktiv sind. Subscribe-Anfragen von uns nicht bekannten Personen -können wir deshalb leider nicht annehmen. Falls wir dich nicht nicht +können wir deshalb leider nicht annehmen. Falls wir dich nicht kennen, komm doch gerne mal bei einem unserer Treffen vorbei. - **Mitglieder-Liste:** diff --git a/content/veranstaltungen/neujahresempfang.md b/content/veranstaltungen/neujahresempfang.md index 9a97796..64450b4 100644 --- a/content/veranstaltungen/neujahresempfang.md +++ b/content/veranstaltungen/neujahresempfang.md @@ -1,7 +1,7 @@ --- title: "Neujahresempfang" subtitle: "#NJE 26" -date: 2025-03-05T18:00:00+02:00 +date: 2026-05-23T18:00:00+02:00 dtstart: 20260523T180000 dtend: 20260524T100000 menu: diff --git a/content/veranstaltungen/plenum.md b/content/veranstaltungen/plenum.md index 202ed96..64cd407 100644 --- a/content/veranstaltungen/plenum.md +++ b/content/veranstaltungen/plenum.md @@ -2,16 +2,16 @@ title: "Plenum (club closed - members only)" subtitle: "Instrument der internen Konsensfindung" date: 2018-05-18T01:11:54+02:00 -dtstart: 20180704T200000 -dtend: 20180704T220000 +dtstart: 20180710T200000 +dtend: 20180710T220000 rrule: "FREQ=MONTHLY;BYDAY=2TU,4TU;INTERVAL=1;WKST=MO" -tags: ["Mitgliedschaft", "Veranstaltung"] -draft: true +members_only: true +tag: ["Mitgliedschaft", "Veranstaltung"] --- Das Plenum findet in der Regel am 2. und 4. Dienstag im Monat ab 20:00 Uhr in den Clubräumen des CCCB statt. Als Instrument der internen Konsensbildung, bei dem Interna diskutiert werden, -richtet sich das Plenum **aussließlich an Mitglieder oder explizit eingeladene Gäste**. +richtet sich das Plenum **ausschließlich an Mitglieder oder explizit eingeladene Gäste**. diff --git a/content/veranstaltungen/spieleabend.md b/content/veranstaltungen/spieleabend.md index ab3d120..3ba69b3 100644 --- a/content/veranstaltungen/spieleabend.md +++ b/content/veranstaltungen/spieleabend.md @@ -2,8 +2,8 @@ title: "Spieleabend" subtitle: "Der Spieleabend jeden 2. und 4. Samstag im Monat" date: 2025-01-08T20:00:00+02:00 -dtstart: 20250125T170000 -dtend: 20250125T230000 +dtstart: 20250111T170000 +dtend: 20250111T230000 rrule: "FREQ=MONTHLY;BYDAY=2SA,4SA;WKST=MO" menu: main: diff --git a/content/veranstaltungen/subbotnik.md b/content/veranstaltungen/subbotnik.md index a520035..552c035 100644 --- a/content/veranstaltungen/subbotnik.md +++ b/content/veranstaltungen/subbotnik.md @@ -9,7 +9,6 @@ menu: main: parent: "Veranstaltungen" tag: ["Veranstaltung"] -draft: true --- ![Ein possierlicher Nager im CCCB](/img/club/41627141884_48e4bb4dec.jpg) diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..9cbb013 --- /dev/null +++ b/dev.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +# Regenerate the merged calendar so the JS calendar widget has fresh data +./tools/merge_cals.py 2>/dev/null || echo "Note: merge_cals.py skipped (no public/ dir yet)" + +hugo server \ + --buildDrafts \ + --buildFuture \ + --disableFastRender \ + --bind 0.0.0.0 \ + --port 1313 diff --git a/flake.nix b/flake.nix index 39d9b6d..b4095a9 100644 --- a/flake.nix +++ b/flake.nix @@ -64,9 +64,7 @@ hugo --baseURL=https://${domain}/ python3 ./tools/merge_cals.py - upcoming="$(python3 tools/gen_upcoming.py static/all.ics 20 5 | tr '\n' ' ')" cp static/all.ics public/all.ics - sed -i "s#CALENDAR#$upcoming#g" public/index.html ''; # Install phase - copy the public directory to the output diff --git a/layouts/_default/list.xml.html b/layouts/_default/list.xml similarity index 72% rename from layouts/_default/list.xml.html rename to layouts/_default/list.xml index 4e54e3e..9b683a4 100644 --- a/layouts/_default/list.xml.html +++ b/layouts/_default/list.xml @@ -2,10 +2,10 @@ {{ range .Pages }} - {{ .Title }} + {{ .Title | html }} {{ .Permalink }} {{ .Date.Format "2006-01-02" }} - {{ .Summary | plainify }} + {{ .Summary | plainify | html }} {{ with .Params.location }}{{ . }}{{ end }} {{ end }} diff --git a/layouts/_default/single.ics b/layouts/_default/single.ics new file mode 100644 index 0000000..f979b43 --- /dev/null +++ b/layouts/_default/single.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//CCCB//Calendar//DE +{{ with .Title }} +X-WR-CALNAME:{{ . }} +{{ end }} +CALSCALE:GREGORIAN +METHOD:PUBLISH +{{ if and .Date (not .Params.members_only) }} +BEGIN:VEVENT +UID:{{ .File.UniqueID }}@berlin.ccc.de +DTSTAMP:{{ .Date.UTC.Format "20060102T150405Z" }} +DTSTART:{{ .Date.UTC.Format "20060102T150405Z" }} +{{ with .Params.event.end }}DTEND:{{ dateFormat "20060102T150405Z" . }}{{ end }} +SUMMARY:{{ .Title }} +DESCRIPTION:{{ .Summary | plainify }} +URL:{{ .Permalink }} +{{ with .Params.location }}LOCATION:{{ . }}{{ end }} +END:VEVENT +{{ end }} +END:VCALENDAR diff --git a/layouts/datengarten/section.ics b/layouts/datengarten/section.ics index d6a9227..f37b9b8 100644 --- a/layouts/datengarten/section.ics +++ b/layouts/datengarten/section.ics @@ -21,6 +21,7 @@ END:STANDARD END:VTIMEZONE {{ range .Pages }} +{{ if and .Params.event.start .Params.event.end -}} BEGIN:VEVENT ORGANIZER;CN="CCCB":mailto:datengarten@berlin.ccc.de SUMMARY:{{ .Title }}{{ with .Params.subtitle }}: {{ . }}{{ end }} @@ -34,5 +35,6 @@ LOCATION:{{ with .Params.location }}{{ . }}{{ else }}CCCB{{ end }} URL:{{ .Permalink }} DESCRIPTION:{{ .Permalink }} END:VEVENT +{{ end -}} {{ end }} END:VCALENDAR diff --git a/layouts/datengarten/section.xml b/layouts/datengarten/section.xml index 47eaf0c..41673dd 100644 --- a/layouts/datengarten/section.xml +++ b/layouts/datengarten/section.xml @@ -11,21 +11,21 @@ {{range .Pages.ByDate -}} - + {{dateFormat "2006-01-02T15:04:05" .Params.event.start}} {{dateFormat "15:04:05" .Params.event.start}} {{ $end := time .Params.event.end }}{{ $t := time "1970-01-01"}}{{ dateFormat "15:04" ( $t.Add ( $end.Sub (time .Params.event.start) ) ) }} Saal23 dg-{{.Params.no}} - {{htmlEscape .Title}} - {{with .Params.subtitle}}{{htmlEscape .}}{{end}} + {{.Title | html}} + {{with .Params.subtitle}}{{. | html}}{{end}} {{.Params.language}} - - + + - {{htmlEscape .Params.speaker}} + {{.Params.speaker | html}} diff --git a/layouts/page/single.ics b/layouts/page/single.ics index 50a1de3..77ac42e 100644 --- a/layouts/page/single.ics +++ b/layouts/page/single.ics @@ -25,7 +25,7 @@ SUMMARY:{{.Title}} UID:{{lower (replace .Title " " "")}}@berlin.ccc.de SEQUENCE:0 STATUS:CONFIRMED -DTSTAMP:20180508T200000Z +DTSTAMP:{{ now.Format "20060102T150405Z" }} DTSTART;TZID=Europe/Berlin:{{.Params.dtstart}} DTEND;TZID=Europe/Berlin:{{.Params.dtend}} RRULE:{{.Params.rrule}} diff --git a/layouts/partials/extend-footer.html b/layouts/partials/extend-footer.html new file mode 100644 index 0000000..604753b --- /dev/null +++ b/layouts/partials/extend-footer.html @@ -0,0 +1,2 @@ +{{ $fairydust := resources.Get "js/fairydust.js" | resources.Minify | resources.Fingerprint "sha512" }} + diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index 12a2638..1707fcf 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -52,7 +52,7 @@