Initial commit: baseline static site builder (templates, content, build.py)

This commit is contained in:
Walter Jekat 2025-12-05 11:22:49 +01:00
commit d4a85a7787
20 changed files with 1278 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Python
__pycache__/
*.pyc
# Virtual environment
venv/
.env
# Build outputs
../builds/
build/
dist/
# Logs
../logs/
*.log
# Media binaries (optional)
../media/
# OS files
.DS_Store
Thumbs.db

106
build.py Normal file
View File

@ -0,0 +1,106 @@
import os
import datetime
import shutil
import yaml
import markdown
from jinja2 import Environment, FileSystemLoader, select_autoescape
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONTENT_PAGES_DIR = os.path.join(BASE_DIR, "content", "de", "pages")
STATIC_SRC = os.path.join(BASE_DIR, "static")
TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
DIST_BASE = os.path.join(os.path.dirname(BASE_DIR), "builds", "preview")
env = Environment(
loader=FileSystemLoader(TEMPLATES_DIR),
autoescape=select_autoescape(["html", "xml"]),
)
def load_pages():
pages = []
for filename in os.listdir(CONTENT_PAGES_DIR):
if not filename.endswith(".yaml"):
continue
path = os.path.join(CONTENT_PAGES_DIR, filename)
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f)
pages.append(data)
return pages
def render_page(page):
template_name = "page.html" # all use same template for now
template = env.get_template(template_name)
# Process blocks and render markdown for prose
blocks = []
for b in page.get("blocks", []):
block = dict(b) # shallow copy
# Markdown rendering for prose blocks: overwrite `content` with HTML
if block.get("type") == "prose" and "content" in block:
block["content"] = markdown.markdown(
block["content"],
extensions=["extra", "sane_lists"]
)
# Markdown rendering for card_grid card texts
if block.get("type") == "card_grid" and "cards" in block:
new_cards = []
for c in block["cards"]:
card = dict(c)
if "text" in card:
card["text"] = markdown.markdown(
card["text"],
extensions=["extra", "sane_lists"]
)
new_cards.append(card)
block["cards"] = new_cards
blocks.append(block)
html = template.render(
page=page, # NEW: pass whole page object
title=page.get("title", ""),
language=page.get("language", "de"),
blocks=blocks,
current_year=datetime.date.today().year,
)
return html
def write_page(page, html):
slug = page.get("slug", "index")
# For now, every page goes to /slug/index.html
out_dir = os.path.join(DIST_BASE, slug)
os.makedirs(out_dir, exist_ok=True)
out_path = os.path.join(out_dir, "index.html")
with open(out_path, "w", encoding="utf-8") as f:
f.write(html)
print(f"Wrote {out_path}")
def copy_static():
if not os.path.isdir(STATIC_SRC):
print(f"No static dir at {STATIC_SRC}, skipping.")
return
static_dst = os.path.join(DIST_BASE, "static")
if os.path.exists(static_dst):
shutil.rmtree(static_dst)
shutil.copytree(STATIC_SRC, static_dst)
print(f"Copied static assets to {static_dst}")
def main():
os.makedirs(DIST_BASE, exist_ok=True)
pages = load_pages()
for page in pages:
html = render_page(page)
write_page(page, html)
copy_static()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,139 @@
title: Photo Antiquaria
slug: photo-antiquaria
language: de
blocks:
- type: full_width_image
src: /static/images/photo-antiquaria-hero.jpg
alt: Photo Antiquaria ausgewählte Hefte
caption: Die Mitglieder-Zeitschrift des Club Daguerre
fade_speed: 1s
vignette_strength: 0.8
- type: hero
title: Photo Antiquaria
subtitle: Die Mitglieder-Zeitschrift des Club Daguerre
- type: prose
heading: Die Mitglieder-Zeitschrift des Club Daguerre
content: |
Ihre Mitgliedschaft im Club Daguerre umfasst auch den Bezug unserer
Mitgliederzeitschrift **Photo Antiquaria**.
Clubmitglieder erhalten exklusiven digitalen Zugriff mit Schlagwortsuche
auf sämtliche Ausgaben der *Photo Antiquaria* und auf **Club Daguerre aktuell**.
So lassen sich fünf Jahrzehnte Club- und Fotogeschichte gezielt erschließen.
Unser Magazin erscheint im Regelfall viermal im Jahr in hochwertiger
Druckqualität mit reich bebilderten Beiträgen zur Foto- und Filmgeschichte,
ihren Verfahren und Geräten.
Die Redaktion derzeit Wolfgang Kuttig und Dr. Heinz Kamp sammelt,
kuratiert und gestaltet die eingehenden Beiträge. Autorinnen und Autoren
sind vor allem Clubmitglieder, aber auch weitere ausgewiesene Kenner der
Materie und engagierte Sammlerinnen und Sammler, die ihr Wissen teilen möchten.
Die thematische Bandbreite reicht von bekannten und weniger bekannten
Fotografen und ihren Arbeiten über Kameras und Projektoren bis hin zu
Firmen- und Technikgeschichte, historischen Verfahren sowie Einblicken in
das Clubleben. Pro Ausgabe stehen in der Regel deutlich über 60 Seiten zur
Verfügung eine kontinuierliche Plattform für Forschung, Dokumentation
und Austausch innerhalb des Clubs.
Beiträge und Anfragen zur Photo Antiquaria richten Sie bitte an unsere
Redaktion unter **redaktion@club-daguerre.de**.
- type: prose
heading: Leseprobe
content: |
Einen typischen Beitrag aus der *Photo Antiquaria* stellen wir Ihnen als
Leseprobe zur Verfügung: ein ausführlicher Artikel zur **Voigtländer
Prominent**. Er vermittelt gut, wie tief und zugleich anschaulich unsere
Autorinnen und Autoren in ihre Themen einsteigen.
Die Leseprobe wird im Rahmen der neuen Club-Daguerre-Webseite an
geeigneter Stelle verlinkt zum Beispiel über das *Sammelsurium* oder
die Seite **Digitale Dienste**.
- type: card_grid
heading: Bisher erschienen…
columns: 2
cards:
- title: Photo Antiquaria 2025
image: /static/images/pa-2025.jpg
image_alt: Photo Antiquaria 2025
text: |
Schwerpunkte im Jahrgang 2025:
- 50 Jahre Digitalkameras Rückblick und Ausblick
- Die Erfolgsgeschichte von **ARRI**
- Unser neues Ehrenmitglied **Jan Beenken**
- Weitere Beiträge zur Kamera- und Industriegeschichte
button_text: Zum Jahrgang 2025
button_url: /jahrgang-2025/
- title: Photo Antiquaria 2024
image: /static/images/pa-2024.jpg
image_alt: Photo Antiquaria 2024
text: |
Schwerpunkte im Jahrgang 2024:
- 50 Jahre Club Daguerre Jubiläumsausgabe der *Photo Antiquaria*
- Das erste Foto in Deutschland und seine historische Einordnung
- Ein Beitrag zur **Rolleiflex SL 26**
- Weitere Artikel zur Kamera- und Fotogeschichte
button_text: Zum Jahrgang 2024
button_url: /jahrgang-2024/
- title: Photo Antiquaria 2023
image: /static/images/pa-2023.jpg
image_alt: Photo Antiquaria 2023
text: |
Schwerpunkte im Jahrgang 2023:
- Beitrag zu **Gottlieb Zulauf & Co.**, einem Schweizer Kamerahersteller
- Artikel zur **Prontor-Wechselfassung**
- Spurensuche zu **Barnacks Leica Nr. 105**
- Externe Entfernungsmesser und ihr Einsatz in der Praxis
button_text: Zum Jahrgang 2023
button_url: /jahrgang-2023/
- title: Photo Antiquaria 2022
image: /static/images/pa-2022.jpg
image_alt: Photo Antiquaria 2022
text: |
Schwerpunkte im Jahrgang 2022:
- Beobachtungskameras und ihre Rolle in der Literatur
- Die **King Regula III** mit Wechselobjektiven
- Die **Bermpohl-Dreifarbenkamera** für Stereoaufnahmen
- Restaurierung einer **Beira II** und weitere Praxisberichte
button_text: Zum Jahrgang 2022
button_url: /jahrgang-2022/
- title: Photo Antiquaria 2021
image: /static/images/pa-2021.jpg
image_alt: Photo Antiquaria 2021
text: |
Schwerpunkte im Jahrgang 2021:
- Die **Agfa Ambi Silette** und ihre Objektive
- *25 Jahre APS* Rückblick auf ein System
- Die lang erwartete **Kodak Retina II**
- **Momme Andresen** Schöpfer einer neuen Entwicklergeneration
button_text: Zum Jahrgang 2021
button_url: /jahrgang-2021/
- title: Photo Antiquaria 2020
image: /static/images/pa-2020.jpg
image_alt: Photo Antiquaria 2020
text: |
Schwerpunkte im Jahrgang 2020:
- **Minor** und **Colibri** Kleinstkameras aus München
- Ein Blick auf die Filmfabrik **Agfa Wolfen**
- Expeditionen des Forschers **Sven Hedin**
- Die außergewöhnliche **Mentorett** und weitere Raritäten
button_text: Zum Jahrgang 2020
button_url: /jahrgang-2020/

