Align dark mode with Perspective's Pro Dark theme
App chrome now uses Pro Dark's neutral grays (#242526 background, #2a2c2f panels, #4c505b borders) so the surrounding UI sits cleanly against the viewer instead of clashing with its warmer tone. Status accents are desaturated to match. Forecast view sets theme="Pro Dark" or "Pro Light" on the perspective-viewer in sync with the toggle. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1a3209cbc2
commit
d70d813604
@ -13,17 +13,20 @@
|
|||||||
--accent-text: #1d4ed8;
|
--accent-text: #1d4ed8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark palette tuned to Perspective's "Pro Dark" theme:
|
||||||
|
bg #242526, tooltip #2a2c2f, gridline #3b3f46, inactive #61656e,
|
||||||
|
inactive border #4c505b, active #2770a9, legend #c5c9d0. */
|
||||||
.dark {
|
.dark {
|
||||||
--bg-primary: #111827;
|
--bg-primary: #242526;
|
||||||
--bg-secondary: #1f2937;
|
--bg-secondary: #2a2c2f;
|
||||||
--bg-tertiary: #374151;
|
--bg-tertiary: #3b3f46;
|
||||||
--text-primary: #f9fafb;
|
--text-primary: #ffffff;
|
||||||
--text-secondary: #e5e7eb;
|
--text-secondary: #c5c9d0;
|
||||||
--text-muted: #6b7280;
|
--text-muted: #61656e;
|
||||||
--border-color: #374151;
|
--border-color: #4c505b;
|
||||||
--border-light: #1f2937;
|
--border-light: #3b3f46;
|
||||||
--accent-bg: #1e3a5f;
|
--accent-bg: rgba(39, 113, 170, 0.32);
|
||||||
--accent-text: #60a5fa;
|
--accent-text: #4778c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
body { margin: 0; background-color: var(--bg-primary); color: var(--text-primary); }
|
body { margin: 0; background-color: var(--bg-primary); color: var(--text-primary); }
|
||||||
@ -47,19 +50,29 @@ body { margin: 0; background-color: var(--bg-primary); color: var(--text-primary
|
|||||||
.dark .text-blue-700 { color: var(--accent-text); }
|
.dark .text-blue-700 { color: var(--accent-text); }
|
||||||
.dark .border-blue-300 { border-color: var(--accent-text); }
|
.dark .border-blue-300 { border-color: var(--accent-text); }
|
||||||
.dark .hover\:bg-blue-50:hover { background-color: var(--accent-bg); }
|
.dark .hover\:bg-blue-50:hover { background-color: var(--accent-bg); }
|
||||||
.dark .bg-green-50 { background-color: #064e3b; }
|
|
||||||
.dark .text-green-600 { color: #34d399; }
|
/* Status accents — desaturated to sit on Pro Dark's neutral background */
|
||||||
.dark .text-green-700 { color: #34d399; }
|
.dark .bg-green-50 { background-color: #1a3d2c; }
|
||||||
.dark .text-green-400 { color: #34d399; }
|
.dark .text-green-600 { color: #6ee7b7; }
|
||||||
.dark .bg-yellow-50 { background-color: #451a03; }
|
.dark .text-green-700 { color: #6ee7b7; }
|
||||||
.dark .text-yellow-700 { color: #fbbf24; }
|
.dark .text-green-400 { color: #6ee7b7; }
|
||||||
.dark .bg-purple-50 { background-color: #1e1b4b; }
|
.dark .bg-yellow-50 { background-color: #3a2e14; }
|
||||||
.dark .text-purple-700 { color: #a78bfa; }
|
.dark .text-yellow-700 { color: #f5c66f; }
|
||||||
.dark .bg-red-50 { background-color: #450a0a; }
|
.dark .bg-amber-50 { background-color: #3a2e14; }
|
||||||
.dark .text-red-700 { color: #f87171; }
|
.dark .border-amber-200 { border-color: #5a4a26; }
|
||||||
|
.dark .text-amber-700 { color: #f5c66f; }
|
||||||
|
.dark .text-amber-800 { color: #f5c66f; }
|
||||||
|
.dark .bg-purple-50 { background-color: #2a1f3d; }
|
||||||
|
.dark .text-purple-600 { color: #c4a8e8; }
|
||||||
|
.dark .text-purple-700 { color: #c4a8e8; }
|
||||||
|
.dark .bg-red-50 { background-color: #3d1f1f; }
|
||||||
|
.dark .text-red-600 { color: #ff9485; }
|
||||||
|
.dark .text-red-700 { color: #ff9485; }
|
||||||
|
|
||||||
.dark .border-gray-100 { border-color: var(--border-light); }
|
.dark .border-gray-100 { border-color: var(--border-light); }
|
||||||
.dark .border-gray-200 { border-color: var(--border-color); }
|
.dark .border-gray-200 { border-color: var(--border-color); }
|
||||||
.dark .border-gray-300 { border-color: var(--border-color); }
|
.dark .border-gray-300 { border-color: var(--border-color); }
|
||||||
|
.dark .border-blue-100 { border-color: var(--border-color); }
|
||||||
.dark .border-b { border-color: var(--border-color); }
|
.dark .border-b { border-color: var(--border-color); }
|
||||||
.dark .border-t { border-color: var(--border-color); }
|
.dark .border-t { border-color: var(--border-color); }
|
||||||
.dark .border-r { border-color: var(--border-color); }
|
.dark .border-r { border-color: var(--border-color); }
|
||||||
@ -73,8 +86,8 @@ body { margin: 0; background-color: var(--bg-primary); color: var(--text-primary
|
|||||||
.dark .hover\:border-gray-300:hover { border-color: var(--border-color); }
|
.dark .hover\:border-gray-300:hover { border-color: var(--border-color); }
|
||||||
.dark .hover\:border-gray-400:hover { border-color: var(--border-color); }
|
.dark .hover\:border-gray-400:hover { border-color: var(--border-color); }
|
||||||
.dark .focus\:border-gray-300:focus { border-color: var(--border-color); }
|
.dark .focus\:border-gray-300:focus { border-color: var(--border-color); }
|
||||||
.dark ::selection { background-color: var(--accent-bg); color: var(--accent-text); }
|
.dark ::selection { background-color: var(--accent-bg); color: var(--text-primary); }
|
||||||
.dark input { background-color: var(--bg-secondary); color: var(--text-primary); }
|
.dark input { background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border-color); }
|
||||||
.dark select { background-color: var(--bg-secondary); color: var(--text-primary); }
|
.dark select { background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border-color); }
|
||||||
.dark textarea { background-color: var(--bg-secondary); color: var(--text-primary); }
|
.dark textarea { background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border-color); }
|
||||||
.dark .bg-transparent { background-color: transparent; }
|
.dark .bg-transparent { background-color: transparent; }
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
|
import useTheme from '../theme.jsx'
|
||||||
|
|
||||||
const LAYOUT_KEY = (vid) => `pf_layout_v${vid}` // last-used layout (auto restore)
|
const LAYOUT_KEY = (vid) => `pf_layout_v${vid}` // last-used layout (auto restore)
|
||||||
const LAYOUTS_KEY = (vid) => `pf_layouts_v${vid}` // named layout list
|
const LAYOUTS_KEY = (vid) => `pf_layouts_v${vid}` // named layout list
|
||||||
@ -29,6 +30,7 @@ function cleanLayout(cfg, validCols) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Forecast() {
|
export default function Forecast() {
|
||||||
|
const { dark } = useTheme()
|
||||||
const [sources, setSources] = useState([])
|
const [sources, setSources] = useState([])
|
||||||
const [sourceId, setSourceId] = useState('')
|
const [sourceId, setSourceId] = useState('')
|
||||||
const [versions, setVersions] = useState([])
|
const [versions, setVersions] = useState([])
|
||||||
@ -105,6 +107,12 @@ export default function Forecast() {
|
|||||||
initViewer(versionId, sourceId)
|
initViewer(versionId, sourceId)
|
||||||
}, [versionId, sourceId])
|
}, [versionId, sourceId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (viewerRef.current) {
|
||||||
|
viewerRef.current.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
||||||
|
}
|
||||||
|
}, [dark, versionId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const blank = Object.fromEntries(Object.keys(slice).map(k => [k, '']))
|
const blank = Object.fromEntries(Object.keys(slice).map(k => [k, '']))
|
||||||
setRecodeSet(blank)
|
setRecodeSet(blank)
|
||||||
@ -196,6 +204,7 @@ export default function Forecast() {
|
|||||||
: await worker.table([], { name: tableName, index: 'pf_id' })
|
: await worker.table([], { name: tableName, index: 'pf_id' })
|
||||||
|
|
||||||
await viewer.load(worker)
|
await viewer.load(worker)
|
||||||
|
viewer.setAttribute('theme', dark ? 'Pro Dark' : 'Pro Light')
|
||||||
|
|
||||||
// restore last-used layout or build default
|
// restore last-used layout or build default
|
||||||
const saved = localStorage.getItem(LAYOUT_KEY(vid))
|
const saved = localStorage.getItem(LAYOUT_KEY(vid))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user