theme fix
This commit is contained in:
97
src/App.css
97
src/App.css
@@ -12,46 +12,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ============================
|
/* ============================
|
||||||
PROTECTED ROUTE SPINNER
|
PAGE LOADER (swaying tree)
|
||||||
============================ */
|
============================ */
|
||||||
.protected-route__loading {
|
.page-loader {
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1rem;
|
background: var(--color-bg-soft, #eef6ee);
|
||||||
background: #eef6ee;
|
|
||||||
color: #9ca3af;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.protected-route__spinner {
|
.page-loader__tree {
|
||||||
width: 28px;
|
transform-origin: center bottom;
|
||||||
height: 28px;
|
animation: tree-sway 1.8s ease-in-out infinite alternate;
|
||||||
border: 3px solid #e5e7eb;
|
|
||||||
border-top-color: #22c55e;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 0.7s linear infinite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes tree-sway {
|
||||||
to {
|
from { transform: rotate(-5deg); }
|
||||||
transform: rotate(360deg);
|
to { transform: rotate(5deg); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================
|
/* ============================
|
||||||
LOGIN PAGE
|
LOGIN PAGE
|
||||||
============================ */
|
============================ */
|
||||||
/* ── Loading state ──────────────────────────────────────── */
|
|
||||||
.login-page__spinner {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border: 3px solid #e5e7eb;
|
|
||||||
border-top-color: #22c55e;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 0.7s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Login page — two-panel layout ─────────────────────── */
|
/* ── Login page — two-panel layout ─────────────────────── */
|
||||||
.lp {
|
.lp {
|
||||||
@@ -62,18 +45,6 @@
|
|||||||
background: linear-gradient(160deg, #eef6ee 0%, #dcfce7 100%);
|
background: linear-gradient(160deg, #eef6ee 0%, #dcfce7 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loading state wrapper */
|
|
||||||
.lp--loading {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 1rem;
|
|
||||||
background: var(--color-bg-soft);
|
|
||||||
color: #9ca3af;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Left: animated tree hero ───────────────────────────── */
|
/* ── Left: animated tree hero ───────────────────────────── */
|
||||||
.lp__hero {
|
.lp__hero {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -835,6 +806,17 @@
|
|||||||
[data-theme="dark"] .sba-overlay {
|
[data-theme="dark"] .sba-overlay {
|
||||||
background: rgba(10, 18, 10, 0.76);
|
background: rgba(10, 18, 10, 0.76);
|
||||||
}
|
}
|
||||||
|
[data-theme="dark"] .sba-left-group rect {
|
||||||
|
fill: #1a2e1a;
|
||||||
|
stroke: #2d4a2d;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sba-left-group line {
|
||||||
|
stroke: #2d4a2d;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .sba-right-group rect {
|
||||||
|
fill: #162416;
|
||||||
|
stroke: #2d4a2d;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================
|
/* ============================
|
||||||
BOTTOM NAVIGATION — Static flex item, always at bottom
|
BOTTOM NAVIGATION — Static flex item, always at bottom
|
||||||
@@ -1252,7 +1234,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(135deg, #f9a8d4 0%, #f472b6 100%);
|
background: linear-gradient(135deg, #86efac 0%, #22c55e 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -2199,6 +2181,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================
|
||||||
|
ALERT MESSAGES
|
||||||
|
============================ */
|
||||||
|
.alert-msg {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.alert-msg--error {
|
||||||
|
background-color: #fef2f2;
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
.alert-msg--success {
|
||||||
|
background-color: #f0fdf4;
|
||||||
|
color: #15803d;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================
|
/* ============================
|
||||||
DARK THEME
|
DARK THEME
|
||||||
============================ */
|
============================ */
|
||||||
@@ -2504,16 +2504,19 @@
|
|||||||
color: #4ade80;
|
color: #4ade80;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Spinners -- */
|
/* -- Page loader -- */
|
||||||
[data-theme="dark"] .protected-route__spinner,
|
[data-theme="dark"] .page-loader {
|
||||||
[data-theme="dark"] .login-page__spinner {
|
background: #0f0f0f;
|
||||||
border-color: #2a2a2a;
|
|
||||||
border-top-color: #4ade80;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .protected-route__loading {
|
/* -- Alert messages -- */
|
||||||
background: #0f0f0f;
|
[data-theme="dark"] .alert-msg--error {
|
||||||
color: #5a6a5a;
|
background-color: rgba(185, 28, 28, 0.18);
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .alert-msg--success {
|
||||||
|
background-color: rgba(21, 128, 61, 0.18);
|
||||||
|
color: #86efac;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Icon btn hover -- */
|
/* -- Icon btn hover -- */
|
||||||
|
|||||||
26
src/components/PageLoader.tsx
Normal file
26
src/components/PageLoader.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export function PageLoader() {
|
||||||
|
return (
|
||||||
|
<div className="page-loader" role="status" aria-label="Loading">
|
||||||
|
<svg
|
||||||
|
className="page-loader__tree"
|
||||||
|
viewBox="0 0 60 90"
|
||||||
|
width="72"
|
||||||
|
height="72"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{/* Trunk */}
|
||||||
|
<rect x="26" y="58" width="8" height="28" rx="4" fill="#A0722A" />
|
||||||
|
{/* Side canopy depth */}
|
||||||
|
<circle cx="14" cy="52" r="14" fill="#16a34a" />
|
||||||
|
<circle cx="46" cy="52" r="14" fill="#16a34a" />
|
||||||
|
{/* Main canopy */}
|
||||||
|
<circle cx="30" cy="37" r="22" fill="#22c55e" />
|
||||||
|
{/* Light highlight */}
|
||||||
|
<circle cx="20" cy="27" r="10" fill="#4ade80" opacity="0.6" />
|
||||||
|
{/* Top tip */}
|
||||||
|
<circle cx="30" cy="17" r="10" fill="#4ade80" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { type ReactNode } from 'react'
|
import { type ReactNode } from 'react'
|
||||||
import { Navigate, useLocation } from 'react-router-dom'
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
import { PageLoader } from './PageLoader'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
@@ -11,12 +12,7 @@ export function ProtectedRoute({ children }: Props) {
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return <PageLoader />
|
||||||
<div className="protected-route__loading" aria-live="polite">
|
|
||||||
<span className="protected-route__spinner" aria-hidden />
|
|
||||||
<p>Loading…</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { decryptEntry } from '../lib/crypto'
|
|||||||
import { formatIST, getISTDateComponents } from '../lib/timezone'
|
import { formatIST, getISTDateComponents } from '../lib/timezone'
|
||||||
import BottomNav from '../components/BottomNav'
|
import BottomNav from '../components/BottomNav'
|
||||||
import { useOnboardingTour, hasPendingTourStep, clearPendingTourStep } from '../hooks/useOnboardingTour'
|
import { useOnboardingTour, hasPendingTourStep, clearPendingTourStep } from '../hooks/useOnboardingTour'
|
||||||
|
import { PageLoader } from '../components/PageLoader'
|
||||||
|
|
||||||
interface DecryptedEntry extends JournalEntry {
|
interface DecryptedEntry extends JournalEntry {
|
||||||
decryptedTitle?: string
|
decryptedTitle?: string
|
||||||
@@ -194,12 +195,7 @@ export default function HistoryPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return <PageLoader />
|
||||||
<div className="history-page" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
||||||
<p style={{ color: '#9ca3af' }}>Loading…</p>
|
|
||||||
<BottomNav />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -274,13 +270,13 @@ export default function HistoryPage() {
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{loadingEntries ? (
|
{loadingEntries ? (
|
||||||
<p style={{ color: '#9ca3af', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: '"Sniglet", system-ui' }}>
|
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: '"Sniglet", system-ui' }}>
|
||||||
Loading entries…
|
Loading entries…
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="entries-list">
|
<div className="entries-list">
|
||||||
{selectedDateEntries.length === 0 ? (
|
{selectedDateEntries.length === 0 ? (
|
||||||
<p style={{ color: '#9ca3af', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: '"Sniglet", system-ui' }}>
|
<p style={{ color: 'var(--color-text-muted)', fontSize: '0.875rem', textAlign: 'center', padding: '1.5rem 0', fontFamily: '"Sniglet", system-ui' }}>
|
||||||
No entries for this day yet. Start writing!
|
No entries for this day yet. Start writing!
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import BottomNav from '../components/BottomNav'
|
|||||||
import WelcomeModal from '../components/WelcomeModal'
|
import WelcomeModal from '../components/WelcomeModal'
|
||||||
import { SaveBookAnimation } from '../components/SaveBookAnimation'
|
import { SaveBookAnimation } from '../components/SaveBookAnimation'
|
||||||
import { useOnboardingTour, hasSeenOnboarding, markOnboardingDone } from '../hooks/useOnboardingTour'
|
import { useOnboardingTour, hasSeenOnboarding, markOnboardingDone } from '../hooks/useOnboardingTour'
|
||||||
|
import { PageLoader } from '../components/PageLoader'
|
||||||
|
|
||||||
const AFFIRMATIONS = [
|
const AFFIRMATIONS = [
|
||||||
'You showed up for yourself today 🌱',
|
'You showed up for yourself today 🌱',
|
||||||
@@ -61,18 +62,14 @@ export default function HomePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return <PageLoader />
|
||||||
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center' }}>
|
|
||||||
<p style={{ color: '#9ca3af' }}>Loading…</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return (
|
return (
|
||||||
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center', gap: '1rem' }}>
|
<div className="home-page" style={{ alignItems: 'center', justifyContent: 'center', gap: '1rem' }}>
|
||||||
<h1 style={{ fontFamily: '"Sniglet", system-ui', color: '#1a1a1a' }}>Grateful Journal</h1>
|
<h1 style={{ fontFamily: '"Sniglet", system-ui', color: 'var(--color-text)' }}>Grateful Journal</h1>
|
||||||
<p style={{ color: '#6b7280' }}>Sign in to start your journal.</p>
|
<p style={{ color: 'var(--color-text-muted)' }}>Sign in to start your journal.</p>
|
||||||
<Link to="/login" className="home-login-link">Go to login</Link>
|
<Link to="/login" className="home-login-link">Go to login</Link>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -188,15 +185,7 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{message && (
|
{message && (
|
||||||
<div style={{
|
<div className="alert-msg alert-msg--error" style={{ marginTop: '1rem' }}>
|
||||||
padding: '0.75rem',
|
|
||||||
marginTop: '1rem',
|
|
||||||
borderRadius: '8px',
|
|
||||||
fontSize: '0.875rem',
|
|
||||||
backgroundColor: '#fef2f2',
|
|
||||||
color: '#b91c1c',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}>
|
|
||||||
{message.text}
|
{message.text}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { GoogleSignInButton } from '../components/GoogleSignInButton'
|
import { GoogleSignInButton } from '../components/GoogleSignInButton'
|
||||||
import { TreeAnimation } from '../components/TreeAnimation'
|
import { TreeAnimation } from '../components/TreeAnimation'
|
||||||
|
import { PageLoader } from '../components/PageLoader'
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const { user, loading, signInWithGoogle, authError } = useAuth()
|
const { user, loading, signInWithGoogle, authError } = useAuth()
|
||||||
@@ -28,12 +29,7 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return <PageLoader />
|
||||||
<div className="lp lp--loading" aria-live="polite">
|
|
||||||
<span className="login-page__spinner" aria-hidden />
|
|
||||||
<p>Loading…</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { clearDeviceKey, clearEncryptedSecretKey } from '../lib/crypto'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import BottomNav from '../components/BottomNav'
|
import BottomNav from '../components/BottomNav'
|
||||||
import { useOnboardingTour, hasPendingTourStep, clearPendingTourStep } from '../hooks/useOnboardingTour'
|
import { useOnboardingTour, hasPendingTourStep, clearPendingTourStep } from '../hooks/useOnboardingTour'
|
||||||
|
import { PageLoader } from '../components/PageLoader'
|
||||||
|
|
||||||
const MAX_PHOTO_SIZE = 200 // px — resize to 200x200
|
const MAX_PHOTO_SIZE = 200 // px — resize to 200x200
|
||||||
|
|
||||||
@@ -187,12 +188,7 @@ export default function SettingsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return <PageLoader />
|
||||||
<div className="settings-page" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
||||||
<p style={{ color: '#9ca3af' }}>Loading…</p>
|
|
||||||
<BottomNav />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -211,7 +207,7 @@ export default function SettingsPage() {
|
|||||||
{photoURL ? (
|
{photoURL ? (
|
||||||
<img src={photoURL} alt={displayName} className="settings-avatar-img" />
|
<img src={photoURL} alt={displayName} className="settings-avatar-img" />
|
||||||
) : (
|
) : (
|
||||||
<div className="settings-avatar-placeholder" style={{ fontSize: '1.75rem', background: 'linear-gradient(135deg, #86efac 0%, #22c55e 100%)' }}>
|
<div className="settings-avatar-placeholder" style={{ fontSize: '1.75rem' }}>
|
||||||
{displayName.charAt(0).toUpperCase()}
|
{displayName.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -345,15 +341,7 @@ export default function SettingsPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{message && (
|
{message && (
|
||||||
<div style={{
|
<div className={`alert-msg alert-msg--${message.type}`} style={{ marginBottom: '1rem' }}>
|
||||||
padding: '0.75rem',
|
|
||||||
marginBottom: '1rem',
|
|
||||||
borderRadius: '8px',
|
|
||||||
fontSize: '0.875rem',
|
|
||||||
backgroundColor: message.type === 'success' ? '#f0fdf4' : '#fef2f2',
|
|
||||||
color: message.type === 'success' ? '#15803d' : '#b91c1c',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}>
|
|
||||||
{message.text}
|
{message.text}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -481,9 +469,8 @@ export default function SettingsPage() {
|
|||||||
disabled={saving}
|
disabled={saving}
|
||||||
maxLength={50}
|
maxLength={50}
|
||||||
autoFocus
|
autoFocus
|
||||||
style={{ borderColor: '#d1d5db' }}
|
onFocus={(e) => (e.target.style.borderColor = 'var(--color-primary)')}
|
||||||
onFocus={(e) => (e.target.style.borderColor = '#22c55e')}
|
onBlur={(e) => (e.target.style.borderColor = '')}
|
||||||
onBlur={(e) => (e.target.style.borderColor = '#d1d5db')}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="confirm-modal-actions" style={{ marginTop: '1rem' }}>
|
<div className="confirm-modal-actions" style={{ marginTop: '1rem' }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user