From f6796d51ae2bd4c5972e33f3ad060011b9e83da7 Mon Sep 17 00:00:00 2001 From: Ben Owen Date: Tue, 30 Dec 2025 10:39:39 -0600 Subject: [PATCH] Support system dark mode auto switching Wrapped base64urls in Unit8Arrays --- src/data/webAuthnAuthenticate.ts | 4 +-- src/data/webAuthnRegister.ts | 6 ++-- src/index.css | 2 +- src/index.ts | 36 +++++++++++++++++++ tailwind.config.ts | 1 + .../keywind/login/components/atoms/alert.ftl | 10 +++--- theme/keywind/login/components/atoms/body.ftl | 2 +- .../keywind/login/components/atoms/button.ftl | 8 ++--- theme/keywind/login/components/atoms/card.ftl | 2 +- .../login/components/atoms/checkbox.ftl | 4 +-- .../login/components/atoms/heading.ftl | 2 +- .../keywind/login/components/atoms/input.ftl | 6 ++-- theme/keywind/login/components/atoms/link.ftl | 6 ++-- theme/keywind/login/components/atoms/logo.ftl | 2 +- .../keywind/login/components/atoms/radio.ftl | 4 +-- .../molecules/identity-provider.ftl | 4 +-- .../components/molecules/locale-provider.ftl | 2 +- .../login/components/molecules/username.ftl | 2 +- theme/keywind/login/document.ftl | 17 +++++++++ .../resources/dist/assets/index-a7b84447.js | 1 - .../dist/assets/module.esm-1a8ca6bf.js | 5 +++ .../dist/assets/module.esm-9a996e1c.js | 5 --- .../resources/dist/assets/rfc4648-22fd5bf4.js | 1 + theme/keywind/login/resources/dist/index.css | 2 +- theme/keywind/login/resources/dist/index.js | 2 +- .../login/resources/dist/recoveryCodes.js | 2 +- .../resources/dist/webAuthnAuthenticate.js | 2 +- .../login/resources/dist/webAuthnRegister.js | 2 +- theme/keywind/login/template.ftl | 2 +- 29 files changed, 99 insertions(+), 45 deletions(-) delete mode 100644 theme/keywind/login/resources/dist/assets/index-a7b84447.js create mode 100644 theme/keywind/login/resources/dist/assets/module.esm-1a8ca6bf.js delete mode 100644 theme/keywind/login/resources/dist/assets/module.esm-9a996e1c.js create mode 100644 theme/keywind/login/resources/dist/assets/rfc4648-22fd5bf4.js diff --git a/src/data/webAuthnAuthenticate.ts b/src/data/webAuthnAuthenticate.ts index 8aab5bb94..f1bd8fb4e 100644 --- a/src/data/webAuthnAuthenticate.ts +++ b/src/data/webAuthnAuthenticate.ts @@ -58,7 +58,7 @@ document.addEventListener('alpine:init', () => { } const publicKey: PublicKeyCredentialRequestOptions = { - challenge: base64url.parse(challenge, { loose: true }), + challenge: new Uint8Array(base64url.parse(challenge, { loose: true })), rpId: rpId, }; @@ -121,7 +121,7 @@ document.addEventListener('alpine:init', () => { authnSelectFormElements.forEach((element) => { if (element instanceof HTMLInputElement) { allowCredentials.push({ - id: base64url.parse(element.value, { loose: true }), + id: new Uint8Array(base64url.parse(element.value, { loose: true })), type: 'public-key', }); } diff --git a/src/data/webAuthnRegister.ts b/src/data/webAuthnRegister.ts index 23744df96..f51c41282 100644 --- a/src/data/webAuthnRegister.ts +++ b/src/data/webAuthnRegister.ts @@ -91,7 +91,7 @@ document.addEventListener('alpine:init', () => { excludeCredentialIdsList.forEach((value) => { excludeCredentials.push({ - id: base64url.parse(value, { loose: true }), + id: new Uint8Array(base64url.parse(value, { loose: true })), type: 'public-key', }); }); @@ -121,7 +121,7 @@ document.addEventListener('alpine:init', () => { } const publicKey: PublicKeyCredentialCreationOptions = { - challenge: base64url.parse(challenge, { loose: true }), + challenge: new Uint8Array(base64url.parse(challenge, { loose: true })), pubKeyCredParams: getPubKeyCredParams(signatureAlgorithms), rp: { id: rpId, @@ -129,7 +129,7 @@ document.addEventListener('alpine:init', () => { }, user: { displayName: username, - id: base64url.parse(userId, { loose: true }), + id: new Uint8Array(base64url.parse(userId, { loose: true })), name: username, }, }; diff --git a/src/index.css b/src/index.css index 06248629a..ee6ddebca 100644 --- a/src/index.css +++ b/src/index.css @@ -20,7 +20,7 @@ .separate::after, .separate::before { content: ''; - @apply border-b border-secondary-200 flex-1; + @apply border-b border-secondary-200 dark:border-gray-700 flex-1; } .separate:not(:empty)::after { diff --git a/src/index.ts b/src/index.ts index 213348173..1a9f331d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,3 +5,39 @@ import Alpine from 'alpinejs'; window.Alpine = Alpine; Alpine.start(); + +// Dark mode detection and class management +// This runs after page load to handle dynamic changes +function initDarkMode() { + const htmlElement = document.documentElement; + + // Check if Keycloak has already added the dark class + // If not, detect system preference and add it + const hasDarkClass = htmlElement.classList.contains('dark'); + const hasLightClass = htmlElement.classList.contains('light'); + + if (!hasDarkClass && !hasLightClass) { + // Check system preference + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + + if (prefersDark) { + htmlElement.classList.add('dark'); + } else { + htmlElement.classList.add('light'); + } + } + + // Listen for system preference changes + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + if (e.matches) { + htmlElement.classList.add('dark'); + htmlElement.classList.remove('light'); + } else { + htmlElement.classList.remove('dark'); + htmlElement.classList.add('light'); + } + }); +} + +// Initialize dark mode +initDarkMode(); diff --git a/tailwind.config.ts b/tailwind.config.ts index cb7d1a0d6..67ed04b99 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -3,6 +3,7 @@ import colors from 'tailwindcss/colors'; export default { content: ['./theme/**/*.ftl'], + darkMode: 'class', experimental: { optimizeUniversalDefaults: true, }, diff --git a/theme/keywind/login/components/atoms/alert.ftl b/theme/keywind/login/components/atoms/alert.ftl index 58e8309f0..345e8c1e3 100644 --- a/theme/keywind/login/components/atoms/alert.ftl +++ b/theme/keywind/login/components/atoms/alert.ftl @@ -1,19 +1,19 @@ <#macro kw color=""> <#switch color> <#case "error"> - <#assign colorClass="bg-red-100 text-red-600"> + <#assign colorClass="bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400"> <#break> <#case "info"> - <#assign colorClass="bg-blue-100 text-blue-600"> + <#assign colorClass="bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400"> <#break> <#case "success"> - <#assign colorClass="bg-green-100 text-green-600"> + <#assign colorClass="bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400"> <#break> <#case "warning"> - <#assign colorClass="bg-orange-100 text-orange-600"> + <#assign colorClass="bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400"> <#break> <#default> - <#assign colorClass="bg-blue-100 text-blue-600"> + <#assign colorClass="bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400">