From 92caf1ae2409af1f291968bbed2953c79b042a2f Mon Sep 17 00:00:00 2001 From: ephraimduncan Date: Fri, 31 Jul 2026 16:53:31 +0000 Subject: [PATCH] fix(elements): respect explicit light class over OS theme in persona --- packages/elements/__tests__/persona.test.tsx | 65 ++++++++++++++++++++ packages/elements/src/persona.tsx | 6 +- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/packages/elements/__tests__/persona.test.tsx b/packages/elements/__tests__/persona.test.tsx index 01bb276f..086ed593 100644 --- a/packages/elements/__tests__/persona.test.tsx +++ b/packages/elements/__tests__/persona.test.tsx @@ -691,6 +691,71 @@ describe("persona - Dynamic Color", () => { }); }); + it("prefers explicit light class over a dark OS preference", async () => { + setupPersonaTests(); + const mockSetRgb = vi.fn(); + mockUseViewModelInstanceColor.mockReturnValue({ setRgb: mockSetRgb }); + + const matchMediaSpy = vi.spyOn(window, "matchMedia").mockReturnValue({ + addEventListener: vi.fn(), + matches: true, + removeEventListener: vi.fn(), + } as unknown as MediaQueryList); + document.documentElement.classList.add("light"); + + render(); + + await waitFor(() => { + expect(mockSetRgb).toHaveBeenCalledWith(0, 0, 0); + }); + + document.documentElement.classList.remove("light"); + matchMediaSpy.mockRestore(); + }); + + it("prefers explicit dark class over a light OS preference", async () => { + setupPersonaTests(); + const mockSetRgb = vi.fn(); + mockUseViewModelInstanceColor.mockReturnValue({ setRgb: mockSetRgb }); + + const matchMediaSpy = vi.spyOn(window, "matchMedia").mockReturnValue({ + addEventListener: vi.fn(), + matches: false, + removeEventListener: vi.fn(), + } as unknown as MediaQueryList); + document.documentElement.classList.add("dark"); + + render(); + + await waitFor(() => { + expect(mockSetRgb).toHaveBeenCalledWith(255, 255, 255); + }); + + document.documentElement.classList.remove("dark"); + matchMediaSpy.mockRestore(); + }); + + it("falls back to the OS preference when no theme class is set", async () => { + setupPersonaTests(); + const mockSetRgb = vi.fn(); + mockUseViewModelInstanceColor.mockReturnValue({ setRgb: mockSetRgb }); + + const matchMediaSpy = vi.spyOn(window, "matchMedia").mockReturnValue({ + addEventListener: vi.fn(), + matches: true, + removeEventListener: vi.fn(), + } as unknown as MediaQueryList); + document.documentElement.classList.remove("light", "dark"); + + render(); + + await waitFor(() => { + expect(mockSetRgb).toHaveBeenCalledWith(255, 255, 255); + }); + + matchMediaSpy.mockRestore(); + }); + it("does not set RGB for non-dynamic-color variants", async () => { setupPersonaTests(); const mockSetRgb = vi.fn(); diff --git a/packages/elements/src/persona.tsx b/packages/elements/src/persona.tsx index 521d23ce..b079d222 100644 --- a/packages/elements/src/persona.tsx +++ b/packages/elements/src/persona.tsx @@ -92,9 +92,13 @@ const sources = { const getCurrentTheme = (): "light" | "dark" => { if (typeof window !== "undefined") { - if (document.documentElement.classList.contains("dark")) { + const { classList } = document.documentElement; + if (classList.contains("dark")) { return "dark"; } + if (classList.contains("light")) { + return "light"; + } if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) { return "dark"; }