- theme.properties: keep loading the keycloak.v2 parent stylesheet (previously shadowed by our same-named css/styles.css, so the base card grid/layout never loaded); our overrides now live in resources/css/custom.css. - custom.css: rewritten against the real rendered markup/PatternFly tokens (#keycloak-bg, .pf-v5-c-login__main, #kc-login) instead of guessed selectors, with a frosted-glass card, dark-mode variant, and rounded inputs/buttons. - bg-picker.js: picks the background photo deterministically by epoch-day from an IMAGES list (add filenames as new photos are uploaded), and derives the card's accent color (top border + primary button) from the chosen photo via saturation-weighted dominant-hue extraction. - dev/: local static preview (real login markup snapshot + vendored Font Awesome glyph) to iterate on the theme without redeploying.
177 lines
6.7 KiB
HTML
177 lines
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<html class="login-pf" lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
<meta name="color-scheme" content="light dark">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
<title>Sign in to Vilanet (local preview)</title>
|
|
<link rel="icon" href="https://id.vilanet.fr/resources/3gbnb/login/vilanet/img/favicon.ico" />
|
|
<link href="https://id.vilanet.fr/resources/3gbnb/common/keycloak/vendor/patternfly-v5/patternfly.min.css" rel="stylesheet" />
|
|
<link href="https://id.vilanet.fr/resources/3gbnb/common/keycloak/vendor/patternfly-v5/patternfly-addons.css" rel="stylesheet" />
|
|
<!-- parent theme (keycloak.v2) base styles - loaded live, this one rarely changes -->
|
|
<link href="https://id.vilanet.fr/resources/3gbnb/login/keycloak.v2/css/styles.css" rel="stylesheet" />
|
|
<!-- OUR theme - loaded from disk, edit + refresh to iterate -->
|
|
<link href="../assets/login/resources/css/custom.css" rel="stylesheet" />
|
|
<script src="../assets/login/resources/js/bg-picker.js"></script>
|
|
<!-- patternfly.min.css's fa-solid-900.woff2 is fetched cross-origin from
|
|
id.vilanet.fr, which has no Access-Control-Allow-Origin header - browsers
|
|
enforce CORS on @font-face specifically, so those icon glyphs silently
|
|
fail to render here. Redeclare same-origin so icons show in the preview;
|
|
not needed in the real deployment, where everything is same-origin. -->
|
|
<style>
|
|
@font-face {
|
|
font-family: "Font Awesome 5 Free";
|
|
font-weight: 900;
|
|
font-style: normal;
|
|
src: url("vendor/fa-solid-900.woff2") format("woff2");
|
|
}
|
|
</style>
|
|
<script type="module" async blocking="render">
|
|
const DARK_MODE_CLASS = "pf-v5-theme-dark";
|
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
|
|
updateDarkMode(mediaQuery.matches);
|
|
mediaQuery.addEventListener("change", (event) => updateDarkMode(event.matches));
|
|
|
|
function updateDarkMode(isEnabled) {
|
|
const { classList } = document.documentElement;
|
|
|
|
if (isEnabled) {
|
|
classList.add(DARK_MODE_CLASS);
|
|
} else {
|
|
classList.remove(DARK_MODE_CLASS);
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
|
|
<!--
|
|
Static snapshot of the real login-login page for realm "vilanet", fetched
|
|
from id.vilanet.fr. Not wired to a real Keycloak session - the form does
|
|
not submit anywhere. This is for iterating on assets/login/resources/css/custom.css
|
|
without redeploying the pod: edit that file, then just refresh this page.
|
|
|
|
Serve from the REPO ROOT so the relative paths above resolve correctly:
|
|
python3 -m http.server 8000
|
|
then open http://localhost:8000/dev/preview.html
|
|
|
|
Re-fetch this snapshot any time the FreeMarker markup itself changes
|
|
(e.g. after a Keycloak upgrade) - see the curl recipe in the project notes.
|
|
-->
|
|
<body id="keycloak-bg" class="" data-page-id="login-login">
|
|
<div class="pf-v5-c-login">
|
|
<div class="pf-v5-c-login__container">
|
|
<header id="kc-header" class="pf-v5-c-login__header">
|
|
<div id="kc-header-wrapper"
|
|
class="pf-v5-c-brand">Vilanet</div>
|
|
</header>
|
|
<main class="pf-v5-c-login__main">
|
|
<div class="pf-v5-c-login__main-header">
|
|
<h1 class="pf-v5-c-title pf-m-3xl" id="kc-page-title">
|
|
Sign in to your account
|
|
</h1>
|
|
</div>
|
|
<div class="pf-v5-c-login__main-body">
|
|
|
|
<div id="kc-form">
|
|
<div id="kc-form-wrapper">
|
|
<form id="kc-form-login" class="pf-v5-c-form pf-v5-u-w-100" onsubmit="return false;" action="#" method="post" novalidate="novalidate">
|
|
|
|
<div class="pf-v5-c-form__group">
|
|
<div class="pf-v5-c-form__group-label pf-v5-u-pb-xs">
|
|
<label for="username" class="pf-v5-c-form__label">
|
|
<span class="pf-v5-c-form__label-text">
|
|
Username
|
|
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<span class="pf-v5-c-form-control ">
|
|
<input id="username" name="username" value="" type="text" autocomplete="username" autofocus
|
|
|
|
aria-invalid=""/>
|
|
</span>
|
|
|
|
<div id="input-error-container-username">
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="pf-v5-c-form__group">
|
|
<div class="pf-v5-c-form__group-label pf-v5-u-pb-xs">
|
|
<label for="password" class="pf-v5-c-form__label">
|
|
<span class="pf-v5-c-form__label-text">
|
|
Password
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="pf-v5-c-input-group">
|
|
<div class="pf-v5-c-input-group__item pf-m-fill">
|
|
<span class="pf-v5-c-form-control ">
|
|
<input id="password" name="password" value="" type="password" autocomplete="current-password"
|
|
aria-invalid=""/>
|
|
</span>
|
|
</div>
|
|
<div class="pf-v5-c-input-group__item">
|
|
<button class="pf-v5-c-button pf-m-control" type="button" aria-label="Show password"
|
|
aria-controls="password" data-password-toggle
|
|
data-icon-show="fa-eye fas" data-icon-hide="fa-eye-slash fas"
|
|
data-label-show="Show password" data-label-hide="Hide password" id="password-show-password"
|
|
onclick="const i=this.querySelector('i'); const pw=document.getElementById('password'); const showing=pw.type==='text'; pw.type = showing ? 'password' : 'text'; i.className = showing ? 'fa-eye fas' : 'fa-eye-slash fas';">
|
|
<i class="fa-eye fas" aria-hidden="true"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="pf-v5-c-form__helper-text" aria-live="polite">
|
|
<div class="pf-v5-c-helper-text pf-v5-u-display-flex pf-v5-u-justify-content-space-between">
|
|
<div class="pf-v5-c-check">
|
|
<label for="rememberMe" class="pf-v5-c-check">
|
|
<input
|
|
class="pf-v5-c-check__input"
|
|
type="checkbox"
|
|
id="rememberMe"
|
|
name="rememberMe"
|
|
|
|
/>
|
|
<span class="pf-v5-c-check__label">Remember me</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="input-error-container-password">
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<input type="hidden" id="id-hidden-input" name="credentialId" />
|
|
<div class="pf-v5-c-form__group">
|
|
<div class="pf-v5-c-form__actions pf-v5-u-pt-xs pf-v5-u-flex-wrap">
|
|
<button class="pf-v5-c-button pf-m-primary pf-m-block" name="login" id="kc-login"
|
|
type="submit" >
|
|
Sign In
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pf-v5-c-login__main-footer">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pf-v5-c-login__main-footer">
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|