From 6c9281698d7e428bbd0103daf962b477afd894de Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos <157128530+BillLeoutsakosvl346@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:11:38 -0700 Subject: [PATCH 1/2] improvement(ui): reuse shared button and focus treatments --- .../message-container/message-container.tsx | 3 ++- .../message/components/file-download.tsx | 2 +- .../app/workspace/[workspaceId]/home/home.tsx | 4 ++-- .../table-sidebar-header.tsx | 8 +++----- .../components/sidebar-row-actions.tsx | 4 ++-- .../src/components/button/button.test.tsx | 20 +++++++++++++++++++ .../emcn/src/components/button/button.tsx | 7 ++++++- .../field-divider/field-divider.tsx | 3 ++- .../emcn/src/components/slider/slider.tsx | 8 +++++++- .../emcn/src/components/switch/switch.tsx | 4 +++- packages/emcn/src/index.ts | 1 + packages/emcn/src/lib/focus-ring.ts | 3 +++ 12 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 packages/emcn/src/lib/focus-ring.ts diff --git a/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx b/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx index 9a355614557..b9dd835af01 100644 --- a/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx @@ -72,7 +72,8 @@ export function ChatMessageContainer({ + ) + const after = renderToStaticMarkup( + + ) + expect(normalizeClasses(after)).toBe(normalizeClasses(before)) + expect(after).not.toContain('focusRing=') + expect(renderToStaticMarkup()).not.toContain('focus-visible:ring-2') + }) + it('forwards refs and native focus, submission and disabled behavior with responsive sizing', () => { const container = document.createElement('div') document.body.appendChild(container) diff --git a/packages/emcn/src/components/button/button.tsx b/packages/emcn/src/components/button/button.tsx index 649c3524432..dfbfea46fb0 100644 --- a/packages/emcn/src/components/button/button.tsx +++ b/packages/emcn/src/components/button/button.tsx @@ -1,6 +1,7 @@ import { type ButtonHTMLAttributes, forwardRef } from 'react' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '../../lib/cn' +import { mutedFocusRingClass } from '../../lib/focus-ring' /** * `size='icon'` is the square 20px icon-only button — a chip field's trailing @@ -61,6 +62,9 @@ const buttonVariants = cva( shape: { round: 'rounded-full', }, + focusRing: { + muted: mutedFocusRingClass, + }, iconPadding: { sm: 'p-1', md: 'p-1.5', @@ -118,7 +122,7 @@ export interface ButtonProps } const Button = forwardRef( - ({ className, variant, size, iconSize, iconPadding, shape, ...props }, ref) => { + ({ className, variant, size, iconSize, iconPadding, shape, focusRing, ...props }, ref) => { const baseIconSize = typeof iconSize === 'object' ? iconSize?.base : iconSize const smIconSize = typeof iconSize === 'object' ? iconSize?.sm : undefined return ( @@ -131,6 +135,7 @@ const Button = forwardRef( iconSize: baseIconSize, iconPadding, shape, + focusRing, }), smIconSize && responsiveIconSizes[smIconSize], className diff --git a/packages/emcn/src/components/field-divider/field-divider.tsx b/packages/emcn/src/components/field-divider/field-divider.tsx index 4b15d867f80..9e11f4b5a10 100644 --- a/packages/emcn/src/components/field-divider/field-divider.tsx +++ b/packages/emcn/src/components/field-divider/field-divider.tsx @@ -79,8 +79,9 @@ function FieldDisclosure({ expanded, children, ...props }: FieldDisclosureProps) type='button' variant='ghost' size={null} + focusRing='muted' aria-expanded={expanded} - className='gap-1.5 whitespace-nowrap p-0 text-small focus-visible:ring-2 focus-visible:ring-[color-mix(in_srgb,var(--text-muted)_30%,transparent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--surface-2)]' + className='gap-1.5 whitespace-nowrap p-0 text-small' > {children} {} @@ -30,7 +31,12 @@ const Slider = React.forwardRef, S - + ) ) diff --git a/packages/emcn/src/components/switch/switch.tsx b/packages/emcn/src/components/switch/switch.tsx index 1d5b0c8be9c..fdf3c797ac7 100644 --- a/packages/emcn/src/components/switch/switch.tsx +++ b/packages/emcn/src/components/switch/switch.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import * as SwitchPrimitives from '@radix-ui/react-switch' import { cn } from '../../lib/cn' +import { mutedFocusRingClass } from '../../lib/focus-ring' /** * Switch component styled to match Sim's design system. @@ -16,7 +17,8 @@ const Switch = React.memo( Date: Tue, 22 Sep 2026 19:26:04 -0700 Subject: [PATCH 2/2] fix(emcn): include outline reset in muted focus ring --- packages/emcn/src/components/slider/slider.tsx | 2 +- packages/emcn/src/components/switch/switch.tsx | 2 +- packages/emcn/src/lib/focus-ring.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/emcn/src/components/slider/slider.tsx b/packages/emcn/src/components/slider/slider.tsx index 9d5303a7787..b3c1b313889 100644 --- a/packages/emcn/src/components/slider/slider.tsx +++ b/packages/emcn/src/components/slider/slider.tsx @@ -33,7 +33,7 @@ const Slider = React.forwardRef, S diff --git a/packages/emcn/src/components/switch/switch.tsx b/packages/emcn/src/components/switch/switch.tsx index fdf3c797ac7..ed8e455e9f4 100644 --- a/packages/emcn/src/components/switch/switch.tsx +++ b/packages/emcn/src/components/switch/switch.tsx @@ -17,7 +17,7 @@ const Switch = React.memo(