Skip to content

Commit 31458b5

Browse files
committed
adding day/night support
Signed-off-by: Chris Lyons <52037738+mephmanx@users.noreply.github.com>
1 parent 8390ed2 commit 31458b5

2 files changed

Lines changed: 138 additions & 15 deletions

File tree

preview/index.html

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,42 @@
88
:root {
99
color-scheme: light;
1010
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
11+
--bg: #f8fafc;
12+
--text: #0f172a;
13+
--panel-bg: #ffffff;
14+
--panel-border: #cbd5e1;
15+
--muted: #64748b;
16+
--label: #475569;
17+
--input-bg: #ffffff;
18+
--input-border: #cbd5e1;
19+
--button-bg: #1d4ed8;
20+
--button-border: #1d4ed8;
21+
--button-text: #ffffff;
22+
--button-secondary-bg: #ffffff;
23+
--button-secondary-border: #94a3b8;
24+
--button-secondary-text: #0f172a;
25+
}
26+
:root[data-theme-mode-resolved="dark"] {
27+
color-scheme: dark;
28+
--bg: #0b1220;
29+
--text: #e2e8f0;
30+
--panel-bg: #111b2f;
31+
--panel-border: #334155;
32+
--muted: #94a3b8;
33+
--label: #cbd5e1;
34+
--input-bg: #0f172a;
35+
--input-border: #334155;
36+
--button-bg: #3b82f6;
37+
--button-border: #3b82f6;
38+
--button-text: #ffffff;
39+
--button-secondary-bg: #0f172a;
40+
--button-secondary-border: #475569;
41+
--button-secondary-text: #e2e8f0;
1142
}
1243
body {
1344
margin: 0;
14-
background: #f8fafc;
15-
color: #0f172a;
45+
background: var(--bg);
46+
color: var(--text);
1647
}
1748
.wrap {
1849
max-width: 1120px;
@@ -25,17 +56,17 @@
2556
display: grid;
2657
gap: 0.65rem;
2758
padding: 0.9rem;
28-
border: 1px solid #cbd5e1;
59+
border: 1px solid var(--panel-border);
2960
border-radius: 10px;
30-
background: #ffffff;
61+
background: var(--panel-bg);
3162
}
3263
.row {
3364
display: grid;
3465
gap: 0.35rem;
3566
}
3667
.row > label {
3768
font-size: 0.8rem;
38-
color: #475569;
69+
color: var(--label);
3970
}
4071
input,
4172
textarea,
@@ -44,36 +75,43 @@
4475
}
4576
input,
4677
textarea {
47-
border: 1px solid #cbd5e1;
78+
border: 1px solid var(--input-border);
4879
border-radius: 8px;
4980
padding: 0.5rem 0.6rem;
50-
background: #ffffff;
81+
background: var(--input-bg);
82+
color: var(--text);
5183
}
5284
textarea {
5385
min-height: 5rem;
5486
}
87+
.heading {
88+
display: flex;
89+
align-items: center;
90+
justify-content: space-between;
91+
gap: 0.75rem;
92+
}
5593
.actions {
5694
display: flex;
5795
gap: 0.5rem;
5896
}
5997
button {
60-
border: 1px solid #1d4ed8;
61-
background: #1d4ed8;
62-
color: #ffffff;
98+
border: 1px solid var(--button-border);
99+
background: var(--button-bg);
100+
color: var(--button-text);
63101
border-radius: 8px;
64102
padding: 0.45rem 0.85rem;
65103
cursor: pointer;
66104
}
67105
button.secondary {
68-
border-color: #94a3b8;
69-
background: #ffffff;
70-
color: #0f172a;
106+
border-color: var(--button-secondary-border);
107+
background: var(--button-secondary-bg);
108+
color: var(--button-secondary-text);
71109
}
72110
.host {
73111
min-height: 220px;
74112
}
75113
.muted {
76-
color: #64748b;
114+
color: var(--muted);
77115
font-size: 0.82rem;
78116
}
79117
#authStatus[data-state="error"] {
@@ -87,7 +125,10 @@
87125
<body>
88126
<main class="wrap">
89127
<section class="panel">
90-
<h1 style="margin:0; font-size: 1.1rem;">Example MFE Local Preview</h1>
128+
<div class="heading">
129+
<h1 style="margin:0; font-size: 1.1rem;">Example MFE Local Preview</h1>
130+
<button id="themeToggle" class="secondary" type="button">Theme: Auto</button>
131+
</div>
91132
<p class="muted" style="margin:0;">
92133
Edit GraphQL endpoints and remount the module. Query/mutation docs are configurable from Directus props in production.
93134
</p>

