Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/native-select-dark-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@executor-js/react": patch
---

Keep native `<select>` dropdown options readable in dark mode. The console themes through `prefers-color-scheme` and never sets a `.dark` class, so Tailwind `dark:` utilities never matched and the native option popup rendered with a light color scheme over dark text. `NativeSelect` now uses a solid themed surface (`bg-popover`) and pins `color-scheme` to the active theme, so the browser draws a matching, readable popup.
12 changes: 10 additions & 2 deletions packages/react/src/components/native-select.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import * as React from "react";
import { ChevronDownIcon } from "lucide-react";

import { useIsDark } from "../hooks/use-is-dark";
import { cn } from "../lib/utils";

function NativeSelect({
className,
size = "default",
style,
...props
}: Omit<React.ComponentProps<"select">, "size"> & { size?: "sm" | "default" }) {
const isDark = useIsDark();
return (
<div
className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
data-slot="native-select-wrapper"
>
{/* The native option popup follows the select's opaque background and
color-scheme, not option-level styles. Give it a solid themed surface
and pin color-scheme so options stay readable in dark mode, which is
driven by prefers-color-scheme (no .dark class in this app). */}
{/* oxlint-disable-next-line react/forbid-elements */}
<select
data-slot="native-select"
data-size={size}
style={{ colorScheme: isDark ? "dark" : "light", ...style }}
className={cn(
"h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1 dark:bg-input/30 dark:hover:bg-input/50",
"h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-popover text-popover-foreground px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground hover:bg-accent disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1",
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
className,
)}
{...props}
Expand Down
Loading