Themes: Dracula, Material, Adwaita, Flat Remix, Paper, Nordic, each light, dark or Auto

The theme picker lists Modern (the old Dark and Light), Classic and six new
palettes, from Dracula's spec (with Alucard), Material 3's baseline scheme,
libadwaita's CSS variables, Flat Remix's _colors.scss, Paper and Nord. A second
setting picks Light, Dark or Auto where a theme has both; Classic and Paper do
not, so it is hidden for them.

The page gets data-mode, light or dark, and Auto is worked out in theme.ts from
the system, so each palette is written once instead of again under a media
query. Every new palette clears WCAG AA for text on its backgrounds. An old
ipx.theme of dark, light or auto carries over as Modern.

Closes #27.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 12:53:25 +00:00
parent 26802d2b23
commit e2969bcee8
6 changed files with 294 additions and 51 deletions

View File

@@ -30,7 +30,10 @@
--bad:#e2705f;
--shadow:0 8px 28px rgba(6,10,16,.55);
}
:root[data-theme="light"] {
/* Every other theme overrides the same variables, under data-theme and data-mode. The page's
script sets data-mode to light or dark, working Auto out from the system, so each theme has
one block per variant here and none needs repeating under a media query. */
:root[data-theme="modern"][data-mode="light"] {
--bg:#f2f4f7;
--panel:#ffffff; /* #FFFFFF device body */
--panel2:#e9edf3;
@@ -47,28 +50,203 @@
--bad:#b3402f;
--shadow:0 8px 28px rgba(45,83,145,.14);
}
/* Auto: the same palette as Light, but only while the system is set to light -- the default
:root above is already dark, so nothing is needed for the dark half of Auto. Duplicated
rather than shared with [data-theme="light"], the same way Classic repeats its own values;
CSS custom properties have no way to say "these vars, but only under this media query". */
@media (prefers-color-scheme: light) {
:root[data-theme="auto"] {
--bg:#f2f4f7;
--panel:#ffffff;
--panel2:#e9edf3;
--raise:#dde3ec;
--line:#d6d6d6;
--fg:#1a1a1a;
--dim:#606060;
--faint:#767676;
--accent:#2d5391;
--accent2:#9a5f0a;
--ink:#ffffff;
--good:#2f7d4f;
--warn:#b06f10;
--bad:#b3402f;
--shadow:0 8px 28px rgba(45,83,145,.14);
}
/* Dracula, and Alucard, its light half, from draculatheme.com/spec. The spec's comment colour
(#6272A4, #6C664B) is too faint for small text on its own background, so --faint is lifted. */
:root[data-theme="dracula"] {
--bg:#282a36;
--panel:#21222c;
--panel2:#343746;
--raise:#44475a; /* selection */
--line:#414558;
--fg:#f8f8f2;
--dim:#c9cbd6;
--faint:#9ea8c7;
--accent:#bd93f9; /* purple */
--accent2:#ff79c6; /* pink */
--ink:#282a36;
--good:#50fa7b;
--warn:#ffb86c;
--bad:#ff8080;
--shadow:0 8px 28px rgba(0,0,0,.45);
}
:root[data-theme="dracula"][data-mode="light"] {
--bg:#fffbeb;
--panel:#f7f2df;
--panel2:#efe9d3;
--raise:#cfcfde; /* selection */
--line:#ddd6bd;
--fg:#1f1f1f;
--dim:#454137;
--faint:#635d44;
--accent:#644ac9;
--accent2:#a3144d;
--ink:#ffffff;
--good:#14710a;
--warn:#a34d14;
--bad:#b83322;
--shadow:0 8px 28px rgba(108,102,75,.18);
}
/* Material 3's baseline scheme: surface, the surface containers, outline and primary. */
:root[data-theme="material"] {
--bg:#141218; /* surface */
--panel:#1d1b20; /* surface-container-low */
--panel2:#211f26; /* surface-container */
--raise:#36343b; /* surface-container-highest */
--line:#49454f; /* outline-variant */
--fg:#e6e0e9; /* on-surface */
--dim:#cac4d0; /* on-surface-variant */
--faint:#9a95a0; /* outline, a shade up to clear AA on the containers */
--accent:#d0bcff; /* primary */
--accent2:#efb8c8; /* tertiary */
--ink:#381e72; /* on-primary */
--good:#8fd6a0;
--warn:#f2c46b;
--bad:#f2b8b5; /* error */
--shadow:0 8px 28px rgba(0,0,0,.5);
}
:root[data-theme="material"][data-mode="light"] {
--bg:#fef7ff;
--panel:#f7f2fa;
--panel2:#f3edf7;
--raise:#e6e0e9;
--line:#cac4d0;
--fg:#1d1b20;
--dim:#49454f;
--faint:#67626c; /* outline (#79747E), a shade down to clear AA on the containers */
--accent:#6750a4;
--accent2:#7d5260;
--ink:#ffffff;
--good:#2b6c3c;
--warn:#7c5800;
--bad:#b3261e;
--shadow:0 8px 28px rgba(103,80,164,.14);
}
/* Adwaita, from libadwaita's own CSS variables: view, window and sidebar backgrounds, the blue
accent, and its destructive, success and warning colours. */
:root[data-theme="adwaita"] {
--bg:#1d1d20; /* view */
--panel:#2e2e32; /* sidebar, headerbar */
--panel2:#222226; /* window */
--raise:#3a3a3f;
--line:#3f3f45;
--fg:#ffffff;
--dim:#c4c4c8;
--faint:#a0a0a7;
--accent:#81d0ff;
--accent2:#3584e4;
--ink:#1d1d20;
--good:#78e9ab;
--warn:#ffc252;
--bad:#ff938c;
--shadow:0 8px 28px rgba(0,0,0,.5);
}
:root[data-theme="adwaita"][data-mode="light"] {
--bg:#ffffff;
--panel:#ebebed;
--panel2:#fafafb;
--raise:#dcdce0;
--line:#d9d9dd;
--fg:#333338; /* rgb(0 0 6 / 80%) on white */
--dim:#57575d;
--faint:#66666c;
--accent:#0461be;
--accent2:#3584e4;
--ink:#ffffff;
--good:#00753a;
--warn:#905400;
--bad:#c30000;
--shadow:0 8px 28px rgba(0,0,6,.12);
}
/* Flat Remix, from its GTK theme's _colors.scss: base and bg, fg, the selection blue, and its
link colours, which are the selection blue taken down (light) or up (dark) until readable. */
:root[data-theme="flatremix"] {
--bg:#272a34; /* base */
--panel:#23262f; /* bg: base darkened 2% */
--panel2:#2e323d;
--raise:#363b48;
--line:#3a3f4c;
--fg:#eeeeec;
--dim:#babec8;
--faint:#959baa;
--accent:#8fb6ff; /* link */
--accent2:#fd7d00; /* warning orange */
--ink:#1a1c23;
--good:#4cc28c;
--warn:#ff9a3c;
--bad:#ff6b6b;
--shadow:0 8px 28px rgba(0,0,0,.3);
}
:root[data-theme="flatremix"][data-mode="light"] {
--bg:#fafafa; /* base */
--panel:#ffffff; /* bg */
--panel2:#f0f0f1;
--raise:#e4e5e7;
--line:#d9d9d9; /* borders: bg darkened 15% */
--fg:#22252b;
--dim:#5c616c; /* fg */
--faint:#686d78;
--accent:#0060f0; /* link */
--accent2:#fd7d00;
--ink:#ffffff;
--good:#23794f;
--warn:#a85400;
--bad:#d41919;
--shadow:0 8px 28px rgba(0,0,0,.1);
}
/* Paper: a single light palette, black on the colour of paper, colour kept for what matters.
Its blue and red are taken down a shade to clear AA on that background. */
:root[data-theme="paper"] {
--bg:#f2eede;
--panel:#ebe7d7;
--panel2:#e4e0d0;
--raise:#d8d5c7; /* highlight */
--line:#cdc9b9;
--fg:#000000;
--dim:#3d3a33;
--faint:#5a564c;
--accent:#1a5fae;
--accent2:#b58900;
--ink:#ffffff;
--good:#216609;
--warn:#795a00;
--bad:#b1331f;
--shadow:0 8px 28px rgba(0,0,0,.12);
}
/* Nordic: the Nord palette. Polar Night for the dark half, Snow Storm for the light, Frost for
the accent and Aurora for the rest. Aurora red and Frost blue are shifted until they read. */
:root[data-theme="nordic"] {
--bg:#2e3440; /* nord0 */
--panel:#3b4252; /* nord1 */
--panel2:#434c5e; /* nord2 */
--raise:#4c566a; /* nord3 */
--line:#434c5e;
--fg:#eceff4; /* nord6 */
--dim:#d8dee9; /* nord4 */
--faint:#b4bccb;
--accent:#93c9d8; /* nord8, a shade up */
--accent2:#ebcb8b; /* nord13 */
--ink:#2e3440;
--good:#b0c99b; /* nord14, a shade up */
--warn:#ebcb8b;
--bad:#f2aeb4;
--shadow:0 8px 28px rgba(0,0,0,.35);
}
:root[data-theme="nordic"][data-mode="light"] {
--bg:#eceff4; /* nord6 */
--panel:#e5e9f0; /* nord5 */
--panel2:#d8dee9; /* nord4 */
--raise:#cdd4e0;
--line:#c5cedb;
--fg:#2e3440; /* nord0 */
--dim:#3b4252; /* nord1 */
--faint:#4c566a; /* nord3 */
--accent:#3a5e8a;
--accent2:#b0674e;
--ink:#ffffff;
--good:#446632;
--warn:#7e590e;
--bad:#9c3f49;
--shadow:0 8px 28px rgba(46,52,64,.15);
}
/* Classic: the 2004 Mac app. Colours here; the chrome it needs is at the end of the sheet. */
:root[data-theme="classic"] {