added liquid glass theme

This commit is contained in:
2026-04-14 15:26:13 +05:30
parent 6e906436cc
commit 816476ed02
4 changed files with 323 additions and 6 deletions

View File

@@ -1580,6 +1580,12 @@
.settings-theme-dot-dark { .settings-theme-dot-dark {
background: #1a1a1a; background: #1a1a1a;
} }
.settings-theme-dot-glass {
background: linear-gradient(135deg, rgba(255,255,255,0.9) 0%, rgba(209,250,229,0.7) 50%, rgba(167,243,208,0.5) 100%);
border: 2px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
.settings-theme-dot:hover:not(:disabled) { .settings-theme-dot:hover:not(:disabled) {
transform: scale(1.15); transform: scale(1.15);
} }
@@ -2997,6 +3003,279 @@
color: #9ca3af; color: #9ca3af;
} }
/* ============================
LIQUID GLASS THEME
============================ */
/* -- Pages must be transparent so body background shows through -- */
[data-theme="liquid-glass"] .home-page,
[data-theme="liquid-glass"] .history-page,
[data-theme="liquid-glass"] .settings-page {
background: transparent;
}
/* -- Glass surface applied to all card/surface elements -- */
[data-theme="liquid-glass"] .journal-card,
[data-theme="liquid-glass"] .calendar-card,
[data-theme="liquid-glass"] .entry-card,
[data-theme="liquid-glass"] .entry-modal,
[data-theme="liquid-glass"] .confirm-modal,
[data-theme="liquid-glass"] .settings-profile,
[data-theme="liquid-glass"] .settings-card,
[data-theme="liquid-glass"] .settings-tutorial-btn,
[data-theme="liquid-glass"] .settings-clear-btn,
[data-theme="liquid-glass"] .settings-signout-btn,
[data-theme="liquid-glass"] .bottom-nav,
[data-theme="liquid-glass"] .lp__form {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: var(--glass-border);
box-shadow: var(--glass-shadow);
}
/* -- Unified shadow (no individual overrides needed) -- */
[data-theme="liquid-glass"] .journal-card,
[data-theme="liquid-glass"] .calendar-card,
[data-theme="liquid-glass"] .entry-card,
[data-theme="liquid-glass"] .settings-profile,
[data-theme="liquid-glass"] .settings-card {
box-shadow: var(--glass-shadow);
}
/* -- Bottom nav glass -- */
[data-theme="liquid-glass"] .bottom-nav {
box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.5), 0 -8px 32px rgba(0, 0, 0, 0.1);
}
/* -- Text colors — dark & crisp for readability on glass -- */
[data-theme="liquid-glass"] .journal-prompt,
[data-theme="liquid-glass"] .settings-header-text h1,
[data-theme="liquid-glass"] .history-header-text h1,
[data-theme="liquid-glass"] .settings-profile-name,
[data-theme="liquid-glass"] .settings-item-title,
[data-theme="liquid-glass"] .calendar-month,
[data-theme="liquid-glass"] .entry-title {
color: #0f172a;
}
[data-theme="liquid-glass"] .journal-date {
color: #16a34a;
}
[data-theme="liquid-glass"] .settings-subtitle,
[data-theme="liquid-glass"] .history-subtitle,
[data-theme="liquid-glass"] .settings-item-subtitle,
[data-theme="liquid-glass"] .settings-section-title,
[data-theme="liquid-glass"] .entry-preview,
[data-theme="liquid-glass"] .entry-date,
[data-theme="liquid-glass"] .entry-time,
[data-theme="liquid-glass"] .recent-entries-title,
[data-theme="liquid-glass"] .calendar-weekday {
color: #334155;
}
[data-theme="liquid-glass"] .journal-title-input,
[data-theme="liquid-glass"] .journal-entry-textarea {
color: #1e293b;
}
[data-theme="liquid-glass"] .journal-title-input::placeholder,
[data-theme="liquid-glass"] .journal-entry-textarea::placeholder {
color: rgba(30, 41, 59, 0.45);
}
[data-theme="liquid-glass"] .journal-title-input {
border-bottom-color: rgba(255, 255, 255, 0.4);
}
[data-theme="liquid-glass"] .journal-title-input:focus {
border-bottom-color: #16a34a;
}
/* -- Settings buttons text -- */
[data-theme="liquid-glass"] .settings-tutorial-btn {
color: #0f172a;
}
[data-theme="liquid-glass"] .settings-clear-btn {
color: #b91c1c;
}
[data-theme="liquid-glass"] .settings-signout-btn {
color: #475569;
}
/* -- Settings buttons hover -- */
[data-theme="liquid-glass"] .settings-tutorial-btn:hover {
background: rgba(255, 255, 255, 0.35);
}
[data-theme="liquid-glass"] .settings-clear-btn:hover {
background: rgba(254, 202, 202, 0.35);
}
[data-theme="liquid-glass"] .settings-signout-btn:hover {
background: rgba(255, 255, 255, 0.3);
color: #1e293b;
}
/* -- Settings item hover -- */
[data-theme="liquid-glass"] .settings-item-button:hover {
background: rgba(255, 255, 255, 0.28);
}
/* -- Settings divider -- */
[data-theme="liquid-glass"] .settings-divider {
background: rgba(255, 255, 255, 0.45);
}
/* -- Settings theme dot -- */
[data-theme="liquid-glass"] .settings-theme-dot {
border-color: rgba(0, 0, 0, 0.12);
}
[data-theme="liquid-glass"] .settings-theme-dot-active {
border-color: #16a34a;
box-shadow: 0 0 0 2px #16a34a;
}
/* -- Settings icon backgrounds -- */
[data-theme="liquid-glass"] .settings-item-icon-green {
background: rgba(34, 197, 94, 0.2);
color: #15803d;
}
[data-theme="liquid-glass"] .settings-item-icon-gray {
background: rgba(100, 116, 139, 0.18);
color: #475569;
}
[data-theme="liquid-glass"] .settings-item-icon-orange {
background: rgba(251, 146, 60, 0.2);
color: #c2410c;
}
[data-theme="liquid-glass"] .settings-item-icon-blue {
background: rgba(59, 130, 246, 0.18);
color: #1d4ed8;
}
[data-theme="liquid-glass"] .settings-item-icon-purple {
background: rgba(139, 92, 246, 0.18);
color: #6d28d9;
}
/* -- Settings misc -- */
[data-theme="liquid-glass"] .settings-toggle-slider {
background: rgba(0, 0, 0, 0.2);
}
[data-theme="liquid-glass"] .settings-toggle input:checked + .settings-toggle-slider {
background: #16a34a;
}
[data-theme="liquid-glass"] .settings-item-arrow {
color: #334155;
}
[data-theme="liquid-glass"] .settings-enc {
color: rgba(15, 23, 42, 0.45);
}
[data-theme="liquid-glass"] .settings-edit-btn {
background: rgba(34, 197, 94, 0.2);
color: #15803d;
}
/* -- Calendar -- */
[data-theme="liquid-glass"] .calendar-day {
color: #334155;
}
[data-theme="liquid-glass"] .calendar-day:not(.calendar-day-empty):hover {
background: rgba(255, 255, 255, 0.4);
color: #0f172a;
}
[data-theme="liquid-glass"] .calendar-day-has-entry {
background: rgba(34, 197, 94, 0.22);
color: #15803d;
}
[data-theme="liquid-glass"] .calendar-day-today {
background: #16a34a;
color: #fff;
}
[data-theme="liquid-glass"] .calendar-nav-btn:hover {
background: rgba(255, 255, 255, 0.35);
color: #0f172a;
}
/* -- Entry card -- */
[data-theme="liquid-glass"] .entry-card {
border-left-color: rgba(22, 163, 74, 0.5);
}
[data-theme="liquid-glass"] .entry-card:hover {
background: rgba(255, 255, 255, 0.28);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.14), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
}
/* -- Entry modal -- */
[data-theme="liquid-glass"] .entry-modal {
border-top-color: #16a34a;
}
[data-theme="liquid-glass"] .entry-modal-title {
color: #0f172a;
}
[data-theme="liquid-glass"] .entry-modal-content {
color: #1e293b;
}
[data-theme="liquid-glass"] .entry-modal-date,
[data-theme="liquid-glass"] .entry-modal-time {
color: #475569;
}
[data-theme="liquid-glass"] .entry-modal-badge {
background: rgba(34, 197, 94, 0.2);
color: #15803d;
}
[data-theme="liquid-glass"] .entry-modal-close {
background: rgba(255, 255, 255, 0.35);
color: #475569;
}
[data-theme="liquid-glass"] .entry-modal-close:hover {
background: rgba(255, 255, 255, 0.5);
color: #0f172a;
}
/* -- Confirm/modal overlays -- */
[data-theme="liquid-glass"] .confirm-modal-overlay,
[data-theme="liquid-glass"] .entry-modal-overlay {
background: rgba(15, 23, 42, 0.2);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
}
/* -- Bottom nav -- */
[data-theme="liquid-glass"] .bottom-nav-btn {
color: #475569;
}
[data-theme="liquid-glass"] .bottom-nav-btn:hover {
color: #15803d;
background: rgba(255, 255, 255, 0.3);
}
[data-theme="liquid-glass"] .bottom-nav-btn-active {
background: #16a34a;
color: #fff;
}
/* -- Write button -- */
[data-theme="liquid-glass"] .journal-write-btn {
background: #16a34a;
box-shadow: 0 4px 16px rgba(22, 163, 74, 0.35);
}
[data-theme="liquid-glass"] .journal-write-btn:hover:not(:disabled) {
background: #15803d;
box-shadow: 0 6px 24px rgba(22, 163, 74, 0.45);
}
/* -- Desktop sidebar nav glass -- */
@media (min-width: 860px) {
[data-theme="liquid-glass"] .bottom-nav {
background: var(--glass-bg);
border-right-color: rgba(255, 255, 255, 0.4);
}
[data-theme="liquid-glass"] .bottom-nav-brand {
border-bottom-color: rgba(255, 255, 255, 0.4);
}
}
[data-theme="dark"] .clock-picker__mode-btn--active { [data-theme="dark"] .clock-picker__mode-btn--active {
background: #4ade80; background: #4ade80;
border-color: #4ade80; border-color: #4ade80;

View File

@@ -96,3 +96,28 @@ button:focus-visible {
[data-theme="dark"] body { [data-theme="dark"] body {
background: #0a0a0a; background: #0a0a0a;
} }
/* ── Liquid Glass theme root overrides ───────────────────── */
[data-theme="liquid-glass"] {
--glass-bg: rgba(255, 255, 255, 0.18);
--glass-blur: blur(28px) saturate(200%) brightness(1.05);
--glass-border: 1px solid rgba(255, 255, 255, 0.55);
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 1px 0 rgba(255, 255, 255, 0.7) inset;
--color-primary: #16a34a;
--color-primary-hover: #15803d;
--color-bg-soft: transparent;
--color-surface: var(--glass-bg);
--color-accent-light: rgba(220, 252, 231, 0.4);
--color-text: #0f172a;
--color-text-muted: #334155;
--color-border: rgba(255, 255, 255, 0.4);
color: var(--color-text);
background-color: transparent;
caret-color: #16a34a;
}
/* Same bg as light theme when no custom image is set */
[data-theme="liquid-glass"] body:not(.gj-has-bg) {
background: #eef6ee;
}

View File

@@ -4,6 +4,10 @@ import './index.css'
import App from './App.tsx' import App from './App.tsx'
import { listenForegroundMessages } from './hooks/useReminder' import { listenForegroundMessages } from './hooks/useReminder'
// Apply saved theme immediately to avoid flash
const savedTheme = localStorage.getItem('gj-theme') || 'light'
document.documentElement.setAttribute('data-theme', savedTheme)
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js') navigator.serviceWorker.register('/sw.js')

View File

@@ -54,8 +54,8 @@ export default function SettingsPage() {
const { user, userId, mongoUser, signOut, loading, refreshMongoUser } = useAuth() const { user, userId, mongoUser, signOut, loading, refreshMongoUser } = useAuth()
// const [passcodeEnabled, setPasscodeEnabled] = useState(false) // Passcode lock — disabled for now // const [passcodeEnabled, setPasscodeEnabled] = useState(false) // Passcode lock — disabled for now
// const [faceIdEnabled, setFaceIdEnabled] = useState(false) // Face ID — disabled for now // const [faceIdEnabled, setFaceIdEnabled] = useState(false) // Face ID — disabled for now
const [theme, setTheme] = useState<'light' | 'dark'>(() => { const [theme, setTheme] = useState<'light' | 'dark' | 'liquid-glass'>(() => {
return (localStorage.getItem('gj-theme') as 'light' | 'dark') || 'light' return (localStorage.getItem('gj-theme') as 'light' | 'dark' | 'liquid-glass') || 'light'
}) })
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null) const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
@@ -229,7 +229,7 @@ export default function SettingsPage() {
} }
// Apply theme to DOM // Apply theme to DOM
const applyTheme = useCallback((t: 'light' | 'dark') => { const applyTheme = useCallback((t: 'light' | 'dark' | 'liquid-glass') => {
document.documentElement.setAttribute('data-theme', t) document.documentElement.setAttribute('data-theme', t)
localStorage.setItem('gj-theme', t) localStorage.setItem('gj-theme', t)
}, []) }, [])
@@ -239,10 +239,11 @@ export default function SettingsPage() {
applyTheme(theme) applyTheme(theme)
}, [theme, applyTheme]) }, [theme, applyTheme])
const handleThemeChange = (newTheme: 'light' | 'dark') => { const handleThemeChange = (newTheme: 'light' | 'dark' | 'liquid-glass') => {
setTheme(newTheme) setTheme(newTheme)
applyTheme(newTheme) applyTheme(newTheme)
setMessage({ type: 'success', text: `Switched to ${newTheme === 'light' ? 'Light' : 'Dark'} theme` }) const label = newTheme === 'light' ? 'Light' : newTheme === 'dark' ? 'Dark' : 'Liquid Glass'
setMessage({ type: 'success', text: `Switched to ${label} theme` })
setTimeout(() => setMessage(null), 2000) setTimeout(() => setMessage(null), 2000)
} }
@@ -560,7 +561,9 @@ export default function SettingsPage() {
</div> </div>
<div className="settings-item-content"> <div className="settings-item-content">
<h4 className="settings-item-title">Theme</h4> <h4 className="settings-item-title">Theme</h4>
<p className="settings-item-subtitle">Currently: {theme === 'light' ? 'Light' : 'Dark'}</p> <p className="settings-item-subtitle">
Currently: {theme === 'light' ? 'Light' : theme === 'dark' ? 'Dark' : 'Liquid Glass'}
</p>
</div> </div>
<div className="settings-theme-colors"> <div className="settings-theme-colors">
<button <button
@@ -575,6 +578,12 @@ export default function SettingsPage() {
className={`settings-theme-dot settings-theme-dot-dark${theme === 'dark' ? ' settings-theme-dot-active' : ''}`} className={`settings-theme-dot settings-theme-dot-dark${theme === 'dark' ? ' settings-theme-dot-active' : ''}`}
title="Dark theme" title="Dark theme"
></button> ></button>
<button
type="button"
onClick={() => handleThemeChange('liquid-glass')}
className={`settings-theme-dot settings-theme-dot-glass${theme === 'liquid-glass' ? ' settings-theme-dot-active' : ''}`}
title="Liquid Glass theme"
></button>
</div> </div>
</div> </div>
</div> </div>