572
static/css/site.css Normal file
View File

@ -0,0 +1,572 @@
/* === GLOBAL DARK THEME ===================================== */
html, body {
margin: 0;
padding: 0;
background-color: #000000;
color: #e6e6e6;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
"Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
}
a {
color: #ffcc66;
text-decoration: none;
}
a:hover {
color: #ffe299;
text-decoration: underline;
}
/* === GLOBAL SHELL FADE-IN ================================= */
.cd-header,
.cd-main,
.cd-footer {
opacity: 0;
transform: translateY(4px);
transition:
opacity 1.0s ease-out,
transform 1.0s ease-out;
}
body.cd-ready .cd-header,
body.cd-ready .cd-main,
body.cd-ready .cd-footer {
opacity: 1;
transform: translateY(0);
}
/* === HEADER (desktop) ===================================== */
.cd-header {
background: #ffffff;
border-bottom: 1px solid #e5e5e5;
position: relative;
z-index: 500; /* ensure dropdown is above hero image */
}
.cd-header-inner {
max-width: 1200px;
margin: 0 auto;
padding: 0.75rem 1.25rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.cd-logo {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.cd-logo-image {
height: 72px;
width: auto;
display: block;
}
.cd-site-title {
display: none;
}
.cd-nav { }
.cd-nav-list {
list-style: none;
display: flex;
gap: 1.8rem;
margin: 0;
padding: 0;
font-size: 0.95rem;
}
.cd-nav-item a {
color: #444;
text-decoration: none;
padding-bottom: 2px;
}
.cd-nav-item a:hover {
color: #000;
text-decoration: underline;
}
.cd-nav-active a {
color: #000;
border-bottom: 2px solid #000;
text-decoration: none;
font-weight: 500;
}
/* === DESKTOP "Mehr" DROPDOWN =============================== */
.cd-nav-more {
position: relative;
}
.cd-nav-more-label {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font: inherit;
color: #444;
cursor: default;
padding-bottom: 2px;
}
.cd-nav-more-label::after {
content: "▾";
font-size: 0.7em;
}
.cd-nav-more-menu {
position: absolute;
top: 120%;
right: 0;
min-width: 260px;
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 6px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
margin: 0;
padding: 0.35rem 0;
list-style: none;
display: none;
z-index: 200;
}
.cd-nav-more-menu .cd-nav-item a {
display: block;
padding: 0.35rem 0.75rem;
font-size: 0.9rem;
color: #333;
border-bottom: none;
}
.cd-nav-more-menu .cd-nav-item a:hover {
background: #f4f4f4;
color: #000;
}
/* Show dropdown on hover (desktop) */
.cd-nav-more:hover .cd-nav-more-menu {
display: block;
}
/* Hamburger (desktop hidden) */
.cd-nav-toggle {
display: none;
border: none;
background: transparent;
cursor: pointer;
padding: 0.25rem;
}
.cd-nav-toggle-bar {
display: block;
width: 20px;
height: 2px;
margin: 3px 0;
background: #333;
border-radius: 999px;
}
/* === MAIN LAYOUT ========================================== */
.cd-main {
max-width: 900px;
margin: 0 auto;
padding: 2.5rem 1.25rem 3.5rem;
}
.cd-main h1,
.cd-main h2,
.cd-main h3,
.cd-main h4 {
color: #ffffff;
}
.cd-main p {
color: #e0e0e0;
margin: 0 0 0.9rem;
}
/* === FOOTER =============================================== */
.cd-footer {
border-top: 1px solid #222;
margin-top: 2rem;
}
.cd-footer-inner {
max-width: 900px;
margin: 0 auto;
padding: 1rem 1.25rem;
font-size: 0.85rem;
color: #aaaaaa;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 0.5rem;
}
.cd-footer-links {
display: inline-flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.cd-footer-inner a {
color: #ffcc66;
}
.cd-footer-inner a:hover {
color: #ffe299;
}
/* Shared link transitions */
.cd-nav-item a,
.cd-footer-inner a {
transition:
color 0.18s ease-out,
border-color 0.18s ease-out,
background-color 0.18s ease-out;
}
/* === BLOCKS BASE ========================================== */
.block {
margin-bottom: 2.5rem;
}
/* Hero block (title + subtitle) */
.block-hero {
text-align: center;
}
.hero-text h1 {
margin: 0 0 0.75rem;
font-size: 2.4rem;
}
.hero-subtitle {
margin: 0;
font-size: 1.05rem;
color: #d0d0d0;
}
/* Prose */
.block-prose h2 {
margin-top: 0;
margin-bottom: 0.75rem;
font-size: 1.4rem;
}
.prose-content p {
margin: 0 0 0.9rem;
}
/* Prose lists */
.prose-content ul,
.prose-content ol {
margin: 0 0 0.9rem 0;
padding-left: 1.3rem;
}
.prose-content li {
margin: 0.1rem 0 0.1rem 0;
}
/* === FULL-WIDTH IMAGE BLOCK (Option D) ==================== */
.block-full-width-image {
position: relative;
margin-bottom: 2.7rem;
overflow: hidden; /* keep vignette & shadow inside block */
}
.block-full-width-image .cd-figure {
margin: 0;
}
/* Image itself: inset, rounded, border, shadow, base state */
.block-full-width-image img {
max-width: 860px;
width: 100%;
height: auto;
display: block;
margin: 0 auto;
border-radius: 14px;
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow: 0 0 40px rgba(0, 0, 0, 0.55);
opacity: 0;
transform: translateY(12px);
transition:
opacity var(--fade-speed, 0.8s) ease,
transform var(--fade-speed, 0.8s) ease;
}
/* Vignette overlay */
.block-full-width-image::after {
content: "";
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 100%;
max-width: 860px;
height: 100%;
border-radius: 14px;
pointer-events: none;
box-shadow:
inset 0 0 calc(60px * var(--vignette-strength, 0.75))
rgba(0, 0, 0, 1);
}
/* Caption */
.block-full-width-image figcaption {
text-align: center;
margin-top: 0.55rem;
color: #aaaaaa;
font-size: 0.9rem;
font-style: italic;
letter-spacing: 0.02em;
}
/* Fade-in activated */
.block-full-width-image.cd-visible img {
opacity: 1;
transform: translateY(0);
}
/* === CARD GRID ============================================ */
.block-card-grid h2 {
margin-top: 0;
margin-bottom: 1.25rem;
}
.card-grid {
display: grid;
gap: 1.5rem;
align-items: stretch; /* equal-height rows */
}
.card-grid-cols-2 {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.card-grid-cols-3 {
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.card-grid-cols-4 {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
/* Card shell */
.card {
border-radius: 12px;
overflow: hidden;
background: #111111;
border: 1px solid #333333;
display: flex;
flex-direction: column;
box-shadow: 0 0 24px rgba(0, 0, 0, 0.45);
transition:
box-shadow 0.22s ease-out,
transform 0.18s ease-out,
border-color 0.18s ease-out;
}
.card:hover {
transform: translateY(-4px);
box-shadow:
0 8px 28px rgba(0, 0, 0, 0.6),
0 0 12px rgba(255, 204, 102, 0.15);
border-color: #555555;
}
/* Card image: uniform heights */
.card-image {
height: 260px;
overflow: hidden;
background: #000;
display: flex;
align-items: center;
justify-content: center;
}
.card-image img {
height: 100%;
width: auto;
object-fit: cover;
display: block;
}
/* Card body & text */
.card-body {
padding: 1rem 1.1rem 1.1rem;
display: flex;
flex-direction: column;
flex: 1;
}
.card-title {
margin: 0 0 0.5rem;
font-size: 1.05rem;
color: #ffffff;
}
.card-text {
font-size: 0.95rem;
color: #d2d2d2;
}
/* Lists inside cards */
.card-text ul,
.card-text ol {
margin: 0.5rem 0 0.9rem 0;
padding-left: 1.4rem;
}
.card-text li {
margin: 0.25rem 0;
line-height: 1.45;
}
.card-text ul li::marker {
color: #ffcc66;
font-size: 1.05em;
}
/* Card actions: button at bottom, centered */
.card-actions {
margin-top: auto;
text-align: center;
}
/* Frosted-glass button */
.btn {
display: inline-block;
padding: 0.45rem 1.2rem;
border-radius: 24px;
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(4px);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.28);
text-decoration: none;
font-size: 0.92rem;
transition:
background-color 0.22s ease,
border-color 0.22s ease,
box-shadow 0.25s ease,
transform 0.18s ease;
}
.btn:hover {
background: rgba(255, 255, 255, 0.22);
border-color: rgba(255, 255, 255, 0.45);
box-shadow:
0 0 18px rgba(255, 255, 255, 0.25),
0 0 8px rgba(255, 204, 102, 0.3);
transform: translateY(-2px);
}
/* Optional subtle divider under big grids */
.card-grid::after {
content: "";
display: block;
width: 100%;
height: 1px;
background: linear-gradient(
90deg,
rgba(255,255,255,0.05) 0%,
rgba(255,255,255,0.18) 50%,
rgba(255,255,255,0.05) 100%
);
margin: 2.5rem 0;
}
/* === MOBILE HEADER / NAV ================================= */
@media (max-width: 800px) {
.cd-header-inner {
padding: 0.5rem 0.9rem;
}
.cd-logo-image {
height: 54px;
}
.cd-nav-toggle {
display: inline-block;
}
.cd-nav {
position: fixed;
top: 64px;
left: 0;
right: 0;
background: #ffffff;
border-bottom: 1px solid #e5e5e5;
transform: translateY(-110%);
transition: transform 0.18s ease-out;
z-index: 100;
}
body.cd-nav-open .cd-nav {
transform: translateY(0);
}
.cd-nav-list {
flex-direction: column;
gap: 0;
padding: 0.5rem 1rem 0.75rem;
}
.cd-nav-item a {
display: block;
padding: 0.3rem 0;
font-size: 0.95rem;
}
/* Mobile: Mehr becomes inline list items */
.cd-nav-more {
position: static;
}
.cd-nav-more-label {
display: none;
}
.cd-nav-more-menu {
position: static;
border: none;
box-shadow: none;
display: block !important;
padding: 0;
margin: 0;
}
.cd-nav-more-menu .cd-nav-item a {
padding-left: 0;
}
}

View File

@ -0,0 +1 @@
.css-9s1hn{background-color:#FFFFFF;}.css-1c375l0 .hugeTitleFontSize h1:not(.customScale){font-size:64px !important;}.css-1c375l0 h1:not(.customScale){font-size:48px !important;}.css-1c375l0 h2:not(.customScale), .css-1c375l0 .smallTitleFontSize h1, [data-css-1c375l0] .smallTitleFontSize h1, .css-1c375l0 h1.smallTitleFontSize, [data-css-1c375l0] h1.smallTitleFontSize{font-size:36px !important;}.css-1c375l0 h3:not(.customScale), .css-1c375l0 h4:not(.customScale), [data-css-1c375l0] h4:not(.customScale), .css-1c375l0 h5:not(.customScale), [data-css-1c375l0] h5:not(.customScale), .css-1c375l0 h6:not(.customScale), [data-css-1c375l0] h6:not(.customScale){font-size:24px !important;}.css-1c375l0 li:not(.customScale){font-size:18px !important;}.css-1c375l0 p:not(.customScale){font-size:18px !important;}.css-1c375l0 .customScale p, .css-1c375l0 .customScale h1, [data-css-1c375l0] .customScale h1, .css-1c375l0 .customScale h2, [data-css-1c375l0] .customScale h2, .css-1c375l0 .customScale h3, [data-css-1c375l0] .customScale h3, .css-1c375l0 .customScale h4, [data-css-1c375l0] .customScale h4, .css-1c375l0 .customScale h5, [data-css-1c375l0] .customScale h5, .css-1c375l0 .customScale h6, [data-css-1c375l0] .customScale h6{font-size:inherit !important;}@media only screen and (max-width: 1280px){.css-1c375l0 h1:not(.customScale){font-size:36px !important;}.css-1c375l0 h2:not(.customScale), .css-1c375l0 .smallTitleFontSize h1, [data-css-1c375l0] .smallTitleFontSize h1, .css-1c375l0 h1.smallTitleFontSize, [data-css-1c375l0] h1.smallTitleFontSize{font-size:24px !important;}.css-1c375l0 h3:not(.customScale), .css-1c375l0 h4:not(.customScale), [data-css-1c375l0] h4:not(.customScale), .css-1c375l0 h5:not(.customScale), [data-css-1c375l0] h5:not(.customScale), .css-1c375l0 h6:not(.customScale), [data-css-1c375l0] h6:not(.customScale){font-size:18px !important;}.css-1c375l0 p:not(.customScale){font-size:16px !important;}.css-1c375l0 li:not(.customScale){font-size:16px !important;}.css-1c375l0 .customScale p, .css-1c375l0 .customScale li, [data-css-1c375l0] .customScale li, .css-1c375l0 .customScale h1, [data-css-1c375l0] .customScale h1, .css-1c375l0 .customScale h2, [data-css-1c375l0] .customScale h2, .css-1c375l0 .customScale h3, [data-css-1c375l0] .customScale h3, .css-1c375l0 .customScale h4, [data-css-1c375l0] .customScale h4, .css-1c375l0 .customScale h5, [data-css-1c375l0] .customScale h5, .css-1c375l0 .customScale h6, [data-css-1c375l0] .customScale h6{font-size:inherit !important;}}@media only screen and (max-width: 800px){.css-1c375l0 .hugeTitleFontSize h1:not(.customScale){font-size:40px !important;}}.css-q2nxa6{background-color:#000000;}.css-1sxfiya{min-height:calc(100vh - 80px);}.css-1xo07ii{background-color:rgba(0, 0, 0, 0.55);}.css-xi3bv8{background-color:#FFFFFF !important;border-radius:5px !important;}.css-1wta0f{border-color:transparent !important;}.css-gpurr0{color:#fff;transition:all .2s ease-in;-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;}

202
static/dg_theme_base.css Normal file

File diff suppressed because one or more lines are too long

BIN
static/images/pa-2020.jpg Normal file

Binary file not shown.

BIN
static/images/pa-2021.jpg Normal file

Binary file not shown.

BIN
static/images/pa-2022.jpg Normal file

Binary file not shown.

BIN
static/images/pa-2023.jpg Normal file

Binary file not shown.

BIN
static/images/pa-2024.jpg Normal file

Binary file not shown.

BIN
static/images/pa-2025.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

159
templates/base.html Normal file
View File

@ -0,0 +1,159 @@
<!doctype html>
<html lang="{{ page.language or 'de' }}">
<head>
<meta charset="utf-8">
<title>{{ page.title }} | Club Daguerre e.V.</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Optional: legacy Jimdo theme CSS (can be removed later if desired) -->
<link rel="stylesheet" href="/static/dg_theme_base.css">
<link rel="stylesheet" href="/static/dg_theme_ab12c3.css">
<!-- Our main theme -->
<link rel="stylesheet" href="/static/css/site.css">
</head>
<body>
<!-- HEADER -->
<header class="cd-header">
<div class="cd-header-inner">
<a href="/" class="cd-logo">
<img src="/static/logo.png" alt="Club Daguerre" class="cd-logo-image">
<span class="cd-site-title">CLUB DAGUERRE E.V.</span>
</a>
<button class="cd-nav-toggle" type="button"
aria-expanded="false"
aria-controls="cd-nav">
<span class="cd-nav-toggle-bar"></span>
<span class="cd-nav-toggle-bar"></span>
<span class="cd-nav-toggle-bar"></span>
</button>
<nav class="cd-nav" id="cd-nav">
<ul class="cd-nav-list">
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'index' else '' }}">
<a href="/">Home</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'der-club' else '' }}">
<a href="/der-club/">Der Club</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'photo-antiquaria' else '' }}">
<a href="/photo-antiquaria/">Photo Antiquaria</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'regionaltreffen' else '' }}">
<a href="/regionaltreffen/">Regionaltreffen</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'jahrestreffen' else '' }}">
<a href="/jahrestreffen/">Jahrestreffen</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'sammlerwissen-kompakt' else '' }}">
<a href="/sammlerwissen-kompakt/">Sammlerwissen kompakt</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'mitglied-werden' else '' }}">
<a href="/mitglied-werden/">Mitglied werden</a>
</li>
<!-- DESKTOP: Mehr as dropdown. MOBILE: items appear inline. -->
<li class="cd-nav-item cd-nav-more">
<span class="cd-nav-more-label">Mehr</span>
<ul class="cd-nav-more-menu">
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'bewahren' else '' }}">
<a href="/bewahren/">Bewahren</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'workshops' else '' }}">
<a href="/workshops/">Workshops</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'nachrichten' else '' }}">
<a href="/nachrichten/">Nachrichten</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'sammelsurium' else '' }}">
<a href="/sammelsurium/">Sammelsurium</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'club-intern' else '' }}">
<a href="/club-intern/">Club intern</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'digitale-dienste' else '' }}">
<a href="/digitale-dienste/">Digitale Dienste</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'ehrenvorsitzender-ehrenmitglieder' else '' }}">
<a href="/ehrenvorsitzender-ehrenmitglieder/">
Ehrenvorsitzender / Ehrenmitglieder
</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'english-language-summary' else '' }}">
<a href="/english-language-summary/">English Language Summary</a>
</li>
<li class="cd-nav-item {{ 'cd-nav-active' if page.slug == 'kontakte' else '' }}">
<a href="/kontakte/">Kontakt</a>
</li>
</ul>
</li>
</ul>
</nav>
</div>
</header>
<!-- MAIN CONTENT -->
<main class="cd-main">
{% block content %}{% endblock %}
</main>
<!-- FOOTER -->
<footer class="cd-footer">
<div class="cd-footer-inner">
<div>© {{ current_year }} Club Daguerre e.V.</div>
<div class="cd-footer-links">
<a href="/impressum/">Impressum</a>
<a href="/datenschutz/">Datenschutz</a>
<a href="/cookie-einstellungen/">Cookie-Einstellungen</a>
</div>
</div>
</footer>
<script>
(function () {
function init() {
// Mark shell as ready for fade-in (slight delay so initial state is applied)
requestAnimationFrame(function () {
document.body.classList.add('cd-ready');
});
// Mobile nav toggle
var btn = document.querySelector('.cd-nav-toggle');
if (btn) {
btn.addEventListener('click', function () {
var isOpen = document.body.classList.toggle('cd-nav-open');
btn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
}
// Hero image fade-in
var hero = document.querySelector('.block-full-width-image');
if (hero && 'IntersectionObserver' in window) {
var ob = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
hero.classList.add('cd-visible');
ob.disconnect();
}
});
}, { threshold: 0.3 });
ob.observe(hero);
} else if (hero) {
hero.classList.add('cd-visible');
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
<section class="block block-card-grid">
{% if block.heading %}
<h2>{{ block.heading }}</h2>
{% endif %}
<div class="card-grid card-grid-cols-{{ block.columns or 3 }}">
{% for card in block.cards %}
<article class="card">
{% if card.image %}
<div class="card-image">
<img src="{{ card.image }}" alt="{{ card.image_alt or '' }}">
</div>
{% endif %}
<div class="card-body">
<h3 class="card-title">{{ card.title }}</h3>
{% if card.text %}
<div class="card-text">
{{ card.text | safe }}
</div>
{% endif %}
{% if card.button_url %}
<div class="card-actions">
<a href="{{ card.button_url }}" class="btn">
{{ card.button_text or "Mehr erfahren" }}
</a>
</div>
{% endif %}
</div>
</article>
{% endfor %}
</div>
</section>

View File

@ -0,0 +1,14 @@
<section class="block block-full-width-image"
style="
--fade-speed: {{ block.fade_speed or '0.8s' }};
--vignette-strength: {{ block.vignette_strength or '0.75' }};
">
<figure class="cd-figure">
{% if block.src %}
<img src="{{ block.src }}" alt="{{ block.alt or '' }}">
{% endif %}
{% if block.caption %}
<figcaption>{{ block.caption }}</figcaption>
{% endif %}
</figure>
</section>

View File

@ -0,0 +1,15 @@
<section class="block block-hero block-hero-{{ block.align|default('center') }}">
<div class="hero-inner">
{% if block.image %}
<div class="hero-image">
<img src="{{ block.image }}" alt="{{ block.image_alt or block.title }}">
</div>
{% endif %}
<div class="hero-text">
<h1>{{ block.title }}</h1>
{% if block.subtitle %}
<p class="hero-subtitle">{{ block.subtitle }}</p>
{% endif %}
</div>
</div>
</section>

View File

@ -0,0 +1,8 @@
<section class="block block-prose">
{% if block.heading %}
<h2>{{ block.heading }}</h2>
{% endif %}
<div class="prose-content">
{{ block.content | safe }}
</div>
</section>

7
templates/page.html Normal file
View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
{% for block in blocks %}
{% include "blocks/" + block.type + ".html" %}
{% endfor %}
{% endblock %}