fix(webui): soften form control focus rings

This commit is contained in:
chengyongru
2026-08-11 14:39:07 +08:00
committed by chengyongru
parent cac39477ba
commit 3778e7e628
10 changed files with 47 additions and 12 deletions
+1 -1
View File
@@ -362,7 +362,7 @@ function AuthForm({
disabled={submitting}
aria-invalid={validationError ? true : undefined}
aria-describedby={validationError ? "webui-auth-error" : undefined}
className="pr-10 focus-visible:ring-1 focus-visible:ring-ring/30 focus-visible:ring-offset-0"
className="pr-10"
autoFocus
/>
<Button
@@ -19,7 +19,6 @@ import { cn } from "@/lib/utils";
export const SETTINGS_SEARCH_INPUT_CLASS = cn(
"border-border/45 bg-settings-surface transition-colors hover:border-border/70",
"focus-visible:border-border/70 focus-visible:bg-background",
"focus-visible:ring-0 focus-visible:ring-offset-0",
);
export function CapabilityInstallNotice({
@@ -35,6 +35,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { formControlFocusClassName } from "@/components/ui/form-control";
import { Input } from "@/components/ui/input";
import { SegmentedControl } from "@/components/ui/segmented-control";
import { Textarea } from "@/components/ui/textarea";
@@ -835,7 +836,10 @@ export function AutomationEditDialog({
everyUnit: event.target.value as AutomationEveryUnit,
}))
}
className="h-10 w-full rounded-[12px] border border-input bg-background px-3 text-[13px] text-foreground outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
className={cn(
"h-10 w-full rounded-[12px] border border-input bg-background px-3 text-[13px] text-foreground transition-colors",
formControlFocusClassName,
)}
>
{AUTOMATION_EVERY_UNITS.map((unit) => (
<option key={unit.value} value={unit.value}>
@@ -3,6 +3,7 @@ import { ListTree, Search } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { formControlFocusClassName } from "@/components/ui/form-control";
import {
Sheet,
SheetContent,
@@ -83,7 +84,8 @@ export function PromptNavigator({
placeholder={t("thread.promptNavigator.search")}
className={cn(
"h-10 w-full rounded-full border border-border bg-background pl-9 pr-3 text-sm",
"outline-none transition focus:border-ring focus:ring-2 focus:ring-ring/20",
"transition-colors",
formControlFocusClassName,
)}
/>
</div>
@@ -256,10 +256,7 @@ export function WorkspaceProjectPicker({
aria-label={t("workspace.dialog.manual")}
aria-invalid={pathError || error ? true : undefined}
aria-describedby={pathError || error ? pathErrorId : undefined}
className={cn(
"h-9 rounded-full border-border/55 bg-background/80 px-3 text-[12.5px]",
"focus-visible:ring-1 focus-visible:ring-foreground/10 focus-visible:ring-offset-0",
)}
className="h-9 rounded-full border-border/55 bg-background/80 px-3 text-[12.5px]"
/>
<Button
type="submit"
+2
View File
@@ -0,0 +1,2 @@
export const formControlFocusClassName =
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/50";
+3 -1
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import { formControlFocusClassName } from "@/components/ui/form-control";
import { cn } from "@/lib/utils";
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
@@ -10,7 +11,8 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
formControlFocusClassName,
className,
)}
ref={ref}
+3 -1
View File
@@ -1,5 +1,6 @@
import * as React from "react";
import { formControlFocusClassName } from "@/components/ui/form-control";
import { cn } from "@/lib/utils";
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
@@ -9,7 +10,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
return (
<textarea
className={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"flex min-h-[60px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
formControlFocusClassName,
className,
)}
ref={ref}
+26
View File
@@ -0,0 +1,26 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
describe("form control focus styles", () => {
it.each([
["input", <Input aria-label="input" />],
["textarea", <Textarea aria-label="textarea" />],
])("uses a subdued inset focus ring for the %s", (label, control) => {
render(control);
const element = screen.getByLabelText(label);
expect(element).toHaveClass(
"focus-visible:ring-2",
"focus-visible:ring-inset",
"focus-visible:ring-ring/50",
);
expect(element).not.toHaveClass(
"ring-offset-background",
"focus-visible:ring-ring",
"focus-visible:ring-offset-2",
);
});
});
+3 -2
View File
@@ -834,8 +834,9 @@ describe("Settings channels", () => {
const emailRow = await screen.findByRole("button", { name: "View Email settings" });
expect(screen.getByPlaceholderText("Search channels")).toHaveClass(
"focus-visible:ring-0",
"focus-visible:ring-offset-0",
"focus-visible:ring-2",
"focus-visible:ring-inset",
"focus-visible:ring-ring/50",
);
expect(screen.queryByRole("switch", { name: "Email channel" })).not.toBeInTheDocument();