diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-Italic-VariableFont_wght.ttf b/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-Italic-VariableFont_wght.ttf new file mode 100644 index 0000000..75865b9 Binary files /dev/null and b/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-Italic-VariableFont_wght.ttf differ diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-VariableFont_wght.ttf b/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-VariableFont_wght.ttf new file mode 100644 index 0000000..5b7248b Binary files /dev/null and b/juconnect-strict-theme-v3/juconnect-strict/assets/fonts/SourceSans3-VariableFont_wght.ttf differ diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/gruppe.png b/juconnect-strict-theme-v3/juconnect-strict/assets/img/gruppe.png new file mode 100644 index 0000000..3c54caa Binary files /dev/null and b/juconnect-strict-theme-v3/juconnect-strict/assets/img/gruppe.png differ diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/helfer.png b/juconnect-strict-theme-v3/juconnect-strict/assets/img/helfer.png new file mode 100644 index 0000000..5156986 Binary files /dev/null and b/juconnect-strict-theme-v3/juconnect-strict/assets/img/helfer.png differ diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect.svg b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect.svg new file mode 100644 index 0000000..ad3ce82 --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect.svg @@ -0,0 +1,51 @@ + + \ No newline at end of file diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon.svg b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon.svg new file mode 100644 index 0000000..2e4a4ea --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon.svg @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon_light.svg b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon_light.svg new file mode 100644 index 0000000..a489005 --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_icon_light.svg @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_light.svg b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_light.svg new file mode 100644 index 0000000..aca41c9 --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/assets/img/juconnect_light.svg @@ -0,0 +1,51 @@ + + \ No newline at end of file diff --git a/juconnect-strict-theme-v3/juconnect-strict/assets/js/app.js b/juconnect-strict-theme-v3/juconnect-strict/assets/js/app.js new file mode 100644 index 0000000..ba03cbb --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/assets/js/app.js @@ -0,0 +1,381 @@ +/* JuConnect · Tool-first UI interactions (v0.2) */ + +const $ = (sel, root=document) => root.querySelector(sel); +const $$ = (sel, root=document) => Array.from(root.querySelectorAll(sel)); + +function normalizeHexColor(value) { + if (!value) return ""; + const trimmed = value.trim(); + if (trimmed.startsWith("#")) return trimmed; + + const match = trimmed.match(/^rgba?\(([^)]+)\)$/i); + if (!match) return trimmed; + + const parts = match[1].split(",").map(p => p.trim()); + if (parts.length < 3) return trimmed; + + const toHex = (n) => { + const num = Number.parseInt(n, 10); + if (Number.isNaN(num)) return "00"; + return Math.max(0, Math.min(255, num)).toString(16).padStart(2, "0"); + }; + + return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`.toLowerCase(); +} + +function syncBackgroundSwatch() { + const swatch = document.querySelector('[data-swatch][data-name="bg"]'); + if (!swatch) return; + + const rawBg = getComputedStyle(document.documentElement).getPropertyValue("--bg"); + const hex = normalizeHexColor(rawBg); + if (!hex) return; + + swatch.setAttribute("data-hex", hex); + const label = swatch.querySelector("[data-bg-hex]"); + if (label) label.textContent = hex; +} + +function syncThemeLogos() { + const isDark = document.documentElement.getAttribute("data-theme") === "dark"; + $$("[data-logo-light][data-logo-dark]").forEach((img) => { + const src = isDark ? img.getAttribute("data-logo-dark") : img.getAttribute("data-logo-light"); + if (src) img.setAttribute("src", src); + }); +} + +/* Toasts */ +const Toast = (() => { + const container = () => document.querySelector(".toasts"); + + const show = (text, timeoutMs=2600) => { + const c = container(); + if (!c) return; + + const el = document.createElement("div"); + el.className = "toast"; + el.innerHTML = ` +
+ + `; + $(".toast__text", el).textContent = text; + + const close = () => { el.remove(); clearTimeout(t); }; + $(".toast__close", el).addEventListener("click", close); + c.appendChild(el); + const t = setTimeout(close, timeoutMs); + }; + + return { show }; +})(); + +/* Clipboard copy helper */ +async function copyText(text, label="Kopiert.") { + try { + await navigator.clipboard.writeText(text); + Toast.show(label); + } catch { + // Fallback + const ta = document.createElement("textarea"); + ta.value = text; + ta.style.position = "fixed"; + ta.style.left = "-9999px"; + document.body.appendChild(ta); + ta.select(); + document.execCommand("copy"); + ta.remove(); + Toast.show(label); + } +} + +function decodeCopyText(text) { + if (!text) return ""; + return text + .replace(/\\r\\n/g, "\n") + .replace(/\\n/g, "\n") + .replace(/\\t/g, "\t"); +} + +/* Generic [data-copy] buttons */ +document.addEventListener("click", (e) => { + const btn = e.target.closest("[data-copy]"); + if (!btn) return; + const text = decodeCopyText(btn.getAttribute("data-copy") || ""); + const label = btn.getAttribute("data-copy-label") || "Kopiert."; + copyText(text, label); +}); + +/* Copy for snippet blocks */ +document.addEventListener("click", (e) => { + const btn = e.target.closest("[data-snippet-copy]"); + if (!btn) return; + const snippet = btn.closest("[data-snippet]") || btn.closest(".card"); + const code = snippet?.querySelector("pre code"); + if (!code) return; + copyText(code.innerText, btn.getAttribute("data-copy-label") || "Snippet kopiert"); +}); + +/* Swatch copy buttons */ +document.addEventListener("click", (e) => { + const btn = e.target.closest("[data-swatch-copy]"); + if (!btn) return; + const swatch = btn.closest("[data-swatch]"); + if (!swatch) return; + + const mode = btn.getAttribute("data-swatch-copy"); + const name = swatch.getAttribute("data-name") || ""; + const hex = swatch.getAttribute("data-hex") || ""; + if (mode === "hex") return copyText(hex, "Hex kopiert"); + if (mode === "token") return copyText(`var(--${name})`, "Token kopiert"); +}); + +/* Theme toggle */ +(() => { + const btn = document.querySelector("[data-theme-toggle]"); + const saved = localStorage.getItem("juconnect_theme"); + if (saved) document.documentElement.setAttribute("data-theme", saved); + syncBackgroundSwatch(); + syncThemeLogos(); + + btn?.addEventListener("click", () => { + const current = document.documentElement.getAttribute("data-theme"); + const next = current === "dark" ? "light" : "dark"; + document.documentElement.setAttribute("data-theme", next); + localStorage.setItem("juconnect_theme", next); + syncBackgroundSwatch(); + syncThemeLogos(); + Toast.show(`Theme: ${next}`); + }); +})(); + +/* Active nav link on scroll */ +(() => { + const links = $$(".navlink"); + const sections = links + .map(a => document.querySelector(a.getAttribute("href"))) + .filter(Boolean); + + const obs = new IntersectionObserver((entries) => { + const visible = entries + .filter(e => e.isIntersecting) + .sort((a,b) => b.intersectionRatio - a.intersectionRatio)[0]; + if (!visible) return; + + links.forEach(a => a.classList.remove("is-active")); + const id = "#" + visible.target.id; + const active = links.find(a => a.getAttribute("href") === id); + active?.classList.add("is-active"); + }, { rootMargin: "-25% 0px -65% 0px", threshold: [0.12, 0.25, 0.45] }); + + sections.forEach(s => obs.observe(s)); +})(); + +/* Nav search filter */ +(() => { + const input = document.querySelector("[data-nav-search]"); + const nav = document.querySelector("[data-nav]"); + if (!input || !nav) return; + + const allLinks = $$("a.navlink", nav); + + input.addEventListener("input", () => { + const q = input.value.trim().toLowerCase(); + allLinks.forEach(a => { + const show = !q || a.textContent.toLowerCase().includes(q) || a.getAttribute("href").toLowerCase().includes(q); + a.style.display = show ? "" : "none"; + }); + + // hide empty groups + $$(".navgroup", nav).forEach(g => { + const anyVisible = $$("a.navlink", g).some(a => a.style.display !== "none"); + g.style.display = anyVisible ? "" : "none"; + }); + }); +})(); + +/* Bildsprache prompt generator */ +(() => { + const topicInput = document.querySelector("[data-bild-topic]"); + const copyBtn = document.querySelector("[data-bild-copy]"); + if (!topicInput || !copyBtn) return; + + const template = `Erstelle eine ruhige, sachliche Illustration zum Thema: {THEMA}. + +Stil: +- reduzierte, professionelle Vektorillustration +- klare Formen, weiche Kanten, keine Comic-\u00dcberzeichnung +- keine Verniedlichung, keine \u00fcbertriebenen Emotionen +- sachlich, freundlich, respektvoll +- geeignet f\u00fcr Jugend- und Familienhilfe im Kontext \u00f6ffentlicher Tr\u00e4ger + +Farbwelt: +- Prim\u00e4rfarbe: tiefes, seri\u00f6ses Blau (#1d354f) f\u00fcr Struktur, Kleidung, Rahmen +- Akzentfarbe: dezenter, warmer Salbeiton (#8FAE9A) nur f\u00fcr kleine Hervorhebungen +- neutrale Off-White- und Grau-T\u00f6ne f\u00fcr Hintergr\u00fcnde +- keine grellen Farben, kein hoher Kontrast, kein Schwarz + +Motivik: +- abstrahierte Menschen oder Symbole (keine realistischen Portr\u00e4ts) +- keine konkreten Alters-, Ethnie- oder Rollenklischees +- Fokus auf Handlung, Beziehung oder Prozess \u2013 nicht auf Drama +- positive, ruhige K\u00f6rpersprache +- ausreichend Freiraum (Whitespace), damit Text erg\u00e4nzt werden kann + +Komposition: +- klarer Bildaufbau, gut lesbar auch in klein +- geeignet f\u00fcr Website-Sektionen, Infoboxen oder Erkl\u00e4rgrafiken +- Hintergrund ruhig und nicht detailreich + +Ausschl\u00fcsse: +- keine Fotos, kein Fotorealismus +- keine Stock-Illustrations-Klischees +- keine kindlichen Comic-Stile +- keine starken Schatten, Glows oder Effekte`; + + const buildPrompt = () => { + const topic = topicInput.value.trim() || "[THEMA EINF\u00dcGEN]"; + return template.replace("{THEMA}", topic); + }; + + copyBtn.addEventListener("click", () => { + copyText(buildPrompt(), copyBtn.getAttribute("data-copy-label") || "Bildprompt kopiert"); + }); +})(); + +/* Tabs */ +(() => { + $$("[data-tabs]").forEach((tabs) => { + const buttons = $$(".tab", tabs); + const panels = $$(".tabs__panel", tabs); + + const activate = (btn) => { + buttons.forEach(b => { + const active = b === btn; + b.classList.toggle("is-active", active); + b.setAttribute("aria-selected", String(active)); + b.tabIndex = active ? 0 : -1; + }); + + const id = btn.getAttribute("aria-controls"); + panels.forEach(p => p.classList.toggle("is-active", p.id === id)); + }; + + buttons.forEach((btn) => { + btn.addEventListener("click", () => activate(btn)); + btn.addEventListener("keydown", (e) => { + const idx = buttons.indexOf(btn); + if (e.key === "ArrowRight") { e.preventDefault(); buttons[(idx + 1) % buttons.length].focus(); } + if (e.key === "ArrowLeft") { e.preventDefault(); buttons[(idx - 1 + buttons.length) % buttons.length].focus(); } + if (e.key === "Enter" || e.key === " ") { e.preventDefault(); activate(btn); } + }); + }); + }); +})(); + +/* Accordion */ +(() => { + $$("[data-accordion]").forEach((acc) => { + $$(".acc__trigger", acc).forEach((btn) => { + const panel = btn.nextElementSibling; + if (!panel) return; + + btn.addEventListener("click", () => { + const open = btn.getAttribute("aria-expanded") === "true"; + btn.setAttribute("aria-expanded", String(!open)); + panel.hidden = open; + }); + }); + }); +})(); + +/* Modal */ +(() => { + let lastFocus = null; + + const openModal = (modal) => { + if (!modal) return; + lastFocus = document.activeElement; + modal.hidden = false; + + const focusables = $$("button,[href],input,select,textarea,[tabindex]:not([tabindex='-1'])", modal) + .filter(el => !el.hasAttribute("disabled")); + const first = focusables[0]; + const last = focusables[focusables.length - 1]; + first?.focus(); + + const onKey = (e) => { + if (e.key === "Escape") closeModal(modal); + if (e.key !== "Tab") return; + if (!focusables.length) return; + + if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } + else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } + }; + + modal.__onKey = onKey; + document.addEventListener("keydown", onKey); + }; + + const closeModal = (modal) => { + if (!modal) return; + modal.hidden = true; + document.removeEventListener("keydown", modal.__onKey); + lastFocus?.focus?.(); + }; + + document.addEventListener("click", (e) => { + const openBtn = e.target.closest("[data-modal-open]"); + if (openBtn) { + const sel = openBtn.getAttribute("data-modal-open"); + openModal(document.querySelector(sel)); + return; + } + + const closeBtn = e.target.closest("[data-modal-close]"); + if (closeBtn) { + const modal = closeBtn.closest(".modal"); + closeModal(modal); + } + }); +})(); + +/* Demo form validation */ +(() => { + const form = document.querySelector("[data-demo-form]"); + if (!form) return; + + const status = $(".form__status", form); + + const setStatus = (msg, type="info") => { + status.textContent = msg; + status.style.marginTop = "6px"; + status.style.color = type === "danger" ? "var(--danger)" : "var(--muted)"; + }; + + form.addEventListener("submit", (e) => { + e.preventDefault(); + + const name = form.name.value.trim(); + const email = form.email.value.trim(); + const topic = form.topic.value.trim(); + const privacy = form.privacy.checked; + + const errors = []; + if (!name) errors.push("Name fehlt."); + if (!email || !email.includes("@")) errors.push("E-Mail ungültig."); + if (!topic) errors.push("Thema auswählen."); + if (!privacy) errors.push("Datenschutz bestätigen."); + + if (errors.length) { + setStatus("Bitte prüfen: " + errors.join(" "), "danger"); + Toast.show("Formular unvollständig."); + return; + } + + setStatus("Demo: Formular wäre jetzt versendet.", "info"); + Toast.show("Danke. Anfrage gespeichert (Demo)."); + form.reset(); + }); + + form.addEventListener("reset", () => setStatus("Zurückgesetzt.")); +})(); diff --git a/juconnect-strict-theme-v3/juconnect-strict/footer.php b/juconnect-strict-theme-v3/juconnect-strict/footer.php new file mode 100644 index 0000000..7b7b845 --- /dev/null +++ b/juconnect-strict-theme-v3/juconnect-strict/footer.php @@ -0,0 +1,14 @@ + + + + + + + + + + +