taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit b3b87417f1e1bf42fa2aad4b93167764a470f265
parent a0258f14439129bec53ed718ffe1d9082117afcb
Author: Florian Dold <dold@taler.net>
Date:   Sun, 13 Sep 2026 12:06:28 +0200

challenger web UI: fix dark mode issues

Diffstat:
Mpackages/challenger-webui/src/components/VerificationUi.tsx | 16++++++++++------
Mpackages/challenger-webui/src/scss/main.css | 37+++++++++++++++++++++++++++++++++++--
Mpackages/web-util/src/components/LangSelector.tsx | 14+++++++-------
3 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/packages/challenger-webui/src/components/VerificationUi.tsx b/packages/challenger-webui/src/components/VerificationUi.tsx @@ -92,7 +92,11 @@ export function LoadingState({ }): VNode { return ( <VerificationCard title={title} description={description}> - <div class="flex justify-center" role="status" aria-live="polite"> + <div + class="flex justify-center text-primary dark:text-darkPrimary" + role="status" + aria-live="polite" + > <Spinner /> </div> </VerificationCard> @@ -102,7 +106,7 @@ export function LoadingState({ export function Spinner(): VNode { return ( <svg - class="h-7 w-7 animate-spin text-primary dark:text-darkPrimary" + class="h-7 w-7 animate-spin" viewBox="0 0 24 24" fill="none" aria-hidden="true" @@ -142,7 +146,7 @@ export function ActionButton({ return ( <button type={submit ? "submit" : "button"} - class="flex min-h-11 w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-onPrimary shadow-sm hover:brightness-110 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:bg-outlineVariant disabled:text-secondary disabled:opacity-60 dark:bg-darkPrimary dark:text-darkOnPrimary dark:focus-visible:outline-darkPrimary dark:disabled:bg-darkSecondaryContainer dark:disabled:text-darkSecondary" + class="flex min-h-11 w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-onPrimary shadow-sm enabled:hover:brightness-110 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:bg-outlineVariant disabled:text-secondary disabled:opacity-60 dark:bg-darkPrimary dark:text-darkOnPrimary dark:focus-visible:outline-darkPrimary dark:disabled:bg-darkSecondaryContainer dark:disabled:text-darkOnSecondaryContainer" disabled={disabled || running || (!submit && !onClick)} aria-busy={running} onClick={onClick} @@ -163,7 +167,7 @@ export function SecondaryAction({ return ( <a href={href} - class="flex min-h-11 w-full items-center justify-center rounded-lg border border-outline bg-background px-4 py-2.5 text-center text-sm font-semibold text-onBackground hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-darkSecondary dark:bg-darkBackground dark:text-darkOnBackground dark:hover:bg-darkSecondaryContainer dark:focus-visible:outline-darkPrimary" + class="flex min-h-11 w-full items-center justify-center rounded-lg border border-outline bg-background px-4 py-2.5 text-center text-sm font-semibold text-onBackground hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-darkSecondary dark:bg-darkBackground dark:text-darkOnBackground dark:hover:bg-darkSecondaryContainer dark:hover:text-darkOnSecondaryContainer dark:focus-visible:outline-darkPrimary" > {children} </a> @@ -186,7 +190,7 @@ export function SecondaryButton({ return ( <button type="button" - class="flex min-h-11 w-full items-center justify-center gap-2 rounded-lg border border-outline bg-background px-4 py-2.5 text-center text-sm font-semibold text-onBackground hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:border-outlineVariant disabled:text-secondary disabled:opacity-60 dark:border-darkSecondary dark:bg-darkBackground dark:text-darkOnBackground dark:hover:bg-darkSecondaryContainer dark:focus-visible:outline-darkPrimary dark:disabled:border-darkSecondaryContainer dark:disabled:text-darkSecondary" + class="flex min-h-11 w-full items-center justify-center gap-2 rounded-lg border border-outline bg-background px-4 py-2.5 text-center text-sm font-semibold text-onBackground enabled:hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:border-outlineVariant disabled:text-secondary disabled:opacity-60 dark:border-darkSecondary dark:bg-darkBackground dark:text-darkOnBackground dark:enabled:hover:bg-darkSecondaryContainer dark:enabled:hover:text-darkOnSecondaryContainer dark:focus-visible:outline-darkPrimary dark:disabled:border-darkSecondaryContainer dark:disabled:text-darkSecondary" disabled={disabled || running || !onClick} aria-busy={running} onClick={onClick} @@ -287,7 +291,7 @@ export function TechnicalDetails({ {copyText ? ( <button type="button" - class="mt-3 min-h-9 rounded-md border border-outline px-3 py-1.5 font-sans text-xs font-semibold text-onBackground hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-darkSecondary dark:text-darkOnBackground dark:hover:bg-darkSecondaryContainer dark:focus-visible:outline-darkPrimary" + class="mt-3 min-h-9 rounded-md border border-outline px-3 py-1.5 font-sans text-xs font-semibold text-onBackground hover:bg-secondaryContainer focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-darkSecondary dark:text-darkOnBackground dark:hover:bg-darkSecondaryContainer dark:hover:text-darkOnSecondaryContainer dark:focus-visible:outline-darkPrimary" onClick={async () => { try { await copyToClipboard(copyText); diff --git a/packages/challenger-webui/src/scss/main.css b/packages/challenger-webui/src/scss/main.css @@ -5,8 +5,13 @@ :root { color-scheme: light; font-family: - Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, - "Segoe UI", sans-serif; + Inter, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + sans-serif; background: #fdfdff; } @@ -17,6 +22,11 @@ body { color: #1a1c1f; } +/* Shared forms must use the surrounding verification card's surface. */ +.challenger-card form { + background: transparent; +} + .challenger-card input, .challenger-card select, .challenger-card textarea { @@ -42,6 +52,13 @@ body { color: #1a1c1f !important; } +.challenger-card input[aria-invalid="true"], +.challenger-card select[aria-invalid="true"], +.challenger-card textarea[aria-invalid="true"] { + border-color: #b3261e !important; + --tw-ring-color: #b3261e !important; +} + @media (prefers-color-scheme: dark) { :root { color-scheme: dark; @@ -75,6 +92,22 @@ body { .challenger-card label { color: #e2e2eb !important; } + + /* Shared form help, validation messages, and required-field markers. */ + .challenger-card .text-gray-500 { + color: #a4c9ff; + } + + .challenger-card .text-red-600 { + color: #ffb4aa; + } + + .challenger-card input[aria-invalid="true"], + .challenger-card select[aria-invalid="true"], + .challenger-card textarea[aria-invalid="true"] { + border-color: #ffb4aa !important; + --tw-ring-color: #ffb4aa !important; + } } @media (prefers-reduced-motion: reduce) { diff --git a/packages/web-util/src/components/LangSelector.tsx b/packages/web-util/src/components/LangSelector.tsx @@ -82,7 +82,7 @@ export function LangSelector({ return ( <button type="button" - class="relative w-full rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6" + class="relative w-full rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 dark:bg-gray-950 dark:text-gray-100 dark:ring-gray-600 dark:focus:ring-indigo-400 sm:text-sm sm:leading-6" aria-haspopup="true" aria-expanded={!hidden} aria-label={i18n.str`Select language`} @@ -144,7 +144,7 @@ export function LangSelector({ return ( <button type="button" - class="inline-flex min-h-11 items-center border-0 bg-transparent p-0 font-semibold text-onBackground hover:text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 max-md:min-h-9 max-md:w-10 max-md:overflow-hidden max-md:whitespace-nowrap" + class="inline-flex min-h-11 items-center border-0 bg-transparent p-0 font-semibold text-onBackground hover:text-primary dark:text-gray-100 dark:hover:text-indigo-300 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 max-md:min-h-9 max-md:w-10 max-md:overflow-hidden max-md:whitespace-nowrap" aria-haspopup="true" aria-expanded={!hidden} aria-label={i18n.str`Select language`} @@ -169,9 +169,9 @@ export function LangSelector({ {!hidden && ( <ul - class={`absolute right-0 z-50 m-0 max-h-60 overflow-auto bg-white text-left shadow-lg focus:outline-none ${ + class={`absolute right-0 z-50 m-0 max-h-60 overflow-auto bg-white text-left shadow-lg focus:outline-none dark:bg-gray-950 dark:text-gray-100 dark:ring-gray-700 ${ type === "plain" - ? "top-[calc(100%-0.1rem)] w-[9.5rem] max-w-[calc(100vw-2rem)] rounded-[0.2rem] border border-outlineVariant py-[0.3rem] text-sm" + ? "top-[calc(100%-0.1rem)] w-[9.5rem] max-w-[calc(100vw-2rem)] rounded-[0.2rem] border border-outlineVariant dark:border-gray-700 py-[0.3rem] text-sm" : "min-w-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 sm:text-sm" }`} tabIndex={-1} @@ -181,7 +181,7 @@ export function LangSelector({ {type === "icon" ? ( <Fragment> <li - class="text-gray-900 relative border-b boder-gray-200 select-none py-2 pl-3 pr-9" + class="text-gray-900 dark:text-gray-100 relative border-b border-gray-200 dark:border-gray-700 select-none py-2 pl-3 pr-9" aria-current="true" > <span class="font-normal truncate flex justify-between "> @@ -207,8 +207,8 @@ export function LangSelector({ role="none" class={ type === "plain" - ? "relative flex min-h-[2.4rem] cursor-pointer select-none items-center px-3 text-onBackground hover:text-primary" - : "text-gray-900 hover:bg-primary hover:bg-gray-300 cursor-pointer relative select-none py-2 pl-3 pr-9" + ? "relative flex min-h-[2.4rem] cursor-pointer select-none items-center px-3 text-onBackground hover:text-primary dark:text-gray-100 dark:hover:text-indigo-300" + : "text-gray-900 hover:bg-primary hover:bg-gray-300 dark:text-gray-100 dark:hover:bg-gray-800 cursor-pointer relative select-none py-2 pl-3 pr-9" } > <button