Add dark mode support to welcome theme and unify approach (#32495)

Closes #26178

Signed-off-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Co-authored-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Jon Koops 2024-10-04 14:27:37 +02:00 committed by GitHub
parent 95c529104e
commit 05e8b932c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 106 additions and 44 deletions

View file

@ -0,0 +1,10 @@
= Dark mode enabled for the welcome theme
We've now enabled dark mode support for all the `keycloak` themes. This feature was previously present in the admin console, account console and login, and is now also available in the welcome page. If a user indicates their preference through an operating system setting (e.g. light or dark mode) or a user agent setting, the theme will automatically follow these preferences.
If you are using a custom theme that extends any of the `keycloak` themes and are not yet ready to support dark mode, or have styling conflicts that prevent you from implementing dark mode, you can disable support by adding the following property to your theme:
[source,properties]
----
darkMode=false
----

View file

@ -5,6 +5,7 @@
<base href="${resourceUrl}/">
<link rel="icon" type="${properties.favIconType!'image/svg+xml'}" href="${resourceUrl}${properties.favIcon!'/favicon.svg'}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light${(properties.darkMode)?boolean?then(' dark', '')}">
<meta name="description" content="${properties.description!'The Account Console is a web-based interface for managing your account.'}">
<title>${properties.title!'Account Management'}</title>
<style>
@ -57,6 +58,25 @@
}
}
</script>
<#if properties.darkMode?boolean>
<script type="module" async blocking="render">
const DARK_MODE_CLASS = "${properties.kcDarkModeClass}";
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>
</#if>
<#if !isSecureContext>
<script type="module" src="${resourceCommonUrl}/vendor/web-crypto-shim/web-crypto-shim.js"></script>
</#if>

View file

@ -1,2 +1,5 @@
parent=base
deprecatedMode=false
deprecatedMode=false
darkMode=true
kcDarkModeClass=pf-v5-theme-dark

View file

@ -1,15 +1,12 @@
import "@patternfly/react-core/dist/styles/base.css";
import "@patternfly/patternfly/patternfly-addons.css";
import { initializeDarkMode } from "@keycloak/keycloak-ui-shared";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { i18n } from "./i18n";
import { routes } from "./routes";
initializeDarkMode();
// Initialize required components before rendering app.
await i18n.init();

View file

@ -5,6 +5,7 @@
<base href="${resourceUrl}/">
<link rel="icon" type="${properties.favIconType!'image/svg+xml'}" href="${resourceUrl}${properties.favIcon!'/favicon.svg'}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light${(properties.darkMode)?boolean?then(' dark', '')}">
<meta name="description" content="${properties.description!'The Keycloak Administration Console is a web-based interface for managing Keycloak.'}">
<title>${properties.title!'Keycloak Administration Console'}</title>
<style>
@ -57,6 +58,25 @@
}
}
</script>
<#if properties.darkMode?boolean>
<script type="module" async blocking="render">
const DARK_MODE_CLASS = "${properties.kcDarkModeClass}";
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>
</#if>
<#if !isSecureContext>
<script type="module" src="${resourceCommonUrl}/vendor/web-crypto-shim/web-crypto-shim.js"></script>
</#if>

View file

@ -1 +1,4 @@
parent=base
parent=base
darkMode=true
kcDarkModeClass=pf-v5-theme-dark

View file

@ -1,7 +1,6 @@
import "@patternfly/patternfly/patternfly-addons.css";
import "@patternfly/react-core/dist/styles/base.css";
import { initializeDarkMode } from "@keycloak/keycloak-ui-shared";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { createHashRouter, RouterProvider } from "react-router-dom";
@ -10,8 +9,6 @@ import { RootRoute } from "./routes";
import "./index.css";
initializeDarkMode();
// Initialize required components before rendering app.
await i18n.init();

View file

@ -93,4 +93,3 @@ export {
} from "./utils/ErrorBoundary";
export type { FallbackProps } from "./utils/ErrorBoundary";
export { OrganizationTable } from "./controls/OrganizationTable";
export { initializeDarkMode } from "./utils/darkMode";

View file

@ -1,19 +0,0 @@
const DARK_MODE_CLASS = "pf-v5-theme-dark";
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
function updateDarkMode(isEnabled: boolean) {
const { classList } = document.documentElement;
if (isEnabled) {
classList.add(DARK_MODE_CLASS);
} else {
classList.remove(DARK_MODE_CLASS);
}
}
export function initializeDarkMode() {
updateDarkMode(mediaQuery.matches);
mediaQuery.addEventListener("change", (event) =>
updateDarkMode(event.matches),
);
}

View file

@ -28,6 +28,7 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="robots" content="noindex, nofollow">
<meta name="color-scheme" content="light${(properties.darkMode)?boolean?then(' dark', '')}">
<#if properties.meta?has_content>
<#list properties.meta?split(' ') as meta>
@ -53,6 +54,25 @@
}
}
</script>
<#if properties.darkMode?boolean>
<script type="module" async blocking="render">
const DARK_MODE_CLASS = "${properties.kcDarkModeClass}";
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>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(' ') as script>
<script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
@ -70,21 +90,6 @@
checkCookiesAndSetTimer(
"${url.ssoLoginInOtherTabsUrl?no_esc}"
);
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>

View file

@ -4,6 +4,8 @@ import=common/keycloak
styles=css/styles.css
stylesCommon=vendor/patternfly-v5/patternfly.min.css vendor/patternfly-v5/patternfly-addons.css
darkMode=true
kcFormGroupClass=pf-v5-c-form__group
kcFormGroupLabelClass=pf-v5-c-form__group-label
kcLabelClass=pf-v5-c-form__label
@ -90,4 +92,6 @@ kcLoginOTPListItemHeaderClass=pf-v5-c-tile__header pf-m-stacked
kcLoginOTPListItemIconBodyClass=pf-v5-c-tile__icon
kcLoginOTPListItemIconClass=fa fa-mobile
kcLoginOTPListItemTitleClass=pf-v5-c-tile__title
kcLoginOTPListSelectedClass=pf-m-selected
kcLoginOTPListSelectedClass=pf-m-selected
kcDarkModeClass=pf-v5-theme-dark

View file

@ -4,8 +4,28 @@
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light${(properties.darkMode)?boolean?then(' dark', '')}">
<title>Welcome to ${productName}</title>
<link rel="shortcut icon" href="${resourcesCommonPath}/img/favicon.ico">
<#if properties.darkMode?boolean>
<script type="module" async blocking="render">
const DARK_MODE_CLASS = "${properties.kcDarkModeClass}";
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>
</#if>
<#if properties.stylesCommon?has_content>
<#list properties.stylesCommon?split(' ') as style>
<link rel="stylesheet" href="${resourcesCommonPath}/${style}">

View file

@ -5,3 +5,6 @@ styles=css/welcome.css
# When set to true, the user will be redirected to the Administration Console if an administrative users already exists.
redirectToAdmin=true
darkMode=true
kcDarkModeClass=pf-v5-theme-dark