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