Improve keycloak theming
This commit is contained in:
parent
3c8ef7c3d2
commit
cac941a300
0
.dev-import/.gitkeep
Normal file
0
.dev-import/.gitkeep
Normal file
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
tags
|
||||
.direnv
|
||||
common/resources/css/*.css
|
||||
.dev-import/*.json
|
||||
|
|
33
README.md
33
README.md
|
@ -1,14 +1,25 @@
|
|||
# pub.solar Keycloak theme
|
||||
|
||||
To start a dev keycloak instance that can show the theme.
|
||||
|
||||
```
|
||||
docker run \
|
||||
--name keycloak-theme-dev \
|
||||
-p 8080:8080 \
|
||||
-e KEYCLOAK_ADMIN=admin \
|
||||
-e KEYCLOAK_ADMIN_PASSWORD=admin \
|
||||
-v $(pwd):/opt/keycloak/themes/pub.solar \
|
||||
quay.io/keycloak/keycloak:20.0.1 \
|
||||
start-dev
|
||||
```
|
||||
## Development setup
|
||||
|
||||
To start a dev keycloak instance that can show the theme, you need to do the following:
|
||||
|
||||
1. Go into Keycloak, and export the `pub.solar` realm config. In Keycloak, open the pub.solar realm, click on the menu item "Realm settings", open the dropdown "Action", and click "Partial export". Move the generated JSON file into `./.dev-imports` in this repo.
|
||||
|
||||
2. Run the following command:
|
||||
```
|
||||
docker run \
|
||||
--name keycloak-theme-dev \
|
||||
-p 8080:8080 \
|
||||
-e KEYCLOAK_ADMIN=admin \
|
||||
-e KEYCLOAK_ADMIN_PASSWORD=admin \
|
||||
-v $(pwd):/opt/keycloak/themes/pub.solar \
|
||||
-v $(pwd)/.dev-import:/opt/keycloak/data/import \
|
||||
quay.io/keycloak/keycloak:20.0.1 \
|
||||
start-dev --import-realm
|
||||
```
|
||||
|
||||
3. After this, you can start and stop the container using `docker start keycloak-theme-dev` and `docker-stop keycloak-theme-dev`.
|
||||
|
||||
4. Connect to the local keycloak instance at `http://localhost:8080` and open the administration console. In the `pub.solar` realm, click on the menu item "Clients", then the client "account", "Advanced" tab, "Authentication flow overrides" section, select "Webauthn Browser" for the "Browser Flow". Press save.
|
||||
|
|
|
@ -57,22 +57,22 @@
|
|||
<main class="ps-main--page ps-page">
|
||||
<header class="ps-page--header ps-header">
|
||||
<a href="https://pub.solar/" class="ps-homelink">pub.solar/</a>
|
||||
|
||||
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
|
||||
<ul class="ps-i18n-links">
|
||||
<#list locale.supported as l>
|
||||
<li class="ps-i18n-links--item">
|
||||
<a
|
||||
class="ps-i18n-links--link <#if locale.current == l.label >ps-i18n-links--link_active</#if>"
|
||||
href="${l.url}"
|
||||
>${l.label}</a>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</#if>
|
||||
|
||||
<nav class="ps-header--nav" role="navigation">
|
||||
<ul class="ps-header--nav-list">
|
||||
<#if realm.internationalizationEnabled>
|
||||
<li class="ps-header--nav-link">
|
||||
<div class="kc-dropdown" id="kc-locale-dropdown">
|
||||
<a href="#" id="kc-current-locale-link">${locale.current}</a>
|
||||
<ul>
|
||||
<#list locale.supported as l>
|
||||
<li class="kc-dropdown-item">
|
||||
<a class="ps-link" href="${l.url}">${l.label}</a>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</div>
|
||||
<li>
|
||||
</#if>
|
||||
<#if referrer?has_content && referrer.url?has_content>
|
||||
<li class="ps-header--nav-item">
|
||||
<a
|
||||
|
|
|
@ -121,6 +121,10 @@ html {
|
|||
font-weight: bold; }
|
||||
.ps-form-group .ps-button {
|
||||
align-self: flex-start; }
|
||||
.ps-form-group--error {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--accent);
|
||||
font-weight: bold; }
|
||||
|
||||
.ps-homelink {
|
||||
position: fixed;
|
||||
|
@ -147,6 +151,41 @@ html {
|
|||
.ps-homelink {
|
||||
font-size: 32px;
|
||||
padding: 12px; } }
|
||||
.ps-i18n-links {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
margin: 0; }
|
||||
.ps-i18n-links--item {
|
||||
display: flex; }
|
||||
.ps-i18n-links--link {
|
||||
pointer-events: all;
|
||||
color: var(--foreground);
|
||||
background: white;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
font-size: 24px;
|
||||
padding: 8px;
|
||||
line-height: 1em;
|
||||
text-shadow: 0.15vw 0px 0px var(--background);
|
||||
transition: text-shadow 0.1s ease;
|
||||
border: 12px solid var(--foreground);
|
||||
border-top: 0;
|
||||
border-right: 0; }
|
||||
.ps-i18n-links--link:hover {
|
||||
text-shadow: 0.3vw 0px 0px var(--accent); }
|
||||
@media screen and (min-width: 1200px) {
|
||||
.ps-i18n-links--link {
|
||||
font-size: 32px;
|
||||
padding: 12px; } }
|
||||
.ps-i18n-links--link_active {
|
||||
background-color: var(--foreground);
|
||||
color: var(--background);
|
||||
text-shadow: 0.15vw 0px 0px var(--foreground); }
|
||||
|
||||
.ps-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -170,9 +209,6 @@ html {
|
|||
z-index: 1; }
|
||||
.ps-main_full {
|
||||
padding: 0; }
|
||||
.ps-main_full .ps-main--page {
|
||||
overflow: hidden;
|
||||
height: 100vh; }
|
||||
|
||||
.ps-header {
|
||||
display: flex;
|
||||
|
@ -233,13 +269,17 @@ html {
|
|||
right: 0;
|
||||
height: auto;
|
||||
position: fixed; }
|
||||
.ps-page--title {
|
||||
font-size: 2rem;
|
||||
border-bottom: 0.5rem solid var(--foreground);
|
||||
padding-bottom: 0.5rem;
|
||||
margin: 2rem; }
|
||||
.ps-page--section {
|
||||
border: 12px solid black;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
max-width: 700px;
|
||||
flex-basis: 100%;
|
||||
font-size: 16px;
|
||||
flex-shrink: 1;
|
||||
pointer-events: all;
|
||||
color: var(--foreground);
|
||||
|
@ -255,9 +295,7 @@ html {
|
|||
.ps-page--section_full {
|
||||
max-width: unset;
|
||||
width: calc(100% - 8rem);
|
||||
margin: 4rem;
|
||||
height: calc(100vh - 8rem);
|
||||
overflow: auto; }
|
||||
margin: 4rem; }
|
||||
.ps-page--section a {
|
||||
color: var(--accent);
|
||||
border-bottom: 1px solid transparent;
|
||||
|
@ -305,8 +343,8 @@ html {
|
|||
margin-top: 1rem; }
|
||||
.ps-page--section-contents > .ps-table + * {
|
||||
margin-top: 1rem; }
|
||||
.ps-page--section-contents > h1, .ps-page--section-contents h2 {
|
||||
margin-top: 2rem;
|
||||
.ps-page--section-contents > h2, .ps-page--section-contents h3, .ps-page--section-contents h4 {
|
||||
margin-top: 1.5rem;
|
||||
line-height: 1.5; }
|
||||
|
||||
.ps-section-nav {
|
||||
|
|
|
@ -11,4 +11,11 @@
|
|||
.ps-button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
&--error {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--accent);
|
||||
font-weight: bold;
|
||||
// font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
|
44
common/resources/scss/i18n-links.scss
Normal file
44
common/resources/scss/i18n-links.scss
Normal file
|
@ -0,0 +1,44 @@
|
|||
.ps-i18n-links {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
margin: 0;
|
||||
|
||||
&--item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&--link {
|
||||
pointer-events: all;
|
||||
color: var(--foreground);
|
||||
background: white;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
font-size: 24px;
|
||||
padding: 8px;
|
||||
line-height: 1em;
|
||||
text-shadow: 0.15vw 0px 0px var(--background);
|
||||
transition: text-shadow 0.1s ease;
|
||||
border: 12px solid var(--foreground);
|
||||
border-top: 0;
|
||||
border-right: 0;
|
||||
|
||||
&:hover {
|
||||
text-shadow: 0.3vw 0px 0px var(--accent);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
font-size: 32px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
&_active {
|
||||
background-color: var(--foreground);
|
||||
color: var(--background);
|
||||
text-shadow: 0.15vw 0px 0px var(--foreground);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ html {
|
|||
@import './form-group';
|
||||
|
||||
@import './homelink';
|
||||
@import './i18n-links';
|
||||
@import './main';
|
||||
@import './header';
|
||||
@import './page';
|
||||
|
|
|
@ -27,9 +27,4 @@
|
|||
&_full {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&_full &--page {
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,19 @@
|
|||
position: fixed;
|
||||
}
|
||||
|
||||
&--title {
|
||||
font-size: 2rem;
|
||||
border-bottom: 0.5rem solid var(--foreground);
|
||||
padding-bottom: 0.5rem;
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
&--section {
|
||||
border: 12px solid black;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
max-width: 700px;
|
||||
flex-basis: 100%;
|
||||
font-size: 16px;
|
||||
flex-shrink: 1;
|
||||
pointer-events: all;
|
||||
color: var(--foreground);
|
||||
|
@ -54,8 +60,6 @@
|
|||
max-width: unset;
|
||||
width: calc(100% - 8rem);
|
||||
margin: 4rem;
|
||||
height: calc(100vh - 8rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -130,8 +134,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
> h1, h2 {
|
||||
margin-top: 2rem;
|
||||
> h2, h3, h4 {
|
||||
margin-top: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,55 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('password'); section>
|
||||
<#if section = "header">
|
||||
${msg("doLogIn")}
|
||||
<#elseif section = "form">
|
||||
<div id="kc-form">
|
||||
<div id="kc-form-wrapper">
|
||||
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}"
|
||||
method="post">
|
||||
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||
<hr/>
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
|
||||
type="password" autocomplete="on" autofocus
|
||||
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
<#if section = "header">
|
||||
${msg("doLogIn")}
|
||||
<#elseif section = "form">
|
||||
<form
|
||||
id="kc-form-login"
|
||||
class="ps-container"
|
||||
onsubmit="login.disabled = true; return true;"
|
||||
action="${url.loginAction}"
|
||||
method="post"
|
||||
>
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
<input
|
||||
tabindex="2"
|
||||
id="password"
|
||||
class="${properties.kcInputClass!}"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="on"
|
||||
autofocus
|
||||
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
||||
/>
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||
<div id="kc-form-options">
|
||||
</div>
|
||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
||||
<#if realm.resetPasswordAllowed>
|
||||
<span><a tabindex="5"
|
||||
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||
<#if realm.resetPasswordAllowed>
|
||||
<span><a tabindex="5"
|
||||
href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<button
|
||||
tabindex="4"
|
||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||
name="login"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
>
|
||||
${msg("doLogIn")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -1,87 +1,111 @@
|
|||
<#import "template.ftl" as layout>
|
||||
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username') displayInfo=(realm.password && realm.registrationAllowed && !registrationDisabled??); section>
|
||||
<#if section = "header">
|
||||
${msg("loginAccountTitle")}
|
||||
<#elseif section = "form">
|
||||
<div id="kc-form">
|
||||
<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")}</#if></label>
|
||||
<#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="ps-container"
|
||||
>
|
||||
<#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")}</#if>
|
||||
</label>
|
||||
|
||||
<input tabindex="1" id="username"
|
||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||
class="${properties.kcInputClass!}" name="username"
|
||||
value="${(login.username!'')}"
|
||||
type="text" autofocus autocomplete="off"/>
|
||||
<input
|
||||
tabindex="1"
|
||||
id="username"
|
||||
aria-invalid="<#if messagesPerField.existsError('username')>true</#if>"
|
||||
class="${properties.kcInputClass!}"
|
||||
name="username"
|
||||
value="${(login.username!'')}"
|
||||
type="text"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<#if messagesPerField.existsError('username')>
|
||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
<#if messagesPerField.existsError('username')>
|
||||
<span id="input-error-username" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('username'))?no_esc}
|
||||
</span>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||
<div id="kc-form-options">
|
||||
<#if realm.rememberMe && !usernameHidden??>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<#if login.rememberMe??>
|
||||
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"
|
||||
checked> ${msg("rememberMe")}
|
||||
<#else>
|
||||
<input tabindex="3" id="rememberMe" name="rememberMe"
|
||||
type="checkbox"> ${msg("rememberMe")}
|
||||
</#if>
|
||||
</label>
|
||||
</div>
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<input tabindex="4"
|
||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
|
||||
</div>
|
||||
</form>
|
||||
</#if>
|
||||
<#if realm.rememberMe && !usernameHidden??>
|
||||
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
|
||||
<div id="kc-form-options">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<#if login.rememberMe??>
|
||||
<input
|
||||
tabindex="3"
|
||||
id="rememberMe"
|
||||
name="rememberMe"
|
||||
type="checkbox"
|
||||
checked
|
||||
/> ${msg("rememberMe")}
|
||||
<#else>
|
||||
<input
|
||||
tabindex="3"
|
||||
id="rememberMe"
|
||||
name="rememberMe"
|
||||
type="checkbox"
|
||||
/> ${msg("rememberMe")}
|
||||
</#if>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
|
||||
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
|
||||
<button
|
||||
tabindex="4"
|
||||
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
|
||||
name="login"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
>${msg("doLogIn")}</button>
|
||||
</div>
|
||||
|
||||
<#elseif section = "info" >
|
||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||
<div id="kc-registration">
|
||||
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||
</div>
|
||||
</#if>
|
||||
<#elseif section = "socialProviders" >
|
||||
<#if realm.password && social.providers??>
|
||||
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||
<hr/>
|
||||
<h4>${msg("identity-provider-login-label")}</h4>
|
||||
|
||||
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||
<#list social.providers as p>
|
||||
<a 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>
|
||||
</#list>
|
||||
</ul>
|
||||
</div>
|
||||
</#if>
|
||||
</form>
|
||||
</#if>
|
||||
|
||||
<#elseif section = "info" >
|
||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||
<div id="kc-registration">
|
||||
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
|
||||
</div>
|
||||
</#if>
|
||||
<#elseif section = "socialProviders" >
|
||||
<#if realm.password && social.providers??>
|
||||
<div id="kc-social-providers" class="${properties.kcFormSocialAccountSectionClass!}">
|
||||
<hr/>
|
||||
<h4>${msg("identity-provider-login-label")}</h4>
|
||||
|
||||
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 3>${properties.kcFormSocialAccountListGridClass!}</#if>">
|
||||
<#list social.providers as p>
|
||||
<a 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>
|
||||
</#list>
|
||||
</ul>
|
||||
</div>
|
||||
</#if>
|
||||
</#if>
|
||||
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -59,9 +59,30 @@
|
|||
</div>
|
||||
</div>
|
||||
<main class="ps-main--page ps-page">
|
||||
<header class="ps-page--header ps-header">
|
||||
|
||||
<header class="ps-page--header ps-header">
|
||||
|
||||
<a href="https://pub.solar/" class="ps-homelink">pub.solar/</a>
|
||||
|
||||
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
|
||||
<ul class="ps-i18n-links">
|
||||
<#list locale.supported as l>
|
||||
<li class="ps-i18n-links--item">
|
||||
<a
|
||||
class="ps-i18n-links--link <#if locale.current == l.label >ps-i18n-links--link_active</#if>"
|
||||
href="${l.url}"
|
||||
>${l.label}</a>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</#if>
|
||||
|
||||
</header>
|
||||
|
||||
<section class="ps-page--section ps-page--section_full">
|
||||
|
||||
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
|
||||
<h1 class="ps-header--title"><#nested "header"></h1>
|
||||
<h1 class="ps-page--title"><#nested "header"></h1>
|
||||
<#if displayRequiredFields>
|
||||
<div class="${properties.kcLabelWrapperClass!} subtitle">
|
||||
<span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
|
||||
|
@ -99,25 +120,11 @@
|
|||
</div>
|
||||
</#if>
|
||||
</#if>
|
||||
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
|
||||
<div class="ps-dropdown">
|
||||
<a href="#" class="ps-link ps-dropdown--trigger">${locale.current}</a>
|
||||
<div class="ps-dropdown--body">
|
||||
<ul class="ps-list">
|
||||
<#list locale.supported as l>
|
||||
<li class="ps-list--item">
|
||||
<a class="ps-list--item" href="${l.url}">${l.label}</a>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
</header>
|
||||
<section class="ps-page--section ps-page--section_full">
|
||||
|
||||
<div class="ps-page--section-contents ps-container">
|
||||
<#-- App-initiated actions should not see warning messages about the need to complete the action -->
|
||||
<#-- during login. -->
|
||||
|
||||
<#-- App-initiated actions should not see warning messages about the need to complete the action -->
|
||||
<#-- during login. -->
|
||||
<#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)>
|
||||
<div class="alert-${message.type} ${properties.kcAlertClass!} pf-m-<#if message.type = 'error'>danger<#else>${message.type}</#if>">
|
||||
<div class="pf-c-alert__icon">
|
||||
|
|
|
@ -11,4 +11,6 @@ kcButtonLargeClass=ps-button_large
|
|||
|
||||
kcFormGroupClass=ps-form-group
|
||||
kcLabelClass=ps-form-group--label
|
||||
kcInputErrorMessageClass=ps-form-group--error
|
||||
|
||||
kcInputClass=ps-input
|
||||
|
|
Loading…
Reference in a new issue