feat(11-01): MD3 focus-visible ring-3 indicators on all interactive elements

- md3-buttons.ts: upgrade ring-2/50 to ring-3 on all three constants (FILLED, OUTLINED, TEXT)
- BackendCard: add focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary
- StepIndicator: upgrade group-focus-visible completed-step circle from ring-2/50 to ring-3
- ThemeToggle: add focus-visible ring-3 with ring-inset to both selected/unselected branches
- FieldRenderer: select uses focus-visible:ring-2 (was focus:ring-2); tooltip buttons get focus-visible:ring-2 rounded
This commit is contained in:
2026-04-01 13:25:00 +02:00
parent 07f97d9f25
commit 9015db565e
5 changed files with 10 additions and 10 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ export function BackendCard({ name, description, selected = false, onClick, ...r
onClick={onClick}
data-selected={selected}
className={[
'w-full flex flex-col items-start gap-1 rounded-xl border-2 p-4 text-left transition-all',
'w-full flex flex-col items-start gap-1 rounded-xl border-2 p-4 text-left transition-all focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary',
selected
? 'border-primary bg-primary/10 shadow-md'
: 'border-outline bg-surface-container shadow hover:border-primary hover:bg-surface hover:shadow-md',
+3 -3
View File
@@ -53,7 +53,7 @@ export function FieldRenderer({ field, register, error }: FieldRendererProps) {
onMouseEnter={() => setHoverTooltip(true)}
onMouseLeave={() => setHoverTooltip(false)}
aria-label={`More info about ${field.label}`}
className="text-primary hover:text-primary text-xs leading-none"
className="text-primary hover:text-primary text-xs leading-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"
>
</button>
@@ -67,7 +67,7 @@ export function FieldRenderer({ field, register, error }: FieldRendererProps) {
<select
id={field.key}
className={[
'w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2',
'w-full rounded-md border px-3 py-2 text-sm focus:outline-none focus-visible:ring-2',
error ? 'border-error focus:ring-error/50' : 'border-outline focus:ring-primary/50',
].join(' ')}
{...register(field.key)}
@@ -90,7 +90,7 @@ export function FieldRenderer({ field, register, error }: FieldRendererProps) {
onMouseEnter={() => setHoverTooltip(true)}
onMouseLeave={() => setHoverTooltip(false)}
aria-label={`More info about ${field.label}`}
className="text-primary hover:text-primary/80 text-xs leading-none mt-px shrink-0"
className="text-primary hover:text-primary/80 text-xs leading-none mt-px shrink-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary rounded"
>
</button>
+2 -2
View File
@@ -58,8 +58,8 @@ export function ThemeToggle() {
aria-pressed={theme === v}
className={
theme === v
? 'flex-1 px-3 py-1 bg-primary text-on-primary font-medium'
: 'flex-1 px-3 py-1 bg-surface-container text-on-surface-container hover:bg-surface'
? 'flex-1 px-3 py-1 bg-primary text-on-primary font-medium focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary focus-visible:ring-inset'
: 'flex-1 px-3 py-1 bg-surface-container text-on-surface-container hover:bg-surface focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary focus-visible:ring-inset'
}
>
{LABELS[v]}
+1 -1
View File
@@ -39,7 +39,7 @@ export function StepIndicator() {
aria-label={`Go to step ${i + 1}: ${label}`}
className="flex flex-col items-center gap-1 group focus-visible:outline-none"
>
<span className="w-8 h-8 rounded-full bg-primary text-on-primary flex items-center justify-center text-sm font-medium group-focus-visible:ring-2 group-focus-visible:ring-primary/50">
<span className="w-8 h-8 rounded-full bg-primary text-on-primary flex items-center justify-center text-sm font-medium group-focus-visible:ring-3 group-focus-visible:ring-primary">
&#10003;
</span>
<span className="text-xs text-primary hidden sm:block">{label}</span>
+3 -3
View File
@@ -11,16 +11,16 @@ export const MD3_BTN_FILLED =
'px-6 py-2.5 rounded-full bg-primary text-on-primary text-sm font-medium ' +
'hover:opacity-90 active:opacity-80 transition-opacity ' +
'disabled:opacity-40 disabled:cursor-not-allowed ' +
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50';
'focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary';
export const MD3_BTN_OUTLINED =
'px-6 py-2.5 rounded-full border border-outline text-on-surface text-sm font-medium ' +
'hover:bg-primary/8 active:bg-primary/12 transition-colors ' +
'disabled:opacity-40 disabled:cursor-not-allowed ' +
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50';
'focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary';
export const MD3_BTN_TEXT =
'px-4 py-2.5 rounded-full text-primary text-sm font-medium ' +
'hover:bg-primary/8 active:bg-primary/12 transition-colors ' +
'disabled:opacity-40 disabled:cursor-not-allowed ' +
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50';
'focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-primary';