asdsad
This commit is contained in:
@@ -26,27 +26,39 @@ Wenn du in Entwicklung arbeitest:
|
||||
Wenn du in Produktion Theme-Caches aktiv hast:
|
||||
|
||||
1. Keycloak stoppen oder neu starten
|
||||
2. falls noetig Theme-Cache loeschen:
|
||||
2. falls nötig Theme-Cache löschen:
|
||||
- `data/tmp/kc-gzip-cache`
|
||||
|
||||
## 3. Theme nur fuer den Desktop-Client setzen
|
||||
## 3. Theme nur für den Desktop-Client setzen
|
||||
|
||||
In Keycloak:
|
||||
|
||||
1. Realm `KuscheBerlin` oeffnen
|
||||
2. Client `desktop-kusche-berlin` oeffnen
|
||||
3. Die Login-Theme-Override fuer den Client auf `kusche-desktop` setzen
|
||||
1. Realm `KuscheBerlin` öffnen
|
||||
2. Client `desktop-kusche-berlin` öffnen
|
||||
3. Die Login-Theme-Override für den Client auf `kusche-desktop` setzen
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Das Theme soll **nicht** pauschal als Realm-Login-Theme fuer alle Clients gesetzt werden, wenn andere Clients das Standard-Keycloak-Login behalten sollen.
|
||||
- Das Theme soll **nicht** pauschal als Realm-Login-Theme für alle Clients gesetzt werden, wenn andere Clients das Standard-Keycloak-Login behalten sollen.
|
||||
- Wenn die Texte automatisch nach Browser-Sprache wechseln sollen:
|
||||
- im Realm unter `Realm settings` die Internationalisierung aktivieren
|
||||
- `de` und `en` als erlaubte Sprachen hinterlegen
|
||||
- optional `de` als Standard-Sprache setzen
|
||||
- Wenn `Passwort vergessen?` sichtbar sein soll:
|
||||
- im Realm unter `Login` die Option `Forgot password` aktivieren
|
||||
- Wenn `Angemeldet bleiben` sichtbar sein soll:
|
||||
- im Realm unter `Login` die Option `Remember Me` aktivieren
|
||||
- Wenn ein Passwort-Reset auch wirklich ins angebundene LDAP zurückschreiben soll:
|
||||
- in der LDAP-User-Federation muss Passwort-Änderung erlaubt sein
|
||||
- das hängt von der Keycloak-LDAP-Konfiguration ab, nicht vom Theme selbst
|
||||
- falls LDAP nur read-only angebunden ist, kann Keycloak zwar den Reset-Flow anzeigen, aber das Passwort nicht ins LDAP zurückschreiben
|
||||
|
||||
Falls deine Keycloak-Version im Client-UI keine direkte Login-Theme-Auswahl anzeigt:
|
||||
|
||||
- pruefen, ob die Client-spezifische Login-Theme-Option in den Client-Einstellungen / Advanced Settings vorhanden ist
|
||||
- prüfen, ob die Client-spezifische Login-Theme-Option in den Client-Einstellungen / Advanced Settings vorhanden ist
|
||||
- falls nicht vorhanden, bleibt als saubere Alternative nur:
|
||||
- eigener Realm fuer Desktop
|
||||
- oder spaeter ein Theme Selector SPI
|
||||
- eigener Realm für Desktop
|
||||
- oder später ein Theme Selector SPI
|
||||
|
||||
## 4. Testen
|
||||
|
||||
@@ -57,12 +69,12 @@ Danach testen:
|
||||
|
||||
Erwartung:
|
||||
|
||||
- fuer den Client `desktop-kusche-berlin` erscheint das neue Desktop-Login-Theme
|
||||
- nach erfolgreichem Login geht es zur Desktop-Shell zurueck
|
||||
- für den Client `desktop-kusche-berlin` erscheint das neue Desktop-Login-Theme
|
||||
- nach erfolgreichem Login geht es zur Desktop-Shell zurück
|
||||
|
||||
## 5. Falls das Theme nicht erscheint
|
||||
|
||||
Dann diese Punkte pruefen:
|
||||
Dann diese Punkte prüfen:
|
||||
|
||||
- Theme-Ordner liegt wirklich im Keycloak-`themes/`-Verzeichnis
|
||||
- Verzeichnisname ist exakt `kusche-desktop`
|
||||
@@ -71,17 +83,17 @@ Dann diese Punkte pruefen:
|
||||
- Cache wurde geleert
|
||||
- wirklich der Client `desktop-kusche-berlin` wird verwendet
|
||||
|
||||
## 6. Welche Datei du spaeter bei Aenderungen ersetzt
|
||||
## 6. Welche Datei du später bei Änderungen ersetzt
|
||||
|
||||
Wenn du nur Styling aendern willst, reicht meist:
|
||||
Wenn du nur Styling ändern willst, reicht meist:
|
||||
|
||||
- `theme/kusche-desktop/login/resources/css/desktop-kusche.css`
|
||||
|
||||
Wenn du die Struktur aendern willst:
|
||||
Wenn du die Struktur ändern willst:
|
||||
|
||||
- `theme/kusche-desktop/login/login.ftl`
|
||||
|
||||
## 7. Was ich bewusst nicht fuer dich serverseitig tue
|
||||
## 7. Was ich bewusst nicht für dich serverseitig tue
|
||||
|
||||
Diese Schritte musst du selbst auf dem Keycloak-System machen:
|
||||
|
||||
|
||||
@@ -1,146 +1,121 @@
|
||||
<#import "template.ftl" as layout>
|
||||
<#import "passkeys.ftl" as passkeys>
|
||||
|
||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section>
|
||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=false; section>
|
||||
|
||||
<#if section = "header">
|
||||
${msg("loginAccountTitle")}
|
||||
Desktop-Anmeldung
|
||||
|
||||
<#elseif section = "form">
|
||||
<section class="kb-login-shell">
|
||||
<div class="kb-login-curtains" aria-hidden="true">
|
||||
<span class="kb-login-curtain kb-login-curtain-top kb-login-curtain-top-a"></span>
|
||||
<span class="kb-login-curtain kb-login-curtain-top kb-login-curtain-top-b"></span>
|
||||
<span class="kb-login-curtain kb-login-curtain-bottom kb-login-curtain-bottom-a"></span>
|
||||
<span class="kb-login-curtain kb-login-curtain-bottom kb-login-curtain-bottom-b"></span>
|
||||
</div>
|
||||
<div class="kb-login-ambient" aria-hidden="true"></div>
|
||||
<div class="kb-login-backdrop" aria-hidden="true"></div>
|
||||
|
||||
<div class="kb-login-panel">
|
||||
<div class="kb-login-copy">
|
||||
<p class="kb-login-kicker">Kusche.Berlin</p>
|
||||
<h1 class="kb-login-title">${msg("kbDesktopLoginHeadline")}</h1>
|
||||
<p class="kb-login-subtitle">${msg("kbDesktopLoginSubheadline")}</p>
|
||||
</div>
|
||||
<div class="kb-login-stage">
|
||||
<header class="kb-login-clock" aria-live="polite">
|
||||
<p class="kb-login-date" id="kb-login-date">${msg("kbClockDateFallback", "Montag, 18. November")}</p>
|
||||
<p class="kb-login-time" id="kb-login-time">${msg("kbClockTimeFallback", "4:20")}</p>
|
||||
</header>
|
||||
|
||||
<div id="kc-form" class="kb-login-form-shell">
|
||||
<div id="kc-form-wrapper">
|
||||
<#if realm.password>
|
||||
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
|
||||
<#if !usernameHidden??>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<label for="username" class="${properties.kcLabelClass!}">
|
||||
<#if !realm.loginWithEmailAllowed>
|
||||
${msg("username")}
|
||||
<#elseif !realm.registrationEmailAsUsername>
|
||||
${msg("usernameOrEmail")}
|
||||
<#else>
|
||||
${msg("email")}
|
||||
<div class="kb-login-card">
|
||||
<div class="kb-login-brand">
|
||||
<p class="kb-login-kicker">Kusche.Berlin</p>
|
||||
</div>
|
||||
|
||||
<div id="kc-form" class="kb-login-form-shell">
|
||||
<div id="kc-form-wrapper">
|
||||
<#if realm.password>
|
||||
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
|
||||
<#if !usernameHidden??>
|
||||
<div class="kb-form-group">
|
||||
<input tabindex="2" id="username" class="kb-text-input" name="username" value="${(login.username!'')}" type="text"
|
||||
autofocus autocomplete="${(enableWebAuthnConditionalUI?has_content)?then('username webauthn', 'username')}"
|
||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||
dir="ltr"
|
||||
placeholder="Benutzername oder E-Mail eingeben" />
|
||||
|
||||
<#if messagesPerField.existsError('username','password')>
|
||||
<span id="input-error" class="kb-input-error" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</label>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<input tabindex="2" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text"
|
||||
autofocus autocomplete="${(enableWebAuthnConditionalUI?has_content)?then('username webauthn', 'username')}"
|
||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||
dir="ltr" />
|
||||
<div class="kb-form-group">
|
||||
<div class="kb-password-row" dir="ltr">
|
||||
<input tabindex="3" id="password" class="kb-text-input kb-text-input-password" name="password" type="password" autocomplete="current-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||
placeholder="Passwort eingeben" />
|
||||
|
||||
<#if messagesPerField.existsError('username','password')>
|
||||
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
<button class="kb-password-toggle" type="button" aria-label="${msg("showPassword")}"
|
||||
aria-controls="password" data-password-toggle tabindex="4"
|
||||
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<span aria-hidden="true">◉</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
||||
<span id="input-error" class="kb-input-error" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
<div class="${properties.kcInputGroup!}" dir="ltr">
|
||||
<input tabindex="3" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="current-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>" />
|
||||
|
||||
<button class="${properties.kcFormPasswordVisibilityButtonClass!}" type="button" aria-label="${msg("showPassword")}"
|
||||
aria-controls="password" data-password-toggle tabindex="4"
|
||||
data-icon-show="${properties.kcFormPasswordVisibilityIconShow!}" data-icon-hide="${properties.kcFormPasswordVisibilityIconHide!}"
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="${properties.kcFormPasswordVisibilityIconShow!}" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
||||
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||
<div id="kc-form-options">
|
||||
<#if realm.rememberMe && !usernameHidden??>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<div class="kb-form-meta">
|
||||
<div class="kb-form-options" id="kc-form-options">
|
||||
<#if !usernameHidden??>
|
||||
<label class="kb-remember">
|
||||
<#if login.rememberMe??>
|
||||
<input tabindex="5" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
|
||||
<input tabindex="5" id="rememberMe" name="rememberMe" type="checkbox" checked>
|
||||
<#else>
|
||||
<input tabindex="5" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
|
||||
<input tabindex="5" id="rememberMe" name="rememberMe" type="checkbox">
|
||||
</#if>
|
||||
<span>Angemeldet bleiben</span>
|
||||
</label>
|
||||
</div>
|
||||
</#if>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="kb-form-links">
|
||||
<#if (url.loginResetCredentialsUrl!'')?has_content>
|
||||
<a tabindex="6" href="${url.loginResetCredentialsUrl}">Passwort vergessen?</a>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||
<#if realm.resetPasswordAllowed>
|
||||
<span><a tabindex="6" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||
</#if>
|
||||
<div id="kc-form-buttons" class="kb-form-submit">
|
||||
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
|
||||
<input tabindex="7" class="kb-submit-button" name="login" id="kc-login" type="submit" value="Anmelden"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
|
||||
<input tabindex="7" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||
</div>
|
||||
</form>
|
||||
</#if>
|
||||
<div id="kc-registration-container" class="kb-registration">
|
||||
<div id="kc-registration">
|
||||
<a class="kb-register-button" id="kb-register-button" tabindex="8" href="#" hidden>Registrieren</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="kb-login-info">
|
||||
<strong>Willkommen im Familienbereich</strong>
|
||||
<p>Dieser geschützte Dashboardbereich ist für die Familie Kusche und enge Freunde gedacht. Hier entstehen persönliche Übersichten, gemeinsame Tools, interne Informationen und weitere digitale Bereiche für Alltag, Organisation und Zusammenarbeit.</p>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<@passkeys.conditionalUIData />
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
<script src="${url.resourcesPath}/js/login-clock.js"></script>
|
||||
<script src="${url.resourcesPath}/js/desktop-register-link.js"></script>
|
||||
|
||||
<#elseif section = "info" >
|
||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||
<div id="kc-registration-container">
|
||||
<div id="kc-registration">
|
||||
<span>${msg("noAccount")} <a tabindex="8" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
<#-- Intentionally unused. Registration is rendered inside the form section. -->
|
||||
|
||||
<#elseif section = "socialProviders" >
|
||||
<#if realm.password && social?? && social.providers?has_content>
|
||||
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||
<hr/>
|
||||
<h2>${msg("identity-provider-login-label")}</h2>
|
||||
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||
<#list social.providers as p>
|
||||
<li>
|
||||
<a data-once-link data-disabled-class="${properties.kcFormSocialAccountListButtonDisabledClass!}" id="social-${p.alias}"
|
||||
class="${properties.kcFormSocialAccountListButtonClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
|
||||
type="button" href="${p.loginUrl}">
|
||||
<#if p.iconClasses?has_content>
|
||||
<i class="${properties.kcCommonLogoIdP!} ${p.iconClasses!}" aria-hidden="true"></i>
|
||||
<span class="${properties.kcFormSocialAccountNameClass!} kc-social-icon-text">${p.displayName!}</span>
|
||||
<#else>
|
||||
<span class="${properties.kcFormSocialAccountNameClass!}">${p.displayName!}</span>
|
||||
</#if>
|
||||
</a>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</div>
|
||||
</#if>
|
||||
<#-- Social logins are intentionally hidden for this theme. -->
|
||||
</#if>
|
||||
|
||||
</@layout.registrationLayout>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
kbDesktopLoginHeadline=Desktop Login
|
||||
kbDesktopLoginSubheadline=Melde dich mit deinem Kusche.Berlin Konto an, um auf Desktop, Apps und geschützte Inhalte zuzugreifen.
|
||||
kbUsernameLabel=Benutzername
|
||||
kbUsernameOrEmailLabel=Benutzername oder E-Mail
|
||||
kbEmailLabel=E-Mail
|
||||
kbPasswordLabel=Passwort
|
||||
kbUsernamePlaceholder=Benutzername oder E-Mail eingeben
|
||||
kbPasswordPlaceholder=Passwort eingeben
|
||||
kbRememberMeLabel=Angemeldet bleiben
|
||||
kbForgotPasswordLabel=Passwort vergessen?
|
||||
kbLoginButtonLabel=Anmelden
|
||||
kbRegisterButtonLabel=Registrieren
|
||||
kbClockDateFallback=Montag, 18. November
|
||||
kbClockTimeFallback=4:20
|
||||
kbInfoTitle=Willkommen im Familienbereich
|
||||
kbInfoBody=Dieser geschützte Dashboardbereich ist für die Familie Kusche und enge Freunde gedacht. Hier entstehen persönliche Übersichten, gemeinsame Tools, interne Informationen und künftig weitere digitale Bereiche für Alltag, Organisation und Zusammenarbeit.
|
||||
@@ -1,2 +1,16 @@
|
||||
kbDesktopLoginHeadline=Desktop Login
|
||||
kbDesktopLoginSubheadline=Geschuetzter Zugang zur Desktop-Shell und kuenftigen Modulen.
|
||||
kbDesktopLoginSubheadline=Melde dich mit deinem Kusche.Berlin Konto an, um auf Desktop, Apps und geschützte Inhalte zuzugreifen.
|
||||
kbUsernameLabel=Benutzername
|
||||
kbUsernameOrEmailLabel=Benutzername oder E-Mail
|
||||
kbEmailLabel=E-Mail
|
||||
kbPasswordLabel=Passwort
|
||||
kbUsernamePlaceholder=Benutzername oder E-Mail eingeben
|
||||
kbPasswordPlaceholder=Passwort eingeben
|
||||
kbRememberMeLabel=Angemeldet bleiben
|
||||
kbForgotPasswordLabel=Passwort vergessen?
|
||||
kbLoginButtonLabel=Anmelden
|
||||
kbRegisterButtonLabel=Registrieren
|
||||
kbClockDateFallback=Montag, 18. November
|
||||
kbClockTimeFallback=4:20
|
||||
kbInfoTitle=Willkommen im Familienbereich
|
||||
kbInfoBody=Dieser geschützte Dashboardbereich ist für die Familie Kusche und enge Freunde gedacht. Hier entstehen persönliche Übersichten, gemeinsame Tools, interne Informationen und künftig weitere digitale Bereiche für Alltag, Organisation und Zusammenarbeit.
|
||||
|
||||
@@ -1,2 +1,16 @@
|
||||
kbDesktopLoginHeadline=Desktop Login
|
||||
kbDesktopLoginSubheadline=Protected access to the desktop shell and upcoming modules.
|
||||
kbDesktopLoginSubheadline=Sign in with your Kusche.Berlin account to access the desktop, apps and protected content.
|
||||
kbUsernameLabel=Username
|
||||
kbUsernameOrEmailLabel=Username or email
|
||||
kbEmailLabel=Email
|
||||
kbPasswordLabel=Password
|
||||
kbUsernamePlaceholder=Enter your username or email
|
||||
kbPasswordPlaceholder=Enter your password
|
||||
kbRememberMeLabel=Keep me signed in
|
||||
kbForgotPasswordLabel=Forgot password?
|
||||
kbLoginButtonLabel=Sign in
|
||||
kbRegisterButtonLabel=Register
|
||||
kbClockDateFallback=Monday, November 18
|
||||
kbClockTimeFallback=4:20
|
||||
kbInfoTitle=Welcome to the family area
|
||||
kbInfoBody=This protected dashboard area is intended for the Kusche family and close friends. It will host personal overviews, shared tools, internal information and additional digital spaces for daily life, organization and collaboration.
|
||||
|
||||
@@ -1,307 +1,339 @@
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(255, 221, 177, 0.30), transparent 26%),
|
||||
radial-gradient(circle at 80% 18%, rgba(132, 216, 166, 0.18), transparent 20%),
|
||||
linear-gradient(145deg, #101827 0%, #18253d 52%, #0f172a 100%) !important;
|
||||
radial-gradient(circle at 18% 84%, rgba(142, 214, 119, 0.22), transparent 18%),
|
||||
linear-gradient(0deg, rgba(14, 27, 19, 0.28), rgba(14, 27, 19, 0.28)),
|
||||
linear-gradient(135deg, #26442b 0%, #355939 26%, #5f4a2f 58%, #22351f 100%) !important;
|
||||
color: #f8fafc;
|
||||
font-family: "Roboto", "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
|
||||
body > div,
|
||||
body > main {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#kc-header-wrapper,
|
||||
#kc-header,
|
||||
.login-pf-header,
|
||||
#kc-page-title {
|
||||
#kc-page-title,
|
||||
#kc-locale,
|
||||
#kc-social-providers {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#kc-content,
|
||||
#kc-content-wrapper,
|
||||
#kc-locale {
|
||||
#kc-content,
|
||||
#kc-form,
|
||||
#kc-form-wrapper,
|
||||
.login-pf-page,
|
||||
.card-pf,
|
||||
.pf-v5-c-login,
|
||||
.pf-v5-c-login__container,
|
||||
.pf-v5-c-login__main,
|
||||
.pf-v5-c-login__main-body,
|
||||
.pf-v5-c-login__main-header,
|
||||
.pf-v5-c-login__main-footer {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
max-width: none !important;
|
||||
width: 100% !important;
|
||||
border: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#kc-content-wrapper,
|
||||
#kc-content,
|
||||
.login-pf-page,
|
||||
.pf-v5-c-login,
|
||||
.pf-v5-c-login__container,
|
||||
.pf-v5-c-login__main,
|
||||
.pf-v5-c-login__main-body {
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
|
||||
#kc-content,
|
||||
#kc-content-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#kc-content-wrapper {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
#kc-form-login .pf-v5-c-form__label-text,
|
||||
#kc-form-login label,
|
||||
#kc-form-options,
|
||||
#kc-form-options a,
|
||||
#kc-registration,
|
||||
#kc-registration a,
|
||||
#kc-social-providers h2,
|
||||
#kc-social-providers a {
|
||||
color: #e2e8f0 !important;
|
||||
.pf-v5-c-alert,
|
||||
.alert,
|
||||
#kc-info-message,
|
||||
#kc-error-message {
|
||||
margin: 0 0 14px !important;
|
||||
border: 0 !important;
|
||||
border-radius: 12px !important;
|
||||
background: rgba(127, 29, 29, 0.24) !important;
|
||||
color: #fee2e2 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.kb-login-shell {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-height: calc(100vh - 80px);
|
||||
padding: 24px;
|
||||
min-height: 100vh;
|
||||
padding: 24px 20px 30px;
|
||||
}
|
||||
|
||||
.kb-login-curtains {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.kb-login-curtain {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 180vmax;
|
||||
height: 180vmax;
|
||||
opacity: 0.72;
|
||||
border-radius: 999px;
|
||||
transform-origin: 0 50%;
|
||||
animation-duration: 1.1s;
|
||||
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.kb-login-curtain-top,
|
||||
.kb-login-curtain-bottom {
|
||||
top: 50%;
|
||||
margin-top: -90vmax;
|
||||
}
|
||||
|
||||
.kb-login-curtain-top-a {
|
||||
background: linear-gradient(135deg, rgba(236, 101, 105, 0.84), rgba(238, 175, 129, 0.76));
|
||||
animation-name: kbLoginCurtainTopA;
|
||||
}
|
||||
|
||||
.kb-login-curtain-top-b {
|
||||
background: linear-gradient(135deg, rgba(255, 196, 104, 0.66), rgba(255, 138, 76, 0.6));
|
||||
animation-name: kbLoginCurtainTopB;
|
||||
animation-delay: 0.08s;
|
||||
}
|
||||
|
||||
.kb-login-curtain-bottom-a {
|
||||
background: linear-gradient(135deg, rgba(96, 184, 212, 0.7), rgba(55, 69, 181, 0.82));
|
||||
animation-name: kbLoginCurtainBottomA;
|
||||
animation-delay: 0.12s;
|
||||
}
|
||||
|
||||
.kb-login-curtain-bottom-b {
|
||||
background: linear-gradient(135deg, rgba(72, 125, 255, 0.5), rgba(36, 74, 179, 0.76));
|
||||
animation-name: kbLoginCurtainBottomB;
|
||||
animation-delay: 0.18s;
|
||||
}
|
||||
|
||||
.kb-login-ambient {
|
||||
.kb-login-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 50% 10%, rgba(255, 255, 255, 0.12), transparent 30%),
|
||||
url("../img/desktop-glow.svg") center/cover no-repeat;
|
||||
opacity: 0.9;
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.10), transparent 16%),
|
||||
radial-gradient(circle at center, transparent 24%, rgba(7, 13, 8, 0.18) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.kb-login-panel {
|
||||
.kb-login-stage {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 22px;
|
||||
width: min(540px, 100%);
|
||||
padding: 28px;
|
||||
border-radius: 28px;
|
||||
background: rgba(15, 23, 42, 0.62);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
box-shadow: 0 24px 80px rgba(2, 6, 23, 0.42);
|
||||
backdrop-filter: blur(24px);
|
||||
opacity: 0;
|
||||
transform: translateY(28px) scale(0.98);
|
||||
animation: kbLoginPanelReveal 0.72s cubic-bezier(0.22, 1, 0.36, 1) 0.35s forwards;
|
||||
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
|
||||
justify-items: center;
|
||||
align-content: start;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.kb-login-panel:hover,
|
||||
.kb-login-panel:focus-within {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 30px 90px rgba(2, 6, 23, 0.5);
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
.kb-login-clock {
|
||||
margin-top: 22px;
|
||||
text-align: center;
|
||||
color: #f8fafc;
|
||||
text-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.kb-login-copy {
|
||||
.kb-login-date {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.01em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.kb-login-time {
|
||||
margin: 0;
|
||||
font-size: clamp(3.8rem, 8vw, 5.6rem);
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.kb-login-card {
|
||||
width: min(420px, 100%);
|
||||
margin-top: 148px;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.kb-login-brand {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kb-login-kicker {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.18em;
|
||||
font-size: 0.76rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: #cbd5e1;
|
||||
color: rgba(248, 250, 252, 0.84);
|
||||
}
|
||||
|
||||
.kb-login-title {
|
||||
.kb-login-form-shell,
|
||||
#kc-form-wrapper,
|
||||
#kc-form {
|
||||
margin: 0;
|
||||
font-size: clamp(30px, 4vw, 40px);
|
||||
line-height: 1.04;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.kb-login-subtitle {
|
||||
margin: 0;
|
||||
color: #dbe5f1;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.kb-login-form-shell {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#kc-form-wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#kc-form-login input[type="text"],
|
||||
#kc-form-login input[type="password"] {
|
||||
#kc-form-login {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.kb-form-group {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.kb-text-input {
|
||||
width: 100%;
|
||||
min-height: 48px;
|
||||
border-radius: 14px !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
padding: 0 18px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.10);
|
||||
border-radius: 999px;
|
||||
background: rgba(213, 223, 193, 0.54) !important;
|
||||
color: #f8fafc !important;
|
||||
box-shadow: none !important;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08) !important;
|
||||
outline: none;
|
||||
font-size: 1rem;
|
||||
backdrop-filter: blur(9px);
|
||||
transition: background-color 160ms ease, border-color 160ms ease;
|
||||
}
|
||||
|
||||
#kc-form-login input::placeholder {
|
||||
color: rgba(226, 232, 240, 0.72);
|
||||
.kb-text-input:focus {
|
||||
border-color: rgba(255, 255, 255, 0.26);
|
||||
background: rgba(213, 223, 193, 0.62) !important;
|
||||
}
|
||||
|
||||
#kc-form-login .pf-v5-c-button,
|
||||
#kc-login {
|
||||
min-height: 50px;
|
||||
border: 0 !important;
|
||||
border-radius: 14px !important;
|
||||
background: linear-gradient(180deg, #a7f3d0, #6ee7b7) !important;
|
||||
color: #082032 !important;
|
||||
.kb-text-input::placeholder {
|
||||
color: rgba(248, 250, 252, 0.82);
|
||||
}
|
||||
|
||||
.kb-password-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.kb-text-input-password {
|
||||
padding-right: 56px;
|
||||
}
|
||||
|
||||
.kb-password-toggle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 10px;
|
||||
transform: translateY(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
color: rgba(248, 250, 252, 0.88);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kb-input-error {
|
||||
padding: 0 10px;
|
||||
font-size: 0.82rem;
|
||||
color: #fee2e2;
|
||||
}
|
||||
|
||||
.kb-form-meta {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.kb-remember {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: rgba(248, 250, 252, 0.94);
|
||||
font-size: 0.88rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kb-remember input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
accent-color: #93c5fd;
|
||||
}
|
||||
|
||||
.kb-form-links a {
|
||||
color: rgba(248, 250, 252, 0.94) !important;
|
||||
text-decoration: none;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.kb-form-links a:hover,
|
||||
.kb-registration a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.kb-form-submit {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.kb-submit-button,
|
||||
.kb-register-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 48px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
font-size: 0.98rem;
|
||||
font-weight: 700;
|
||||
box-shadow: none !important;
|
||||
transition: transform 160ms ease, filter 160ms ease, background-color 160ms ease, border-color 160ms ease;
|
||||
letter-spacing: 0.01em;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: transform 160ms ease, filter 160ms ease;
|
||||
}
|
||||
|
||||
#kc-form-login .pf-v5-c-button:hover,
|
||||
#kc-login:hover {
|
||||
.kb-submit-button {
|
||||
background: linear-gradient(180deg, #7dd3fc, #0ea5e9) !important;
|
||||
color: #082f49 !important;
|
||||
box-shadow: 0 14px 30px rgba(14, 165, 233, 0.22);
|
||||
}
|
||||
|
||||
.kb-register-button {
|
||||
background: rgba(248, 250, 252, 0.18);
|
||||
color: #f8fafc !important;
|
||||
border: 1px solid rgba(248, 250, 252, 0.16);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.kb-submit-button:hover,
|
||||
.kb-register-button:hover {
|
||||
transform: translateY(-1px);
|
||||
filter: brightness(1.03);
|
||||
}
|
||||
|
||||
#kc-form-options {
|
||||
padding-top: 4px;
|
||||
.kb-login-info {
|
||||
padding: 16px 18px;
|
||||
border-radius: 18px;
|
||||
background: rgba(9, 18, 12, 0.34);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: rgba(248, 250, 252, 0.94);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
#kc-form-options .checkbox label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
.kb-login-info strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
#kc-social-providers {
|
||||
margin-top: 10px;
|
||||
.kb-login-info p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#kc-social-providers hr {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
#kc-registration {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.pf-v5-c-alert {
|
||||
border-radius: 14px !important;
|
||||
}
|
||||
|
||||
@keyframes kbLoginPanelReveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(28px) scale(0.98);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kbLoginCurtainTopA {
|
||||
from {
|
||||
transform: rotate(46deg) translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(46deg) translateX(22vmax);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kbLoginCurtainTopB {
|
||||
from {
|
||||
transform: rotate(136deg) translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(136deg) translateX(18vmax);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kbLoginCurtainBottomA {
|
||||
from {
|
||||
transform: rotate(-44deg) translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(-44deg) translateX(22vmax);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kbLoginCurtainBottomB {
|
||||
from {
|
||||
transform: rotate(-134deg) translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(-134deg) translateX(18vmax);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.kb-login-curtain,
|
||||
.kb-login-panel {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.kb-login-panel {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
.kb-registration {
|
||||
width: min(420px, 100%);
|
||||
margin: 14px auto 0;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
#kc-content-wrapper {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.kb-login-shell {
|
||||
min-height: auto;
|
||||
padding: 16px;
|
||||
padding: 18px 14px 24px;
|
||||
}
|
||||
|
||||
.kb-login-panel {
|
||||
padding: 22px 18px;
|
||||
border-radius: 22px;
|
||||
.kb-login-clock {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.kb-login-time {
|
||||
font-size: 3.7rem;
|
||||
}
|
||||
|
||||
.kb-login-card {
|
||||
margin-top: 88px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
(function () {
|
||||
const registerLink = document.getElementById('kb-register-button');
|
||||
|
||||
if (!registerLink) {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const redirectUri = params.get('redirect_uri');
|
||||
|
||||
if (!redirectUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const target = new URL(redirectUri);
|
||||
registerLink.href = target.origin + '/auth/register';
|
||||
registerLink.hidden = false;
|
||||
} catch (error) {
|
||||
console.warn('Registrierungslink konnte nicht vorbereitet werden.', error);
|
||||
}
|
||||
}());
|
||||
@@ -0,0 +1,26 @@
|
||||
(function () {
|
||||
const dateNode = document.getElementById('kb-login-date');
|
||||
const timeNode = document.getElementById('kb-login-time');
|
||||
|
||||
if (!dateNode || !timeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderClock = () => {
|
||||
const now = new Date();
|
||||
|
||||
dateNode.textContent = new Intl.DateTimeFormat(document.documentElement.lang || 'de', {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}).format(now);
|
||||
|
||||
timeNode.textContent = new Intl.DateTimeFormat(document.documentElement.lang || 'de', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
}).format(now);
|
||||
};
|
||||
|
||||
renderClock();
|
||||
window.setInterval(renderClock, 1000);
|
||||
}());
|
||||
Reference in New Issue
Block a user