Startseite, Server-Seite v2 mit Logos, Lightbox-Fix, Navbar-Ausrichtung

- Startseite (/): Hero mit CTAs (Discord-Invite + Devlogs), Live-Zahlen
  (Member, Server online, Spieler), Bereichs-Kacheln, neuestes Devlog
  als Teaser — kein Redirect mehr auf /devlogs
- Server-Seite v2: Übersichtsleiste (X/Y online, Spieler gesamt),
  2-Spalten-Grid, Server-Logo (neues Feld image_url, https-only, auch
  als Embed-Thumbnail im Discord-Status), große Spielerzahl, Uptime %
  + Peak aus den 24h-Samples, Adresse als Copy-Button
- Lightbox: rendert jetzt per Portal in <body> (Header/Scanlines
  schienen durchs Overlay), Pfeile stehen neben dem Bild statt drauf,
  Info-Zeile unterm Bild
- Navbar: Community-Dropdown-Button auf Link-Höhe ausgerichtet
  (line-height + align-items)
- Setup Server-Tab: Logo-URL-Feld

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 22:25:30 +02:00
co-authored by Claude Fable 5
parent be11398f7c
commit ef0fb066f7
10 changed files with 316 additions and 68 deletions
+20 -17
View File
@@ -1,5 +1,8 @@
// Bild-Overlay mit Blättern (Pfeiltasten, Buttons, Wischen) statt neuem Tab.
// Rendert per Portal direkt in <body>, damit Header/Scanlines nicht durchscheinen;
// die Pfeile stehen NEBEN dem Bild, die Info-Zeile darunter.
import { useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
export default function Lightbox({ images, start = 0, onClose }) {
const [index, setIndex] = useState(start);
@@ -25,7 +28,7 @@ export default function Lightbox({ images, start = 0, onClose }) {
const img = images[index];
if (!img) return null;
return (
return createPortal(
<div
className="lightbox"
onClick={onClose}
@@ -40,22 +43,22 @@ export default function Lightbox({ images, start = 0, onClose }) {
role="dialog" aria-modal="true"
>
<button className="lb-close" onClick={onClose} title="Schließen (Esc)"></button>
{images.length > 1 && (
<button className="lb-nav lb-prev" onClick={(e) => { e.stopPropagation(); prev(); }} title="Zurück (←)"></button>
)}
<img
className="lb-img"
src={img.src ?? img}
alt={img.alt ?? ''}
onClick={(e) => e.stopPropagation()}
/>
{images.length > 1 && (
<button className="lb-nav lb-next" onClick={(e) => { e.stopPropagation(); next(); }} title="Weiter (→)"></button>
)}
<div className="lb-meta" onClick={(e) => e.stopPropagation()}>
{img.caption && <span>{img.caption}</span>}
{images.length > 1 && <span className="lb-count">{index + 1} / {images.length}</span>}
<div className="lb-row">
{images.length > 1 && (
<button className="lb-nav" onClick={(e) => { e.stopPropagation(); prev(); }} title="Zurück (←)"></button>
)}
<figure className="lb-figure" onClick={(e) => e.stopPropagation()}>
<img className="lb-img" src={img.src ?? img} alt={img.alt ?? ''} />
<figcaption className="lb-meta">
{img.caption && <span>{img.caption}</span>}
{images.length > 1 && <span className="lb-count">{index + 1} / {images.length}</span>}
</figcaption>
</figure>
{images.length > 1 && (
<button className="lb-nav" onClick={(e) => { e.stopPropagation(); next(); }} title="Weiter (→)"></button>
)}
</div>
</div>
</div>,
document.body
);
}