Familienangebote im August: Unterschied zwischen den Versionen
Aus Bündnis für Familie Tübingen.
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
<!doctype html> | |||
<html lang="de"> | <html lang="de"> | ||
<head> | <head> | ||
<meta charset=" | <meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<title>Kalender | <title>Kalender August 2026</title> | ||
<style> | <style> | ||
:root { | |||
--bg: #f6f7fb; | |||
--card: #ffffff; | |||
--text: #1f2937; | |||
--muted: #6b7280; | |||
--accent: #2563eb; | |||
--accent-2: #dbeafe; | |||
--border: #e5e7eb; | |||
--shadow: 0 10px 30px rgba(0,0,0,.08); | |||
} | |||
* { box-sizing: border-box; } | |||
body { | body { | ||
font-family: | margin: 0; | ||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; | |||
color: | color: var(--text); | ||
background: var(--bg); | |||
} | } | ||
.wrap { | |||
max-width: 1200px; | |||
margin: 0 auto; | |||
padding: 24px; | |||
} | } | ||
h1 { margin: 0 0 8px; font-size: clamp(1.5rem, 3vw, 2.2rem); } | |||
.sub { margin: 0 0 20px; color: var(--muted); } | |||
.calendar { | .calendar { | ||
display: grid; | |||
grid-template-columns: repeat(7, minmax(0, 1fr)); | |||
background: | gap: 10px; | ||
} | |||
.dow, .day { | |||
background: var(--card); | |||
border: 1px solid var(--border); | |||
border-radius: 16px; | border-radius: 16px; | ||
overflow: hidden; | overflow: hidden; | ||
min-height: 120px; | |||
} | } | ||
.dow { | |||
. | padding: 12px; | ||
font-weight: 700; | |||
text-align: center; | text-align: center; | ||
color: var(--muted); | |||
min-height: auto; | |||
} | } | ||
.day { | .day { | ||
padding: 10px; | |||
padding: | |||
display: flex; | display: flex; | ||
flex-direction: column; | flex-direction: column; | ||
gap: | gap: 8px; | ||
align-items: stretch; | |||
} | } | ||
.day.empty { background: transparent; border: 1px dashed transparent; } | |||
.day.empty { background: | .day-num { font-weight: 700; color: var(--muted); } | ||
.event { | |||
display: block; | |||
width: 100%; | |||
text-align: left; | |||
border: 1px solid var(--accent-2); | |||
background: var(--accent-2); | |||
color: #0f172a; | |||
border-radius: 12px; | |||
align | padding: 8px 10px; | ||
border-radius: | |||
padding: | |||
cursor: pointer; | cursor: pointer; | ||
font: inherit; | |||
line-height: 1.25; | |||
} | } | ||
.event:hover { border-color: var(--accent); box-shadow: 0 2px 8px rgba(37,99,235,.12); } | |||
.event .time { display: block; font-size: .82rem; color: var(--accent); font-weight: 700; margin-bottom: 2px; } | |||
.event .title { display: block; font-weight: 600; } | |||
.legend { margin-top: 18px; color: var(--muted); font-size: .95rem; } | |||
dialog { | |||
border: none; | |||
border-radius: 18px; | |||
width: min(92vw, 560px); | |||
box-shadow: var(--shadow); | |||
padding: 0; | |||
} | } | ||
dialog::backdrop { background: rgba(15,23,42,.55); } | |||
. | .modal { | ||
padding: 20px; | |||
background: white; | |||
} | } | ||
.modal h2 { margin: 0 0 12px; font-size: 1.35rem; } | |||
.modal | .meta { | ||
display: grid; | |||
gap: 10px; | |||
margin: 14px 0 18px; | |||
} | } | ||
.row { display: grid; grid-template-columns: 140px 1fr; gap: 10px; } | |||
. | .label { color: var(--muted); font-weight: 600; } | ||
.actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; } | |||
. | .btn { | ||
border: 1px solid var(--border); | |||
background: #fff; | background: #fff; | ||
padding: 10px 14px; | |||
border-radius: 12px; | |||
padding: | |||
border-radius: | |||
cursor: pointer; | cursor: pointer; | ||
text-decoration: none; | |||
color: var(--text); | |||
color: | |||
font-weight: 600; | font-weight: 600; | ||
} | } | ||
.btn.primary { background: var(--accent); border-color: var(--accent); color: white; } | |||
@media (max-width: 900px) { | |||
.calendar { gap: 8px; } | |||
.dow, .day { border-radius: 14px; } | |||
} | } | ||
@media (max-width: 700px) { | |||
.wrap { padding: 14px; } | |||
.calendar { | |||
grid-template-columns: 1fr; | |||
} | |||
.dow { display: none; } | |||
.day { min-height: auto; } | |||
.day.empty { display: none; } | |||
.day::before { | |||
content: attr(data-date); | |||
font-weight: 700; | |||
color: var(--muted); | |||
margin-bottom: 2px; | |||
} | |||
.row { grid-template-columns: 1fr; } | |||
dialog { width: min(95vw, 560px); } | |||
. | |||
} | } | ||
</style> | </style> | ||
</head> | </head> | ||
<body> | <body> | ||
<div class="wrap"> | |||
<h1>August 2026</h1> | |||
<p class="sub">Im Grid werden nur Uhrzeit und Titel angezeigt. Details öffnen sich per Klick in einem Popup.</p> | |||
< | <div id="calendar" class="calendar" aria-label="Kalender August 2026"></div> | ||
</ | |||
< | <p class="legend">Beispieleinträge sind direkt im Quelltext als Array gepflegt.</p> | ||
</div> | </div> | ||
<div class="modal | <dialog id="eventDialog"> | ||
<div class="modal"> | |||
<h2 id="dlgTitle">Event</h2> | |||
<div class="meta"> | |||
<div class="row"><div class="label">Datum</div><div id="dlgDate"></div></div> | |||
<div class="row"><div class="label">Uhrzeit</div><div id="dlgTime"></div></div> | |||
<div class="row"><div class="label">Zielgruppe</div><div id="dlgAudience"></div></div> | |||
<div class="row"><div class="label">Zusatztext</div><div id="dlgNote"></div></div> | |||
<div class="row"><div class="label">Mehr Infos</div><div><a id="dlgUrl" href="#" target="_blank" rel="noopener noreferrer">Link öffnen</a></div></div> | |||
</div> | |||
<div class="actions"> | |||
</div> | <button class="btn" id="closeDialog">Schließen</button> | ||
<a class="btn primary" id="openUrl" href="#" target="_blank" rel="noopener noreferrer">Mehr Infos öffnen</a> | |||
</div> | |||
</div> | |||
</dialog> | |||
<script> | <script> | ||
const events = [ | |||
{ | |||
date: '2026-08-05', | |||
title: 'Workshop: HTML Basics', | |||
time: '10:00', | |||
zielgruppe: 'Einsteiger', | |||
zusatztext: 'Kurzer Praxis-Workshop mit Beispielseiten und Q&A.', | |||
url: 'https://example.com/html-basics' | |||
}, | |||
{ | |||
date: '2026-08-14', | |||
title: 'Team Meeting', | |||
time: '14:30', | |||
zielgruppe: 'Internes Team', | |||
zusatztext: 'Monatliches Update zu laufenden Projekten und nächsten Schritten.', | |||
url: 'https://example.com/team-meeting' | |||
}, | |||
{ | |||
date: '2026-08-23', | |||
title: 'Netzwerkabend', | |||
time: '18:00', | |||
zielgruppe: 'Öffentlich', | |||
zusatztext: 'Lockeres Treffen zum Kennenlernen und Austauschen.', | |||
url: 'https://example.com/netzwerkabend' | |||
} | |||
]; | |||
const calendar = document.getElementById('calendar'); | |||
const dialog = document.getElementById('eventDialog'); | |||
const byDate = events.reduce((acc, e) => ((acc[e.date] ??= []).push(e), acc), {}); | |||
const year = 2026, month = 7; | |||
const start = new Date(year, month, 1); | |||
const end = new Date(year, month + 1, 0); | |||
const daysInMonth = end.getDate(); | |||
const startDow = (start.getDay() + 6) % 7; | |||
const weekdays = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']; | |||
weekdays.forEach(d => { | |||
const el = document.createElement('div'); | |||
el.className = 'dow'; | |||
el.textContent = d; | |||
calendar.appendChild(el); | |||
}); | |||
for (let i = 0; i < startDow; i++) { | |||
const empty = document.createElement('div'); | |||
empty.className = 'day empty'; | |||
calendar.appendChild(empty); | |||
} | |||
function openEvent(event) { | |||
document.getElementById('dlgTitle').textContent = event.title; | |||
document.getElementById('dlgDate').textContent = new Date(event.date).toLocaleDateString('de-DE', { year: 'numeric', month: 'long', day: 'numeric' }); | |||
document.getElementById('dlgTime').textContent = event.time; | |||
document.getElementById('dlgAudience').textContent = event.zielgruppe; | |||
document.getElementById('dlgNote').textContent = event.zusatztext; | |||
document.getElementById('dlgUrl').href = event.url; | |||
document.getElementById('openUrl').href = event.url; | |||
dialog.showModal(); | |||
} | |||
for (let day = 1; day <= daysInMonth; day++) { | |||
const dateStr = `2026-08-${String(day).padStart(2, '0')}`; | |||
const cell = document.createElement('div'); | |||
cell.className = 'day'; | |||
cell.dataset.date = `${String(day).padStart(2, '0')}.08.2026`; | |||
cell.innerHTML = `<div class="day-num">${day}</div>`; | |||
(byDate[dateStr] || []).forEach(event => { | |||
const btn = document.createElement('button'); | |||
btn.className = 'event'; | |||
btn.type = 'button'; | |||
btn.innerHTML = `<span class="time">${event.time}</span><span class="title">${event.title}</span>`; | |||
btn.addEventListener('click', () => openEvent(event)); | |||
cell.appendChild(btn); | |||
cell.appendChild( | |||
}); | }); | ||
calendar.appendChild(cell); | |||
} | } | ||
document.getElementById('closeDialog').addEventListener('click', () => dialog.close()); | |||
dialog.addEventListener('click', (e) => { | |||
const rect = dialog.getBoundingClientRect(); | |||
</script> | const clickedOutside = e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom; | ||
if (clickedOutside) dialog.close(); | |||
}); | |||
</script> | |||
</body> | </body> | ||
</html> | </html> | ||
Version vom 10. Juni 2026, 10:02 Uhr
<!doctype html>
August 2026
Im Grid werden nur Uhrzeit und Titel angezeigt. Details öffnen sich per Klick in einem Popup.
Beispieleinträge sind direkt im Quelltext als Array gepflegt.