- Custom theme under themes/ecosplay/login (extends keycloak parent) with template.ftl and login.ftl matching the e-cosplay.fr style: thick black borders, hard offset shadows, italic uppercase, indigo accent, hover translate effect, marquee header, watermark. - Tailwind via Play CDN for utility classes (no build step). - Mount the theme dir read-only into the Keycloak container. - Init container now also sets loginTheme=ecosplay on master realm alongside the SMTP config; service renamed keycloak-init. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
96 lines
6.3 KiB
Plaintext
96 lines
6.3 KiB
Plaintext
<#import "template.ftl" as layout>
|
|
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section>
|
|
<#if section = "header">
|
|
${msg("loginAccountTitle")}
|
|
<#elseif section = "form">
|
|
<#if realm.password>
|
|
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post" class="space-y-6">
|
|
|
|
<#if !usernameHidden??>
|
|
<div>
|
|
<label for="username" class="block font-black uppercase tracking-widest text-xs mb-2 text-gray-900">
|
|
<#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if>
|
|
</label>
|
|
<input tabindex="1" id="username" name="username" type="text" autofocus autocomplete="username"
|
|
value="${(login.username!'')}"
|
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
|
class="w-full px-4 py-4 bg-white text-gray-900 font-bold not-italic border-4 border-gray-900 placeholder-gray-400 focus:outline-none focus:bg-yellow-100 focus:shadow-[6px_6px_0px_rgba(79,70,229,1)] transition-all" />
|
|
<#if messagesPerField.existsError('username','password')>
|
|
<span class="block mt-2 text-red-600 font-black uppercase text-xs tracking-widest" aria-live="polite">
|
|
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
|
</span>
|
|
</#if>
|
|
</div>
|
|
</#if>
|
|
|
|
<div>
|
|
<label for="password" class="block font-black uppercase tracking-widest text-xs mb-2 text-gray-900">
|
|
${msg("password")}
|
|
</label>
|
|
<div class="relative">
|
|
<input tabindex="2" id="password" name="password" type="password" autocomplete="current-password"
|
|
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
|
class="w-full px-4 py-4 bg-white text-gray-900 font-bold not-italic border-4 border-gray-900 placeholder-gray-400 focus:outline-none focus:bg-yellow-100 focus:shadow-[6px_6px_0px_rgba(79,70,229,1)] transition-all" />
|
|
</div>
|
|
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
|
<span class="block mt-2 text-red-600 font-black uppercase text-xs tracking-widest" aria-live="polite">
|
|
${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc}
|
|
</span>
|
|
</#if>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between gap-4 flex-wrap pt-2">
|
|
<#if realm.rememberMe && !usernameHidden??>
|
|
<label for="rememberMe" class="flex items-center gap-3 font-black uppercase text-xs tracking-widest cursor-pointer">
|
|
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"
|
|
<#if login.rememberMe??>checked</#if>
|
|
class="w-5 h-5 border-4 border-gray-900 accent-indigo-600">
|
|
${msg("rememberMe")}
|
|
</label>
|
|
</#if>
|
|
<#if realm.resetPasswordAllowed>
|
|
<a tabindex="5" href="${url.loginResetCredentialsUrl}"
|
|
class="font-black uppercase text-xs tracking-widest text-indigo-600 hover:text-gray-900 underline decoration-4 underline-offset-4">
|
|
${msg("doForgotPassword")}
|
|
</a>
|
|
</#if>
|
|
</div>
|
|
|
|
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
|
|
|
|
<button tabindex="4" name="login" id="kc-login" type="submit"
|
|
class="block w-full px-8 py-5 bg-indigo-600 text-white font-black uppercase italic tracking-widest text-lg border-4 border-gray-900 shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-2 hover:translate-y-2 transition-all">
|
|
${msg("doLogIn")}
|
|
</button>
|
|
</form>
|
|
</#if>
|
|
|
|
<#elseif section = "info">
|
|
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
|
<p class="text-sm">
|
|
<span class="font-bold uppercase tracking-tight text-gray-700">${msg("noAccount")}</span>
|
|
<a tabindex="6" href="${url.registrationUrl}"
|
|
class="ml-2 font-black uppercase text-indigo-600 hover:text-gray-900 underline decoration-4 underline-offset-4">
|
|
${msg("doRegister")}
|
|
</a>
|
|
</p>
|
|
</#if>
|
|
|
|
<#elseif section = "socialProviders">
|
|
<#if realm.password && social?? && social.providers?? && social.providers?has_content>
|
|
<div class="mt-8 pt-6 border-t-4 border-gray-900">
|
|
<p class="text-indigo-600 font-black uppercase tracking-[0.3em] mb-4 text-xs">// ${msg("identity-provider-login-label")}</p>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
<#list social.providers as p>
|
|
<a id="social-${p.alias}" href="${p.loginUrl}"
|
|
class="flex items-center justify-center gap-2 px-4 py-3 bg-white text-gray-900 font-black uppercase tracking-widest text-xs border-4 border-gray-900 shadow-[6px_6px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-1 hover:translate-y-1 transition-all">
|
|
<#if p.iconClasses?has_content><i class="${p.iconClasses!}" aria-hidden="true"></i></#if>
|
|
<span>${p.displayName!}</span>
|
|
</a>
|
|
</#list>
|
|
</div>
|
|
</div>
|
|
</#if>
|
|
</#if>
|
|
</@layout.registrationLayout>
|