Skip to content
30 changes: 29 additions & 1 deletion css/mathfield.less
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,36 @@
);
}

:host([theme='dark']) .ML__container {
--_contains-highlight-color: var(
--contains-highlight-color,
hsl(var(--_hue), 85%, 75%)
);
--_caret-color: var(--caret-color, hsl(var(--_hue), 65%, 55%));
--_selection-color: var(--selection-color, #fff);
--_selection-background-color: var(
--selection-background-color,
hsl(var(--_hue), 65%, 55%)
);
--_text-highlight-background-color: var(
--text-highlight-background-color,
hsla(var(--_hue), 40%, 50%, 0.6)
);
--_contains-highlight-background-color: var(
--contains-highlight-background-color,
hsl(var(--_hue), 5%, 34%)
);
--_latex-color: var(--primary, hsl(var(--_hue), 40%, 50%));
--_composition-background-color: #69571c;
--_composition-text-color: white;
--_placeholder-color: hsl(var(--_hue), 60%, 69%);

--_smart-fence-color: var(--smart-fence-color, #fff);
--_smart-fence-opacity: var(--smart-fence-opacity, 0.7);
}

@media (prefers-color-scheme: dark) {
.ML__container {
:host(:not([theme='light'])) .ML__container {
--_contains-highlight-color: var(
--contains-highlight-color,
hsl(var(--_hue), 85%, 75%)
Expand Down
6 changes: 0 additions & 6 deletions css/virtual-keyboard.less
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,6 @@ Note there are a different set of tooltip rules for the keyboard toggle
--keyboard-toolbar-background-hover,
#303030
);
--keyboard-toolbar-background-hover: #303030;

--_horizontal-rule: var(--keyboard-horizontal-rule, 1px solid #303030);

Expand All @@ -1086,8 +1085,6 @@ Note there are a different set of tooltip rules for the keyboard toggle
#4d5154
);
--_keycap-secondary-text: var(--keycap-secondary-text, #e7ebee);
--keycap-secondary-border: transparent;
--keycap-secondary-border-bottom: transparent;
--_keycap-secondary-border: var(--keycap-secondary-border, transparent);
--_keycap-secondary-border-bottom: var(
--keycap-secondary-border-bottom,
Expand All @@ -1110,7 +1107,6 @@ Note there are a different set of tooltip rules for the keyboard toggle
--keyboard-toolbar-background-hover,
#303030
);
--keyboard-toolbar-background-hover: #303030;

--_horizontal-rule: var(--keyboard-horizontal-rule, 1px solid #303030);

Expand All @@ -1126,8 +1122,6 @@ Note there are a different set of tooltip rules for the keyboard toggle
#4d5154
);
--_keycap-secondary-text: var(--keycap-secondary-text, #e7ebee);
--keycap-secondary-border: transparent;
--keycap-secondary-border-bottom: transparent;
--_keycap-secondary-border: var(--keycap-secondary-border, transparent);
--_keycap-secondary-border-bottom: var(
--keycap-secondary-border-bottom,
Expand Down
2 changes: 1 addition & 1 deletion src/ui/colors/colors.less
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
}

@media (prefers-color-scheme: dark) {
:host {
:host(:not([theme='light'])) {
--semantic-blue: var(--blue-700);
--semantic-red: var(--red-400);
--semantic-orange: var(--orange-400);
Expand Down
2 changes: 1 addition & 1 deletion src/ui/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
}

@media (prefers-color-scheme: dark) {
:host {
:host(:not([theme='light'])) {
--ui-menu-bg: var(--neutral-200);
}
}
Expand Down
112 changes: 112 additions & 0 deletions test/playwright-tests/theme.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { test, expect } from '@playwright/test';

const LIGHT_NEUTRAL_100 = 'rgb(245, 245, 245)';
const DARK_NEUTRAL_100 = 'rgb(18, 18, 18)';

const readVar = (id: string, varName: string) => `
(() => {
const mf = document.getElementById(${JSON.stringify(id)});
mf.style.setProperty('outline-color', 'var(${varName})');
return getComputedStyle(mf).outlineColor;
})()
`;

const readCaretColor = (id: string) => `
(() => {
const mf = document.getElementById(${JSON.stringify(id)});
const container = mf.shadowRoot.querySelector('.ML__container');
container.style.setProperty('outline-color', 'var(--_caret-color)');
return getComputedStyle(container).outlineColor;
})()
`;

test.beforeEach(async ({ page }) => {
await page.goto('/dist/playwright-test-page/');
await page.waitForSelector('math-field', { timeout: 5000 });
});

test('theme="light" overrides prefers-color-scheme: dark', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'dark' });

expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
DARK_NEUTRAL_100
);

await page.evaluate(() => {
document.getElementById('mf-1')!.setAttribute('theme', 'light');
});
expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
LIGHT_NEUTRAL_100
);
});

test('theme="dark" overrides prefers-color-scheme: light', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'light' });

expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
LIGHT_NEUTRAL_100
);

await page.evaluate(() => {
document.getElementById('mf-1')!.setAttribute('theme', 'dark');
});
expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
DARK_NEUTRAL_100
);
});

test('removing theme attribute restores system preference', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'dark' });

await page.evaluate(() => {
document.getElementById('mf-1')!.setAttribute('theme', 'light');
});
expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
LIGHT_NEUTRAL_100
);

await page.evaluate(() => {
document.getElementById('mf-1')!.removeAttribute('theme');
});
expect(await page.evaluate(readVar('mf-1', '--neutral-100'))).toBe(
DARK_NEUTRAL_100
);
});

test('mathfield.less container variables respect theme attribute', async ({
page,
}) => {
await page.emulateMedia({ colorScheme: 'dark' });
const darkCaret = await page.evaluate(readCaretColor('mf-1'));

await page.evaluate(() => {
document.getElementById('mf-1')!.setAttribute('theme', 'light');
});
const lightCaret = await page.evaluate(readCaretColor('mf-1'));

expect(darkCaret).not.toBe(lightCaret);
expect(darkCaret).not.toBe('');
expect(lightCaret).not.toBe('');
});

test('theme="dark" on light OS applies dark container palette', async ({
page,
}) => {
await page.emulateMedia({ colorScheme: 'light' });
const lightCaret = await page.evaluate(readCaretColor('mf-1'));

await page.evaluate(() => {
document.getElementById('mf-1')!.setAttribute('theme', 'dark');
});
const darkCaret = await page.evaluate(readCaretColor('mf-1'));

expect(darkCaret).not.toBe(lightCaret);

// Sanity: this dark caret should match the one produced by dark OS with no theme attribute.
await page.evaluate(() => {
document.getElementById('mf-1')!.removeAttribute('theme');
});
await page.emulateMedia({ colorScheme: 'dark' });
const osDarkCaret = await page.evaluate(readCaretColor('mf-1'));
expect(darkCaret).toBe(osDarkCaret);
});