From ad82843ee5239afd4d0aac11bcc3caf5bd6ea01b Mon Sep 17 00:00:00 2001 From: root Date: Thu, 14 May 2026 09:25:39 -0400 Subject: [PATCH] Add TDS light mode: LotusTerminalLightTheme, light CSS vars, no CRT effects --- src/app/hooks/useTheme.ts | 7 +- src/app/pages/ThemeManager.tsx | 15 ++- src/colors.css.ts | 89 +++++++++++++ src/lotus-terminal.css.ts | 223 +++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 6 deletions(-) diff --git a/src/app/hooks/useTheme.ts b/src/app/hooks/useTheme.ts index 5a82b57cb..923f8e226 100644 --- a/src/app/hooks/useTheme.ts +++ b/src/app/hooks/useTheme.ts @@ -1,7 +1,7 @@ import { lightTheme } from 'folds'; import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { onDarkFontWeight, onLightFontWeight } from '../../config.css'; -import { butterTheme, darkTheme, lotusTerminalTheme, silverTheme } from '../../colors.css'; +import { butterTheme, darkTheme, lotusTerminalLightTheme, lotusTerminalTheme, silverTheme } from '../../colors.css'; import { settingsAtom } from '../state/settings'; import { useSetting } from '../state/hooks/settings'; @@ -42,6 +42,11 @@ export const LotusTerminalTheme: Theme = { kind: ThemeKind.Dark, classNames: ['lotus-terminal-theme', lotusTerminalTheme, onDarkFontWeight, 'prism-dark'], }; +export const LotusTerminalLightTheme: Theme = { + id: 'lotus-terminal-light-theme', + kind: ThemeKind.Light, + classNames: ['lotus-terminal-light-theme', lotusTerminalLightTheme, onLightFontWeight, 'prism-light'], +}; export const useThemes = (): Theme[] => { const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme], []); diff --git a/src/app/pages/ThemeManager.tsx b/src/app/pages/ThemeManager.tsx index df64119f2..c394f5934 100644 --- a/src/app/pages/ThemeManager.tsx +++ b/src/app/pages/ThemeManager.tsx @@ -3,6 +3,7 @@ import { configClass, varsClass } from 'folds'; import { DarkTheme, LightTheme, + LotusTerminalLightTheme, LotusTerminalTheme, ThemeContextProvider, ThemeKind, @@ -22,8 +23,9 @@ export function UnAuthRouteThemeManager() { document.body.className = ''; document.body.classList.add(configClass, varsClass); if (lotusTerminal) { - document.documentElement.setAttribute('data-theme', 'dark'); - document.body.classList.add(...LotusTerminalTheme.classNames); + const isLight = systemThemeKind === ThemeKind.Light; + document.documentElement.setAttribute('data-theme', isLight ? 'light' : 'dark'); + document.body.classList.add(...(isLight ? LotusTerminalLightTheme : LotusTerminalTheme).classNames); document.body.classList.add(lotusTerminalBodyClass); } else { document.documentElement.removeAttribute('data-theme'); @@ -43,14 +45,17 @@ export function AuthRouteThemeManager({ children }: { children: ReactNode }) { const [monochromeMode] = useSetting(settingsAtom, 'monochromeMode'); const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal'); - const effectiveTheme = lotusTerminal ? LotusTerminalTheme : activeTheme; + const terminalIsLight = lotusTerminal && activeTheme.kind === ThemeKind.Light; + const effectiveTheme = lotusTerminal + ? (terminalIsLight ? LotusTerminalLightTheme : LotusTerminalTheme) + : activeTheme; useEffect(() => { document.body.className = ''; document.body.classList.add(configClass, varsClass); document.body.classList.add(...effectiveTheme.classNames); if (lotusTerminal) { - document.documentElement.setAttribute('data-theme', 'dark'); + document.documentElement.setAttribute('data-theme', terminalIsLight ? 'light' : 'dark'); document.body.classList.add(lotusTerminalBodyClass); runLotusBootSequence(); } else { @@ -61,7 +66,7 @@ export function AuthRouteThemeManager({ children }: { children: ReactNode }) { } else { document.body.style.filter = ''; } - }, [effectiveTheme, monochromeMode, lotusTerminal]); + }, [effectiveTheme, monochromeMode, lotusTerminal, terminalIsLight]); return {children}; } diff --git a/src/colors.css.ts b/src/colors.css.ts index 10890529f..bc0855a35 100644 --- a/src/colors.css.ts +++ b/src/colors.css.ts @@ -333,3 +333,92 @@ export const lotusTerminalTheme = createTheme(color, { Overlay: 'rgba(3, 5, 8, 0.94)', }, }); +export const lotusTerminalLightTheme = createTheme(color, { + Background: { + Container: '#edf0f5', + ContainerHover: '#e2e7ef', + ContainerActive: '#d4dae6', + ContainerLine: '#c8d0de', + OnContainer: '#111827', + }, + Surface: { + Container: '#f4f6fa', + ContainerHover: '#e8ecf4', + ContainerActive: '#dde2ed', + ContainerLine: '#d0d7e5', + OnContainer: '#111827', + }, + SurfaceVariant: { + Container: '#e2e7ef', + ContainerHover: '#d4dae6', + ContainerActive: '#c8d0de', + ContainerLine: '#bdc6d6', + OnContainer: '#2d3d56', + }, + Primary: { + Main: '#c44e00', + MainHover: '#a33f00', + MainActive: '#8f3700', + MainLine: '#d45800', + OnMain: '#ffffff', + Container: 'rgba(196,78,0,0.10)', + ContainerHover: 'rgba(196,78,0,0.16)', + ContainerActive: 'rgba(196,78,0,0.22)', + ContainerLine: 'rgba(196,78,0,0.28)', + OnContainer: '#6b2200', + }, + Secondary: { + Main: '#0062b8', + MainHover: '#0052a0', + MainActive: '#004488', + MainLine: '#0070cc', + OnMain: '#ffffff', + Container: 'rgba(0,98,184,0.10)', + ContainerHover: 'rgba(0,98,184,0.16)', + ContainerActive: 'rgba(0,98,184,0.22)', + ContainerLine: 'rgba(0,98,184,0.28)', + OnContainer: '#003580', + }, + Success: { + Main: '#006d35', + MainHover: '#005c2c', + MainActive: '#004a22', + MainLine: '#007d3e', + OnMain: '#ffffff', + Container: 'rgba(0,109,53,0.10)', + ContainerHover: 'rgba(0,109,53,0.16)', + ContainerActive: 'rgba(0,109,53,0.22)', + ContainerLine: 'rgba(0,109,53,0.28)', + OnContainer: '#003520', + }, + Warning: { + Main: '#8a5a00', + MainHover: '#7a4e00', + MainActive: '#6a4400', + MainLine: '#9a6600', + OnMain: '#ffffff', + Container: 'rgba(138,90,0,0.10)', + ContainerHover: 'rgba(138,90,0,0.16)', + ContainerActive: 'rgba(138,90,0,0.22)', + ContainerLine: 'rgba(138,90,0,0.28)', + OnContainer: '#4a3000', + }, + Critical: { + Main: '#b5001f', + MainHover: '#9a0019', + MainActive: '#800015', + MainLine: '#cc0023', + OnMain: '#ffffff', + Container: 'rgba(181,0,31,0.10)', + ContainerHover: 'rgba(181,0,31,0.16)', + ContainerActive: 'rgba(181,0,31,0.22)', + ContainerLine: 'rgba(181,0,31,0.28)', + OnContainer: '#600010', + }, + Other: { + FocusRing: 'rgba(0, 98, 184, 0.50)', + Shadow: 'rgba(0, 0, 0, 0.15)', + Overlay: 'rgba(237, 240, 245, 0.97)', + }, +}); + diff --git a/src/lotus-terminal.css.ts b/src/lotus-terminal.css.ts index 47d4ce87f..48ef26d48 100644 --- a/src/lotus-terminal.css.ts +++ b/src/lotus-terminal.css.ts @@ -352,3 +352,226 @@ globalStyle(`body.${lotusTerminalBodyClass} img:hover`, { globalStyle(`body.${lotusTerminalBodyClass}`, { color: '#c4d9ee', }); + + +// ═══════════════════════════════════════════════════════════════════════════ +// LIGHT MODE — TDS "daylight reading" variant +// html[data-theme="light"] + body.lotusTerminalBodyClass +// Mirrors base.css:3614–3684: desaturated accents, off-white bg, no CRT +// ═══════════════════════════════════════════════════════════════════════════ + +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass}`, { + backgroundColor: '#edf0f5', + backgroundImage: 'radial-gradient(circle, rgba(90,110,150,0.14) 1px, transparent 1px)', + color: '#111827', + vars: { + '--lt-bg-primary': '#edf0f5', + '--lt-bg-secondary': '#e2e7ef', + '--lt-bg-tertiary': '#d4dae6', + '--lt-bg-card': '#ffffff', + '--lt-bg-terminal': '#f4f6fa', + '--lt-accent-orange': '#c44e00', + '--lt-accent-orange-bright':'#d45800', + '--lt-accent-orange-dim': 'rgba(196,78,0,0.10)', + '--lt-accent-orange-border':'rgba(196,78,0,0.28)', + '--lt-accent-amber': '#8a5a00', + '--lt-accent-amber-dim': 'rgba(138,90,0,0.10)', + '--lt-accent-cyan': '#0062b8', + '--lt-accent-cyan-bright': '#0070cc', + '--lt-accent-cyan-dim': 'rgba(0,98,184,0.10)', + '--lt-accent-cyan-border': 'rgba(0,98,184,0.22)', + '--lt-accent-green': '#006d35', + '--lt-accent-green-bright': '#007d3e', + '--lt-accent-green-dim': 'rgba(0,109,53,0.10)', + '--lt-accent-green-border': 'rgba(0,109,53,0.22)', + '--lt-accent-red': '#b5001f', + '--lt-accent-red-dim': 'rgba(181,0,31,0.12)', + '--lt-accent-gold': '#8a5a00', + '--lt-accent-gold-dim': 'rgba(138,90,0,0.10)', + '--lt-accent-purple': '#6b2fb8', + '--lt-accent-purple-dim': 'rgba(107,47,184,0.10)', + '--lt-text-primary': '#111827', + '--lt-text-secondary': '#2d3d56', + '--lt-text-muted': '#5a6e8c', + '--lt-text-dim': '#8a9ab8', + '--lt-border-color': 'rgba(50,80,130,0.18)', + '--lt-border-color-hi': '#0062b8', + '--lt-border-color-dim': 'rgba(50,80,130,0.09)', + '--lt-glow-orange': '0 0 0 1px rgba(196,78,0,0.25), 0 1px 6px rgba(196,78,0,0.18)', + '--lt-glow-orange-intense': '0 0 0 2px rgba(196,78,0,0.35), 0 2px 10px rgba(196,78,0,0.25)', + '--lt-glow-cyan': '0 0 0 1px rgba(0,98,184,0.25), 0 1px 6px rgba(0,98,184,0.18)', + '--lt-glow-cyan-intense': '0 0 0 2px rgba(0,98,184,0.35), 0 2px 10px rgba(0,98,184,0.25)', + '--lt-glow-green': '0 0 0 1px rgba(0,109,53,0.25), 0 1px 6px rgba(0,109,53,0.18)', + '--lt-glow-green-intense': '0 0 0 2px rgba(0,109,53,0.35), 0 2px 10px rgba(0,109,53,0.25)', + '--lt-glow-amber': '0 0 0 1px rgba(138,90,0,0.25), 0 1px 6px rgba(138,90,0,0.18)', + '--lt-glow-amber-intense': '0 0 0 2px rgba(138,90,0,0.35), 0 2px 10px rgba(138,90,0,0.25)', + '--lt-glow-red': '0 0 0 1px rgba(181,0,31,0.25), 0 1px 6px rgba(181,0,31,0.18)', + '--lt-box-glow-orange': '0 0 0 2px rgba(196,78,0,0.22), 0 2px 8px rgba(196,78,0,0.12)', + '--lt-box-glow-cyan': '0 0 0 2px rgba(0,98,184,0.22), 0 2px 8px rgba(0,98,184,0.12)', + '--lt-box-glow-green': '0 0 0 2px rgba(0,109,53,0.22), 0 2px 8px rgba(0,109,53,0.12)', + '--lt-box-glow-red': '0 0 0 2px rgba(181,0,31,0.22), 0 2px 8px rgba(181,0,31,0.12)', + '--lt-box-glow-amber': '0 0 0 2px rgba(138,90,0,0.22), 0 2px 8px rgba(138,90,0,0.12)', + } as any, +}); + +// Scanlines + vignette: OFF in light mode (base.css:3676-3678) +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass}::before,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass}::after`, + { display: 'none' } +); + +// Caret +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass} input,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass} textarea`, + { caretColor: '#c44e00' } +); + +// Scrollbar +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ::-webkit-scrollbar-track`, { + background: '#e2e7ef', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ::-webkit-scrollbar-thumb`, { + background: 'rgba(0,98,184,0.25)', + borderRadius: '3px', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ::-webkit-scrollbar-thumb:hover`, { + background: 'rgba(0,98,184,0.50)', +}); + +// Selection +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ::selection`, { + background: 'rgba(196,78,0,0.18)', + color: '#030508', +}); + +// Links +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} a`, { color: '#0062b8' }); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} a:hover`, { + color: '#c44e00', + textShadow: 'none', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} a:focus-visible`, { + outline: '2px solid #0062b8', + outlineOffset: '2px', +}); + +// Code +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} code`, { + background: '#f0f4f8', + border: '1px solid rgba(0,98,184,0.18)', + color: '#006d35', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} pre`, { + background: '#f0f4f8', + border: '1px solid rgba(0,98,184,0.14)', + borderLeft: '2px solid #006d35', + color: '#006d35', + textShadow: 'none', +}); + +// Inline semantics +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass} strong,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass} b`, + { color: '#c44e00' } +); +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass} em,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass} i`, + { color: '#0062b8' } +); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} mark`, { + background: 'rgba(138,90,0,0.15)', + color: '#8a5a00', +}); +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass} del,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass} s`, + { color: '#b5001f' } +); + +// Blockquote +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} blockquote`, { + borderLeft: '2px solid #c44e00', + background: 'rgba(196,78,0,0.05)', + color: '#5a6e8c', +}); + +// HR +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} hr`, { + borderTop: '1px solid rgba(0,98,184,0.18)', + boxShadow: 'none', +}); + +// Input focus +globalStyle( + `html[data-theme="light"] body.${lotusTerminalBodyClass} input:focus,` + + `html[data-theme="light"] body.${lotusTerminalBodyClass} textarea:focus`, + { + borderColor: '#c44e00', + boxShadow: '0 0 0 2px rgba(196,78,0,0.22), 0 1px 6px rgba(196,78,0,0.12)', + } +); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} [contenteditable="true"]:focus`, { + boxShadow: '0 0 0 1px rgba(196,78,0,0.40), 0 1px 6px rgba(196,78,0,0.10)', +}); + +// Tables +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} table`, { + border: '1px solid rgba(0,98,184,0.18)', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} th`, { + color: '#c44e00', + background: 'rgba(196,78,0,0.06)', + borderBottom: '1px solid rgba(0,98,184,0.18)', + textShadow: 'none', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} td`, { + color: '#2d3d56', + borderBottom: '1px solid rgba(0,98,184,0.08)', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} tr:hover td`, { + background: 'rgba(196,78,0,0.03)', +}); + +// Lists +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ul li::marker`, { + color: '#0062b8', + content: '"▸ "', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} ol li::marker`, { + color: '#c44e00', +}); + +// Structural chrome +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} nav`, { + borderRight: '1px solid rgba(0,98,184,0.12)', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} header`, { + borderBottom: '1px solid rgba(0,98,184,0.14)', + boxShadow: '0 1px 8px rgba(0,0,0,0.07)', +}); + +// kbd +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} kbd`, { + background: '#e2e7ef', + border: '1px solid rgba(0,98,184,0.28)', + borderBottom: '2px solid rgba(0,98,184,0.42)', + color: '#0062b8', + textShadow: 'none', + boxShadow: '0 1px 3px rgba(0,0,0,0.10)', +}); + +// abbr / time / img +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} abbr[title]`, { + textDecoration: 'underline dotted #0062b8', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} time`, { + color: '#5a6e8c', +}); +globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} img:hover`, { + outline: '1px solid rgba(0,98,184,0.28)', + boxShadow: '0 1px 8px rgba(0,98,184,0.08)', +});