diff --git a/package.json b/package.json index 57bf4fcc..8f1544b6 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "next": "^15.1.8", "react": "^19.0.0", "react-dom": "^19.0.0", - "reframe": ".", "tailwind-merge": "^3.6.0", "wasm-feature-detect": "^1.8.0" }, diff --git a/src/app/globals.css b/src/app/globals.css index 408e2d8e..83f75d3b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -12,6 +12,8 @@ --accent: #3b82f6; --accent-hover: #1d4ed8; --accent-muted: rgba(59, 130, 246, 0.12); + --focus-ring: var(--accent); + --focus-ring-glow: rgba(59, 130, 246, 0.45); --radius: 10px; --shadow: 0 2px 12px rgba(15, 23, 42, 0.12); --film-600: #e63946; @@ -33,6 +35,8 @@ --accent: #4f6ef7; --accent-hover: #3a57d4; --accent-muted: rgba(79, 110, 247, 0.12); + --focus-ring: var(--accent); + --focus-ring-glow: rgba(79, 110, 247, 0.55); --radius: 10px; --shadow: 0 2px 12px rgba(0, 0, 0, 0.3); --warning: #fbbf24; @@ -54,6 +58,8 @@ --accent: #FFFF00; --accent-hover: #FFFF00; --accent-muted: rgba(255, 255, 0, 0.22); + --focus-ring: var(--accent); + --focus-ring-glow: rgba(255, 255, 0, 0.55); --radius: 10px; --shadow: none; @@ -125,8 +131,8 @@ textarea { input:focus, select:focus, textarea:focus { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-muted); + border-color: var(--focus-ring); + box-shadow: 0 0 0 4px var(--focus-ring-glow); outline: none; } @@ -139,8 +145,33 @@ textarea:focus { transition-duration: 200ms; } -:focus-visible { - outline: 0; - box-shadow: 0 0 0 3px var(--accent-muted); - border-radius: var(--radius); +/* + * Keyboard focus — rules follow @tailwind utilities so they apply app-wide. + * Uses --focus-ring / --focus-ring-glow for light, dark, and high-contrast themes. + */ +button:focus-visible:not(:disabled), +a:focus-visible, +[role="button"]:focus-visible, +[role="slider"]:focus-visible, +[role="tab"]:focus-visible, +summary:focus-visible, +label:focus-visible { + outline: 2px solid var(--focus-ring); + outline-offset: 2px; + box-shadow: 0 0 0 4px var(--focus-ring-glow); +} + +input:focus-visible, +select:focus-visible, +textarea:focus-visible { + outline: 2px solid var(--focus-ring); + outline-offset: 0; + border-color: var(--focus-ring); + box-shadow: 0 0 0 4px var(--focus-ring-glow); +} + +input[type="range"]:focus-visible { + outline: 2px solid var(--focus-ring); + outline-offset: 2px; + box-shadow: 0 0 0 4px var(--focus-ring-glow); } diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index 03e32bef..ebd1ff07 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -1,5 +1,6 @@ "use client"; +import { cn, focusRing } from "@/lib/utils"; import { useTheme } from "./ThemeProvider"; export function ThemeToggle() { @@ -11,17 +12,12 @@ export function ThemeToggle() { type="button" onClick={toggleTheme} aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"} - className=" - relative flex items-center justify-center - w-9 h-9 rounded-full - bg-[var(--surface)] - text-[var(--text)] - border border-[var(--border)] - hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] - focus:outline-none focus:ring-2 focus:ring-[var(--accent)] focus:ring-offset-2 - focus:ring-offset-[var(--bg)] - transition-all duration-200 - " + className={cn( + "relative flex items-center justify-center w-9 h-9 rounded-full", + "bg-[var(--surface)] text-[var(--text)] border border-[var(--border)]", + "hover:border-[var(--accent)] hover:bg-[var(--accent-muted)] transition-all duration-200", + focusRing + )} > {isDark ? ( {children} @@ -86,13 +86,12 @@ export function Button({ variant = "primary", className = "", children, ...props export function Input({ className = "", ...props }: React.InputHTMLAttributes) { return ( ); diff --git a/src/components/ui/BaseButton.tsx b/src/components/ui/BaseButton.tsx index e889f045..40c713f4 100644 --- a/src/components/ui/BaseButton.tsx +++ b/src/components/ui/BaseButton.tsx @@ -1,6 +1,6 @@ "use client"; -import { cn } from "@/lib/utils"; +import { cn, focusRing } from "@/lib/utils"; import { ButtonHTMLAttributes, forwardRef } from "react"; interface BaseButtonProps extends ButtonHTMLAttributes { @@ -39,6 +39,7 @@ const BaseButton = forwardRef