[client] Add light mode with system, light, and dark theme options (#7344)

* desktop UI light mode

* Theme review fixes plus macOS window outline fix

* Windows runtime chrome re-theming plus apply serialization

* Windows chrome threading and theme event ordering fixes

* Darken toggle and setting sidebar text

* resolve theme appearance, apply on UI thread

* read theme once per window

* Re-assert Windows dark opt-in after SetTheme

* split app-wide GTK theming from per-window chrome

* Update Wails dependency and checksums

* KDE tray icon panel fix

* Five review fixes: theme ordering, cgo dedup, KDE panel resolution

* Path guard hardening, toggle contrast, windows comment

* non-vacuous escape tests

* Default view edits

* Polish settings nav, controls, borders, and disc

* Profiles settings boarder, modals, and buttons

* Additional edits based on feedback

* Switch colors away from slight blue hue

* Update missing lang

* Fix vertical tab active view
This commit is contained in:
Brandon Hopkins
2026-09-11 08:25:10 -07:00
committed by GitHub
parent f422c41654
commit ec0c36b0e7
68 changed files with 1853 additions and 259 deletions
+7 -5
View File
@@ -11,12 +11,14 @@ type Props = HTMLAttributes<HTMLSpanElement> & {
};
const VARIANT_CLASSES: Record<BadgeVariant, string> = {
info: "bg-sky-900 border border-sky-700 text-sky-200",
info: "bg-sky-100 border border-sky-300 text-sky-800 dark:bg-sky-900 dark:border-sky-700 dark:text-sky-200",
neutral: "bg-nb-gray-900 border border-nb-gray-850 text-nb-gray-200",
brand: "bg-netbird/15 border border-netbird/30 text-netbird",
success: "bg-green-900 border border-green-700 text-green-200",
warning: "bg-yellow-900 border border-yellow-700 text-yellow-200",
danger: "bg-red-900 border border-red-700 text-red-200",
brand: "bg-netbird/15 border border-netbird/30 text-netbird-700 dark:text-netbird",
success:
"bg-green-100 border border-green-300 text-green-800 dark:bg-green-900 dark:border-green-700 dark:text-green-200",
warning:
"bg-yellow-100 border border-yellow-300 text-yellow-800 dark:bg-yellow-900 dark:border-yellow-700 dark:text-yellow-200",
danger: "bg-red-100 border border-red-300 text-red-800 dark:bg-red-900 dark:border-red-700 dark:text-red-200",
};
export const Badge = forwardRef<HTMLSpanElement, Props>(function Badge(
@@ -81,7 +81,7 @@ export const CopyToClipboard = ({
aria-live={"polite"}
className={cn(
"group/copy wails-no-draggable pointer-events-auto inline-flex cursor-default items-center gap-2 rounded-sm text-left outline-none",
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
className,
)}
>
@@ -16,7 +16,7 @@ const menuItemVariants = cva("", {
variant: {
default:
"text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50 focus-visible:bg-nb-gray-900 focus-visible:text-nb-gray-50 data-[state=open]:bg-nb-gray-900 data-[state=open]:text-nb-gray-50",
danger: "text-red-500 hover:bg-red-900/20 hover:text-red-500 focus-visible:bg-red-900/20 focus-visible:text-red-500",
danger: "text-red-500 hover:bg-red-500/10 hover:text-red-500 focus-visible:bg-red-500/10 focus-visible:text-red-500 dark:hover:bg-red-900/20 dark:focus-visible:bg-red-900/20",
},
},
defaultVariants: { variant: "default" },
@@ -97,9 +97,9 @@ export function LanguagePicker() {
"rounded-md border bg-white dark:bg-nb-gray-900",
"border-neutral-200 dark:border-nb-gray-700",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-none",
"hover:border-nb-gray-600 data-[state=open]:border-nb-gray-600",
"hover:border-nb-gray-700 data-[state=open]:border-nb-gray-700 dark:hover:border-nb-gray-600 dark:data-[state=open]:border-nb-gray-600",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"disabled:opacity-50",
)}
>
@@ -157,7 +157,7 @@ export function LanguagePicker() {
placeholder={t("settings.general.language.search")}
aria-label={t("settings.general.language.search")}
className={cn(
"w-full bg-transparent text-xs text-nb-gray-100 placeholder:text-nb-gray-300",
"w-full bg-transparent text-xs text-nb-gray-100 placeholder:text-nb-gray-600 dark:placeholder:text-nb-gray-300",
"border-none outline-none",
)}
/>
@@ -5,7 +5,7 @@ import { cn } from "@/lib/cn";
export type SquareIconVariant = "default" | "info" | "warning" | "danger";
const variantClass: Record<SquareIconVariant, string> = {
default: "text-white",
default: "text-nb-gray-50",
info: "text-sky-400",
warning: "text-netbird",
danger: "text-red-500",
@@ -27,7 +27,7 @@ export const SquareIcon = ({
<div
aria-hidden={"true"}
className={cn(
"flex h-11 w-11 items-center justify-center rounded-lg border border-nb-gray-900 bg-nb-gray-920",
"flex h-11 w-11 items-center justify-center rounded-lg border border-nb-gray-800 bg-nb-gray-920 dark:border-nb-gray-900",
variantClass[variant],
className,
)}
@@ -0,0 +1,109 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { ChevronDown, MonitorIcon, MoonIcon, SunMediumIcon, type LucideIcon } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/DropdownMenu";
import { HelpText } from "@/components/typography/HelpText";
import { Label } from "@/components/typography/Label";
import { useTheme, type ThemePreference } from "@/contexts/ThemeContext";
import { useFocusVisible } from "@/hooks/useFocusVisible";
import { cn } from "@/lib/cn";
import { errorDialog, formatErrorMessage } from "@/lib/errors";
const OPTIONS: { value: ThemePreference; icon: LucideIcon; labelKey: string }[] = [
{ value: "system", icon: MonitorIcon, labelKey: "settings.general.theme.system" },
{ value: "light", icon: SunMediumIcon, labelKey: "settings.general.theme.light" },
{ value: "dark", icon: MoonIcon, labelKey: "settings.general.theme.dark" },
];
export function ThemePicker() {
const { t } = useTranslation();
const { theme, setTheme } = useTheme();
const [busy, setBusy] = useState(false);
const isFocusVisible = useFocusVisible();
const current = OPTIONS.find((o) => o.value === theme) ?? OPTIONS[0];
const CurrentIcon = current.icon;
const select = async (value: string) => {
if (busy || value === theme) return;
setBusy(true);
try {
await setTheme(value as ThemePreference);
} catch (e) {
await errorDialog({
Title: t("settings.error.saveTitle"),
Message: formatErrorMessage(e),
});
} finally {
setBusy(false);
}
};
return (
<div className={"flex items-center justify-between gap-6"}>
<div className={"max-w-md flex-1"}>
<Label as={"div"}>{t("settings.general.theme.label")}</Label>
<HelpText margin={false}>{t("settings.general.theme.help")}</HelpText>
</div>
<div className={"shrink-0"}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type={"button"}
tabIndex={0}
disabled={busy}
aria-label={t("settings.general.theme.label")}
className={cn(
"inline-flex h-[40px] min-w-[160px] items-center gap-2 px-3",
"rounded-md border bg-white dark:bg-nb-gray-900",
"border-neutral-200 dark:border-nb-gray-700",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-none",
"hover:border-nb-gray-700 data-[state=open]:border-nb-gray-700 dark:hover:border-nb-gray-600 dark:data-[state=open]:border-nb-gray-600",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"disabled:opacity-50",
)}
>
<CurrentIcon
size={16}
aria-hidden={"true"}
className={"shrink-0 text-nb-gray-200"}
/>
<span className={"flex-1 truncate text-left"}>
{t(current.labelKey)}
</span>
<ChevronDown
size={12}
aria-hidden={"true"}
className={"shrink-0 text-nb-gray-400"}
/>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align={"end"}
className={"w-[var(--radix-dropdown-menu-trigger-width)]"}
>
<DropdownMenuRadioGroup value={theme} onValueChange={(v) => void select(v)}>
{OPTIONS.map(({ value, icon: Icon, labelKey }) => (
<DropdownMenuRadioItem key={value} value={value}>
<Icon
size={14}
aria-hidden={"true"}
className={"mr-2 shrink-0 text-nb-gray-300"}
/>
{t(labelKey)}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
}
@@ -81,12 +81,12 @@ export const Tooltip = ({
onPointerLeave={interactive ? scheduleClose : undefined}
onPointerDownOutside={interactive ? undefined : (e) => e.preventDefault()}
className={cn(
"z-50 select-none text-xs text-nb-gray-100 shadow-lg",
"z-50 select-none text-xs text-nb-gray-100 shadow-sm dark:shadow-lg",
"data-[state=delayed-open]:animate-in data-[state=closed]:animate-out",
"data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0",
!interactive && "pointer-events-none",
contentClassName ??
"rounded-md border border-nb-gray-850 bg-nb-gray-900 px-2 py-1",
"rounded-md border border-nb-gray-800 bg-white px-2 py-1 dark:border-nb-gray-850 dark:bg-nb-gray-900",
)}
>
{content}
@@ -46,12 +46,12 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
<Tabs.Trigger
ref={ref}
className={cn(
"group flex w-full cursor-default items-center gap-3 rounded-lg px-2 py-2.5 text-left outline-none",
"group flex w-full cursor-default items-center gap-3 rounded-md border border-transparent px-2 py-2.5 text-left outline-none dark:border-0",
"transition-colors duration-150",
"data-[state=active]:bg-nb-gray-930",
"data-[state=inactive]:hover:bg-nb-gray-935",
"data-[state=active]:border-nb-gray-800 data-[state=active]:bg-white dark:data-[state=active]:bg-nb-gray-930",
"data-[state=inactive]:hover:bg-nb-gray-850 dark:data-[state=inactive]:hover:bg-nb-gray-935",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
className,
)}
{...props}
@@ -61,13 +61,15 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
aria-hidden={"true"}
className={cn(
"ml-2 shrink-0 transition-colors duration-150",
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
"text-nb-gray-350 dark:text-nb-gray-400",
"group-data-[state=active]:text-nb-gray-100",
)}
/>
<span
className={cn(
"min-w-0 truncate text-sm font-medium transition-colors duration-150",
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
"text-nb-gray-350 dark:text-nb-gray-400",
"group-data-[state=active]:font-semibold group-data-[state=active]:text-nb-gray-100 dark:group-data-[state=active]:font-medium",
)}
>
{title}
@@ -24,71 +24,74 @@ const buttonVariants = cva(
variants: {
variant: {
default: [
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"dark:border-gray-700/30 dark:bg-nb-gray dark:text-gray-400 dark:hover:bg-zinc-800/50 dark:hover:text-white dark:focus:ring-zinc-800/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:border-gray-700/30 dark:bg-nb-gray dark:text-gray-400 dark:hover:bg-zinc-800/50 dark:hover:text-nb-gray-50 dark:focus:ring-zinc-800/50",
],
primary: [
"dark:text-gray-100 dark:ring-offset-neutral-950/50 dark:focus:ring-netbird-600/50 enabled:dark:bg-netbird enabled:dark:hover:bg-netbird-500/80 enabled:dark:hover:text-white disabled:dark:bg-nb-gray-900",
"enabled:bg-netbird enabled:text-white enabled:hover:bg-netbird-500 enabled:focus:ring-netbird-400/50",
"dark:text-gray-100 dark:ring-offset-neutral-950/50 dark:focus:ring-netbird-600/50 enabled:dark:bg-netbird enabled:dark:hover:bg-netbird-500/80 enabled:dark:hover:text-nb-gray-50 disabled:dark:bg-nb-gray-900",
"enabled:bg-netbird enabled:text-white enabled:hover:bg-netbird-500 enabled:focus:ring-netbird-400/50 disabled:bg-nb-gray-700",
],
secondary: [
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
"dark:border-gray-700/40 dark:bg-nb-gray-920 dark:text-gray-400 dark:hover:bg-nb-gray-910 dark:hover:text-white",
"border-neutral-200 bg-white text-neutral-900 hover:border-nb-gray-700 hover:bg-nb-gray-950 hover:text-black focus:ring-nb-gray-500/50 focus:ring-offset-0",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20 dark:focus:ring-offset-1",
"dark:border-gray-700/40 dark:bg-nb-gray-920 dark:text-gray-400 dark:hover:border-gray-700/40 dark:hover:bg-nb-gray-910 dark:hover:text-nb-gray-50",
],
secondaryLighter: [
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
"dark:border-gray-700/70 dark:bg-nb-gray-900/70 dark:text-gray-400 dark:hover:bg-nb-gray-800/60 dark:hover:text-white",
"dark:border-gray-700/70 dark:bg-nb-gray-900/70 dark:text-gray-400 dark:hover:bg-nb-gray-800/60 dark:hover:text-nb-gray-50",
],
subtle: [
"border-nb-gray-200 bg-nb-gray-50 text-nb-gray-900 hover:bg-nb-gray-100 focus:ring-nb-gray-200/60",
"border-neutral-200 bg-neutral-50 text-neutral-900 hover:bg-neutral-100 focus:ring-neutral-200/60",
"dark:ring-offset-neutral-950/50 dark:focus:ring-nb-gray-200/40",
"dark:border-nb-gray-200 dark:bg-nb-gray-50 dark:text-nb-gray-900 dark:hover:bg-nb-gray-100 dark:hover:text-nb-gray-950",
],
input: [
"border-neutral-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
"dark:border-nb-gray-700 dark:bg-nb-gray-900 dark:text-gray-400 dark:hover:bg-nb-gray-900/80",
],
dropdown: [
"border-neutral-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
"dark:border-nb-gray-900 dark:bg-nb-gray-900/40 dark:text-gray-400 dark:hover:bg-nb-gray-900/50",
],
dotted: [
"border-dashed border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-dashed border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
"dark:border-gray-500/40 dark:bg-nb-gray-900/30 dark:text-gray-400 dark:hover:bg-nb-gray-900/50 dark:hover:text-white",
"dark:border-gray-500/40 dark:bg-nb-gray-900/30 dark:text-gray-400 dark:hover:bg-nb-gray-900/50 dark:hover:text-nb-gray-50",
],
tertiary: [
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:border-gray-700/40 dark:bg-white dark:text-gray-800 dark:hover:bg-neutral-200 dark:focus:ring-zinc-800/50 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
],
white: [
"border-white bg-white text-gray-800 outline-none hover:bg-neutral-200 focus:ring-white/50 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
"border-white bg-white text-neutral-800 outline-none hover:bg-neutral-200 focus:ring-white/50 dark:text-gray-800 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
"disabled:dark:border-nb-gray-900 disabled:dark:bg-nb-gray-900 disabled:dark:text-nb-gray-300",
],
outline: [
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:border-netbird dark:bg-transparent dark:text-netbird dark:hover:bg-nb-gray-900/30 dark:focus:ring-zinc-800/50",
],
"danger-outline": [
"bg-transparent text-red-600 enabled:hover:bg-red-50 enabled:focus:ring-red-200/50",
"dark:bg-transparent dark:text-red-500 enabled:dark:hover:border-red-800/50 enabled:hover:dark:bg-red-950/50 enabled:dark:focus:bg-red-950/40 enabled:dark:focus:ring-red-800/20",
],
"danger-text": [
"rounded-sm !px-0 !py-0 !shadow-none focus:ring-red-500/30 dark:border-transparent dark:bg-transparent dark:text-red-500 dark:ring-offset-neutral-950/50 dark:hover:text-red-600",
"rounded-sm border-transparent bg-transparent !px-0 !py-0 text-red-600 !shadow-none hover:text-red-700 focus:ring-red-500/30",
"dark:border-transparent dark:bg-transparent dark:text-red-500 dark:ring-offset-neutral-950/50 dark:hover:text-red-600",
],
"default-outline": [
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
"dark:border-transparent dark:bg-transparent dark:text-nb-gray-400 dark:hover:border-nb-gray-800/50 dark:hover:bg-nb-gray-900/30 dark:hover:text-white",
"data-[state=open]:dark:border-nb-gray-800/50 data-[state=open]:dark:bg-nb-gray-900/30 data-[state=open]:dark:text-white",
"ring-offset-nb-gray-950/50 focus:ring-nb-gray-500/20",
"border-transparent bg-transparent text-nb-gray-400 hover:border-nb-gray-800/50 hover:bg-nb-gray-900/30 hover:text-nb-gray-50",
"data-[state=open]:border-nb-gray-800/50 data-[state=open]:bg-nb-gray-900/30 data-[state=open]:text-nb-gray-50",
],
ghost: [
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
"dark:border-transparent dark:bg-transparent dark:text-nb-gray-400 dark:hover:bg-nb-gray-900/30 dark:hover:text-white",
"ring-offset-nb-gray-950/50 focus:ring-nb-gray-500/20",
"border-transparent bg-transparent text-nb-gray-400 hover:bg-nb-gray-900/30 hover:text-nb-gray-50",
],
danger: [
"bg-red-600 text-red-50 hover:bg-red-700 focus:bg-red-700 focus:ring-red-700/20",
"dark:bg-red-600 dark:text-red-100 dark:hover:border-red-800/50 hover:dark:bg-red-700 dark:focus:bg-red-700 dark:focus:ring-red-700/20",
],
},
@@ -24,7 +24,7 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButt
"flex h-10 w-10 cursor-default items-center justify-center rounded-lg outline-none",
"text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-300",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"wails-no-draggable transition-colors duration-150",
className,
)}
@@ -23,7 +23,7 @@ const Overlay = forwardRef<ElementRef<typeof DialogPrimitive.Overlay>, OverlayPr
ref={ref}
className={cn(
"fixed inset-0 z-50 grid items-center justify-items-center overflow-y-auto px-10 py-16",
"bg-black/60",
"bg-black/25 dark:bg-black/60",
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
exitAnimation &&
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
@@ -67,7 +67,7 @@ export const Content = forwardRef<ElementRef<typeof DialogPrimitive.Content>, Co
className={cn(
"relative z-[52] mx-auto w-full outline-none ring-0",
"focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0",
"rounded-lg border border-nb-gray-900 bg-nb-gray py-7 shadow-2xl",
"rounded-lg border border-nb-gray-800 bg-nb-gray-940 py-7 shadow-2xl dark:border-nb-gray-900 dark:bg-nb-gray",
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
"data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1",
exitAnimation &&
@@ -32,19 +32,19 @@ const inputVariants = cva("", {
variants: {
variant: {
default: [
"border-neutral-200 placeholder:text-neutral-500 dark:border-nb-gray-700 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"border-neutral-200 placeholder:text-nb-gray-600 dark:border-nb-gray-700 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"ring-offset-neutral-200/20 focus-visible:ring-neutral-300/10 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20",
],
darker: [
"border-neutral-300 placeholder:text-neutral-500 dark:border-nb-gray-800 dark:bg-nb-gray-920 dark:placeholder:text-neutral-400/70",
"border-neutral-300 placeholder:text-nb-gray-600 dark:border-nb-gray-800 dark:bg-nb-gray-920 dark:placeholder:text-neutral-400/70",
"ring-offset-neutral-200/20 focus-visible:ring-neutral-300/10 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20",
],
error: [
"border-neutral-200 text-red-500 placeholder:text-neutral-500 dark:border-red-500 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"border-neutral-200 text-red-500 placeholder:text-nb-gray-600 dark:border-red-500 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"ring-offset-red-500/10 focus-visible:ring-red-500/10 dark:ring-offset-red-500/10 dark:focus-visible:ring-red-500/10",
],
warning: [
"border-neutral-200 text-orange-400 placeholder:text-neutral-500 dark:border-orange-400 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"border-neutral-200 text-orange-400 placeholder:text-nb-gray-600 dark:border-orange-400 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
"ring-offset-orange-400/10 focus-visible:ring-orange-400/10 dark:ring-offset-orange-400/10 dark:focus-visible:ring-orange-400/10",
],
},
@@ -158,7 +158,7 @@ function NumberStepper({
className={cn(
"flex h-[40px] shrink-0 flex-col overflow-hidden",
"rounded-r-md border border-l-0",
"border-neutral-200 dark:border-nb-gray-700 dark:bg-nb-gray-900",
"border-neutral-200 bg-white dark:border-nb-gray-700 dark:bg-nb-gray-900",
error && "dark:border-red-500",
disabled && "pointer-events-none opacity-40",
)}
@@ -274,7 +274,9 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
<button
type={"button"}
onClick={() => setShowPassword((s) => !s)}
className={"pointer-events-auto transition-all hover:text-white"}
className={
"pointer-events-auto text-nb-gray-400 transition-colors hover:text-nb-gray-50 dark:text-nb-gray-300 dark:hover:text-nb-gray-50"
}
aria-label={t("common.togglePasswordVisibility")}
aria-pressed={showPassword}
>
@@ -303,7 +305,9 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
<button
type={"button"}
onClick={onCopy}
className={"pointer-events-auto transition-all hover:text-white"}
className={
"pointer-events-auto text-nb-gray-400 transition-colors hover:text-nb-gray-50 dark:text-nb-gray-300 dark:hover:text-nb-gray-50"
}
aria-label={t("common.copy")}
>
{copied ? (
@@ -33,7 +33,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
spellCheck={false}
{...props}
className={cn(
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-400",
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-600 dark:placeholder:text-nb-gray-400",
"border-none outline-none",
disabled && "cursor-not-allowed",
className,
@@ -36,7 +36,7 @@ export default function FancyToggleSwitch({
if (loading) {
const shimmer =
"text-transparent select-none rounded bg-[#25282d] box-decoration-clone animate-pulse";
"text-transparent select-none rounded bg-nb-gray-920 box-decoration-clone animate-pulse";
return (
<div
role={"status"}
@@ -58,7 +58,9 @@ export default function FancyToggleSwitch({
<div className={"mt-2 pr-1"}>
<div
aria-hidden={"true"}
className={"h-[24px] w-[44px] animate-pulse rounded-full bg-[#25282d]"}
className={
"h-[24px] w-[44px] animate-pulse rounded-full bg-nb-gray-920"
}
/>
</div>
</div>
@@ -20,7 +20,7 @@ export const SwitchItem = ({ value, children, className }: Props) => {
className={cn(
"relative inline-flex items-center justify-center gap-1 rounded-md px-3.5 py-2 text-xs font-semibold",
"cursor-default outline-none",
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
active
? "text-nb-gray-100"
: "text-nb-gray-400 hover:text-nb-gray-200 active:text-nb-gray-100",
@@ -30,7 +30,9 @@ export const SwitchItem = ({ value, children, className }: Props) => {
{active && (
<motion.span
layoutId={layoutId}
className={"absolute inset-0 rounded-md bg-nb-gray-700"}
className={
"absolute inset-0 rounded-md bg-white shadow-sm dark:bg-nb-gray-700 dark:shadow-none"
}
transition={{ type: "spring", stiffness: 500, damping: 35 }}
/>
)}
@@ -48,7 +48,7 @@ export const SwitchItemGroup = ({
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={cn(
"flex shrink-0 overflow-hidden rounded-lg border border-nb-gray-850 bg-nb-gray-910 p-1",
"flex shrink-0 overflow-hidden rounded-lg border border-nb-gray-800 bg-nb-gray-910 p-1 dark:border-nb-gray-850",
disabled && "pointer-events-none opacity-50",
className,
)}
@@ -18,8 +18,8 @@ const switchVariants = cva("", {
default: [
"dark:data-[state=checked]:bg-netbird dark:data-[state=unchecked]:bg-nb-gray-700",
"dark:data-[state=checked]:hover:bg-netbird-500 dark:data-[state=unchecked]:hover:bg-nb-gray-600",
"data-[state=checked]:bg-neutral-900 data-[state=unchecked]:bg-neutral-200",
"data-[state=checked]:hover:bg-neutral-800 data-[state=unchecked]:hover:bg-neutral-300",
"data-[state=checked]:bg-netbird data-[state=unchecked]:bg-nb-gray-700",
"data-[state=checked]:hover:bg-netbird-500 data-[state=unchecked]:hover:bg-nb-gray-600/60",
],
"red-green": [
"dark:data-[state=checked]:bg-red-600 dark:data-[state=unchecked]:bg-nb-gray-700",
@@ -52,7 +52,7 @@ const ToggleSwitch = React.forwardRef<
disabled={disabled}
tabIndex={disabled ? -1 : 0}
className={cn(
"wails-no-draggable peer inline-flex shrink-0 cursor-default items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940 disabled:cursor-not-allowed disabled:opacity-50",
"wails-no-draggable peer inline-flex shrink-0 cursor-default items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940 disabled:cursor-not-allowed disabled:opacity-50",
className,
switchVariants({ size, variant }),
)}
@@ -11,7 +11,7 @@ type Props = {
export const HelpText = ({ children, margin = true, className, disabled = false }: Props) => (
<span
className={cn(
"block text-[.81rem] font-light tracking-wide transition-all duration-300 dark:text-nb-gray-300",
"block text-[.81rem] font-light tracking-wide text-nb-gray-300 transition-all duration-300",
margin && "mb-2",
disabled && "pointer-events-none opacity-30",
className,