From 80dac64b569816983ba752ab22ab80050ec0638f Mon Sep 17 00:00:00 2001 From: Gregory Salaun Date: Sun, 9 Aug 2026 10:06:05 +0200 Subject: [PATCH] =?UTF-8?q?feat(theme):=20four=20themes=20with=20an=20actu?= =?UTF-8?q?al=20hue=20=E2=80=94=20Indigo,=20Ocean,=20Plum,=20Nordic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The seven existing themes are warm beige, cool grey, sage grey, slate, warm dark, graphite and black — every one neutral, six of the seven accented orange. Picking a theme changed the shade of grey and little else. These colour the SURFACES, not just the accent, and each takes a different primary so the picker tells them apart at a glance: violet on deep indigo, cyan on deep teal, magenta on aubergine, indigo on a crisp cool white. The semantic colours stay recognisable as themselves. A logger is read for hours and "red means a problem" cannot become a decorative choice, so the hue budget went on the surfaces and the primary. Where a theme's primary would have collided with a meaning, the MEANING kept its identity and the ornament moved: Ocean's info is blue rather than cyan, Plum's danger is red rather than rose, and the matrix entity ramp shifts to cyan under Indigo and teal under Nordic so a "confirmed" cell never reads as a button. Everything else follows for free: the grids resolve var(--…) at runtime, so they re-skin with no re-render. The three shared registrations that do NOT follow automatically are done — the chart palette (light vs dark ramp) and the date-picker icon inversion, which is keyed on an explicit list of dark themes. --- changelog.json | 2 + frontend/src/components/SettingsModal.tsx | 4 + frontend/src/lib/i18n.tsx | 8 +- frontend/src/lib/theme.tsx | 11 +- frontend/src/style.css | 291 +++++++++++++++++++++- 5 files changed, 306 insertions(+), 10 deletions(-) diff --git a/changelog.json b/changelog.json index c7bbdd1..8fd423b 100644 --- a/changelog.json +++ b/changelog.json @@ -6,6 +6,7 @@ "Worked before: an operator's portable callsigns now count as the same station. Typing RK3DWA finds the RK3DWA/3, /P and /MM contacts too, and the other way round — before, the history only appeared if you typed the exact form. Can be turned off in Settings → General; contest dupe checking is unaffected and stays exact.", "QSO editor: the OpsLog card now appears with the other confirmations. It has its own Sent / Received row in the QSL Info table, next to QSL, LoTW, eQSL and the rest, and the \"QSL received\" tick moved there from Contact's details — with its PSE QSL / TNX indicator, which is unchanged. Sent stays read-only: OpsLog stamps it when the card actually goes out.", "QSL card: the {qso.pse_tnx} stamp now prints \"TNX QSL\" instead of just \"TNX\", to read as the counterpart of \"PSE QSL\".", + "Four new themes, with real colour this time: Indigo (deep blue-violet, electric accent), Ocean (deep teal, cyan), Plum (aubergine, magenta) and Nordic light (crisp cool white, indigo). The seven existing ones were all neutral — beige, slate, graphite, black — with an orange accent, so picking a theme only ever changed the shade of grey. Each new one colours the surfaces, not just the accent, and takes its own primary. The status colours stay exactly where they were: red still means a problem.", "Awards: the Prefix now completes a reference the operator typed without it. A station that puts just \"74\" in State counts for DDFM (codes D01…D95) with match-by \"code\" and Prefix \"D\" — before, that found nothing and the only way through was to write a regex, in a mode where you had chosen \"code\" and not \"pattern\". The prefix was applied after the reference lookup, that is, after the step that had already failed. A field that already holds the whole code is no longer prefixed twice either (\"D74\" used to become \"DD74\" as soon as a prefix was set).", "UDP: WSJT-X traffic arriving through a relay is read again. A forwarder such as W&P prepends the origin as text — \"127.0.0.1:2237|\" — in front of the packet it re-broadcasts, which pushed the WSJT-X signature 15 bytes in and made every datagram fail. That header is now skipped, so MSHV / WSJT-X / JTDX behind a relay work on the ordinary \"WSJT-X / JTDX / MSHV\" service: no new setting, nothing to reconfigure.", "UDP: an unreadable packet now says who sent it and what it contained — the sender's address, the size, a text preview and a hex dump — instead of just a magic number. And it stops after five: a port receiving the wrong traffic was writing that line a hundred times a second, filling the log and burying everything else. A closing line points at the two things to check, the sender and the service type.", @@ -16,6 +17,7 @@ "Déjà contacté : les indicatifs portables d'un opérateur comptent désormais comme la même station. Taper RK3DWA retrouve aussi les QSO en RK3DWA/3, /P et /MM, et inversement — avant, l'historique n'apparaissait que si tu tapais la forme exacte. Désactivable dans Réglages → Général ; le contrôle de doublon en concours n'est pas touché et reste strict.", "Éditeur de QSO : la carte OpsLog rejoint les autres confirmations. Elle a sa propre ligne Envoyée / Reçue dans le tableau de l'onglet QSL Info, à côté de QSL, LoTW, eQSL et les autres, et la case « QSL reçue » y a été déplacée depuis Détails du contact — avec son indicateur PSE QSL / TNX, inchangé. « Envoyée » reste en lecture seule : OpsLog l'inscrit quand la carte part réellement.", "Carte QSL : le tampon {qso.pse_tnx} imprime désormais « TNX QSL » et non plus « TNX » seul, pour répondre à « PSE QSL ».", + "Quatre nouveaux thèmes, avec de la vraie couleur cette fois : Indigo (bleu-violet profond, accent électrique), Océan (turquoise profond, cyan), Prune (aubergine, magenta) et Clair nordique (blanc froid net, indigo). Les sept existants étaient tous neutres — beige, ardoise, graphite, noir — avec un accent orange : choisir un thème ne changeait que la nuance de gris. Les nouveaux colorent les surfaces, pas seulement l'accent, et ont chacun leur couleur principale. Les couleurs de statut ne bougent pas : le rouge veut toujours dire un problème.", "Diplômes : le Préfixe complète désormais une référence saisie sans lui. Une station qui met simplement « 74 » dans State compte pour le DDFM (codes D01…D95) avec correspondance « code » et Préfixe « D » — avant, cela ne trouvait rien et le seul recours était d'écrire une regex, dans un mode où tu avais justement choisi « code » et pas « pattern ». Le préfixe était appliqué après la recherche de la référence, c'est-à-dire après l'étape qui venait d'échouer. Un champ contenant déjà le code complet n'est plus préfixé deux fois non plus (« D74 » devenait « DD74 » dès qu'un préfixe était réglé).", "UDP : le trafic WSJT-X qui arrive via un relais est de nouveau lu. Un réexpéditeur comme W&P place l'origine en texte — « 127.0.0.1:2237| » — devant le paquet qu'il rediffuse, ce qui décalait la signature WSJT-X de 15 octets et faisait échouer chaque datagramme. Cet en-tête est désormais ignoré : MSHV / WSJT-X / JTDX derrière un relais fonctionnent avec le service « WSJT-X / JTDX / MSHV » habituel, sans nouveau réglage ni reconfiguration.", "UDP : un paquet illisible indique désormais qui l'a envoyé et ce qu'il contenait — adresse de l'émetteur, taille, aperçu en texte et vidage hexadécimal — au lieu d'un simple nombre magique. Et il s'arrête après cinq : un port recevant le mauvais trafic écrivait cette ligne cent fois par seconde, saturant le journal et enterrant tout le reste. Une ligne finale rappelle les deux choses à vérifier, l'émetteur et le type de service.", diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx index 45134ac..597b042 100644 --- a/frontend/src/components/SettingsModal.tsx +++ b/frontend/src/components/SettingsModal.tsx @@ -403,9 +403,13 @@ const THEME_SWATCH: Record, { bg: string; card: str 'light-warm': { bg: '#e8dfc9', card: '#faf6ea', accent: '#b8410c' }, 'light-cool': { bg: '#f4f6f8', card: '#ffffff', accent: '#2563eb' }, 'light-sage': { bg: '#eef1ec', card: '#f8faf6', accent: '#2f855a' }, + 'light-nordic': { bg: '#eef1f7', card: '#ffffff', accent: '#4f46e5' }, 'dim-slate': { bg: '#343b47', card: '#3d4552', accent: '#fb923c' }, 'dark-warm': { bg: '#221d18', card: '#2e2820', accent: '#e07a2e' }, 'dark-graphite': { bg: '#16181d', card: '#1f232b', accent: '#f97316' }, + 'dark-indigo': { bg: '#0e0f1f', card: '#181a30', accent: '#7c6cff' }, + 'dark-teal': { bg: '#061c21', card: '#0d2c33', accent: '#22d3ee' }, + 'dark-plum': { bg: '#180f1e', card: '#251830', accent: '#f472b6' }, 'high-contrast': { bg: '#000000', card: '#0d0d0d', accent: '#ff7a1a' }, }; diff --git a/frontend/src/lib/i18n.tsx b/frontend/src/lib/i18n.tsx index 86498a4..685deae 100644 --- a/frontend/src/lib/i18n.tsx +++ b/frontend/src/lib/i18n.tsx @@ -108,8 +108,8 @@ const en: Dict = { 'settings.pane.recent': 'Recent QSOs', 'settings.pane.netcontrol': 'Net control', 'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'Yaesu CAT not connected', 'yaesu.meters': 'Meters', 'yaesu.bandMode': 'Band & mode', 'yaesu.receive': 'Receive', 'yaesu.noiseFilter': 'Noise & filter', 'yaesu.transmit': 'Transmit', 'yaesu.refresh': 'Refresh', 'yaesu.tuneHint': 'Start an antenna-tuner cycle', 'yaesu.sToRst': 'Click to fill the RST sent', 'yaesu.sidebandHint': 'Click to select this mode; click again to switch sideband (U/L)', 'yaesu.splitUpHint': 'Transmit this far above the receive frequency, and turn split on', 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': 'Break-in: the rig switches to receive between characters', 'yaesu.zinHint': 'Zero-in: retune so the station you hear lands on your CW pitch', 'theme.auto': 'Auto (system)', 'theme.light-warm': 'Warm light', 'theme.light-cool': 'Cool light', - 'theme.light-sage': 'Sage light', 'theme.dim-slate': 'Dim slate', 'theme.dark-warm': 'Warm dark', - 'theme.dark-graphite': 'Graphite dark', 'theme.high-contrast': 'High contrast', + 'theme.light-sage': 'Sage light', 'theme.light-nordic': 'Nordic light', 'theme.dim-slate': 'Dim slate', 'theme.dark-warm': 'Warm dark', + 'theme.dark-graphite': 'Graphite dark', 'theme.dark-indigo': 'Indigo', 'theme.dark-teal': 'Ocean', 'theme.dark-plum': 'Plum', 'theme.high-contrast': 'High contrast', // Settings navigation (sidebar groups + section names) 'nav.user': 'User Configuration', 'nav.software': 'Software Configuration', 'nav.hardware': 'Hardware Configuration', 'nav.lists': 'Lists', 'sec.station': 'Station Information', 'sec.profiles': 'Profiles', 'sec.operating': 'Operating conditions', @@ -532,8 +532,8 @@ const fr: Dict = { 'settings.pane.recent': 'QSO récents', 'settings.pane.netcontrol': 'Gestion de net', 'settings.pane.flex': 'Flex Console', 'settings.pane.icom': 'Icom Console', 'settings.pane.yaesu': 'Yaesu Console', 'yaesu.notConnected': 'CAT Yaesu non connecté', 'yaesu.meters': 'Mesures', 'yaesu.bandMode': 'Bande et mode', 'yaesu.receive': 'Réception', 'yaesu.noiseFilter': 'Bruit et filtre', 'yaesu.transmit': 'Émission', 'yaesu.refresh': 'Actualiser', 'yaesu.tuneHint': "Lancer un cycle d'accord d'antenne", 'yaesu.sToRst': 'Cliquer pour remplir le RST envoyé', 'yaesu.sidebandHint': 'Cliquer pour choisir ce mode ; recliquer pour changer de bande latérale (U/L)', 'yaesu.splitUpHint': "Émettre à cette distance au-dessus de la fréquence de réception, et activer le split", 'yaesu.txOn': 'TX', 'yaesu.cw': 'CW', 'yaesu.breakInHint': "Break-in : la radio repasse en réception entre les caractères", 'yaesu.zinHint': "Zéro-in : réaccorde pour que la station entendue tombe sur votre note CW", 'theme.auto': 'Auto (système)', 'theme.light-warm': 'Clair chaud', 'theme.light-cool': 'Clair froid', - 'theme.light-sage': 'Clair sauge', 'theme.dim-slate': 'Ardoise tamisé', 'theme.dark-warm': 'Sombre chaud', - 'theme.dark-graphite': 'Sombre graphite', 'theme.high-contrast': 'Contraste élevé', + 'theme.light-sage': 'Clair sauge', 'theme.light-nordic': 'Clair nordique', 'theme.dim-slate': 'Ardoise tamisé', 'theme.dark-warm': 'Sombre chaud', + 'theme.dark-graphite': 'Sombre graphite', 'theme.dark-indigo': 'Indigo', 'theme.dark-teal': 'Océan', 'theme.dark-plum': 'Prune', 'theme.high-contrast': 'Contraste élevé', 'nav.user': 'Configuration utilisateur', 'nav.software': 'Configuration logicielle', 'nav.hardware': 'Configuration matérielle', 'nav.lists': 'Listes', 'sec.station': 'Informations station', 'sec.profiles': 'Profils', 'sec.operating': "Conditions d'opération", 'sec.confirmations': 'Confirmations', 'sec.external': 'Services externes', diff --git a/frontend/src/lib/theme.tsx b/frontend/src/lib/theme.tsx index 3efbd77..ba9c0f5 100644 --- a/frontend/src/lib/theme.tsx +++ b/frontend/src/lib/theme.tsx @@ -6,11 +6,16 @@ import { GetUIPref } from '../../wailsjs/go/main/App'; // CSS variables in style.css key off of. 'auto' follows the OS light/dark // preference. The choice is persisted (localStorage + portable UI pref, so it // travels with the data/ folder like the language). -export type ThemeChoice = 'auto' | 'light-warm' | 'light-cool' | 'light-sage' | 'dim-slate' | 'dark-warm' | 'dark-graphite' | 'high-contrast'; +export type ThemeChoice = 'auto' | 'light-warm' | 'light-cool' | 'light-sage' | 'light-nordic' + | 'dim-slate' | 'dark-warm' | 'dark-graphite' | 'dark-indigo' | 'dark-teal' | 'dark-plum' | 'high-contrast'; -// Selectable, concrete themes (excludes 'auto') in display order. +// Selectable, concrete themes (excludes 'auto') in display order: lights first, +// then darks, with high-contrast last — it is an accessibility choice, not a +// taste one, and listing it among the moods buries it. export const CONCRETE_THEMES: Exclude[] = [ - 'light-warm', 'light-cool', 'light-sage', 'dim-slate', 'dark-warm', 'dark-graphite', 'high-contrast', + 'light-warm', 'light-cool', 'light-sage', 'light-nordic', + 'dim-slate', 'dark-warm', 'dark-graphite', 'dark-indigo', 'dark-teal', 'dark-plum', + 'high-contrast', ]; export const LS_KEY = 'opslog.theme'; diff --git a/frontend/src/style.css b/frontend/src/style.css index 94e82a6..00c567b 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -495,6 +495,286 @@ color-scheme: dark; } +/* ---- Theme 5: Indigo — deep blue-violet, electric accent ---------------- + The first seven themes are all neutral (beige, slate, graphite, black) with an + orange accent, so "another dark theme" only ever meant another grey. These + four carry a real hue in the SURFACES, not just in the accent, and each takes + a different primary — violet, cyan, pink, indigo — so they are told apart at a + glance in the picker. + + Every one keeps the same contract: the semantic colours (success / warning / + caution / danger / info) must stay recognisable as themselves. A logger is + read for hours and "red = a problem" cannot become a decorative choice, so the + hue budget is spent on the surfaces and the primary, never on the meanings. */ +[data-theme="dark-indigo"] { + --background: #0e0f1f; + --foreground: #e4e5f2; + --card: #181a30; + --card-foreground: #e4e5f2; + --popover: #181a30; + --popover-foreground: #e4e5f2; + --primary: #7c6cff; /* electric violet */ + --primary-foreground: #0b0a1a; + --secondary: #222542; + --secondary-foreground: #e4e5f2; + --muted: #1e2038; + --muted-foreground: #9d9fbe; + --accent: #2a2d51; + --accent-foreground: #c7c9ee; + --destructive: #f2555a; + --destructive-foreground: #12030a; + --destructive-muted: #34131c; + --destructive-muted-foreground: #fca5a5; + --border: #2a2d4c; + --input: #2a2d4c; + --ring: #9b8dff; + + --success: #34d399; + --success-foreground: #052018; + --success-muted: #0e2f27; + --success-muted-foreground: #6ee7b7; + --success-border: #165241; + + --warning: #fbbf24; + --warning-foreground: #221803; + --warning-muted: #33280f; + --warning-muted-foreground: #fcd34d; + --warning-border: #4f3e15; + + --caution: #facc15; + --caution-foreground: #221e04; + --caution-muted: #322d0e; + --caution-muted-foreground: #fde047; + --caution-border: #4b4315; + + --danger: #fb7185; + --danger-foreground: #250912; + --danger-muted: #37151f; + --danger-muted-foreground: #fda4af; + --danger-border: #562533; + + --info: #38bdf8; + --info-foreground: #051d29; + --info-muted: #0b2a3a; + --info-muted-foreground: #7dd3fc; + --info-border: #12475c; + + /* The entity ramp moves to CYAN here: violet is the primary, and a violet + "entity confirmed" cell would read as a button. */ + --mx-call-conf: #22c55e; + --mx-call-work: #2c7a52; + --mx-dx-conf: #22d3ee; + --mx-dx-work: #1b6b7c; + --mx-none: #2c2f4d; + + --scrollbar-thumb: #383c63; + --scrollbar-thumb-hover: #4c5182; + --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(228, 229, 242, 0.05); + color-scheme: dark; +} + +/* ---- Theme 6: Ocean — deep teal, cyan accent --------------------------- */ +[data-theme="dark-teal"] { + --background: #061c21; + --foreground: #dcecee; + --card: #0d2c33; + --card-foreground: #dcecee; + --popover: #0d2c33; + --popover-foreground: #dcecee; + --primary: #22d3ee; /* cyan */ + --primary-foreground: #041d24; + --secondary: #143b44; + --secondary-foreground: #dcecee; + --muted: #10333b; + --muted-foreground: #92b4ba; + --accent: #15424c; + --accent-foreground: #a5d8e0; + --destructive: #f2555a; + --destructive-foreground: #12030a; + --destructive-muted: #35161a; + --destructive-muted-foreground: #fca5a5; + --border: #1a4652; + --input: #1a4652; + --ring: #5fe3f5; + + --success: #4ade80; + --success-foreground: #04220f; + --success-muted: #0e3020; + --success-muted-foreground: #86efac; + --success-border: #17593a; + + --warning: #fbbf24; + --warning-foreground: #221803; + --warning-muted: #33280f; + --warning-muted-foreground: #fcd34d; + --warning-border: #4f3e15; + + --caution: #facc15; + --caution-foreground: #221e04; + --caution-muted: #312d0e; + --caution-muted-foreground: #fde047; + --caution-border: #4a4315; + + --danger: #fb7185; + --danger-foreground: #250912; + --danger-muted: #38161f; + --danger-muted-foreground: #fda4af; + --danger-border: #572634; + + /* Info moves to a BLUE: a cyan "info" would be the primary colour again. */ + --info: #7aa2f7; + --info-foreground: #04122e; + --info-muted: #16233f; + --info-muted-foreground: #a9c2fb; + --info-border: #2b3d66; + + --mx-call-conf: #4ade80; + --mx-call-work: #2b7a4d; + --mx-dx-conf: #a78bfa; + --mx-dx-work: #5b4a9c; + --mx-none: #1c4550; + + --scrollbar-thumb: #235a68; + --scrollbar-thumb-hover: #31788a; + --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(220, 236, 238, 0.05); + color-scheme: dark; +} + +/* ---- Theme 7: Plum — aubergine, magenta accent ------------------------- */ +[data-theme="dark-plum"] { + --background: #180f1e; + --foreground: #eee0f0; + --card: #251830; + --card-foreground: #eee0f0; + --popover: #251830; + --popover-foreground: #eee0f0; + --primary: #f472b6; /* magenta-pink */ + --primary-foreground: #250518; + --secondary: #33203f; + --secondary-foreground: #eee0f0; + --muted: #2b1b36; + --muted-foreground: #b39dbd; + --accent: #3b2648; + --accent-foreground: #dcc2e6; + --destructive: #f2555a; + --destructive-foreground: #12030a; + --destructive-muted: #3a1620; + --destructive-muted-foreground: #fca5a5; + --border: #3a2547; + --input: #3a2547; + --ring: #f9a8d4; + + --success: #34d399; + --success-foreground: #052018; + --success-muted: #10302a; + --success-muted-foreground: #6ee7b7; + --success-border: #1a5745; + + --warning: #fbbf24; + --warning-foreground: #221803; + --warning-muted: #352a12; + --warning-muted-foreground: #fcd34d; + --warning-border: #524019; + + --caution: #facc15; + --caution-foreground: #221e04; + --caution-muted: #342e12; + --caution-muted-foreground: #fde047; + --caution-border: #4e4519; + + /* Danger goes RED here, not rose: a rose "danger" beside a pink primary is + indistinguishable, and this is the one colour that must never be missed. */ + --danger: #f43f3f; + --danger-foreground: #2a0606; + --danger-muted: #3d1418; + --danger-muted-foreground: #fca5a5; + --danger-border: #5c2128; + + --info: #38bdf8; + --info-foreground: #051d29; + --info-muted: #0f2b3a; + --info-muted-foreground: #7dd3fc; + --info-border: #16485d; + + --mx-call-conf: #22c55e; + --mx-call-work: #2f7d55; + --mx-dx-conf: #60a5fa; + --mx-dx-work: #35538c; + --mx-none: #3a2647; + + --scrollbar-thumb: #4a3159; + --scrollbar-thumb-hover: #644176; + --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(238, 224, 240, 0.05); + color-scheme: dark; +} + +/* ---- Theme 8: Nordic light — crisp cool white, indigo accent ----------- */ +[data-theme="light-nordic"] { + --background: #eef1f7; + --foreground: #1b2130; + --card: #ffffff; + --card-foreground: #1b2130; + --popover: #ffffff; + --popover-foreground: #1b2130; + --primary: #4f46e5; /* indigo */ + --primary-foreground: #ffffff; + --secondary: #dfe4ee; + --secondary-foreground: #1b2130; + --muted: #e2e7f0; + --muted-foreground: #4a5468; + --accent: #dcdcfb; + --accent-foreground: #312a91; + --destructive: #b91c1c; + --destructive-foreground: #ffffff; + --destructive-muted: #fef2f2; + --destructive-muted-foreground: #b91c1c; + --border: #c9d1e0; + --input: #c9d1e0; + --ring: #6366f1; + + --success: #15803d; + --success-foreground: #ffffff; + --success-muted: #dcfce7; + --success-muted-foreground: #14532d; + --success-border: #86efac; + + --warning: #b45309; + --warning-foreground: #ffffff; + --warning-muted: #fef3c7; + --warning-muted-foreground: #92400e; + --warning-border: #fcd34d; + + --caution: #a16207; + --caution-foreground: #ffffff; + --caution-muted: #fef9c3; + --caution-muted-foreground: #854d0e; + --caution-border: #fde047; + + --danger: #dc2626; + --danger-foreground: #ffffff; + --danger-muted: #fee2e2; + --danger-muted-foreground: #991b1b; + --danger-border: #fca5a5; + + --info: #0369a1; + --info-foreground: #ffffff; + --info-muted: #e0f2fe; + --info-muted-foreground: #075985; + --info-border: #7dd3fc; + + /* Entity ramp shifts to TEAL: indigo is the primary here. */ + --mx-call-conf: #15803d; + --mx-call-work: #86efac; + --mx-dx-conf: #0f766e; + --mx-dx-work: #99f6e4; + --mx-none: #dde3ec; + + --scrollbar-thumb: #b6c0d2; + --scrollbar-thumb-hover: #93a0b8; + --card-shadow: 0 1px 2px rgba(27, 33, 48, 0.06), 0 0 0 1px rgba(27, 33, 48, 0.03); + color-scheme: light; +} + /* ── Data-viz palette (Statistics dashboard) ──────────────────────────────── A VALIDATED categorical palette, not hand-picked: the slot ORDER is what makes it colour-blind-safe (worst adjacent ΔE 24.2 light / 10.3 dark), so never @@ -512,7 +792,8 @@ :root, [data-theme="light-warm"], [data-theme="light-cool"], -[data-theme="light-sage"] { +[data-theme="light-sage"], +[data-theme="light-nordic"] { --chart-1: #2a78d6; /* blue — default single-series hue */ --chart-2: #1baf7a; /* aqua */ --chart-3: #eda100; /* yellow */ @@ -533,7 +814,10 @@ [data-theme="dim-slate"], [data-theme="dark-warm"], [data-theme="dark-graphite"], -[data-theme="high-contrast"] { +[data-theme="high-contrast"], +[data-theme="dark-indigo"], +[data-theme="dark-teal"], +[data-theme="dark-plum"] { --chart-1: #3987e5; --chart-2: #199e70; --chart-3: #c98500; @@ -681,7 +965,8 @@ help. */ Every temporal input carries the same glyph, so the rule names them all: datetime-local was left out at first and its icon stayed invisible. */ -:is([data-theme='dim-slate'], [data-theme='dark-warm'], [data-theme='dark-graphite'], [data-theme='high-contrast']) +:is([data-theme='dim-slate'], [data-theme='dark-warm'], [data-theme='dark-graphite'], [data-theme='high-contrast'], + [data-theme='dark-indigo'], [data-theme='dark-teal'], [data-theme='dark-plum']) :is(input[type='date'], input[type='datetime-local'], input[type='time'], input[type='month'], input[type='week'])::-webkit-calendar-picker-indicator { filter: invert(1) brightness(1.8); opacity: 0.9;