@scalemule/news-publication 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PYCU2T5O.js → chunk-5QHNNRVQ.js} +1 -1
- package/dist/{chunk-R33MJWND.js → chunk-AYSBXLOT.js} +1 -1
- package/dist/chunk-NQD3M7ZY.js +283 -0
- package/dist/{chunk-TJ7RL3PV.js → chunk-RNXTBN5Z.js} +7 -0
- package/dist/cookie-consent-banner.js +2 -2
- package/dist/cookie-settings-view.cjs +7 -0
- package/dist/cookie-settings-view.js +2 -2
- package/dist/index.cjs +296 -0
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +4 -3
- package/dist/theme-toggle.cjs +258 -0
- package/dist/theme-toggle.d.cts +22 -0
- package/dist/theme-toggle.d.ts +22 -0
- package/dist/theme-toggle.js +2 -0
- package/package.json +7 -2
- package/tokens/broadsheet.css +177 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/theme.ts
|
|
8
|
+
var THEME_COOKIE = "news_publication_theme";
|
|
9
|
+
var THEME_STORAGE_KEY = "news_publication_theme";
|
|
10
|
+
var THEME_EVENT = "news_publication_theme_change";
|
|
11
|
+
var THEME_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
12
|
+
var memoryTheme = "system";
|
|
13
|
+
function getSystemTheme() {
|
|
14
|
+
if (typeof window === "undefined" || !window.matchMedia) return "light";
|
|
15
|
+
try {
|
|
16
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
17
|
+
} catch {
|
|
18
|
+
return "light";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function resolveEffectiveTheme(mode) {
|
|
22
|
+
if (mode === "system") return getSystemTheme();
|
|
23
|
+
return mode;
|
|
24
|
+
}
|
|
25
|
+
function getStoredTheme() {
|
|
26
|
+
if (typeof window === "undefined") return memoryTheme;
|
|
27
|
+
try {
|
|
28
|
+
const stored = window.localStorage?.getItem?.(THEME_STORAGE_KEY);
|
|
29
|
+
if (stored === "light" || stored === "dark" || stored === "system") {
|
|
30
|
+
memoryTheme = stored;
|
|
31
|
+
return stored;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
if (typeof document !== "undefined" && document.cookie) {
|
|
36
|
+
try {
|
|
37
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${THEME_COOKIE}=([^;]*)`));
|
|
38
|
+
if (match) {
|
|
39
|
+
const decoded = decodeURIComponent(match[1]);
|
|
40
|
+
if (decoded === "light" || decoded === "dark" || decoded === "system") {
|
|
41
|
+
memoryTheme = decoded;
|
|
42
|
+
return decoded;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return memoryTheme;
|
|
49
|
+
}
|
|
50
|
+
function applyTheme(mode) {
|
|
51
|
+
if (typeof document === "undefined") return;
|
|
52
|
+
try {
|
|
53
|
+
document.documentElement.setAttribute("data-theme", mode);
|
|
54
|
+
const elements = document.querySelectorAll(".publication");
|
|
55
|
+
elements.forEach((el) => el.setAttribute("data-theme", mode));
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var memoryListeners = /* @__PURE__ */ new Set();
|
|
60
|
+
function setTheme(mode) {
|
|
61
|
+
memoryTheme = mode;
|
|
62
|
+
if (typeof window !== "undefined") {
|
|
63
|
+
try {
|
|
64
|
+
window.localStorage?.setItem?.(THEME_STORAGE_KEY, mode);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
if (typeof document !== "undefined") {
|
|
68
|
+
try {
|
|
69
|
+
document.cookie = `${THEME_COOKIE}=${encodeURIComponent(mode)}; path=/; max-age=${THEME_MAX_AGE_SECONDS}; SameSite=Lax`;
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
applyTheme(mode);
|
|
74
|
+
const effective = resolveEffectiveTheme(mode);
|
|
75
|
+
try {
|
|
76
|
+
window.dispatchEvent(
|
|
77
|
+
new CustomEvent(THEME_EVENT, {
|
|
78
|
+
detail: { mode, effective }
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
memoryListeners.forEach((listener) => {
|
|
85
|
+
try {
|
|
86
|
+
listener();
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function subscribeTheme(listener) {
|
|
92
|
+
memoryListeners.add(listener);
|
|
93
|
+
if (typeof window === "undefined") {
|
|
94
|
+
return () => {
|
|
95
|
+
memoryListeners.delete(listener);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const onStorage = (e) => {
|
|
99
|
+
if (e.key === THEME_STORAGE_KEY) {
|
|
100
|
+
applyTheme(getStoredTheme());
|
|
101
|
+
listener();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const onCustom = () => {
|
|
105
|
+
listener();
|
|
106
|
+
};
|
|
107
|
+
window.addEventListener("storage", onStorage);
|
|
108
|
+
window.addEventListener(THEME_EVENT, onCustom);
|
|
109
|
+
let mediaQuery;
|
|
110
|
+
let onMediaChange;
|
|
111
|
+
if (window.matchMedia) {
|
|
112
|
+
try {
|
|
113
|
+
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
114
|
+
onMediaChange = () => {
|
|
115
|
+
if (getStoredTheme() === "system") {
|
|
116
|
+
applyTheme("system");
|
|
117
|
+
listener();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
if (mediaQuery.addEventListener) {
|
|
121
|
+
mediaQuery.addEventListener("change", onMediaChange);
|
|
122
|
+
} else if (mediaQuery.addListener) {
|
|
123
|
+
mediaQuery.addListener(onMediaChange);
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return () => {
|
|
129
|
+
window.removeEventListener("storage", onStorage);
|
|
130
|
+
window.removeEventListener(THEME_EVENT, onCustom);
|
|
131
|
+
if (mediaQuery && onMediaChange) {
|
|
132
|
+
if (mediaQuery.removeEventListener) {
|
|
133
|
+
mediaQuery.removeEventListener("change", onMediaChange);
|
|
134
|
+
} else if (mediaQuery.removeListener) {
|
|
135
|
+
mediaQuery.removeListener(onMediaChange);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function SunIcon() {
|
|
141
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
142
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" })
|
|
144
|
+
] });
|
|
145
|
+
}
|
|
146
|
+
function MoonIcon() {
|
|
147
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) });
|
|
148
|
+
}
|
|
149
|
+
function SystemIcon() {
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }),
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", x2: "16", y1: "21", y2: "21" }),
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "17", y2: "21" })
|
|
154
|
+
] });
|
|
155
|
+
}
|
|
156
|
+
function ThemeToggle({
|
|
157
|
+
variant = "compact",
|
|
158
|
+
className = "",
|
|
159
|
+
showPromotion = false,
|
|
160
|
+
accountUrl = "/account"
|
|
161
|
+
}) {
|
|
162
|
+
const currentTheme = react.useSyncExternalStore(subscribeTheme, getStoredTheme, () => "system");
|
|
163
|
+
const [mounted, setMounted] = react.useState(false);
|
|
164
|
+
const [promoVisible, setPromoVisible] = react.useState(false);
|
|
165
|
+
react.useEffect(() => {
|
|
166
|
+
setMounted(true);
|
|
167
|
+
}, []);
|
|
168
|
+
const handleSelect = (mode) => {
|
|
169
|
+
setTheme(mode);
|
|
170
|
+
if (showPromotion) {
|
|
171
|
+
setPromoVisible(true);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const active = mounted ? currentTheme : "system";
|
|
175
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `pub-theme-toggle-container ${className}`.trim(), children: [
|
|
176
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
177
|
+
"div",
|
|
178
|
+
{
|
|
179
|
+
className: "pub-theme-toggle",
|
|
180
|
+
"data-variant": variant,
|
|
181
|
+
role: "group",
|
|
182
|
+
"aria-label": "Display theme selection",
|
|
183
|
+
children: [
|
|
184
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
185
|
+
"button",
|
|
186
|
+
{
|
|
187
|
+
type: "button",
|
|
188
|
+
onClick: () => handleSelect("light"),
|
|
189
|
+
"aria-pressed": active === "light",
|
|
190
|
+
"data-active": active === "light",
|
|
191
|
+
title: "Light theme (broadsheet day edition)",
|
|
192
|
+
"aria-label": "Light theme",
|
|
193
|
+
children: [
|
|
194
|
+
/* @__PURE__ */ jsxRuntime.jsx(SunIcon, {}),
|
|
195
|
+
variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Light" })
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
),
|
|
199
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
type: "button",
|
|
203
|
+
onClick: () => handleSelect("dark"),
|
|
204
|
+
"aria-pressed": active === "dark",
|
|
205
|
+
"data-active": active === "dark",
|
|
206
|
+
title: "Dark theme (broadsheet evening edition)",
|
|
207
|
+
"aria-label": "Dark theme",
|
|
208
|
+
children: [
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx(MoonIcon, {}),
|
|
210
|
+
variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Dark" })
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
),
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
215
|
+
"button",
|
|
216
|
+
{
|
|
217
|
+
type: "button",
|
|
218
|
+
onClick: () => handleSelect("system"),
|
|
219
|
+
"aria-pressed": active === "system",
|
|
220
|
+
"data-active": active === "system",
|
|
221
|
+
title: "System preference (matches device setting)",
|
|
222
|
+
"aria-label": "System theme",
|
|
223
|
+
children: [
|
|
224
|
+
/* @__PURE__ */ jsxRuntime.jsx(SystemIcon, {}),
|
|
225
|
+
variant !== "compact" && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "System" })
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
),
|
|
232
|
+
promoVisible && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pub-theme-promo", role: "status", children: [
|
|
233
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Theme saved for this browser. " }),
|
|
234
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: accountUrl, children: "Create a free reader account" }),
|
|
235
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: " to sync preferences across all your devices." }),
|
|
236
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
237
|
+
"button",
|
|
238
|
+
{
|
|
239
|
+
type: "button",
|
|
240
|
+
onClick: () => setPromoVisible(false),
|
|
241
|
+
"aria-label": "Dismiss note",
|
|
242
|
+
style: {
|
|
243
|
+
marginLeft: "8px",
|
|
244
|
+
background: "none",
|
|
245
|
+
border: "none",
|
|
246
|
+
cursor: "pointer",
|
|
247
|
+
color: "var(--pub-muted)",
|
|
248
|
+
fontSize: "12px",
|
|
249
|
+
padding: "0 4px"
|
|
250
|
+
},
|
|
251
|
+
children: "\xD7"
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
] })
|
|
255
|
+
] });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
exports.ThemeToggle = ThemeToggle;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
|
|
3
|
+
interface ThemeToggleProps {
|
|
4
|
+
/**
|
|
5
|
+
* "compact": Micro-pill with icons only (ideal for header utility bar).
|
|
6
|
+
* "inline": Broadsheet rectangular tabs with text and icons (ideal for article reading tools).
|
|
7
|
+
* "segmented": Pill with icons and labels (ideal for account, settings, or footer).
|
|
8
|
+
*/
|
|
9
|
+
variant?: "compact" | "inline" | "segmented";
|
|
10
|
+
/** Optional extra class name applied to container */
|
|
11
|
+
className?: string;
|
|
12
|
+
/**
|
|
13
|
+
* When true, displays a subtle, tasteful message when the user changes the theme:
|
|
14
|
+
* "Theme saved on this browser. Create a free reader account to sync across devices."
|
|
15
|
+
*/
|
|
16
|
+
showPromotion?: boolean;
|
|
17
|
+
/** Link for reader account / sync. Default: "/account" */
|
|
18
|
+
accountUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function ThemeToggle({ variant, className, showPromotion, accountUrl, }: ThemeToggleProps): react__default.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { ThemeToggle, type ThemeToggleProps };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
|
|
3
|
+
interface ThemeToggleProps {
|
|
4
|
+
/**
|
|
5
|
+
* "compact": Micro-pill with icons only (ideal for header utility bar).
|
|
6
|
+
* "inline": Broadsheet rectangular tabs with text and icons (ideal for article reading tools).
|
|
7
|
+
* "segmented": Pill with icons and labels (ideal for account, settings, or footer).
|
|
8
|
+
*/
|
|
9
|
+
variant?: "compact" | "inline" | "segmented";
|
|
10
|
+
/** Optional extra class name applied to container */
|
|
11
|
+
className?: string;
|
|
12
|
+
/**
|
|
13
|
+
* When true, displays a subtle, tasteful message when the user changes the theme:
|
|
14
|
+
* "Theme saved on this browser. Create a free reader account to sync across devices."
|
|
15
|
+
*/
|
|
16
|
+
showPromotion?: boolean;
|
|
17
|
+
/** Link for reader account / sync. Default: "/account" */
|
|
18
|
+
accountUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function ThemeToggle({ variant, className, showPromotion, accountUrl, }: ThemeToggleProps): react__default.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { ThemeToggle, type ThemeToggleProps };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalemule/news-publication",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -49,7 +49,12 @@
|
|
|
49
49
|
"import": "./dist/cookie-settings-view.js",
|
|
50
50
|
"require": "./dist/cookie-settings-view.cjs"
|
|
51
51
|
},
|
|
52
|
-
"./cookie-consent.css": "./cookie-consent.css"
|
|
52
|
+
"./cookie-consent.css": "./cookie-consent.css",
|
|
53
|
+
"./theme-toggle": {
|
|
54
|
+
"types": "./dist/theme-toggle.d.ts",
|
|
55
|
+
"import": "./dist/theme-toggle.js",
|
|
56
|
+
"require": "./dist/theme-toggle.cjs"
|
|
57
|
+
}
|
|
53
58
|
},
|
|
54
59
|
"files": [
|
|
55
60
|
"dist",
|
package/tokens/broadsheet.css
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
--pub-rule: #d7dcd7;
|
|
14
14
|
--pub-soft: #f5f6f3;
|
|
15
15
|
--pub-sponsor: #173d32;
|
|
16
|
+
--pub-selection-bg: #f6dcdd;
|
|
16
17
|
--pub-serif: var(--font-editorial), Georgia, "Times New Roman", serif;
|
|
17
18
|
--pub-editorial: var(--font-editorial), Georgia, "Times New Roman", serif;
|
|
18
19
|
--pub-sans: var(--font-inter), Arial, Helvetica, sans-serif;
|
|
@@ -43,7 +44,51 @@
|
|
|
43
44
|
line-height: 1.5;
|
|
44
45
|
min-height: 100dvh;
|
|
45
46
|
overflow-wrap: break-word;
|
|
47
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
/* Explicit Light Mode Override */
|
|
51
|
+
:root[data-theme="light"] .publication,
|
|
52
|
+
.publication[data-theme="light"] {
|
|
53
|
+
--pub-paper: #ffffff;
|
|
54
|
+
--pub-ink: #171917;
|
|
55
|
+
--pub-muted: #545c55;
|
|
56
|
+
--pub-accent: #bb171d;
|
|
57
|
+
--pub-rule: #d7dcd7;
|
|
58
|
+
--pub-soft: #f5f6f3;
|
|
59
|
+
--pub-sponsor: #173d32;
|
|
60
|
+
--pub-selection-bg: #f6dcdd;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Broadsheet Evening Edition: Dark Mode */
|
|
64
|
+
:root[data-theme="dark"] .publication,
|
|
65
|
+
.publication[data-theme="dark"],
|
|
66
|
+
[data-theme="dark"] {
|
|
67
|
+
--pub-paper: #131514;
|
|
68
|
+
--pub-ink: #eaece8;
|
|
69
|
+
--pub-muted: #959e96;
|
|
70
|
+
--pub-accent: #e54b51;
|
|
71
|
+
--pub-rule: #29302a;
|
|
72
|
+
--pub-soft: #1a1e1b;
|
|
73
|
+
--pub-sponsor: #244b3e;
|
|
74
|
+
--pub-selection-bg: #52191d;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* System Preference Mode (Default when not explicitly forced) */
|
|
78
|
+
@media (prefers-color-scheme: dark) {
|
|
79
|
+
:root:not([data-theme="light"]) .publication,
|
|
80
|
+
.publication:not([data-theme="light"]) {
|
|
81
|
+
--pub-paper: #131514;
|
|
82
|
+
--pub-ink: #eaece8;
|
|
83
|
+
--pub-muted: #959e96;
|
|
84
|
+
--pub-accent: #e54b51;
|
|
85
|
+
--pub-rule: #29302a;
|
|
86
|
+
--pub-soft: #1a1e1b;
|
|
87
|
+
--pub-sponsor: #244b3e;
|
|
88
|
+
--pub-selection-bg: #52191d;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
47
92
|
.publication *, .publication *::before, .publication *::after { box-sizing: border-box; }
|
|
48
93
|
.publication :focus-visible { outline: 3px solid #a95c00; outline-offset: 4px; }
|
|
49
94
|
.publication a, .publication button, .publication summary { touch-action: manipulation; }
|
|
@@ -55,7 +100,138 @@
|
|
|
55
100
|
text-wrap: pretty;
|
|
56
101
|
}
|
|
57
102
|
.publication [id] { scroll-margin-top: 24px; }
|
|
58
|
-
.publication ::selection { background: #f6dcdd; color: var(--pub-ink); }
|
|
103
|
+
.publication ::selection { background: var(--pub-selection-bg, #f6dcdd); color: var(--pub-ink); }
|
|
59
104
|
@media (min-width: 960px) {
|
|
60
105
|
.publication { --pub-width: 1280px; }
|
|
61
106
|
}
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* Broadsheet Theme Toggle Controls
|
|
110
|
+
*/
|
|
111
|
+
.pub-theme-toggle-container {
|
|
112
|
+
display: inline-flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
position: relative;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.pub-theme-toggle {
|
|
118
|
+
display: inline-flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: 2px;
|
|
121
|
+
padding: 2px;
|
|
122
|
+
border: 1px solid var(--pub-rule);
|
|
123
|
+
background: var(--pub-soft);
|
|
124
|
+
border-radius: 4px;
|
|
125
|
+
font-family: var(--pub-sans);
|
|
126
|
+
line-height: 1;
|
|
127
|
+
user-select: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.pub-theme-toggle button {
|
|
131
|
+
display: inline-flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
gap: 5px;
|
|
135
|
+
padding: 4px 8px;
|
|
136
|
+
height: 26px;
|
|
137
|
+
border: 0;
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: var(--pub-muted);
|
|
140
|
+
font-size: 11px;
|
|
141
|
+
font-weight: 500;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
border-radius: 3px;
|
|
144
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
145
|
+
text-decoration: none;
|
|
146
|
+
line-height: 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.pub-theme-toggle button:hover {
|
|
150
|
+
color: var(--pub-ink);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.pub-theme-toggle button[aria-pressed="true"],
|
|
154
|
+
.pub-theme-toggle button[data-active="true"] {
|
|
155
|
+
background: var(--pub-paper);
|
|
156
|
+
color: var(--pub-ink);
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.pub-theme-toggle svg {
|
|
162
|
+
width: 14px;
|
|
163
|
+
height: 14px;
|
|
164
|
+
flex-shrink: 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Compact variant (ideal for top utility bar) */
|
|
168
|
+
.pub-theme-toggle[data-variant="compact"] {
|
|
169
|
+
padding: 1px;
|
|
170
|
+
gap: 1px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.pub-theme-toggle[data-variant="compact"] button {
|
|
174
|
+
padding: 3px 6px;
|
|
175
|
+
height: 22px;
|
|
176
|
+
min-width: 24px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.pub-theme-toggle[data-variant="compact"] svg {
|
|
180
|
+
width: 13px;
|
|
181
|
+
height: 13px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Inline variant (for reading toolbar on article pages) */
|
|
185
|
+
.pub-theme-toggle[data-variant="inline"] {
|
|
186
|
+
border-color: var(--pub-rule);
|
|
187
|
+
background: transparent;
|
|
188
|
+
padding: 0;
|
|
189
|
+
border-radius: 0;
|
|
190
|
+
gap: 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.pub-theme-toggle[data-variant="inline"] button {
|
|
194
|
+
height: 30px;
|
|
195
|
+
padding: 4px 10px;
|
|
196
|
+
border: 1px solid var(--pub-rule);
|
|
197
|
+
border-radius: 0;
|
|
198
|
+
background: var(--pub-paper);
|
|
199
|
+
font-size: 12px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.pub-theme-toggle[data-variant="inline"] button:not(:first-child) {
|
|
203
|
+
margin-left: -1px;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.pub-theme-toggle[data-variant="inline"] button[aria-pressed="true"],
|
|
207
|
+
.pub-theme-toggle[data-variant="inline"] button[data-active="true"] {
|
|
208
|
+
background: var(--pub-soft);
|
|
209
|
+
color: var(--pub-ink);
|
|
210
|
+
border-color: var(--pub-ink);
|
|
211
|
+
z-index: 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Reader promotion / device sync prompt banner */
|
|
215
|
+
.pub-theme-promo {
|
|
216
|
+
position: absolute;
|
|
217
|
+
top: calc(100% + 6px);
|
|
218
|
+
left: 0;
|
|
219
|
+
z-index: 40;
|
|
220
|
+
min-width: 240px;
|
|
221
|
+
max-width: 320px;
|
|
222
|
+
padding: 8px 12px;
|
|
223
|
+
font-size: 11px;
|
|
224
|
+
line-height: 1.4;
|
|
225
|
+
border: 1px solid var(--pub-rule);
|
|
226
|
+
background: var(--pub-paper);
|
|
227
|
+
color: var(--pub-ink);
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.pub-theme-promo a {
|
|
233
|
+
color: var(--pub-accent);
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
text-decoration: underline;
|
|
236
|
+
text-underline-offset: 3px;
|
|
237
|
+
}
|