colorsbymax 0.3.0 → 0.3.2
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/CHANGELOG.md +9 -0
- package/README.md +4 -0
- package/dist/{ThemeSwitcher-CFtLm7PM.js → ThemeSwitcher-BlWfKjRg.js} +35 -16
- package/dist/auto.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
What changed in each colorsbymax release. Update with `npm install colorsbymax@latest`.
|
|
4
4
|
|
|
5
|
+
## 0.3.2 (2026-09-25)
|
|
6
|
+
|
|
7
|
+
- **`defaultMode` config option:** the mode first-time visitors start in, `'light'` (the default), `'dark'` or `'system'` to follow their device. Any site can start dark, since every theme has a dark twin, and visitors can still switch. The colorsbymax site now opens in dark mode.
|
|
8
|
+
- The colorsbymax site's browser-tab icon is the mrmaxdesigns logo mark in the current theme's colours, with a version for light tabs and one for dark tabs, each checked to stand out.
|
|
9
|
+
|
|
10
|
+
## 0.3.1 (2026-09-25)
|
|
11
|
+
|
|
12
|
+
- **`colourLogo` config option:** start with "Colour the logo too" on, for sites whose logo is drawn in the theme's colours. It's off by default, so logos keep their own colours, and visitors can still change it in settings. It also applies when the switcher is hidden. The colorsbymax site turns it on for its wordmark.
|
|
13
|
+
|
|
5
14
|
## 0.3.0 (2026-09-25)
|
|
6
15
|
|
|
7
16
|
- **Dark mode for every site, on every load.** The mode now lives in the theme provider: a saved Dark (or Auto on a dark device) turns the whole site dark as soon as it loads, not only when the mode is changed, and it keeps working when the switcher is hidden. Before, the panel could go dark while the site stayed light. Sites without a dark mode of their own get one: every theme's dark twin re-colours the page, contrast-checked.
|
package/README.md
CHANGED
|
@@ -326,6 +326,10 @@ Without a `siteName`, the site group is named from the page's `og:site_name`, it
|
|
|
326
326
|
position: 'bottom-right', // where the button starts: bottom-right (default), bottom-left,
|
|
327
327
|
// top-left, or top-right (just under a floating nav bar)
|
|
328
328
|
hidden: import.meta.env.PROD, // hide the button (the theme still applies), e.g. in production
|
|
329
|
+
defaultMode: 'light', // the mode first-time visitors start in: 'light' (default),
|
|
330
|
+
// 'dark', or 'system' to follow their device
|
|
331
|
+
colourLogo: false, // themes colour the logo too, for first-time visitors (default
|
|
332
|
+
// false: it keeps its own colours); visitors can change it
|
|
329
333
|
intro: true, // the button pops in with a burst of the theme's colours a moment
|
|
330
334
|
// after the page loads (default); reduced motion fades it in
|
|
331
335
|
}
|
|
@@ -1709,24 +1709,28 @@ var PANEL_PRESETS = [
|
|
|
1709
1709
|
height: "full"
|
|
1710
1710
|
}
|
|
1711
1711
|
];
|
|
1712
|
-
/**
|
|
1713
|
-
|
|
1712
|
+
/**
|
|
1713
|
+
* @param {string} storageKey
|
|
1714
|
+
* @param {PanelSettings} [defaults] the site's starting settings (see the config's colourLogo)
|
|
1715
|
+
* @returns {PanelSettings}
|
|
1716
|
+
*/
|
|
1717
|
+
function loadSettings(storageKey, defaults = DEFAULT_SETTINGS) {
|
|
1714
1718
|
try {
|
|
1715
1719
|
const saved = JSON.parse(window.localStorage.getItem(`${storageKey}:settings`) || "null");
|
|
1716
|
-
if (!saved || typeof saved !== "object") return
|
|
1717
|
-
const out = { ...
|
|
1718
|
-
for (const [key, fallback] of Object.entries(
|
|
1720
|
+
if (!saved || typeof saved !== "object") return defaults;
|
|
1721
|
+
const out = { ...defaults };
|
|
1722
|
+
for (const [key, fallback] of Object.entries(defaults)) if (typeof saved[key] === typeof fallback) out[key] = saved[key];
|
|
1719
1723
|
if (![
|
|
1720
1724
|
"light",
|
|
1721
1725
|
"dark",
|
|
1722
1726
|
"system"
|
|
1723
|
-
].includes(out.mode)) out.mode =
|
|
1727
|
+
].includes(out.mode)) out.mode = defaults.mode;
|
|
1724
1728
|
const size = (v) => typeof v === "number" && v > 0 && v < 1e4;
|
|
1725
1729
|
out.panelWidth = size(saved.panelWidth) ? saved.panelWidth : null;
|
|
1726
1730
|
out.panelHeight = size(saved.panelHeight) || saved.panelHeight === "full" ? saved.panelHeight : null;
|
|
1727
1731
|
return out;
|
|
1728
1732
|
} catch {
|
|
1729
|
-
return
|
|
1733
|
+
return defaults;
|
|
1730
1734
|
}
|
|
1731
1735
|
}
|
|
1732
1736
|
function saveSettings(storageKey, settings) {
|
|
@@ -1908,6 +1912,10 @@ var newId = () => `custom-${Date.now().toString(36)}-${Math.random().toString(36
|
|
|
1908
1912
|
* Where each token is used on this site, shown in the editors
|
|
1909
1913
|
* @property {boolean} [scrollbars] Colour the page's scrollbars from the theme (default true)
|
|
1910
1914
|
* @property {() => Promise<any>} [pdf] Enables PDF uploads: pass `loadPdf` from 'colorsbymax/pdf'
|
|
1915
|
+
* @property {'light' | 'dark' | 'system'} [defaultMode] The mode a first-time visitor starts in
|
|
1916
|
+
* (default 'light'; 'system' follows their device). Visitors can still change it.
|
|
1917
|
+
* @property {boolean} [colourLogo] Whether themes colour the site's logo for a first-time visitor
|
|
1918
|
+
* (default false: the logo keeps its own colours). Visitors can still change it in settings.
|
|
1911
1919
|
* @property {boolean} [intro] Bring the colour button in with a short pop and burst of the theme's
|
|
1912
1920
|
* colours, a moment after the page loads (default true). Reduced motion fades it in instead.
|
|
1913
1921
|
* @property {boolean} [hidden] Hide the colour button and panel; the theme still applies. Use
|
|
@@ -2009,7 +2017,16 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
2009
2017
|
recolourer.current = null;
|
|
2010
2018
|
};
|
|
2011
2019
|
}, [recolourMode]);
|
|
2012
|
-
const
|
|
2020
|
+
const settingDefaults = useMemo(() => ({
|
|
2021
|
+
...DEFAULT_SETTINGS,
|
|
2022
|
+
colourLogo: Boolean(initialConfig.colourLogo),
|
|
2023
|
+
mode: [
|
|
2024
|
+
"light",
|
|
2025
|
+
"dark",
|
|
2026
|
+
"system"
|
|
2027
|
+
].includes(initialConfig.defaultMode) ? initialConfig.defaultMode : DEFAULT_SETTINGS.mode
|
|
2028
|
+
}), [initialConfig]);
|
|
2029
|
+
const [logoColouring, setLogoColouring] = useState(() => loadSettings(storageKey, settingDefaults).colourLogo);
|
|
2013
2030
|
useLayoutEffect(() => {
|
|
2014
2031
|
recolourer.current?.setLogoColouring(logoColouring);
|
|
2015
2032
|
}, [logoColouring, pageColours]);
|
|
@@ -2077,7 +2094,7 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
2077
2094
|
tokens: theme.tokens
|
|
2078
2095
|
} : s.snapshot
|
|
2079
2096
|
})), []);
|
|
2080
|
-
const [modeSetting, setModeSetting] = useState(() => loadSettings(storageKey).mode);
|
|
2097
|
+
const [modeSetting, setModeSetting] = useState(() => loadSettings(storageKey, settingDefaults).mode);
|
|
2081
2098
|
const prefersDark = usePrefersDark();
|
|
2082
2099
|
const mode = modeSetting === "system" ? prefersDark ? "dark" : "light" : modeSetting;
|
|
2083
2100
|
const modeRef = useRef(mode);
|
|
@@ -2090,10 +2107,10 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
2090
2107
|
].includes(next)) return;
|
|
2091
2108
|
setModeSetting(next);
|
|
2092
2109
|
saveSettings(storageKey, {
|
|
2093
|
-
...loadSettings(storageKey),
|
|
2110
|
+
...loadSettings(storageKey, settingDefaults),
|
|
2094
2111
|
mode: next
|
|
2095
2112
|
});
|
|
2096
|
-
}, [storageKey]);
|
|
2113
|
+
}, [storageKey, settingDefaults]);
|
|
2097
2114
|
useLayoutEffect(() => {
|
|
2098
2115
|
if (base.custom) return;
|
|
2099
2116
|
if (mode === "dark" && !isDarkTheme(base.tokens)) {
|
|
@@ -2135,6 +2152,8 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
2135
2152
|
}, [setMode]);
|
|
2136
2153
|
const api = {
|
|
2137
2154
|
state,
|
|
2155
|
+
/** The switcher's starting settings for this site. */
|
|
2156
|
+
settingDefaults,
|
|
2138
2157
|
/** The mode in effect ('light' or 'dark'), the visitor's setting ('system' follows the device), and a setter. */
|
|
2139
2158
|
mode,
|
|
2140
2159
|
modeSetting,
|
|
@@ -5711,7 +5730,7 @@ function ThemeSwitcher() {
|
|
|
5711
5730
|
return mount && createPortal(/* @__PURE__ */ jsx(Switcher, {}), mount);
|
|
5712
5731
|
}
|
|
5713
5732
|
function Switcher() {
|
|
5714
|
-
const { issues, storageKey, tokens, position: requested, setLogoColouring, intro, mode, modeSetting, setMode } = useTheme();
|
|
5733
|
+
const { issues, storageKey, tokens, position: requested, setLogoColouring, intro, mode, modeSetting, setMode, settingDefaults } = useTheme();
|
|
5715
5734
|
const position = CORNERS[requested] ? requested : "bottom-right";
|
|
5716
5735
|
const [open, setOpen] = useState(false);
|
|
5717
5736
|
const [arrived, setArrived] = useState(!intro);
|
|
@@ -5737,7 +5756,7 @@ function Switcher() {
|
|
|
5737
5756
|
}, BURST_TIME);
|
|
5738
5757
|
return () => clearTimeout(timer);
|
|
5739
5758
|
}, [burst]);
|
|
5740
|
-
const [settings, setSettings] = useState(() => loadSettings(storageKey));
|
|
5759
|
+
const [settings, setSettings] = useState(() => loadSettings(storageKey, settingDefaults));
|
|
5741
5760
|
useEffect(() => setLogoColouring(settings.colourLogo), [settings.colourLogo, setLogoColouring]);
|
|
5742
5761
|
const [auditOn, setAuditOn] = useState(false);
|
|
5743
5762
|
const [findings, setFindings] = useState([]);
|
|
@@ -5913,7 +5932,7 @@ function Switcher() {
|
|
|
5913
5932
|
const storeSettings = (next) => {
|
|
5914
5933
|
saveSettings(storageKey, {
|
|
5915
5934
|
...next,
|
|
5916
|
-
mode: loadSettings(storageKey).mode
|
|
5935
|
+
mode: loadSettings(storageKey, settingDefaults).mode
|
|
5917
5936
|
});
|
|
5918
5937
|
return next;
|
|
5919
5938
|
};
|
|
@@ -5944,8 +5963,8 @@ function Switcher() {
|
|
|
5944
5963
|
}));
|
|
5945
5964
|
},
|
|
5946
5965
|
reset: () => {
|
|
5947
|
-
setMode(
|
|
5948
|
-
setSettings(storeSettings(
|
|
5966
|
+
setMode(settingDefaults.mode);
|
|
5967
|
+
setSettings(storeSettings(settingDefaults));
|
|
5949
5968
|
},
|
|
5950
5969
|
resetButton: saved ? resetPosition : null
|
|
5951
5970
|
};
|
package/dist/auto.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as ThemeProvider, t as ThemeSwitcher } from "./ThemeSwitcher-
|
|
1
|
+
import { o as ThemeProvider, t as ThemeSwitcher } from "./ThemeSwitcher-BlWfKjRg.js";
|
|
2
2
|
import { createElement } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
//#region src/auto.js
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as PAIRINGS, C as TOKEN_KEYS, D as MIN_CONTRAST_NON_TEXT, E as MIN_CONTRAST_LARGE_TEXT, F as contrastRatio, I as normalizeHex, M as checkTheme, N as fixAll, O as MIN_CONTRAST_TEXT, P as suggestFix, S as TOKEN_GROUPS, T as deriveAppTokens, _ as darkTokens, a as themeFromPalette, b as BASE_TOKENS, c as useTheme, d as DEFAULT_SETTINGS, f as collectColors, g as themeFromRoles, h as suggestThemes, i as rolesFromPalette, j as checkRamp, k as MIN_RAMP_STEP_DELTA_E, l as DEFAULT_STORAGE_KEY, m as inferRoles, n as coloursFromFile, o as ThemeProvider, p as detectSiteName, r as dominantColours, s as applyTokens, t as ThemeSwitcher, u as prePaintScript, v as isDarkTheme, w as completeTokens, x as PRESETS, y as toDark } from "./ThemeSwitcher-
|
|
1
|
+
import { A as PAIRINGS, C as TOKEN_KEYS, D as MIN_CONTRAST_NON_TEXT, E as MIN_CONTRAST_LARGE_TEXT, F as contrastRatio, I as normalizeHex, M as checkTheme, N as fixAll, O as MIN_CONTRAST_TEXT, P as suggestFix, S as TOKEN_GROUPS, T as deriveAppTokens, _ as darkTokens, a as themeFromPalette, b as BASE_TOKENS, c as useTheme, d as DEFAULT_SETTINGS, f as collectColors, g as themeFromRoles, h as suggestThemes, i as rolesFromPalette, j as checkRamp, k as MIN_RAMP_STEP_DELTA_E, l as DEFAULT_STORAGE_KEY, m as inferRoles, n as coloursFromFile, o as ThemeProvider, p as detectSiteName, r as dominantColours, s as applyTokens, t as ThemeSwitcher, u as prePaintScript, v as isDarkTheme, w as completeTokens, x as PRESETS, y as toDark } from "./ThemeSwitcher-BlWfKjRg.js";
|
|
2
2
|
export { BASE_TOKENS, DEFAULT_SETTINGS, DEFAULT_STORAGE_KEY, MIN_CONTRAST_LARGE_TEXT, MIN_CONTRAST_NON_TEXT, MIN_CONTRAST_TEXT, MIN_RAMP_STEP_DELTA_E, PAIRINGS, PRESETS, TOKEN_GROUPS, TOKEN_KEYS, ThemeProvider, ThemeSwitcher, applyTokens, checkRamp, checkTheme, collectColors, coloursFromFile, completeTokens, contrastRatio, darkTokens, deriveAppTokens, detectSiteName, dominantColours, fixAll, inferRoles, isDarkTheme, normalizeHex, prePaintScript, rolesFromPalette, suggestFix, suggestThemes, themeFromPalette, themeFromRoles, toDark, useTheme };
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -90,6 +90,16 @@ export interface ColorsByMaxConfig {
|
|
|
90
90
|
* the page loads (default true). With reduced motion it fades in instead.
|
|
91
91
|
*/
|
|
92
92
|
intro?: boolean
|
|
93
|
+
/**
|
|
94
|
+
* Whether themes colour the site's logo for a first-time visitor (default false: the logo keeps
|
|
95
|
+
* its own colours). Visitors can still change it in the panel's settings.
|
|
96
|
+
*/
|
|
97
|
+
colourLogo?: boolean
|
|
98
|
+
/**
|
|
99
|
+
* The mode a first-time visitor starts in (default 'light'; 'system' follows their device). Any
|
|
100
|
+
* site can start dark: every theme has a dark twin. Visitors can still change it.
|
|
101
|
+
*/
|
|
102
|
+
defaultMode?: 'light' | 'dark' | 'system'
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
export interface ThemeProviderProps {
|