diff --git a/invokeai/frontend/webv2/src/platform/ui/FormControlInteraction.browser.test.tsx b/invokeai/frontend/webv2/src/platform/ui/FormControlInteraction.browser.test.tsx index 3c2f768f616..c1ac214dc26 100644 --- a/invokeai/frontend/webv2/src/platform/ui/FormControlInteraction.browser.test.tsx +++ b/invokeai/frontend/webv2/src/platform/ui/FormControlInteraction.browser.test.tsx @@ -15,6 +15,9 @@ let host: HTMLDivElement | null = null; let root: Root | null = null; (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; +// Border colors transition, so a fixed sleep can read an interpolated color on a loaded runner. +const transitionsSettled = (element: Element) => expect.poll(() => element.getAnimations().length).toBe(0); + afterEach(async () => { await act(() => root?.unmount()); host?.remove(); @@ -53,6 +56,7 @@ describe('form control interaction styles', () => { globalThis.setTimeout(resolve, 200); }); }); + await transitionsSettled(input); expect(getComputedStyle(input).outlineStyle).toBe('none'); expect(getComputedStyle(input).borderTopColor).toBe(accentBorderColor); await act(async () => { @@ -62,6 +66,7 @@ describe('form control interaction styles', () => { }); }); expect(document.activeElement).toBe(trigger); + await transitionsSettled(trigger); expect(getComputedStyle(trigger).outlineStyle).toBe('none'); expect(getComputedStyle(trigger).borderTopColor).toBe(accentBorderColor); @@ -73,6 +78,7 @@ describe('form control interaction styles', () => { }); }); expect(trigger.getAttribute('data-state')).toBe('open'); + await transitionsSettled(trigger); const hoveredFocusBorderColor = getComputedStyle(trigger).borderTopColor; expect(hoveredFocusBorderColor).toBe(accentBorderColor); @@ -82,6 +88,7 @@ describe('form control interaction styles', () => { globalThis.setTimeout(resolve, 200); }); }); + await transitionsSettled(trigger); expect(hoveredFocusBorderColor).toBe(getComputedStyle(trigger).borderTopColor); }); }); diff --git a/invokeai/frontend/webv2/src/platform/ui/theme/ToastContrast.browser.test.tsx b/invokeai/frontend/webv2/src/platform/ui/theme/ToastContrast.browser.test.tsx index 6d06b595cba..432b6522474 100644 --- a/invokeai/frontend/webv2/src/platform/ui/theme/ToastContrast.browser.test.tsx +++ b/invokeai/frontend/webv2/src/platform/ui/theme/ToastContrast.browser.test.tsx @@ -20,8 +20,7 @@ afterEach(async () => { }); describe('toast contrast', () => { - // Success and warning fills (green/orange 600) stay below AA even for full-strength white text. - it.each(['error', 'info'] as const)('renders a %s description at AA contrast', async (type) => { + it.each(['error', 'info', 'success', 'warning'] as const)('renders a %s description at AA contrast', async (type) => { host = document.createElement('div'); document.body.append(host); root = createRoot(host); diff --git a/invokeai/frontend/webv2/src/platform/ui/theme/recipes.ts b/invokeai/frontend/webv2/src/platform/ui/theme/recipes.ts index 997d7845e5f..32fe51c41e9 100644 --- a/invokeai/frontend/webv2/src/platform/ui/theme/recipes.ts +++ b/invokeai/frontend/webv2/src/platform/ui/theme/recipes.ts @@ -69,11 +69,19 @@ export const popoverSlotRecipe = defineSlotRecipe({ }, }); -/** Chakra dims descriptions to 80%, which drops them below AA on the solid status fills. */ +/** + * Keep white status text at AA: Chakra dims descriptions to 80%, and its green/orange 600 fills are too light + * for white at any opacity, so success and warning use the 700 step. + */ export const toastSlotRecipe = defineSlotRecipe({ ...chakraSlotRecipes.toast, base: { ...chakraSlotRecipes.toast.base, + root: { + ...chakraSlotRecipes.toast.base?.root, + '&[data-type=success]': { ...chakraSlotRecipes.toast.base?.root?.['&[data-type=success]'], bg: 'green.700' }, + '&[data-type=warning]': { ...chakraSlotRecipes.toast.base?.root?.['&[data-type=warning]'], bg: 'orange.700' }, + }, description: { ...chakraSlotRecipes.toast.base?.description, opacity: 1 }, }, });