preview/main.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createCmsModuleDefinition } from "../src/host-adapter";
44

55
type Cleanup = (() => void) | undefined;
66
type AuthStatusState = "idle" | "ok" | "error";
7+
type ThemeMode = "auto" | "light" | "dark";
78

89
type OidcDiscovery = {
910
authorizationEndpoint: string;
@@ -26,6 +27,7 @@ type PkceSessionState = AuthFormState & {
2627
const AUTH_TOKEN_STORAGE_KEY = "mfe.preview.authToken";
2728
const AUTH_FORM_STORAGE_KEY = "mfe.preview.authFormState";
2829
const PKCE_SESSION_STORAGE_KEY = "mfe.preview.pkceSessionState";
30+
const THEME_STORAGE_KEY = "suncoast:cms:theme-mode";
2931
const PKCE_MAX_AGE_MS = 10 * 60 * 1000;
3032
const DEFAULT_PREVIEW_GRAPHQL_HTTP_URLS = {
3133
dev: "https://cf-suncoast-graphql-proxy.dev.suncoast.systems/graphql",
@@ -115,6 +117,7 @@ const conversationIdInput = getInput("conversationId");
115117
const applyButton = getButton("applyButton");
116118
const loginButton = getButton("loginButton");
117119
const clearTokenButton = getButton("clearTokenButton");
120+
const themeToggleButton = getButton("themeToggle");
118121
const authStatus = getAuthStatusElement();
119122
const host = getHost();
120123

@@ -158,6 +161,61 @@ function getSavedToken(): string {
158161
}
159162
}
160163

164+
function getSavedTheme(): ThemeMode | null {
165+
try {
166+
const raw = localStorage.getItem(THEME_STORAGE_KEY)?.trim();
167+
if (raw === "auto" || raw === "light" || raw === "dark") {
168+
return raw;
169+
}
170+
return null;
171+
} catch {
172+
return null;
173+
}
174+
}
175+
176+
function saveTheme(theme: ThemeMode): void {
177+
try {
178+
localStorage.setItem(THEME_STORAGE_KEY, theme);
179+
} catch {
180+
// Ignore storage write errors in preview harness.
181+
}
182+
}
183+
184+
function inferSystemTheme(): ThemeMode {
185+
try {
186+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
187+
} catch {
188+
return "light";
189+
}
190+
}
191+
192+
function resolveThemeForRender(theme: ThemeMode): "light" | "dark" {
193+
if (theme === "auto") {
194+
const system = inferSystemTheme();
195+
return system === "dark" ? "dark" : "light";
196+
}
197+
return theme;
198+
}
199+
200+
function applyTheme(theme: ThemeMode): void {
201+
const resolvedTheme = resolveThemeForRender(theme);
202+
document.documentElement.setAttribute("data-theme-mode", theme);
203+
document.documentElement.setAttribute("data-theme-mode-resolved", resolvedTheme);
204+
const label =
205+
theme === "auto"
206+
? "Theme: Auto"
207+
: theme === "light"
208+
? "Theme: Light"
209+
: "Theme: Dark";
210+
themeToggleButton.textContent = label;
211+
}
212+
213+
function initializeTheme(): ThemeMode {
214+
const initial = getSavedTheme() ?? "auto";
215+
applyTheme(initial);
216+
return initial;
217+
}
218+
161219
function saveToken(value: string): void {
162220
const token = value.trim();
163221
try {
@@ -565,6 +623,30 @@ applyButton.addEventListener("click", () => {
565623
void mountFromForm();
566624
});
567625

626+
let activeTheme: ThemeMode = initializeTheme();
627+
628+
themeToggleButton.addEventListener("click", () => {
629+
activeTheme = activeTheme === "auto" ? "light" : activeTheme === "light" ? "dark" : "auto";
630+
saveTheme(activeTheme);
631+
applyTheme(activeTheme);
632+
});
633+
634+
try {
635+
const media = window.matchMedia("(prefers-color-scheme: dark)");
636+
const handleSystemThemeChange = () => {
637+
if (activeTheme === "auto") {
638+
applyTheme("auto");
639+
}
640+
};
641+
if (typeof media.addEventListener === "function") {
642+
media.addEventListener("change", handleSystemThemeChange);
643+
} else if (typeof media.addListener === "function") {
644+
media.addListener(handleSystemThemeChange);
645+
}
646+
} catch {
647+
// Ignore unavailable matchMedia support in preview harness.
648+
}
649+
568650
loginButton.addEventListener("click", () => {
569651
void startLoginRedirect();
570652
});

0 commit comments

Comments
 (0)