fix: consolidate focus states across inputs (#46588)

## Problem
All inputs don't have the same style when focused

## Solution
Consolidate focus states across inputs

## How to test
Checkout the design system form examples:
https://design-system-git-gildasgarcia-depr-354-consoli-0188d7-supabase.vercel.app/design-system/docs/ui-patterns/forms

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Refined focus-visible ring/outline and ring-offset tokens across
buttons, multi-select, radio groups, checkbox, select, and switch for a
more consistent accessible experience.
* Updated focus-state styling details within the shared Button and
related controls (including open-state outline behavior).
* **Refactor**
* Updated icon upload preview triggers in form examples to use the
shared Button component while preserving existing upload behavior and
visuals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Gildas Garcia authored and GitHub committed 2026-06-17 16:00:34 +02:00
1 parent c79f8fc2b0
commit 211b99f144
8 files changed
+22 -18

No files matched your search

@@ -327,16 +327,17 @@ export default function FormPatternsPageLayout() {
>
<FormControl>
<div className="flex gap-4 items-center">
<button
<Button
type="button"
variant="outline"
onClick={() => uploadButtonRef.current?.click()}
className="flex items-center justify-center h-10 w-10 shrink-0 text-foreground-lighter hover:text-foreground-light overflow-hidden rounded-full bg-cover border hover:border-strong"
className="flex items-center justify-center h-10 w-10 shrink-0 overflow-hidden rounded-full"
style={{
backgroundImage: logoUrl ? `url("${logoUrl}")` : 'none',
}}
>
{!logoUrl && <Upload size={14} />}
</button>
</Button>
<div className="flex gap-2 items-center">
<Button
variant="default"
@@ -318,7 +318,7 @@ export default function FormPatternsSidePanel() {
<button
type="button"
onClick={() => uploadButtonRef.current?.click()}
className="flex items-center justify-center h-10 w-10 shrink-0 text-foreground-lighter hover:text-foreground-light overflow-hidden rounded-full bg-cover border hover:border-strong"
className="flex items-center justify-center h-10 w-10 shrink-0 text-foreground-lighter hover:text-foreground-light overflow-hidden rounded-full bg-cover border hover:border-strong focus-visible:outline-brand-600"
style={{
backgroundImage: logoUrl ? `url("${logoUrl}")` : 'none',
}}
@@ -327,7 +327,8 @@ export default function FormPatternsSidePanel() {
</button>
<div className="flex gap-2 items-center">
<Button
variant="default"
type="button"
variant="outline"
size="tiny"
icon={<Upload size={14} />}
onClick={() => uploadButtonRef.current?.click()}
@@ -8,11 +8,11 @@ import React, { isValidElement, ReactElement, useEffect } from 'react'
import {
Badge,
cn,
Command as Command,
CommandEmpty as CommandEmpty,
CommandInput as CommandInput,
CommandItem as CommandItem,
CommandList as CommandList,
Command,
CommandEmpty,
CommandInput,
CommandItem,
CommandList,
Popover,
PopoverAnchor,
PopoverContent,
@@ -294,7 +294,7 @@ const MultiSelectorTrigger = React.forwardRef<HTMLButtonElement, MultiSelectorTr
'flex w-full min-w-[200px] min-h-[40px] items-center justify-between rounded-md border',
'border-alternative bg-control px-3 py-2 text-sm',
'ring-offset-background placeholder:text-muted-foreground',
'focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',
'focus:outline-hidden focus:ring-2 focus:ring-background-control focus:ring-offset-2 focus-visible:ring-offset-foreground-muted',
'disabled:cursor-not-allowed disabled:opacity-50',
'hover:border-primary transition-colors duration-200',
className
+1 -1
View File
@@ -46,7 +46,7 @@ const buttonVariants = cva(
border-strong hover:border-stronger
focus-visible:outline-border-strong
data-[state=open]:bg-selection
data-[state=open]:outline-brand-600
data-[state=open]:outline-border-strong
data-[state=open]:border-button-hover
`,
secondary: `
@@ -47,6 +47,8 @@ const RadioGroupStackedItem = React.forwardRef<
'disabled:opacity-50 disabled:cursor-not-allowed',
// Enabled/hover states
'enabled:cursor-pointer enabled:hover:bg-surface-300 enabled:hover:border-foreground-muted',
// Focus state
'focus:ring-background-control focus:border-control focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted',
// Z-index for interactions
'hover:z-1 focus-visible:z-1 data-[state=checked]:z-1',
// Checked state
@@ -68,7 +70,7 @@ const RadioGroupStackedItem = React.forwardRef<
// States
'group-data-[state=checked]:border-foreground-muted',
'group-focus:border-foreground-muted group-focus:outline-hidden',
'group-focus-visible:ring-2 group-focus-visible:ring-ring group-focus-visible:ring-offset-2',
'group-focus-visible:ring-2 group-focus-visible:ring-background-control group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-foreground-muted',
'group-hover:border-foreground-muted'
)}
>
@@ -1,7 +1,7 @@
'use client'
import { Checkbox as CheckboxPrimitive } from 'radix-ui'
import { Check } from 'lucide-react'
import { Checkbox as CheckboxPrimitive } from 'radix-ui'
import * as React from 'react'
import { cn } from '../../../lib/utils/cn'
@@ -16,7 +16,7 @@ const Checkbox = React.forwardRef<
'peer flex items-center justify-center h-4 w-4 shrink-0 rounded-sm border border-control bg-control/25 ring-offset-background',
'transition-colors duration-150 ease-in-out',
'hover:border-strong',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted',
'disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-foreground data-[state=checked]:text-background',
className
)}
@@ -47,7 +47,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
'flex w-full items-center justify-between rounded-md border border-strong hover:border-stronger bg-alternative dark:bg-muted hover:bg-selection text-xs ring-offset-background-control data-[placeholder]:text-foreground-lighter focus:outline-hidden ring-border-control focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-all duration-200',
'flex w-full items-center justify-between rounded-md border border-strong hover:border-stronger bg-alternative dark:bg-muted hover:bg-selection text-xs ring-offset-background-control data-[placeholder]:text-foreground-lighter focus:outline-hidden ring-border-control focus:ring-2 focus:ring-background-control focus:ring-offset-2 focus-visible:ring-offset-foreground-muted disabled:cursor-not-allowed disabled:opacity-50 transition-all duration-200',
'data-[state=open]:bg-selection data-[state=open]:border-stronger',
'gap-2',
'[&>span]:truncate text-left', // [kemal] This is to prevent double lines rendering when a string is particularly long.
@@ -1,13 +1,13 @@
'use client'
import { Switch as SwitchPrimitives } from 'radix-ui'
import { cva, VariantProps } from 'class-variance-authority'
import { Switch as SwitchPrimitives } from 'radix-ui'
import * as React from 'react'
import { cn } from '../../../lib/utils/cn'
const switchRootVariants = cva(
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-brand data-[state=checked]:hover:bg-brand-600/90 data-[state=unchecked]:bg-control data-[state=unchecked]:hover:bg-border',
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-brand data-[state=checked]:hover:bg-brand-600/90 data-[state=unchecked]:bg-control data-[state=unchecked]:hover:bg-border',
{
variants: {
size: {