mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Add support for prefers-color-scheme using Bootstrap classes. (#3327)
* Add support for prefers-color-scheme using Bootstrap classes. * Move stylesheet changes to separate javascript file. * fix(html): don't specify the integrity hash in the tag for style.js * fix(log): debug-log integrity hashes for troubleshooting * fix(css): move to using bootstrap standard variables for colours and theming * fix(js): rewrite to simplify and use standard bootstrap functionality * fix(makefile): codespell thingie was complaining * run prettier on css/js. --------- Co-authored-by: James Hodgkinson <james@terminaloutcomes.com>
This commit is contained in:
parent
761dda688e
commit
a3358828a8
5
Makefile
5
Makefile
|
@ -178,9 +178,8 @@ codespell:
|
||||||
--skip='*.svg' \
|
--skip='*.svg' \
|
||||||
--skip='*.br' \
|
--skip='*.br' \
|
||||||
--skip='./rlm_python/mods-available/eap' \
|
--skip='./rlm_python/mods-available/eap' \
|
||||||
--skip='./server/lib/src/constants/system_config.rs'
|
--skip='./server/lib/src/constants/system_config.rs' \
|
||||||
--skip='./pykanidm/site' \
|
--skip='./pykanidm/site'
|
||||||
--skip='./server/lib/src/constants/*.json'
|
|
||||||
|
|
||||||
.PHONY: test/pykanidm/pytest
|
.PHONY: test/pykanidm/pytest
|
||||||
test/pykanidm/pytest: ## python library testing
|
test/pykanidm/pytest: ## python library testing
|
||||||
|
|
|
@ -140,11 +140,13 @@ pub(crate) fn get_js_files(role: ServerRole) -> Result<Vec<JavaScriptFile>, ()>
|
||||||
"external/base64.js",
|
"external/base64.js",
|
||||||
"modules/cred_update.mjs",
|
"modules/cred_update.mjs",
|
||||||
"pkhtml.js",
|
"pkhtml.js",
|
||||||
|
"style.js",
|
||||||
];
|
];
|
||||||
|
|
||||||
for filepath in filelist {
|
for filepath in filelist {
|
||||||
match generate_integrity_hash(format!("{}/{}", pkg_path, filepath,)) {
|
match generate_integrity_hash(format!("{}/{}", pkg_path, filepath,)) {
|
||||||
Ok(hash) => {
|
Ok(hash) => {
|
||||||
|
debug!("Integrity hash for {}: {}", filepath, hash);
|
||||||
let js = JavaScriptFile { hash };
|
let js = JavaScriptFile { hash };
|
||||||
all_pages.push(js)
|
all_pages.push(js)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ body {
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--bs-gray-300);
|
background-color: var(--bs-tertiary-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container img {
|
.icon-container img {
|
||||||
|
@ -57,6 +57,23 @@ body {
|
||||||
/*
|
/*
|
||||||
* Navbar
|
* Navbar
|
||||||
*/
|
*/
|
||||||
|
nav.kanidm_navbar {
|
||||||
|
background-color: var(--bs-navbar-brand-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a.navbar-brand,
|
||||||
|
nav a.nav-link {
|
||||||
|
color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a.navbar-brand:hover,
|
||||||
|
nav a.nav-link:hover {
|
||||||
|
color: var(--bs-secondary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: var(--bs-tertiary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.kanidm_logo {
|
.kanidm_logo {
|
||||||
width: 12em;
|
width: 12em;
|
||||||
|
@ -79,7 +96,7 @@ body {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background: #21252915;
|
background-color: #21252915;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
-5px -5px 11px #ededed,
|
-5px -5px 11px #ededed,
|
||||||
5px 5px 11px #ffffff;
|
5px 5px 11px #ffffff;
|
||||||
|
@ -122,15 +139,15 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.totp-timer__path-remaining.green {
|
.totp-timer__path-remaining.green {
|
||||||
color: rgb(65, 184, 131);
|
color: var(--bs-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
.totp-timer__path-remaining.orange {
|
.totp-timer__path-remaining.orange {
|
||||||
color: orange;
|
color: var(--bs-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
.totp-timer__path-remaining.red {
|
.totp-timer__path-remaining.red {
|
||||||
color: red;
|
color: var(--bs-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.totp-timer__path-remaining.no-transition {
|
.totp-timer__path-remaining.no-transition {
|
||||||
|
|
19
server/core/static/style.js
Normal file
19
server/core/static/style.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/** Queries the user's preferred colour scheme and returns the appropriate value.
|
||||||
|
From https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript
|
||||||
|
*/
|
||||||
|
function getPreferredTheme() {
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the theme.
|
||||||
|
*/
|
||||||
|
function updateColourScheme() {
|
||||||
|
const theme = getPreferredTheme();
|
||||||
|
console.debug(`updateColourScheme theme->${theme}`);
|
||||||
|
document.documentElement.setAttribute("data-bs-theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColourScheme();
|
||||||
|
window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", updateColourScheme);
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", updateColourScheme);
|
||||||
|
document.body.addEventListener("htmx:afterOnLoad", updateColourScheme);
|
|
@ -13,7 +13,7 @@
|
||||||
(% match app %)
|
(% match app %)
|
||||||
(% when AppLink::Oauth2 with { name, display_name, redirect_url, has_image }
|
(% when AppLink::Oauth2 with { name, display_name, redirect_url, has_image }
|
||||||
%)
|
%)
|
||||||
<a href="(( redirect_url ))" class="link-dark stretched-link mt-2">
|
<a href="(( redirect_url ))" class="link-emphasis stretched-link mt-2">
|
||||||
(% if has_image %)
|
(% if has_image %)
|
||||||
<img src="/ui/images/oauth2/(( name ))" class="oauth2-img"
|
<img src="/ui/images/oauth2/(( name ))" class="oauth2-img"
|
||||||
alt="((display_name)) icon" id="(( name ))">
|
alt="((display_name)) icon" id="(( name ))">
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<script
|
<script
|
||||||
src="/pkg/external/bootstrap.bundle.min.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
src="/pkg/external/bootstrap.bundle.min.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"></script>
|
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"></script>
|
||||||
|
<script
|
||||||
|
src="/pkg/style.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
|
defer></script>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="/pkg/style.css?v=((crate::https::cache_buster::get_cache_buster_key()))" />
|
href="/pkg/style.css?v=((crate::https::cache_buster::get_cache_buster_key()))" />
|
||||||
|
|
||||||
|
@ -22,7 +25,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body class="flex-column d-flex h-100">
|
<body class="flex-column d-flex h-100">
|
||||||
(% block body %)(% endblock %)
|
(% block body %)(% endblock %)
|
||||||
<footer class="footer mt-auto py-3 bg-light text-end">
|
<footer class="footer mt-auto py-3 bs-secondary-bg text-end">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="text-muted">Powered by <a
|
<span class="text-muted">Powered by <a
|
||||||
href="https://kanidm.com">Kanidm</a></span>
|
href="https://kanidm.com">Kanidm</a></span>
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
<script
|
<script
|
||||||
src="/pkg/external/htmx.min.1.9.12.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
src="/pkg/external/htmx.min.1.9.12.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2"></script>
|
integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2"></script>
|
||||||
|
<script
|
||||||
|
src="/pkg/style.js?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
|
defer></script>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="/pkg/style.css?v=((crate::https::cache_buster::get_cache_buster_key()))" />
|
href="/pkg/style.css?v=((crate::https::cache_buster::get_cache_buster_key()))" />
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@
|
||||||
<body hx-boost="true" class="flex-column d-flex h-100">
|
<body hx-boost="true" class="flex-column d-flex h-100">
|
||||||
(% block nav %)(% endblock %)
|
(% block nav %)(% endblock %)
|
||||||
(% block body %)(% endblock %)
|
(% block body %)(% endblock %)
|
||||||
<footer class="footer mt-auto py-3 bg-light text-end">
|
<footer class="footer mt-auto py-3 bs-secondary-bg text-end">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="text-muted">Powered by <a
|
<span class="text-muted">Powered by <a
|
||||||
href="https://kanidm.com">Kanidm</a></span>
|
href="https://kanidm.com">Kanidm</a></span>
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
(% endmatch %)
|
(% endmatch %)
|
||||||
|
|
||||||
<hr class="my-4" />
|
<hr class="my-4" />
|
||||||
<div id="cred-update-commit-bar" class="toast">
|
<div id="cred-update-commit-bar" class="toast bs-emphasis-color bs-secondary-bg">
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
<span class="d-flex align-items-center">
|
<span class="d-flex align-items-center">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
<div class="input-group mb-3 justify-content-md-center">
|
<div class="input-group mb-3 justify-content-md-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark"
|
class="btn btn-primary"
|
||||||
>Begin</button>
|
>Begin</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<div class="input-group mb-3 justify-content-md-center">
|
<div class="input-group mb-3 justify-content-md-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark"
|
class="btn btn-primary"
|
||||||
>Submit</button>
|
>Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<button
|
<button
|
||||||
(% if mech.autofocus %)autofocus(% endif %)
|
(% if mech.autofocus %)autofocus(% endif %)
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark"
|
class="btn btn-primary"
|
||||||
>(( mech.name ))</button>
|
>(( mech.name ))</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="input-group mb-3 justify-content-md-center">
|
<div class="input-group mb-3 justify-content-md-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark"
|
class="btn btn-primary"
|
||||||
>Submit</button>
|
>Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<div class="input-group mb-3 justify-content-md-center">
|
<div class="input-group mb-3 justify-content-md-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark"
|
class="btn btn-primary"
|
||||||
>Submit</button>
|
>Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
(% if passkey %)
|
(% if passkey %)
|
||||||
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
<form id="cred-form" action="/ui/login/passkey" method="POST">
|
||||||
<input hidden="hidden" name="cred" id="cred">
|
<input hidden="hidden" name="cred" id="cred">
|
||||||
<button hx-disable type="button" autofocus class="btn btn-dark"
|
<button hx-disable type="button" autofocus class="btn btn-primary"
|
||||||
id="start-passkey-button">Use Passkey</button>
|
id="start-passkey-button">Use Passkey</button>
|
||||||
</form>
|
</form>
|
||||||
(% else %)
|
(% else %)
|
||||||
<form id="cred-form" action="/ui/login/seckey" method="POST">
|
<form id="cred-form" action="/ui/login/seckey" method="POST">
|
||||||
<input hidden="hidden" name="cred" id="cred">
|
<input hidden="hidden" name="cred" id="cred">
|
||||||
<button hx-disable type="button" autofocus class="btn btn-dark"
|
<button hx-disable type="button" autofocus class="btn btn-primary"
|
||||||
id="start-seckey-button">Use Security Key</button>
|
id="start-seckey-button">Use Security Key</button>
|
||||||
</form>
|
</form>
|
||||||
(% endif %)
|
(% endif %)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
|
<nav class="navbar navbar-expand-md kanidm_navbar mb-4">
|
||||||
<div class="container-lg">
|
<div class="container-lg">
|
||||||
<a class="navbar-brand d-flex align-items-center" href="/ui/apps">
|
<a class="navbar-brand d-flex align-items-center" href="/ui/apps">
|
||||||
(% if navbar_ctx.domain_info.image().is_some() %)
|
(% if navbar_ctx.domain_info.image().is_some() %)
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="input-group mb-3 justify-content-md-center">
|
<div class="input-group mb-3 justify-content-md-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-dark">Continue</button>
|
class="btn btn-primary">Continue</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</center>
|
</center>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<li>
|
<li>
|
||||||
<a hx-select="main" hx-target="main" hx-swap="outerHTML show:false"
|
<a hx-select="main" hx-target="main" hx-swap="outerHTML show:false"
|
||||||
href="(( href ))"
|
href="(( href ))"
|
||||||
class="side-menu-item d-flex rounded link-dark(% if menu_active_item == menu_item %) active(% endif %)">
|
class="side-menu-item d-flex rounded link-emphasis(% if menu_active_item == menu_item %) active(% endif %)">
|
||||||
<div class="icon-container align-items-center justify-content-center d-flex me-2">
|
<div class="icon-container align-items-center justify-content-center d-flex me-2">
|
||||||
<img class="text-body-secondary"
|
<img class="text-body-secondary"
|
||||||
src="/pkg/img/icons/(( icon_name )).svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
src="/pkg/img/icons/(( icon_name )).svg?v=((crate::https::cache_buster::get_cache_buster_key()))"
|
||||||
|
|
Loading…
Reference in a new issue