diff --git a/assets/Bongo.gif b/assets/Bongo.gif new file mode 100644 index 0000000..74185b1 Binary files /dev/null and b/assets/Bongo.gif differ diff --git a/static/a3l_is.png b/assets/a3l_is.png similarity index 100% rename from static/a3l_is.png rename to assets/a3l_is.png diff --git a/static/jaxoff_logo.png b/assets/jaxoff_logo.png similarity index 100% rename from static/jaxoff_logo.png rename to assets/jaxoff_logo.png diff --git a/static/model.png b/assets/model.png similarity index 100% rename from static/model.png rename to assets/model.png diff --git a/static/nessa.png b/assets/nessa.png similarity index 100% rename from static/nessa.png rename to assets/nessa.png diff --git a/static/sweaty_postkarte.png b/assets/sweaty_postkarte.png similarity index 100% rename from static/sweaty_postkarte.png rename to assets/sweaty_postkarte.png diff --git a/static/visitenkarte.png b/assets/visitenkarte.png similarity index 100% rename from static/visitenkarte.png rename to assets/visitenkarte.png diff --git a/static/xmas_postkarte.png b/assets/xmas_postkarte.png similarity index 100% rename from static/xmas_postkarte.png rename to assets/xmas_postkarte.png diff --git a/static/yan.png b/assets/yan.png similarity index 100% rename from static/yan.png rename to assets/yan.png diff --git a/static/yume_emotes.jpeg b/assets/yume_emotes.jpeg similarity index 100% rename from static/yume_emotes.jpeg rename to assets/yume_emotes.jpeg diff --git a/css/custom.css b/css/custom.css deleted file mode 100644 index 8f0635c..0000000 --- a/css/custom.css +++ /dev/null @@ -1,83 +0,0 @@ -:root { - --parvus-background-color: #161618; - --parvus-color: #f2f5f4; - --parvus-caption-color: #f2f5f4; - --parvus-btn-background-color: #F5A9B8; - --parvus-btn-color: #ffffff; - --parvus-btn-disabled-background-color: #5BCEFA; - --parvus-btn-disabled-color: #161618; - --img-height: 12.5em; - --max-img-width: 22em; /* So breit wie das breiteste Bild */ - } - - -body { - background-color: #161618; - color: #f2f5f4; -} - -a { - color: #f5a9b8; -} -a:hover { - color: #f2f5f4; -} - -a.lightbox { - margin: 10px; -} - -.parvus-trigger:has(img) .parvus-zoom__indicator { - display: none; -} - -.image-box { - height: var(--img-height); - width: var(--max-img-width); - display: flex; - align-items: center; - justify-content: center; - background-color: #222222; - border: 1px solid #222222; - border-radius: 0.5rem; - -} - -.image-box img { - max-height: 100%; - max-width: 100%; - object-fit: contain; -} - -.page-title { - padding-bottom: 3em; - color: #F5A9B8; -} - -.logo { - height: 8em; - width: auto; -} - -a.svg-link { - display: inline-block; - line-height: 0; - padding: 0; - margin: 0; -} - -a.svg-link svg { - display: flex; - width: auto; - height: 3em; - object-fit: contain; -} - -.separator, .source { - color: #f2f5f4; - text-align: center; - margin: 25px auto; - margin-top: 20px; - width: 90%; - max-width: 500px; -} \ No newline at end of file diff --git a/css/sites.css b/css/sites.css deleted file mode 100644 index 32c58e1..0000000 --- a/css/sites.css +++ /dev/null @@ -1,19 +0,0 @@ -.twitch { - color: #9146ff; -} - -.twitter { - color: #1DA1F2; -} - -.vgen { - color: #B8FF26; -} - -.website { - color: #f5a9b8; -} - -.bsky { - color: #1185FE; -} \ No newline at end of file diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000..b60e469 --- /dev/null +++ b/css/styles.css @@ -0,0 +1,453 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +:root { + --primary-pink: #F5A9B8; + --primary-blue: #5BCFFA; + --white: #FFFFFF; + --dark-bg: #161618; + --gray-blue: #A8C5DB; + --dark-blue: #12354B; + --dark-teal: #12404B; + + --card-bg: rgba(22, 22, 24, 0.85); + --card-border: rgba(91, 207, 250, 0.15); + --card-hover-border: rgba(245, 169, 184, 0.25); + + --shadow-sm: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen, Ubuntu, sans-serif; + background-color: var(--dark-bg); + color: var(--white); + min-height: 100vh; + line-height: 1.6; + overflow-x: hidden; +} + +.background-canvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + pointer-events: none; +} + +.main-container { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 1.5rem; + position: relative; +} + +.header { + text-align: center; + padding: 4rem 0 2rem; + margin-bottom: 3rem; + position: relative; +} + +.back-button { + position: absolute; + top: 2rem; + left: 0; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + background: rgba(18, 53, 75, 0.3); + border: 1px solid rgba(91, 207, 250, 0.2); + border-radius: 12px; + color: var(--primary-blue); + text-decoration: none; + font-weight: 600; + transition: border-color 0.2s ease; + z-index: 10; +} + +.back-button:hover { + border-color: var(--primary-blue); +} + +.logo-wrapper { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 2rem; + width: 100%; + height: auto; +} + +.logo { + width: 360px; + height: auto; + object-fit: contain; +} + +.page-title { + font-size: 3.5rem; + font-weight: 800; + background: linear-gradient(135deg, #F5A9B8, #5BCFFA); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 1rem; + letter-spacing: -0.5px; +} + +.page-subtitle { + font-size: 1.25rem; + color: var(--gray-blue); + max-width: 700px; + margin: 0 auto; + opacity: 0.9; + line-height: 1.8; +} + +.bongo { + height: 1.5em; + width: auto; +} + +.loading { + text-align: center; + padding: 4rem; + color: var(--gray-blue); +} + +.spinner { + width: 50px; + height: 50px; + border: 3px solid rgba(91, 207, 250, 0.1); + border-top-color: var(--primary-blue); + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 1rem; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.artist-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 2rem; + margin-bottom: 4rem; + align-items: stretch; +} + +.artist-card { + background: var(--card-bg); + border-radius: 20px; + overflow: hidden; + border: 1px solid var(--card-border); + backdrop-filter: blur(10px); + transition: border-color 0.3s ease, box-shadow 0.3s ease; + display: flex; + flex-direction: column; + height: 100%; +} + +.artist-card:hover { + border-color: var(--card-hover-border); + box-shadow: 0 0 0 1px var(--card-hover-border); +} + +.image-container { + position: relative; + overflow: hidden; + background: linear-gradient(135deg, var(--dark-blue), var(--dark-teal)); + display: flex; + align-items: center; + justify-content: center; + padding: 30px; + min-height: 320px; + max-height: 320px; + flex-shrink: 0; +} + +.image-container.horizontal { + padding: 40px 20px; +} + +.image-container.horizontal .artist-image { + width: 100%; + height: auto; + max-height: 100%; +} + +.image-container.vertical { + padding: 20px 40px; +} + +.image-container.vertical .artist-image { + width: 50%; + height: auto; + max-height: 100%; +} + +.artist-image { + object-fit: contain; + object-position: center; + display: block; +} + +.content { + padding: 1.5rem; + display: flex; + flex-direction: column; + flex-grow: 1; +} + +.artist-name { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #F5A9B8, #5BCFFA); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.artist-role { + font-size: 1rem; + color: var(--primary-blue); + margin-bottom: 1rem; + font-weight: 600; +} + +.artist-description { + color: var(--gray-blue); + margin-bottom: 1.5rem; + line-height: 1.7; + flex-grow: 1; +} + +.social-links { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + margin-top: auto; +} + +.social-link { + width: 40px; + height: 40px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + color: white; + text-decoration: none; + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(18, 53, 75, 0.4); + transition: all 0.2s ease; +} + +.social-link:hover { + border-color: transparent; +} + +.social-link.vgen:hover { + background: #B8FF26; + color: #0F0F16; +} + +.social-link.twitter:hover { + background: #1DA1F2; +} + +.social-link.bluesky:hover { + background: #0085FF; +} + +.social-link.youtube:hover { + background: #FF0000; +} + +.social-link.twitch:hover { + background: #9146FF; +} + +.social-link.instagram:hover { + background: linear-gradient(45deg, #405DE6, #833AB4, #C13584, #E1306C, #FD1D1D); +} + +.social-link.pixiv:hover { + background: #0096FA; +} + +.social-link.website:hover { + background: var(--primary-blue); + color: #161618; +} + +.footer { + text-align: center; + padding: 3rem 0 1rem; + border-top: 1px solid rgba(91, 207, 250, 0.1); + position: relative; +} + +.footer::before { + content: ''; + position: absolute; + top: -1px; + left: 50%; + transform: translateX(-50%); + width: 100px; + height: 2px; + background: linear-gradient(135deg, #F5A9B8, #5BCFFA); +} + +.footer-text { + font-size: 1.25rem; + color: var(--gray-blue); + margin-bottom: 1rem; +} + +.footer a { + color: rgba(168, 197, 219, 0.6); +} + +.heart { + color: var(--primary-pink); + display: inline-block; + margin: 0 0.25rem; +} + +.copyright { + color: rgba(168, 197, 219, 0.6); + font-size: 0.875rem; +} + +.error { + text-align: center; + padding: 3rem; + color: var(--primary-pink); + background: rgba(245, 169, 184, 0.1); + border-radius: 12px; + border: 1px solid rgba(245, 169, 184, 0.2); +} + +@media (max-width: 768px) { + .main-container { + padding: 1rem; + } + + .header { + padding: 3rem 0 1.5rem; + } + + .back-button { + position: relative; + top: auto; + left: auto; + margin-bottom: 2rem; + } + + .page-title { + font-size: 2.5rem; + } + + .page-subtitle { + font-size: 1.1rem; + } + + .artist-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } + + .image-container { + padding: 20px; + min-height: 280px; + max-height: 280px; + } + + .image-container.horizontal { + padding: 30px 15px; + } + + .image-container.horizontal .artist-image { + width: 100%; + height: auto; + } + + .image-container.vertical { + padding: 15px 30px; + } + + .image-container.vertical .artist-image { + width: 60%; + height: auto; + } + + .logo { + width: 240 px; + height: auto; + } +} + +@media (max-width: 480px) { + .page-title { + font-size: 2rem; + } + + .logo { + width: 200px; + height: auto; + } + + .back-button { + padding: 0.5rem 1rem; + font-size: 0.875rem; + } + + .image-container { + padding: 15px; + min-height: 250px; + max-height: 250px; + } + + .image-container.horizontal { + padding: 20px 10px; + } + + .image-container.vertical { + padding: 10px 20px; + } + + .image-container.vertical .artist-image { + width: 70%; + } +} + +.social-link.bluesky:hover { + background: #0085FF; +} + +.social-link.fiverr:hover { + background: #1DBF73; +} + +@media (prefers-reduced-motion: reduce) { + .artist-card, + .social-link, + .back-button { + transition: none !important; + } + + .spinner { + animation: spin 3s linear infinite; + } +} \ No newline at end of file diff --git a/data/artists.json b/data/artists.json new file mode 100644 index 0000000..0898318 --- /dev/null +++ b/data/artists.json @@ -0,0 +1,96 @@ +[ + { + "name": "Yume Shirokuro", + "role": "Twitch Emotes", + "description": "", + "image": "./assets/yume_emotes.jpeg", + "orientation": "horizontal", + "links": { + "vgen": "https://vgen.co/YumeShirokuro", + "bluesky": "https://bsky.app/profile/yumeshirokuro.bsky.social", + "twitch": "https://twitch.tv/yume_shirokuro" + } + }, + { + "name": "A3l_is", + "role": "Logo, Background, Stream Overlay, Schedule, Rigging, Splat Emote & Refsheet", + "description": "", + "image": "./assets/a3l_is.png", + "orientation": "horizontal", + "links": { + "vgen": "https://vgen.co/A3l_is", + "bluesky": "https://bsky.app/profile/a3lis.bsky.social", + "twitch": "https://twitch.tv/a3l_is" + } + }, + { + "name": "Milky Blanc", + "role": "Model Art", + "description": "", + "image": "./assets/model.png", + "orientation": "horizontal", + "links": { + "vgen": "https://vgen.co/m1lkyblanc", + "twitter": "https://twitter.com/m1lkyblanc" + } + }, + { + "name": "Nessai", + "role": "Sticker Design", + "description": "", + "image": "./assets/nessa.png", + "orientation": "horizontal", + "links": { + "instagram": "https://www.instagram.com/nessaihai/", + "twitch": "https://www.twitch.tv/nessai", + "website": "https://nessai.carrd.co/" + } + }, + { + "name": "lontongs_", + "role": "Sweaty Jax Postkarte", + "description": "", + "image": "./assets/sweaty_postkarte.png", + "orientation": "vertical", + "links": { + "twitter": "https://twitter.com/Lontongs_", + "instagram": "https://www.instagram.com/lontongs_" + } + }, + { + "name": "OriOttero", + "role": "Christmas Jax Postkarte", + "description": "", + "image": "./assets/xmas_postkarte.png", + "orientation": "vertical", + "links": { + "bluesky": "https://bsky.app/profile/oriottero.orifans.com", + "website": "https://orifans.com/links", + "twitch": "https://twitch.tv/OriOttero", + "instagram": "https://www.instagram.com/oriottero/" + } + }, + { + "name": "YanOrion", + "role": "PNG Tuber", + "description": "", + "image": "./assets/yan.png", + "orientation": "vertical", + "links": { + "instagram": "https://www.instagram.com/yanorion/" + } + }, + { + "name": "sleepingglitch", + "role": "Visitenkarte", + "description": "", + "image": "./assets/visitenkarte.png", + "orientation": "vertical", + "links": { + "vgen": "https://vgen.co/sleepingglitch", + "twitter": "https://twitter.com/sleepingglitch", + "twitch": "https://twitch.tv/sleepingglitch_" + } + } + +] \ No newline at end of file diff --git a/index.html b/index.html index 8eccfc4..5ab9276 100644 --- a/index.html +++ b/index.html @@ -1,336 +1,67 @@ - - - - - - - JaxOff 🌸💦 Art Credits - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-

- -

Artist Credits

-

-
-
- - - -
- - -
-
- -
-
-
- -

Twitch Emotes & Sticker Designs

- -

by Yume Shirokuro


- -
- - - - - - - - - - - - - - - - -
- - -
-
- - - -
- - -
-
- -
-
-
- -

Logo, Background, Stream Overlay, Schedule, Rigging, Splat Emote & Refsheet

- -

by A3l_is


- -
- - - - - - - - - - - - - - - - -
- - -
-
- - - -
- - -
-
- -
-
-
- -

Model Art

- -

by Milky Blanc


- -
- - - - - - - - - -
- - -
-
- - - -
- - -
-
- -
-
-
- -

Sticker Design

- -

by Nessai


- -
- - - - - -
- - -
-
- - - - -
- - -
-
- -
-
-
- -

PNG Tuber

- -

by YanOrion


- -
- - - - - - -
- - -
-
- - - - -
- - -
-
- -
-
-
- -

Sweaty Jax Postkarte

- -

by lontongs_


- -
- - - - -
- - -
-
- - - -
- - -
-
- -
-
-
- -

Christmas Jax Postkarte

- -

by OriOttero


- -
- - - - - - - - - - - - - - - - - -
- - -
-
- - - -
- - -
-
- -
-
-
- -

Visitenkarte

- -

by sleepingglitch


- -
- - - - - - - - - - - - - - -
- - -
-
- - -
-
- - - - - \ No newline at end of file + + + + + + JaxOff 🌸💦 Artist Credits + + + + + + + + + +
+
+ + + + + Zurück zu JaxOff + + +
+ + + +
+ +

Artist Credits

+ +

+
+ +
+
+

Lade Künstler-Daten...

+
+ + + +
+ +
+ + +
+
+ + + + + diff --git a/js/background.js b/js/background.js new file mode 100644 index 0000000..91c4bf6 --- /dev/null +++ b/js/background.js @@ -0,0 +1,119 @@ +(() => { + document.addEventListener("DOMContentLoaded", () => { + const canvas = document.getElementById("backgroundCanvas"); + if (!canvas) return; + + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; + + const ctx = canvas.getContext("2d", { alpha: true, desynchronized: true }); + + const isMobile = window.matchMedia("(max-width: 768px)").matches; + const DPR_CAP = Math.min(1.5, isMobile ? 1.0 : 1.25); + + const getOrbCount = () => { + const baseCount = Math.floor((window.innerWidth * window.innerHeight) / 40000); + return Math.min(Math.max(baseCount, isMobile ? 15 : 25), isMobile ? 30 : 50); + }; + + const R_MIN = isMobile ? 3 : 4; + const R_MAX = isMobile ? 8 : 12; + const SPEED = isMobile ? 0.08 : 0.12; + + const PINK = [245, 169, 184]; + const BLUE = [91, 207, 250]; + + let w = 0, h = 0, dpr = 1; + let orbs = []; + let raf = 0; + let last = 0; + + function resize() { + w = window.innerWidth; + h = window.innerHeight; + + dpr = Math.min(DPR_CAP, window.devicePixelRatio || 1); + canvas.width = Math.floor(w * dpr); + canvas.height = Math.floor(h * dpr); + canvas.style.width = w + "px"; + canvas.style.height = h + "px"; + + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + createOrbs(); + last = 0; + } + + function createOrbs() { + const count = getOrbCount(); + orbs = new Array(count); + + for (let i = 0; i < count; i++) { + const r = Math.random() * (R_MAX - R_MIN) + R_MIN; + const color = Math.random() > 0.5 ? PINK : BLUE; + orbs[i] = { + x: Math.random() * w, + y: Math.random() * h, + r, + a: Math.random() * 0.15 + 0.4, + vx: (Math.random() - 0.5) * SPEED, + vy: (Math.random() - 0.5) * SPEED, + color: color + }; + } + } + + function step() { + ctx.clearRect(0, 0, w, h); + + for (let i = 0; i < orbs.length; i++) { + const o = orbs[i]; + o.x += o.vx; + o.y += o.vy; + + const margin = o.r * 3; + if (o.x < -margin) o.x = w + margin; + else if (o.x > w + margin) o.x = -margin; + if (o.y < -margin) o.y = h + margin; + else if (o.y > h + margin) o.y = -margin; + + ctx.beginPath(); + ctx.arc(o.x, o.y, o.r, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${o.color[0]}, ${o.color[1]}, ${o.color[2]}, ${o.a})`; + ctx.fill(); + } + } + + function tick(now) { + if (document.hidden) { + raf = 0; + return; + } + + if (!last) last = now; + if (now - last >= 16) { + last = now; + step(); + } + + raf = requestAnimationFrame(tick); + } + + let resizeTimer; + window.addEventListener("resize", () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(resize, 150); + }, { passive: true }); + + document.addEventListener("visibilitychange", () => { + if (document.hidden) { + cancelAnimationFrame(raf); + raf = 0; + } else if (!raf) { + last = 0; + raf = requestAnimationFrame(tick); + } + }); + + resize(); + raf = requestAnimationFrame(tick); + }); +})(); \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e2591f4 --- /dev/null +++ b/js/main.js @@ -0,0 +1,146 @@ +(() => { + document.addEventListener("DOMContentLoaded", init); + + async function init() { + document.getElementById("currentYear").textContent = new Date().getFullYear(); + await loadArtists(); + } + + async function loadArtists() { + const loadingEl = document.getElementById("loading"); + const errorEl = document.getElementById("error"); + const gridEl = document.getElementById("artistGrid"); + + errorEl.style.display = "none"; + loadingEl.style.display = "block"; + + try { + const url = `./data/artists.json?v=${Date.now()}`; + const res = await fetch(url, { + cache: "no-store", + headers: { "Accept": "application/json" } + }); + + if (!res.ok) { + throw new Error(`artists.json fetch failed: ${res.status} ${res.statusText}`); + } + + const artists = await res.json(); + + loadingEl.style.display = "none"; + + if (!Array.isArray(artists) || artists.length === 0) { + gridEl.innerHTML = '

Keine Künstler-Daten verfügbar.

'; + return; + } + + const frag = document.createDocumentFragment(); + for (const artist of artists) { + frag.appendChild(createArtistCardNode(artist)); + } + + gridEl.replaceChildren(frag); + + } catch (err) { + console.error("Error loading artists:", err); + + loadingEl.style.display = "none"; + errorEl.style.display = "block"; + + gridEl.textContent = ""; + } + } + + function createArtistCardNode(artist) { + const article = document.createElement("article"); + article.className = "artist-card"; + + const inner = document.createElement("div"); + inner.className = "card-inner"; + + const imageContainer = document.createElement("div"); + imageContainer.className = "image-container"; + if (artist.orientation) { + imageContainer.classList.add(artist.orientation); + } + + const img = document.createElement("img"); + img.className = "artist-image"; + img.src = artist.image; + img.alt = `${artist.role} von ${artist.name}`; + img.loading = "lazy"; + img.decoding = "async"; + img.dataset.orientation = artist.orientation || 'auto'; + + imageContainer.appendChild(img); + + const content = document.createElement("div"); + content.className = "content"; + + const name = document.createElement("h2"); + name.className = "artist-name"; + name.textContent = artist.name; + + const role = document.createElement("div"); + role.className = "artist-role"; + role.textContent = artist.role; + + const desc = document.createElement("p"); + desc.className = "artist-description"; + desc.textContent = artist.description ?? ""; + + content.appendChild(name); + content.appendChild(role); + content.appendChild(desc); + + const links = createSocialLinksNode(artist.links); + if (links) content.appendChild(links); + + inner.appendChild(imageContainer); + inner.appendChild(content); + article.appendChild(inner); + + return article; + } + + function createSocialLinksNode(links) { + if (!links) return null; + + const wrap = document.createElement("div"); + wrap.className = "social-links"; + + const order = ["vgen", "twitter", "bluesky", "youtube", "twitch", "instagram", "fiverr", "website"]; + for (const platform of order) { + if (!links[platform]) continue; + wrap.appendChild(createIconLink(platform, links[platform])); + } + + return wrap.childElementCount ? wrap : null; + } + + function createIconLink(platform, url) { + const a = document.createElement("a"); + a.href = url; + a.className = `social-link ${platform}`; + a.target = "_blank"; + a.rel = "noopener"; + a.ariaLabel = platform; + + a.innerHTML = getPlatformSvg(platform); + return a; + } + + function getPlatformSvg(platform) { + const svgs = { + vgen: ``, + twitter: ``, + bluesky: ``, + youtube: ``, + twitch: ``, + instagram: ``, + fiverr: ``, + website: `` + } + return svgs[platform] || ""; + } +})(); \ No newline at end of file