style(dify-ui): align focus rings (#37144)

This commit is contained in:
yyh 2026-06-08 09:34:43 +08:00 committed by GitHub
parent 813bfea730
commit 12bd8d2aa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 31 additions and 30 deletions

View File

@ -135,7 +135,7 @@ const autocompleteControlVariants = cva(
[
'flex shrink-0 touch-manipulation items-center justify-center rounded-md text-text-tertiary outline-hidden transition-colors',
'hover:bg-components-input-bg-hover hover:text-text-secondary focus-visible:bg-components-input-bg-hover focus-visible:text-text-secondary',
'focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:ring-inset',
'focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-inset',
'disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-text-tertiary disabled:focus-visible:bg-transparent disabled:focus-visible:ring-0',
'group-data-disabled/autocomplete:cursor-not-allowed group-data-disabled/autocomplete:hover:bg-transparent group-data-disabled/autocomplete:focus-visible:bg-transparent group-data-disabled/autocomplete:focus-visible:ring-0',
'group-data-readonly/autocomplete:hidden',

View File

@ -17,7 +17,7 @@ describe('Checkbox', () => {
await expect.element(checkbox).toHaveAttribute('data-unchecked', '')
await expect.element(checkbox).not.toHaveAttribute('data-checked')
await expect.element(checkbox).not.toHaveAttribute('data-indeterminate')
await expect.element(checkbox).toHaveClass('focus-visible:ring-2', 'focus-visible:ring-components-input-border-hover')
await expect.element(checkbox).toHaveClass('focus-visible:ring-2', 'focus-visible:ring-state-accent-solid')
})
it('should expose checked data attributes and icon styling hooks', async () => {

View File

@ -9,7 +9,7 @@ const checkboxRootClassName = cn(
'inline-flex size-4 shrink-0 touch-manipulation items-center justify-center rounded-sm shadow-xs shadow-shadow-shadow-3 transition-colors motion-reduce:transition-none',
'border border-components-checkbox-border bg-components-checkbox-bg-unchecked text-components-checkbox-icon',
'hover:border-components-checkbox-border-hover hover:bg-components-checkbox-bg-unchecked-hover',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-components-input-border-hover focus-visible:ring-offset-0',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-offset-0',
'data-checked:border-transparent data-checked:bg-components-checkbox-bg data-checked:hover:bg-components-checkbox-bg-hover',
'data-indeterminate:border-transparent data-indeterminate:bg-components-checkbox-bg data-indeterminate:hover:bg-components-checkbox-bg-hover',
'data-disabled:cursor-not-allowed data-disabled:border-components-checkbox-border-disabled data-disabled:bg-components-checkbox-bg-disabled',

View File

@ -198,7 +198,7 @@ const comboboxControlVariants = cva(
[
'flex shrink-0 touch-manipulation items-center justify-center rounded-md text-text-tertiary outline-hidden transition-colors',
'hover:bg-components-input-bg-hover hover:text-text-secondary focus-visible:bg-components-input-bg-hover focus-visible:text-text-secondary',
'focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:ring-inset',
'focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-inset',
'disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-text-tertiary disabled:focus-visible:bg-transparent disabled:focus-visible:ring-0',
'group-data-disabled/combobox:cursor-not-allowed group-data-disabled/combobox:hover:bg-transparent group-data-disabled/combobox:focus-visible:bg-transparent group-data-disabled/combobox:focus-visible:ring-0',
'group-data-readonly/combobox:hidden',
@ -488,7 +488,7 @@ export function ComboboxChipRemove({
<BaseCombobox.ChipRemove
type={type}
aria-label={props['aria-label'] ?? (props['aria-labelledby'] ? undefined : 'Remove selected item')}
className={cn('flex size-3.5 shrink-0 items-center justify-center rounded-sm text-text-tertiary outline-hidden hover:bg-state-base-hover-alt hover:text-text-secondary focus-visible:ring-1 focus-visible:ring-components-input-border-active', className)}
className={cn('flex size-3.5 shrink-0 items-center justify-center rounded-sm text-text-tertiary outline-hidden hover:bg-state-base-hover-alt hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid', className)}
{...props}
>
{children ?? <span className="i-ri-close-line size-3" aria-hidden="true" />}

View File

@ -29,7 +29,7 @@ export function DialogCloseButton({
aria-label={ariaLabel}
{...props}
className={cn(
'absolute top-6 right-6 z-10 flex h-5 w-5 cursor-pointer items-center justify-center rounded-2xl hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
'absolute top-6 right-6 z-10 flex h-5 w-5 cursor-pointer items-center justify-center rounded-2xl hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
>

View File

@ -105,7 +105,7 @@ export function DrawerCloseButton({
type={type}
aria-label={ariaLabel}
className={cn(
'flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-text-tertiary outline-hidden hover:bg-state-base-hover hover:text-text-secondary focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover disabled:cursor-not-allowed disabled:opacity-50',
'flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-text-tertiary outline-hidden hover:bg-state-base-hover hover:text-text-secondary focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}

View File

@ -134,7 +134,7 @@ const numberFieldControlButtonVariants = cva(
[
'flex touch-manipulation items-center justify-center px-1.5 text-text-tertiary outline-hidden transition-colors select-none',
'hover:bg-components-input-bg-hover focus-visible:bg-components-input-bg-hover',
'focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:ring-inset',
'focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-inset',
'disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:focus-visible:bg-transparent disabled:focus-visible:ring-0',
'group-data-disabled/number-field:cursor-not-allowed hover:group-data-disabled/number-field:bg-transparent focus-visible:group-data-disabled/number-field:bg-transparent focus-visible:group-data-disabled/number-field:ring-0',
'group-data-readonly/number-field:cursor-default hover:group-data-readonly/number-field:bg-transparent focus-visible:group-data-readonly/number-field:bg-transparent focus-visible:group-data-readonly/number-field:ring-0',

View File

@ -245,7 +245,7 @@ type PaginationButtonProps = Omit<BaseButtonNS.Props, 'children'> & {
const paginationArrowButtonClassName = [
'inline-flex size-7 shrink-0 touch-manipulation items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg text-components-button-secondary-text shadow-xs outline-hidden backdrop-blur-[10px] transition-[background-color,border-color,color,box-shadow]',
'hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover',
'focus-visible:ring-2 focus-visible:ring-components-input-border-hover',
'focus-visible:ring-2 focus-visible:ring-state-accent-solid',
'disabled:cursor-not-allowed disabled:border-components-button-secondary-border-disabled disabled:bg-components-button-secondary-bg-disabled disabled:text-components-button-secondary-text-disabled disabled:shadow-none',
'motion-reduce:transition-none',
]
@ -391,7 +391,7 @@ export function PaginationPageJump({
type="button"
aria-label={ariaLabel ?? `Edit page number, current page ${pagination.page} of ${pagination.totalPages}`}
className={cn(
'inline-flex h-7 touch-manipulation items-center justify-center gap-0.5 rounded-lg px-2 py-1.5 system-xs-medium tabular-nums text-text-secondary outline-hidden transition-colors hover:cursor-text hover:bg-state-base-hover-alt focus-visible:ring-2 focus-visible:ring-components-input-border-hover motion-reduce:transition-none',
'inline-flex h-7 touch-manipulation items-center justify-center gap-0.5 rounded-lg px-2 py-1.5 system-xs-medium tabular-nums text-text-secondary outline-hidden transition-colors hover:cursor-text hover:bg-state-base-hover-alt focus-visible:ring-2 focus-visible:ring-state-accent-solid motion-reduce:transition-none',
className,
)}
onClick={(event) => {
@ -464,7 +464,7 @@ export function PaginationPage({
aria-current={current ? 'page' : undefined}
aria-label={ariaLabel ?? (current ? `Page ${page}, current page` : `Go to page ${page}`)}
className={cn(
'inline-flex h-8 min-w-8 touch-manipulation items-center justify-center rounded-lg px-1 py-2 system-sm-medium tabular-nums text-text-tertiary outline-hidden transition-colors hover:bg-components-button-ghost-bg-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-components-input-border-hover',
'inline-flex h-8 min-w-8 touch-manipulation items-center justify-center rounded-lg px-1 py-2 system-sm-medium tabular-nums text-text-tertiary outline-hidden transition-colors hover:bg-components-button-ghost-bg-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid',
current && 'bg-components-button-tertiary-bg text-components-button-tertiary-text hover:bg-components-button-ghost-bg-hover',
'motion-reduce:transition-none',
className,

View File

@ -9,7 +9,7 @@ const radioRootClassName = cn(
'inline-flex size-4 shrink-0 touch-manipulation items-center justify-center rounded-full p-0 transition-colors motion-reduce:transition-none',
'border border-components-radio-border bg-components-radio-bg shadow-xs shadow-shadow-shadow-3',
'hover:border-components-radio-border-hover hover:bg-components-radio-bg-hover',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-components-input-border-hover focus-visible:ring-offset-0',
'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-offset-0',
'data-checked:border-[5px] data-checked:border-components-radio-border-checked data-checked:hover:border-components-radio-border-checked-hover',
'data-disabled:cursor-not-allowed data-disabled:border-components-radio-border-disabled data-disabled:bg-components-radio-bg-disabled',
'data-disabled:hover:border-components-radio-border-disabled data-disabled:hover:bg-components-radio-bg-disabled',

View File

@ -191,9 +191,9 @@ describe('scroll-area wrapper', () => {
'min-h-0',
'min-w-0',
'outline-hidden',
'focus-visible:ring-1',
'focus-visible:ring-2',
'focus-visible:ring-inset',
'focus-visible:ring-components-input-border-hover',
'focus-visible:ring-state-accent-solid',
'custom-viewport-class',
)
})

View File

@ -42,7 +42,7 @@ const scrollAreaThumbClassName = cn(
const scrollAreaViewportClassName = cn(
'size-full min-h-0 min-w-0 outline-hidden',
'focus-visible:ring-1 focus-visible:ring-components-input-border-hover focus-visible:ring-inset',
'focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-inset',
)
const scrollAreaCornerClassName = 'bg-transparent'

View File

@ -25,6 +25,7 @@ describe('SegmentedControl wrappers', () => {
await expect.element(screen.getByRole('button', { name: 'One' })).toHaveClass(
'data-pressed:bg-components-segmented-control-item-active-bg',
'data-pressed:text-text-accent-light-mode-only',
'focus-visible:z-10',
)
})

View File

@ -33,7 +33,7 @@ export function SegmentedControlItem<Value extends string = string>({
}: SegmentedControlItemProps<Value>) {
return (
<BaseToggle
className={cn('relative flex h-7 min-w-0 touch-manipulation items-center justify-center gap-0.5 overflow-hidden whitespace-nowrap rounded-lg border-[0.5px] border-transparent px-2 py-1 system-sm-medium text-text-secondary transition-colors duration-150 hover:bg-state-base-hover hover:text-text-secondary focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-components-input-border-hover data-pressed:border-components-segmented-control-item-active-border data-pressed:bg-components-segmented-control-item-active-bg data-pressed:text-text-accent-light-mode-only data-pressed:shadow-xs data-pressed:shadow-shadow-shadow-3 data-disabled:cursor-not-allowed data-disabled:bg-transparent data-disabled:text-text-disabled data-disabled:shadow-none data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled motion-reduce:transition-none', className)}
className={cn('relative flex h-7 min-w-0 touch-manipulation items-center justify-center gap-0.5 overflow-hidden whitespace-nowrap rounded-lg border-[0.5px] border-transparent px-2 py-1 system-sm-medium text-text-secondary transition-colors duration-150 hover:bg-state-base-hover hover:text-text-secondary focus-visible:z-10 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid data-pressed:border-components-segmented-control-item-active-border data-pressed:bg-components-segmented-control-item-active-bg data-pressed:text-text-accent-light-mode-only data-pressed:shadow-xs data-pressed:shadow-shadow-shadow-3 data-disabled:cursor-not-allowed data-disabled:bg-transparent data-disabled:text-text-disabled data-disabled:shadow-none data-disabled:hover:bg-transparent data-disabled:hover:text-text-disabled motion-reduce:transition-none', className)}
{...props}
/>
)

View File

@ -87,7 +87,7 @@ describe('Slider', () => {
expect(thumb).toHaveClass(
'has-[:focus-visible]:ring-2',
'has-[:focus-visible]:ring-components-input-border-hover',
'has-[:focus-visible]:ring-state-accent-solid',
)
})

View File

@ -73,7 +73,7 @@ const sliderThumbClassName = cn(
'border-components-slider-knob-border bg-components-slider-knob shadow-sm',
'transition-[background-color,border-color,box-shadow,opacity] motion-reduce:transition-none',
'hover:bg-components-slider-knob-hover',
'has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-components-input-border-hover has-[:focus-visible]:ring-offset-0',
'has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-state-accent-solid has-[:focus-visible]:ring-offset-0',
'active:shadow-md',
'group-data-disabled/slider:border-components-slider-knob-border group-data-disabled/slider:bg-components-slider-knob-disabled group-data-disabled/slider:shadow-none',
)

View File

@ -10,7 +10,7 @@ import { cn } from '../cn'
const switchRootStateClassName = 'bg-components-toggle-bg-unchecked hover:bg-components-toggle-bg-unchecked-hover data-checked:bg-components-toggle-bg data-checked:hover:bg-components-toggle-bg-hover data-disabled:cursor-not-allowed data-disabled:bg-components-toggle-bg-unchecked-disabled data-disabled:hover:bg-components-toggle-bg-unchecked-disabled data-disabled:data-checked:bg-components-toggle-bg-disabled data-disabled:data-checked:hover:bg-components-toggle-bg-disabled'
const switchRootVariants = cva(
`group relative inline-flex shrink-0 cursor-pointer touch-manipulation items-center transition-colors duration-200 ease-in-out focus-visible:ring-2 focus-visible:ring-components-toggle-bg motion-reduce:transition-none ${switchRootStateClassName}`,
`group relative inline-flex shrink-0 cursor-pointer touch-manipulation items-center transition-colors duration-200 ease-in-out focus-visible:ring-2 focus-visible:ring-state-accent-solid motion-reduce:transition-none ${switchRootStateClassName}`,
{
variants: {
size: {

View File

@ -34,7 +34,7 @@ export function TabsTab({
}: TabsTabProps) {
return (
<BaseTabs.Tab
className={cn('touch-manipulation focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-components-input-border-hover data-disabled:cursor-not-allowed data-disabled:text-text-disabled', className)}
className={cn('touch-manipulation focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid data-disabled:cursor-not-allowed data-disabled:text-text-disabled', className)}
{...props}
/>
)

View File

@ -153,16 +153,16 @@ function ToastCard({
<BaseToast.Root
toast={toastItem}
className={cn(
'pointer-events-auto absolute top-0 right-0 w-[360px] max-w-[calc(100vw-2rem)] origin-top cursor-default rounded-xl select-none focus-visible:ring-2 focus-visible:ring-components-input-border-hover focus-visible:outline-hidden',
'pointer-events-auto absolute top-0 right-0 w-90 max-w-[calc(100vw-2rem)] origin-top cursor-default rounded-xl select-none focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
'[--toast-current-height:var(--toast-frontmost-height,var(--toast-height))] [--toast-gap:8px] [--toast-peek:5px] [--toast-scale:calc(1-(var(--toast-index)*0.0225))] [--toast-shrink:calc(1-var(--toast-scale))]',
'z-[calc(100-var(--toast-index))] h-(--toast-current-height)',
'[transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms] motion-reduce:transition-none',
'[transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek))+(var(--toast-shrink)*var(--toast-current-height))))_scale(var(--toast-scale))]',
'data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-offset-y)+var(--toast-swipe-movement-y)+(var(--toast-index)*8px)))_scale(1)]',
'data-ending-style:[transform:translateY(-150%)] data-ending-style:opacity-0',
'data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]',
'data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))]',
'data-limited:pointer-events-none data-limited:opacity-0 data-starting-style:[transform:translateY(-150%)] data-starting-style:opacity-0',
'transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-peek))+(var(--toast-shrink)*var(--toast-current-height))))_scale(var(--toast-scale))]',
'data-expanded:h-(--toast-height) data-expanded:transform-[translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-offset-y)+var(--toast-swipe-movement-y)+(var(--toast-index)*8px)))_scale(1)]',
'data-ending-style:transform-[translateY(-150%)] data-ending-style:opacity-0',
'data-ending-style:data-[swipe-direction=down]:transform-[translateY(calc(var(--toast-swipe-movement-y)+150%))]',
'data-ending-style:data-[swipe-direction=right]:transform-[translateX(calc(var(--toast-swipe-movement-x)+150%))]',
'data-limited:pointer-events-none data-limited:opacity-0 data-starting-style:transform-[translateY(-150%)] data-starting-style:opacity-0',
'after:pointer-events-auto after:absolute after:top-full after:left-0 after:h-[calc(var(--toast-gap)+1px)] after:w-full after:content-[\'\']',
)}
>
@ -193,7 +193,7 @@ function ToastCard({
<BaseToast.Action
className={cn(
'inline-flex items-center justify-center overflow-hidden rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 py-2 system-sm-medium text-components-button-secondary-text shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]',
'hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover focus-visible:outline-hidden',
'hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
)}
/>
</div>
@ -203,7 +203,7 @@ function ToastCard({
<BaseToast.Close
aria-label={toastCloseLabel}
className={cn(
'flex h-5 w-5 items-center justify-center rounded-md hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
'flex h-5 w-5 items-center justify-center rounded-md hover:bg-state-base-hover focus-visible:bg-state-base-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
)}
>
<span aria-hidden="true" className="i-ri-close-line h-4 w-4 text-text-tertiary" />
@ -227,7 +227,7 @@ function ToastViewport() {
>
<div
className={cn(
'pointer-events-none absolute top-4 right-4 w-[360px] max-w-[calc(100vw-2rem)] sm:right-8',
'pointer-events-none absolute top-4 right-4 w-90 max-w-[calc(100vw-2rem)] sm:right-8',
)}
>
{toasts.map(toastItem => (