diff --git a/partials/landing/account/dashboard.php b/partials/landing/account/dashboard.php index 797ddb1..a508e52 100755 --- a/partials/landing/account/dashboard.php +++ b/partials/landing/account/dashboard.php @@ -297,12 +297,16 @@ $sectionLinks = [
+
-

Wähle eine Lorelei-Variante oder lade dir direkt neue Vorschläge. Die Vorschau aktualisiert sich sofort.

+

Wähle eine Komponente und passe dein neues Profilbild Schritt für Schritt an. Gespeichert wird erst nach Klick auf den Button unten.

+
+ +
$meta): ?> -
-
- Variante wählen -
- $meta): ?> - - -
-
-
diff --git a/public/assets/css/app.css b/public/assets/css/app.css index 7483b29..3e0d4ad 100755 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -247,9 +247,8 @@ body { .profile-avatar-panel__preview { display: flex; justify-content: center; } .profile-avatar-panel__content h3 { margin: 0 0 8px; } .profile-avatar-panel__content p { margin: 0 0 14px; } -.avatar-builder__layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 24px; align-items: start; } -.avatar-builder__preview { position: sticky; top: 0; display: grid; justify-items: center; gap: 12px; padding: 18px; background: linear-gradient(180deg, #fffdfa, #f8f4ec); border: 1px solid var(--color-border); border-radius: 18px; } -.avatar-builder__controls { display: grid; gap: 18px; } +.avatar-builder__layout { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: start; } +.avatar-builder__preview { display: grid; justify-items: center; gap: 14px; padding: 18px; background: linear-gradient(180deg, #fffdfa, #f8f4ec); border: 1px solid var(--color-border); border-radius: 18px; } .avatar-component-nav { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; } .avatar-component-nav__button { border: 1px solid var(--color-border); background: #fff; color: var(--color-text); border-radius: 999px; padding: 8px 12px; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; } .avatar-component-nav__button.is-active { background: var(--color-primary); color: var(--color-primary-contrast); border-color: var(--color-primary); } @@ -257,22 +256,21 @@ body { .avatar-component-panel { display: none; width: 100%; gap: 10px; } .avatar-component-panel.is-active { display: grid; } .avatar-component-panel__head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; } -.avatar-component-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; max-height: 360px; overflow: auto; padding-right: 4px; } -.avatar-component-card { position: relative; display: grid; gap: 6px; padding: 8px; background: #fff; border: 1px solid var(--color-border); border-radius: 14px; cursor: pointer; } +.avatar-component-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; max-height: 460px; overflow: auto; padding-right: 4px; } +.avatar-component-card { position: relative; display: grid; gap: 8px; padding: 10px; background: #fff; border: 1px solid var(--color-border); border-radius: 16px; cursor: pointer; } .avatar-component-card input { position: absolute; opacity: 0; pointer-events: none; } -.avatar-component-card__thumb { display: block; aspect-ratio: 1; border-radius: 12px; overflow: hidden; background: linear-gradient(180deg, #f7efe6, #f0e4d7); border: 1px solid rgba(74, 52, 38, 0.08); } -.avatar-component-card__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; } +.avatar-component-card__thumb { display: block; aspect-ratio: 1; border-radius: 14px; overflow: hidden; background: linear-gradient(180deg, #f7efe6, #f0e4d7); border: 1px solid rgba(74, 52, 38, 0.08); } +.avatar-component-card__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; transform-origin: center; } .avatar-component-card__label { font-size: 12px; font-weight: 700; color: var(--color-text); text-align: center; } .avatar-component-card:has(input:checked) { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(52,72,90,0.14); } -.avatar-preset-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 12px; } -.avatar-preset-card { position: relative; display: grid; gap: 8px; align-content: start; padding: 10px; border: 1px solid var(--color-border); border-radius: 16px; background: #fff; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; } -.avatar-preset-card:hover { border-color: var(--color-primary); box-shadow: 0 10px 20px rgba(41, 31, 22, 0.08); transform: translateY(-1px); } -.avatar-preset-card input { position: absolute; opacity: 0; pointer-events: none; } -.avatar-preset-card__thumb { display: block; aspect-ratio: 1; border-radius: 14px; overflow: hidden; background: linear-gradient(180deg, #f7efe6, #f0e4d7); border: 1px solid rgba(74, 52, 38, 0.08); } -.avatar-preset-card__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; } -.avatar-preset-card__label { font-size: 13px; font-weight: 700; color: var(--color-text); } -.avatar-preset-card:has(input:checked) { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(52,72,90,0.14); } -@media (max-width: 860px){ .avatar-builder__layout { grid-template-columns: 1fr; } .avatar-builder__preview { position: static; } .avatar-component-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } +.avatar-component-panel--eyes .avatar-component-card__thumb img, +.avatar-component-panel--eyebrows .avatar-component-card__thumb img { transform: scale(2.5) translateY(-14%); } +.avatar-component-panel--mouth .avatar-component-card__thumb img { transform: scale(2.45) translateY(14%); } +.avatar-component-panel--glasses .avatar-component-card__thumb img { transform: scale(2.25) translateY(-6%); } +.avatar-component-panel--hair .avatar-component-card__thumb img { transform: scale(2.2) translateY(-26%); } +.avatar-component-panel--beard .avatar-component-card__thumb img { transform: scale(2.3) translateY(8%); } +.avatar-component-panel--earrings .avatar-component-card__thumb img { transform: scale(2.15) translateY(-2%); } +@media (max-width: 860px){ .avatar-component-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } .avatar-swatch-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; } .avatar-swatch { display: grid; grid-template-columns: 20px 18px minmax(0, 1fr); gap: 10px; align-items: center; padding: 10px 12px; border: 1px solid var(--color-border); border-radius: 14px; background: #fff; cursor: pointer; } .avatar-swatch__color { width: 18px; height: 18px; border-radius: 999px; background: var(--swatch-color); border: 1px solid rgba(0,0,0,0.12); } diff --git a/public/assets/js/app.js b/public/assets/js/app.js index d82c8dd..f7391c2 100755 --- a/public/assets/js/app.js +++ b/public/assets/js/app.js @@ -763,8 +763,7 @@ document.addEventListener('DOMContentLoaded', () => { }; const updateAvatarOptionPreviews = (form) => { - const checked = form.querySelector('[data-avatar-field="avatar_preset"]:checked'); - const baseSeed = checked?.value || ''; + const baseSeed = readValue('avatar_preset'); if (!baseSeed) return; const currentValues = {}; @@ -794,10 +793,7 @@ document.addEventListener('DOMContentLoaded', () => { form.querySelectorAll('[data-avatar-option-thumb]').forEach((image) => { const field = image.getAttribute('data-avatar-option-field') || ''; const value = image.getAttribute('data-avatar-option-value') || ''; - const seed = field === 'avatar_preset' ? value : baseSeed; - if (!seed) return; - - const params = new URLSearchParams({ seed }); + const params = new URLSearchParams({ seed: baseSeed }); Object.entries(currentValues).forEach(([currentField, currentValue]) => { const param = fieldMap[currentField]; if (!param) return; @@ -879,24 +875,9 @@ document.addEventListener('DOMContentLoaded', () => { }; form.querySelector('[data-avatar-reroll]')?.addEventListener('click', () => { - const radios = Array.from(form.querySelectorAll('[data-avatar-field="avatar_preset"]')); - const used = new Set(); - radios.forEach((radio, index) => { - let seed = buildSeed(); - while (used.has(seed)) { - seed = buildSeed(); - } - used.add(seed); - const url = dicebearUrl(seed); - radio.value = seed; - radio.setAttribute('data-avatar-url', url); - radio.checked = index === 0; - const card = radio.closest('.avatar-preset-card'); - card?.querySelector('img')?.setAttribute('src', url); - card?.querySelector('img')?.setAttribute('alt', `Variante ${index + 1}`); - const label = card?.querySelector('.avatar-preset-card__label'); - if (label) label.textContent = index === 0 ? 'Neue Auswahl' : `Variante ${index + 1}`; - }); + const seedField = form.querySelector('[data-avatar-field="avatar_preset"]'); + if (!(seedField instanceof HTMLInputElement)) return; + seedField.value = buildSeed(); updateAvatarPreview(form); updateAvatarOptionPreviews(form); });