In a previous post, I wrote about the steps I followed to create a Keycloak theme.
In this post, we'll use Material Components for the Web to change the look and feel of Keycloak's Login templates.
Material Components for the Web
I installed Material (Design) Components for the Web (MDC Web) using npm:
npm install -P material-components-web
Themes
A Keycloak theme consists of:
- FreeMarker templates
- Stylesheets
- Scripts
- Theme properties
- Images
- Message bundles
And each theme is comprised of the following categories (types):
- Account - Account management
- Admin - Administration console
- Email - Email templates
- Login - Login forms
- Welcome - Welcome page
The directory structure:
├── /serendipity-keycloak-theme
└── /theme
└── /account
└── /admin
└── /email
└── /login
└── /messages
└── /resources
└── /css
└── /img
└── /js
├── login.ftl
├── template.ftl
├── theme.properties
└── /welcome
Stylesheets
Material Components for the Web uses Sass. I installed Sass globally:
npm install -g sass
Now we'll be able to run the sass
executable (from the command line) to compile .sass and .scss files to .css files. For example:
sass --load-path=/Users/robferguson/workspace/Robinyo/serendipity-keycloak-theme/node_modules login.scss:login.css
You can also setup a File Watcher in your IDE, for example:
I created partials (a Sass file named with a leading underscore that contains little snippets of CSS that you can include in other Sass files) for the theme's fonts, icons, palette and typography, for example:
# _typography.scss
$mdc-typography-font-family: "Open Sans", sans-serif;
$mdc-typography-styles-headline4: (
font-weight: 200
);
I created a Sass file called login.scss that imports the partials and the Sass files for the MDC Web components:
@import "./fonts";
@import "./icons";
@import "./palette";
@import "./typography";
@import "@material/button/mdc-button";
@import "@material/card/mdc-card";
@import "@material/checkbox/mdc-checkbox";
@import "@material/form-field/mdc-form-field";
@import "@material/list/mdc-list";
@import "@material/textfield/mdc-text-field";
@import "@material/typography/mdc-typography";
html, body {
width: 100vw;
height: 100vh;
}
...
Scripts
I created a JavaScipt file called login.js that initialises the MDC Web components:
window.onload = function() {
// Initialise all MDC text fields
document.querySelectorAll('.mdc-text-field').forEach(function(e) {
new mdc.textField.MDCTextField(e);
});
// Initialise all MDC text field icons
document.querySelectorAll('.mdc-text-field__icon').forEach(function(e) {
new mdc.textField.MDCTextFieldIcon(e);
});
// Add a ripple effect to all MDC buttons
document.querySelectorAll('.mdc-button').forEach(function(e) {
mdc.ripple.MDCRipple.attachTo(e);
});
};
Theme properties
Each category (type) has a configuration file named theme.properties
.
I updated the Login theme.properties as follows:
...
styles=css/login.css
scripts=js/material-components-web.min.js js/login.js
...
Message bundles
I copied the English language properties file (messages_en.properties) from the base theme and updated it as follows:
doLogIn=Sign in
doRegister=Sign up
noAccount=Don''t have an Account?
...
FreeMarker templates
Keycloak uses templates written in the FreeMarker Template Language (FTL) to generate HTML.
Keycloak's Server Developer guide recommends that when you create a custom template, that you copy the template from the base theme to your own theme, then apply the modifications you need.
I copied the category template (template.ftl) and the Login template (login.ftl) from the base theme and updated them to use MDC Web components, for example:
<#-- login.ftl -->
...
<div class="${properties.kcFormGroupClass!}">
<div class="mdc-text-field mdc-text-field--with-leading-icon ${properties.kcLabelClass!} <#if usernameEditDisabled??>mdc-text-field--disabled</#if>">
<i class="material-icons mdc-text-field__icon" role="button">person</i>
<input tabindex="0" required id="username" class="mdc-text-field__input ${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off" <#if usernameEditDisabled??>disabled</#if>>
<div class="mdc-line-ripple"></div>
<label for="username" class="mdc-floating-label ${properties.kcLabelClass!}">
<#if !realm.loginWithEmailAllowed>
${msg("username")}
<#elseif !realm.registrationEmailAsUsername>
${msg("usernameOrEmail")}
<#else>
${msg("email")}
</#if>
</label>
</div>
</div>
...
The Login page:
Source Code:
- GitHub: Serendipity Keycloak Theme
- GitHub: Serendipity the open source Customer Engagement Platform
- GitHub: The REST API for the Serendipity Customer Engagement Platform
References:
- FreeMarker docs: Apache FreeMarker Manual