Files
buzz/desktop/tests/e2e/identity-key-help.spec.ts
mikeyandGitHub be275cfc6c fix(desktop): keep identity key help dialog readable in dark mode (#2854)
## Problem

With macOS in dark mode (the default for fresh profiles is to follow the
system scheme), the onboarding "What's an identity key?" help dialog
renders
its title in near-white on the always-white textured card, making it
unreadable. The body paragraphs stay readable because they use the fixed
olive `--buzz-onboarding-backup-ink`; only the `text-foreground` title
(and
the close button's hover color) flip with the theme.

## Cause

The dialog's `DialogContent` carries `buzz-onboarding-neutral-theme` but
is
portaled outside the `buzz-startup-shell` subtree, so in dark mode it
matches
`.dark .buzz-onboarding-neutral-theme:not(.buzz-startup-shell)`
(`components.css`), which flips `--foreground` to `0 0% 98%`. The
textured
powder card (`buzz-card-textured`) has no dark variant — it is baked
light —
so the near-white title disappears against it.

## Fix

One attribute: pin the dialog to the light neutral theme with
`data-system-color-scheme="light"`. This is the established pattern for
always-light onboarding dialogs (`HostedCommunityOnboarding.tsx`, the
`CommunityOnboardingFlow.tsx` avatar dialog), and the pinned-light CSS
rule
already exists and out-specifies the dark-mode flip. No new CSS.

## Testing

- Added a dark-mode regression test to
`tests/e2e/identity-key-help.spec.ts`
  (already registered in the Playwright smoke project): emulates
  `prefers-color-scheme: dark`, opens the dialog, and asserts the title
resolves to the pinned light-neutral ink `rgb(23, 23, 23)`. Before the
fix
  it rendered `rgb(250, 250, 250)`.
- Manual repro: macOS appearance set to Dark → fresh profile → machine
  onboarding → click "What's an identity key?".

Before/after screenshots are in the comment below.

Signed-off-by: Michael Pfister <pfista@gmail.com>
2026-07-27 09:47:02 -07:00

94 lines
3.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from "@playwright/test";
import { waitForAnimations } from "../helpers/animations";
import { installMockBridge } from "../helpers/bridge";
const HELP_SEEN_KEY = "buzz.machine-onboarding.identity-key-help-seen.v1";
test("identity key help explains the first-run choice", async ({ page }) => {
await installMockBridge(page, undefined, {
skipCommunitySeed: true,
skipOnboardingSeed: true,
});
await page.goto("/");
const trigger = page.getByTestId("identity-key-help-trigger");
// No initial opacity-0 assertion: on a slow runner the 2s reveal timer can
// fire before the first assertion runs, failing the test for the wrong
// reason. The reveal + persistence assertions below carry the coverage.
await expect(trigger).toHaveCSS("opacity", "1", { timeout: 5000 });
await expect
.poll(() =>
page.evaluate((key) => localStorage.getItem(key), HELP_SEEN_KEY),
)
.toBe("true");
await page.setViewportSize({ width: 720, height: 620 });
await trigger.click();
const dialog = page.getByTestId("identity-key-help-dialog");
await expect(dialog).toBeVisible();
await waitForAnimations(page);
await expect(
dialog.getByRole("heading", { name: "What’s an identity key?" }),
).toBeVisible();
await expect(dialog).toHaveClass(/shadow-none/);
await expect(page.getByTestId("dialog-overlay")).toHaveCSS(
"background-color",
"rgba(0, 0, 0, 0)",
);
const dialogWrapper = dialog.locator("..");
await expect(dialogWrapper).toHaveCSS("overflow-x", "hidden");
const dialogBounds = await dialog.boundingBox();
expect(dialogBounds).not.toBeNull();
expect(dialogBounds?.x).toBeGreaterThanOrEqual(0);
expect(
(dialogBounds?.x ?? 0) + (dialogBounds?.width ?? 0),
).toBeLessThanOrEqual(720);
await page.keyboard.press("Escape");
await expect(dialog).not.toBeVisible();
await expect(trigger).toHaveCSS("opacity", "1");
await page.reload();
await expect(page.getByTestId("identity-key-help-trigger")).toHaveCSS(
"opacity",
"1",
);
});
test("identity key help stays readable when the app resolves dark mode", async ({
page,
}) => {
await page.emulateMedia({ colorScheme: "dark" });
await installMockBridge(page, undefined, {
skipCommunitySeed: true,
skipOnboardingSeed: true,
});
await page.goto("/");
// Fresh profiles follow the system scheme, so the emulated dark scheme is
// the first-run repro: the app resolves the dark theme while onboarding.
await expect
.poll(() =>
page.evaluate(() => document.documentElement.classList.contains("dark")),
)
.toBe(true);
const trigger = page.getByTestId("identity-key-help-trigger");
await expect(trigger).toHaveCSS("opacity", "1", { timeout: 5000 });
await trigger.click();
const dialog = page.getByTestId("identity-key-help-dialog");
await expect(dialog).toBeVisible();
await waitForAnimations(page);
// The textured powder card is baked light in both themes, so the dialog pins
// the neutral onboarding theme to its light variant. Without the pin the
// dark theme flips --foreground to near-white and the title disappears
// against the white card.
await expect(
dialog.getByRole("heading", { name: "What’s an identity key?" }),
).toHaveCSS("color", "rgb(23, 23, 23)");
});