Website der NetzAG Lousberg (Astro, statisch)
  • Astro 72.6%
  • CSS 9.4%
  • Python 7.3%
  • TypeScript 6.7%
  • Dockerfile 1.9%
  • Other 2.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-21 20:05:03 +00:00
.gitea/workflows feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
deployment chore(release): 1.2.1 [skip ci] 2026-09-21 20:05:03 +00:00
nginx feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
public feat(inhalt): Seitentexte halbieren, Aktivitaeten festschreiben 2026-09-21 19:01:09 +02:00
src fix(inhalt): Hausnamen von der Trägerschaftsseite entfernen 2026-09-21 22:04:04 +02:00
.dockerignore feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
.gitignore feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
astro.config.mjs feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
CHANGELOG.md chore(release): 1.2.1 [skip ci] 2026-09-21 20:05:03 +00:00
check.py feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
Containerfile chore(deps): update all non-major dependencies (#2) 2026-09-21 02:08:27 +02:00
LOGO-BRIEF.md feat: Logo einbauen — Kopf, Favicon und Downloadfassungen 2026-09-19 20:55:24 +02:00
Makefile feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00
package-lock.json chore(deps): semantic-release-Plugins aus der Registry statt von GitHub 2026-09-21 20:37:58 +02:00
package.json chore(deps): semantic-release-Plugins aus der Registry statt von GitHub 2026-09-21 20:37:58 +02:00
README.md feat(inhalt): Seitentexte halbieren, Aktivitaeten festschreiben 2026-09-21 19:01:09 +02:00
renovate.json feat: statische Website der NetzAG Lousberg 2026-09-19 18:39:10 +02:00
TODO.md feat(inhalt): Seitentexte halbieren, Aktivitaeten festschreiben 2026-09-21 19:01:09 +02:00
tsconfig.json feat: Website auf Astro umstellen 2026-09-19 19:13:46 +02:00

netzag-lousberg.de

Website der NetzAG Lousberg (Verein in Gründung) — der studentischen Netzgruppe der Wohnheime Lousbergstraße 44 und 46 in Aachen.

Die Seite ist Pflichtvoraussetzung für die Akkreditierung als studentische Eigeninitiative an der RWTH Aachen (LUCOM-Formular, Feld „Social Media Auftritt“). Die Akkreditierungsstelle verlangt Vereinsdarstellung, Ziele, Bedingungen der Mitgliedschaft und Kontakt — alle vier sind vorhanden.

Bauen

npm ci            # einmalig: Abhängigkeiten aus dem Lockfile
npm run dev       # Entwicklungsserver mit Live-Reload
npm run build     # erzeugt dist/
npm run check     # astro check: Typen, Props, Templates
npm run verify    # prüft das fertige dist/ (siehe unten)

Oder über den Makefile-Wrapper: make install, make dev, make build, make check (führt astro check, den Build und check.py nacheinander aus), make clean.

Gebraucht werden Node und npm; check.py braucht zusätzlich ein Python 3. Das Container-Image bringt beides selbst mit und baut ohne beides auf dem Rechner.

Technik

Astro 6, rein statisch (output: 'static', build.format: 'directory'), ohne Integrationen und ohne Tailwind. Kein SSR, kein Client-JavaScript: Der Build enthält kein einziges <script>-Tag.

Die Seite lief zunächst auf einem selbstgeschriebenen Generator aus Python-Standardbibliothek. Das Argument dafür — möglichst wenige Abhängigkeiten — trug nicht mehr, sobald klar war, dass der gemeinsame TRB-CI-Workflow ohnehin eine package.json mit commitlint und semantic-release verlangt: Der npm-Baum war längst da, nur für die Releases statt für den Seitenbau. Dazu kam ein Ausfall, den ein etablierter Generator als harten Fehler behandelt hätte (eine Inhaltsdatei ohne Frontmatter ergab eine leere Seite, fehlerfrei gebaut). Den Ausschlag gab die Nachbarschaft: trb-new-external fährt ebenfalls Astro, im selben Cluster über dieselbe CI. Zwei Vereinsseiten mit zwei Build-Systemen sind für einen wechselnden studentischen Vorstand schlechter als eine Technik, die er einmal lernt.

Gestaltung, Farbtokens, System-Font-Stack und prefers-color-scheme sind eigen und wurden aus der Vorgängerversion unverändert übernommen — die Nachbarseite sieht bewusst anders aus.

Verzeichnisse

astro.config.mjs        statische Ausgabe, Verzeichnis-URLs, keine Integrationen
check.py                Prüfskript für das fertige dist/ (siehe unten)
Makefile                Wrapper um npm: install / dev / build / check / clean
Containerfile           dreistufig: Astro-Build, Prüfung, nginx-Auslieferung
nginx/default.conf      nginx-Konfiguration des Containers (Port 8080)
deployment/             Kustomize-Basis für Kubernetes (nicht von hier angewendet)
.gitea/workflows/       CI: baut und pusht das Image nach registry.trb.nrw
package.json            Astro, commitlint und semantic-release
src/i18n/site.ts        Seitenliste, Slugs je Sprache, Navigation, UI-Texte
src/layouts/            BaseLayout.astro — Grundgerüst, Kopf, Sprachlinks
src/components/         Header.astro, Footer.astro
src/styles/style.css    das gesamte CSS, eine Datei
src/pages/*.astro       Inhalte deutsch (vollständig)
src/pages/en/*.astro    Inhalte englisch (Gerüst, wird bis zur Reakkreditierung
                        vervollständigt)
src/pages/sitemap.xml.ts  sitemap.xml aus der Seitenliste, ohne Zusatzpaket
public/                 wird unverändert übernommen: Favicon, robots.txt,
                        beitrittserklaerung.pdf
dist/                   Ergebnis des Builds (nicht im Git)

Die Dateien liegen unter public/logo/ und sind damit unter https://netzag-lousberg.de/logo/… abrufbar — für Briefkopf, Folien und Dritte, die sich auf den Verein beziehen.

Datei wofür
NetzAG_Lousberg_Logo_Light.svg Wort- und Bildmarke auf hellem Grund (#0b4f6c)
NetzAG_Lousberg_Logo_Dark.svg dieselbe Form auf dunklem Grund (#7fc4e8)
NetzAG_Lousberg_Logo_Monochrom.svg reines Schwarz, für Druck und Kopien
NetzAG_Lousberg_Favicon_Quadrat.svg Bildmarke allein, quadratisch
NetzAG_Lousberg_Favicon_16px.svg vereinfachte Fassung für 16 Pixel
Markenrichtlinien_NetzAG.pdf Begründung der Form, Farbsystem, Mindestgrößen
LIZENZ.md was mit dem Logo erlaubt ist und was nicht

Die Bildmarke ist die Kontur des Lousbergs über zwei parallelen Linien: Die beiden Linien stehen für die zwei Häuser auf gleicher Höhe, ohne Über- und Unterordnung; der Bogen ist der Berg als Ortsanker.

Im Seitenkopf steckt die Wortmarke inline in src/components/Logo.astro, mit currentColor statt fester Farbe — so trägt eine Form beide Farbmodi und folgt dem Token --accent, das hell und dunkel genau die beiden Originalfarben annimmt. Die Schrift ist in Pfade umgewandelt, es wird keine Schriftdatei nachgeladen. Favicon: die quadratische Bildmarke, dazu die 16-Pixel-Fassung über sizes="16x16".

Die Wortmarke steht in Versalien (NETZAG LOUSBERG); im Fließtext bleibt es bei „NetzAG Lousberg“.

Was check.py prüft

astro check prüft Typen und Templates, sagt aber nichts über das Ergebnis. Diese vier Prüfungen bringt kein Generator mit, deshalb hat das Skript die Migration überlebt:

  1. Leere Seiten — Text zwischen <main> und </main>, mit Mindestmaß. Genau der Fall, der einmal ein inhaltsleeres Impressum ausgeliefert hat.
  2. Externe Ressourcen — jeder href/src mit http- oder https-Schema ist ein Fehler. Das hält die Datenschutzerklärung einwilligungsfrei.
  3. Tote Links — jeder interne Link und jeder PDF-Download muss auf eine Datei im Build zeigen.
  4. Platzhalter-Inventar — listet alle […] mit Fundstellenzahl.

Das Skript läuft als eigene Stufe im Containerfile: Fällt eine Prüfung durch, entsteht kein Image.

Seiten

Seite deutsch englisch
Start / /en/
Der Verein /verein/ /en/about/
Was wir machen /was-wir-machen/ /en/what-we-do/
Mitglied werden /mitglied-werden/ /en/membership/
Kontakt /kontakt/ /en/contact/
Impressum /impressum/ /en/legal-notice/
Datenschutz /datenschutz/ /en/privacy/

Inhalte ändern

Jede Seite ist eine .astro-Datei: oben zwischen --- Titel und Beschreibung, darunter der Inhalt als normales HTML im BaseLayout.

---
import BaseLayout from '../layouts/BaseLayout.astro';

const title = 'Kontakt';
const description = 'Kurzbeschreibung für Suchmaschinen.';
---

<BaseLayout lang="de" pageId="kontakt" title={title} description={description}>
  <h1>Kontakt</h1>
  …
</BaseLayout>

Kopf, Navigation, Sprachumschalter und Fuß kommen aus BaseLayout.astro und src/i18n/site.ts und müssen nicht angefasst werden.

Neue Seite anlegen: einen Eintrag in src/i18n/site.ts unter pages ergänzen (id, group, slugs, nav je Sprache), dann je Sprache eine Datei unter src/pages/ bzw. src/pages/en/ mit genau dem Dateinamen aus slugs. npm run check meldet Tippfehler in pageId, check.py findet Links, die ins Leere zeigen.

Achtung bei geschweiften Klammern: In .astro-Dateien sind { und } JavaScript-Ausdrücke — auch in Fließtext. Wer sie literal braucht, schreibt &#123; bzw. &#125;.

Platzhalter stehen durchgängig in eckigen Klammern und sind mit <span class="todo">…</span> ausgezeichnet, damit sie im Browser auffallen. make check listet sie auf; die vollständige Liste steht in TODO.md.

Grundsätze

  • Keine externen Ressourcen zur Laufzeit. Keine Web-Fonts von einem CDN, keine Analytics, kein Drittanbieter-JS, keine eingebetteten Karten oder Videos. Das ist der Grund, warum die Datenschutzerklärung kurz ist und kein Einwilligungsbanner nötig wird. make check erzwingt es.
  • Kein JavaScript. Die Seite enthält keines — nicht als Askese, sondern weil sie keines braucht.
  • Barrierefreiheit. Semantische Landmarks, Sprunglink, lang- und hreflang-Auszeichnung, sichtbare Fokus-Rahmen, ausreichende Kontraste, prefers-color-scheme für hell und dunkel, responsives Layout ohne horizontales Scrollen.
  • Kein „e. V.“ Solange der Verein nicht eingetragen ist, führt er den Namen ohne Rechtsformzusatz; wo der Rechtsstatus zählt, steht „in Gründung“ bzw. „i. Gr.“.

Container

podman build -f Containerfile -t netzag-lousberg-web:dev .
podman run --rm -p 8080:8080 netzag-lousberg-web:dev

Der Build läuft im Image (Stufe 1 Python, Stufe 2 nginx-unprivileged); ein vorher gebautes dist/ wird nicht benötigt. Der Container läuft unprivilegiert als UID 101, hört auf Port 8080, Wurzelverzeichnis /usr/share/nginx/html. Ein Health-Endpunkt liegt auf /healthz.

Deployment

Kubernetes, ausgerollt über ArgoCD. Dieses Repo liefert alles, was dafür nötig ist — angewendet wird von hier aus nichts:

Teil Wo
Image-Build und Push nach registry.trb.nrw/trb/netzag-lousberg-web .gitea/workflows/build.yml, gemeinsamer Workflow TRB/ci-workflows
Versionierung und Bump des Image-Tags im Manifest semantic-release, konfiguriert in package.json
Deployment, Service, Ingress (Apex) deployment/
ArgoCD-Application nicht hierargocd-config/applications/default/netzag-lousberg-web.yaml, von Hand anzulegen (path: deployment/, automated sync + selfHeal)

Eckdaten für den Cluster: Ingress-Class nginx-public, TLS über cert-manager mit dem ClusterIssuer letsencrypt-http01, Container unprivilegiert auf Port 8080, Static-Root /usr/share/nginx/html, Health-Endpunkt /healthz. Die Umleitung von www. auf den Apex macht der nginx im Container, nicht der Ingress-Controller.

Zwei Dinge fehlen noch im Cluster und stehen in TODO.md: Der ClusterIssuer letsencrypt-http01 muss angelegt werden (bisher existiert nur letsencrypt-production mit einem DNS-01-Solver, der ausschließlich trb.nrw abdeckt), und www.netzag-lousberg.de hat keinen A-Record — deshalb gibt es vorerst kein www-Ingress.