Match the browser/OS chrome to the theme and clear the status bar
Build and Push Docker Image / build-and-push (push) Successful in 4m30s
Build and Push Docker Image / build-and-push (push) Successful in 4m30s
The app now sets theme-color, color-scheme and the page background from the chosen theme, so the iPad status-bar area no longer shows a light band over a dark theme. Every top bar leaves room for the status bar via a safe-top class (replacing a rule that padded every <header>). Install defaults match the paper theme. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec59f7c82d
commit
7fc5e4b534
12 files changed
+90
-15
No files matched your search
@@ -0,0 +1,20 @@
|
||||
// Tells the browser / OS what the page looks like, so the parts it draws itself (the iPad and
|
||||
// iPhone status bar area, the address bar, overscroll, scrollbars, form controls) match the
|
||||
// chosen theme instead of defaulting to a light page.
|
||||
import { READER_THEMES } from '../components/reader/readerThemes.js';
|
||||
|
||||
export function applySystemChrome(themeId, doc = document) {
|
||||
const theme = READER_THEMES[themeId] ?? READER_THEMES.paper;
|
||||
const { '--r-bg': bg, '--r-bar': bar } = theme.vars;
|
||||
const root = doc.documentElement;
|
||||
root.style.backgroundColor = bg;
|
||||
root.style.colorScheme = themeId === 'dark' ? 'dark' : 'light';
|
||||
if (doc.body) doc.body.style.backgroundColor = bg;
|
||||
let meta = doc.querySelector('meta[name="theme-color"]');
|
||||
if (!meta) {
|
||||
meta = doc.createElement('meta');
|
||||
meta.setAttribute('name', 'theme-color');
|
||||
doc.head.appendChild(meta);
|
||||
}
|
||||
meta.setAttribute('content', bar); // matches the top bar, so the status bar area blends into it
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { test, expect, beforeEach } from 'vitest';
|
||||
import { applySystemChrome } from './systemChrome.js';
|
||||
import { READER_THEMES } from '../components/reader/readerThemes.js';
|
||||
|
||||
beforeEach(() => {
|
||||
document.head.querySelectorAll('meta[name="theme-color"]').forEach((m) => m.remove());
|
||||
document.documentElement.removeAttribute('style');
|
||||
document.body.removeAttribute('style');
|
||||
});
|
||||
|
||||
const meta = () => document.head.querySelector('meta[name="theme-color"]');
|
||||
|
||||
test('each theme paints the page background, the status-bar colour and the colour scheme', () => {
|
||||
for (const [id, theme] of Object.entries(READER_THEMES)) {
|
||||
applySystemChrome(id);
|
||||
expect(meta().getAttribute('content')).toBe(theme.vars['--r-bar']);
|
||||
expect(document.documentElement.style.backgroundColor).not.toBe('');
|
||||
expect(document.body.style.backgroundColor).not.toBe('');
|
||||
expect(document.documentElement.style.colorScheme).toBe(id === 'dark' ? 'dark' : 'light');
|
||||
}
|
||||
});
|
||||
|
||||
test('an existing theme-color tag is updated, not duplicated', () => {
|
||||
const tag = document.createElement('meta');
|
||||
tag.setAttribute('name', 'theme-color');
|
||||
tag.setAttribute('content', '#000000');
|
||||
document.head.appendChild(tag);
|
||||
applySystemChrome('sepia');
|
||||
applySystemChrome('dark');
|
||||
expect(document.head.querySelectorAll('meta[name="theme-color"]')).toHaveLength(1);
|
||||
expect(tag.getAttribute('content')).toBe(READER_THEMES.dark.vars['--r-bar']);
|
||||
});
|
||||
|
||||
test('an unknown theme falls back to paper', () => {
|
||||
applySystemChrome('nope');
|
||||
expect(meta().getAttribute('content')).toBe(READER_THEMES.paper.vars['--r-bar']);
|
||||
expect(document.documentElement.style.colorScheme).toBe('light');
|
||||
});
|
||||
Reference in new issue
Block a user