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:
George Wu 2025-01-06 02:58:42 -08:00 committed by GitHub
parent 761dda688e
commit a3358828a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 65 additions and 22 deletions

View file

@ -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

View file

@ -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)
} }

View file

@ -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 {

View 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);

View file

@ -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 ))">

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 %)

View file

@ -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() %)

View file

@ -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>

View file

@ -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()))"