added liquid glass theme
This commit is contained in:
279
src/App.css
279
src/App.css
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user