colorsbymax 0.2.2 → 0.3.0
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 +24 -0
- package/README.md +107 -9
- package/dist/{ThemeSwitcher-L1GXY55E.js → ThemeSwitcher-CFtLm7PM.js} +846 -203
- package/dist/auto.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -2
- package/types/index.d.ts +13 -0
|
@@ -950,19 +950,38 @@ var lift = (hex, min, max = .78) => {
|
|
|
950
950
|
const [h, s, l] = hslOf$2(hex);
|
|
951
951
|
return hsl$1(h, s, Math.min(max, Math.max(min, l)));
|
|
952
952
|
};
|
|
953
|
+
/**
|
|
954
|
+
* Lightens a colour, keeping its hue and saturation, until it reaches `ratio` against `bg`.
|
|
955
|
+
* Lightness alone doesn't guarantee that: a vivid blue or violet at 60% lightness still reads dark.
|
|
956
|
+
*/
|
|
957
|
+
var readOn = (hex, bg, ratio) => {
|
|
958
|
+
const [h, s, l] = hslOf$2(hex);
|
|
959
|
+
let out = hex;
|
|
960
|
+
for (let x = l; contrastRatio(out, bg) < ratio && x < .96; x += .01) out = hsl$1(h, s, x);
|
|
961
|
+
return out;
|
|
962
|
+
};
|
|
963
|
+
var TEXT = 4.8;
|
|
964
|
+
var ICON = 3.3;
|
|
953
965
|
/** @param {import('./tokens.js').ThemeTokens} t */
|
|
954
966
|
function darkTokens(t) {
|
|
955
|
-
const
|
|
956
|
-
const
|
|
967
|
+
const background = tone(t.background, .08, .3);
|
|
968
|
+
const secondary = tone(t.secondary, .2, .45);
|
|
969
|
+
const areaBg = deriveAppTokens({
|
|
970
|
+
...t,
|
|
971
|
+
secondary
|
|
972
|
+
})["app-background"];
|
|
973
|
+
const lighterBg = luminance(areaBg) > luminance(background) ? areaBg : background;
|
|
974
|
+
const primary = readOn(lift(t.primary, .58, .7), lighterBg, TEXT);
|
|
975
|
+
const onPrimary = [background, "#ffffff"].sort((a, b) => contrastRatio(b, primary) - contrastRatio(a, primary))[0];
|
|
957
976
|
const tokens = {
|
|
958
977
|
...t,
|
|
959
978
|
primary,
|
|
960
979
|
"on-primary": onPrimary,
|
|
961
980
|
"primary-dark": tone(t["primary-dark"], .82, .7),
|
|
962
|
-
"primary-alt": lift(t["primary-alt"], .6),
|
|
963
|
-
background
|
|
981
|
+
"primary-alt": readOn(lift(t["primary-alt"], .6), lighterBg, ICON),
|
|
982
|
+
background,
|
|
964
983
|
surface: tone(t.background, .12, .25),
|
|
965
|
-
secondary
|
|
984
|
+
secondary,
|
|
966
985
|
"on-secondary": tone(t["on-secondary"], .86, .6),
|
|
967
986
|
accent: tone(t.accent, .17, .45),
|
|
968
987
|
"on-accent": tone(t["on-accent"], .86, .6),
|
|
@@ -971,12 +990,12 @@ function darkTokens(t) {
|
|
|
971
990
|
ink: tone(t.ink, .94, .15),
|
|
972
991
|
"ink-secondary": tone(t["ink-secondary"], .76, .12),
|
|
973
992
|
"ink-muted": tone(t["ink-muted"], .7, .12),
|
|
974
|
-
success: lift(t.success, .6),
|
|
975
|
-
warning: lift(t.warning, .6),
|
|
976
|
-
danger: lift(t.danger, .66),
|
|
977
|
-
info: lift(t.info, .66)
|
|
993
|
+
success: readOn(lift(t.success, .6), background, ICON),
|
|
994
|
+
warning: readOn(lift(t.warning, .6), background, ICON),
|
|
995
|
+
danger: readOn(lift(t.danger, .66), background, ICON),
|
|
996
|
+
info: readOn(lift(t.info, .66), background, ICON)
|
|
978
997
|
};
|
|
979
|
-
for (let n = 1; n <= 8; n++) tokens[`data-${n}`] = lift(t[`data-${n}`], .6);
|
|
998
|
+
for (let n = 1; n <= 8; n++) tokens[`data-${n}`] = readOn(lift(t[`data-${n}`], .6), background, n <= 2 ? TEXT : ICON);
|
|
980
999
|
const full = {
|
|
981
1000
|
...tokens,
|
|
982
1001
|
...deriveAppTokens(tokens)
|
|
@@ -1403,14 +1422,16 @@ function createRecolourer() {
|
|
|
1403
1422
|
const sheet = document.createElement("style");
|
|
1404
1423
|
sheet.dataset.colorsbymax = "recolour";
|
|
1405
1424
|
document.head.append(sheet);
|
|
1406
|
-
const
|
|
1425
|
+
const { roles } = inferRoles(collectColors());
|
|
1426
|
+
const site = themeFromRoles(roles);
|
|
1427
|
+
const plain = !roles.primary;
|
|
1407
1428
|
const siteBgL = hslOf$1(site.background)[2];
|
|
1408
1429
|
const siteInkL = hslOf$1(site.ink)[2];
|
|
1409
1430
|
/** Each distinct painted value, keyed "prop|value", plus the background under text and icons. */
|
|
1410
1431
|
const entries = /* @__PURE__ */ new Map();
|
|
1411
1432
|
let theme = null;
|
|
1412
|
-
const idFor = (prop, value, kind, backdrop = null, role = null) => {
|
|
1413
|
-
const key = `${prop}|${value}|${backdrop ?? ""}|${role ?? ""}`;
|
|
1433
|
+
const idFor = (prop, value, kind, backdrop = null, role = null, accent = false) => {
|
|
1434
|
+
const key = `${prop}|${value}|${backdrop ?? ""}|${role ?? ""}|${accent ? "a" : ""}`;
|
|
1414
1435
|
let entry = entries.get(key);
|
|
1415
1436
|
if (!entry) {
|
|
1416
1437
|
entry = {
|
|
@@ -1419,7 +1440,8 @@ function createRecolourer() {
|
|
|
1419
1440
|
value,
|
|
1420
1441
|
kind,
|
|
1421
1442
|
backdrop,
|
|
1422
|
-
role
|
|
1443
|
+
role,
|
|
1444
|
+
accent
|
|
1423
1445
|
};
|
|
1424
1446
|
entries.set(key, entry);
|
|
1425
1447
|
if (theme) pendingRules += rule(entry);
|
|
@@ -1443,9 +1465,11 @@ function createRecolourer() {
|
|
|
1443
1465
|
const cs = getComputedStyle(el);
|
|
1444
1466
|
const ids = [];
|
|
1445
1467
|
const role = el instanceof SVGElement || !hasOwnText(el) ? "icon" : "text";
|
|
1468
|
+
const ownFill = (parse(cs.backgroundColor)?.alpha ?? 0) >= .5;
|
|
1469
|
+
const accent = plain && !ownFill && (role === "icon" || el.localName === "a");
|
|
1446
1470
|
const colour = (prop, css, kind) => {
|
|
1447
1471
|
if (!parse(css)) return;
|
|
1448
|
-
ids.push(FOREGROUND.has(prop) ? idFor(prop, css, kind, backdropOf(el), prop === "color" ? role : "icon") : idFor(prop, css, kind));
|
|
1472
|
+
ids.push(FOREGROUND.has(prop) ? idFor(prop, css, kind, backdropOf(el), prop === "color" ? role : "icon", accent) : idFor(prop, css, kind));
|
|
1449
1473
|
};
|
|
1450
1474
|
colour("background-color", cs.backgroundColor, "bg");
|
|
1451
1475
|
colour("color", cs.color, "text");
|
|
@@ -1566,7 +1590,12 @@ function createRecolourer() {
|
|
|
1566
1590
|
let value;
|
|
1567
1591
|
if (entry.prop === "background-image") value = entry.value.replace(COLOR_IN_GRADIENT, (stop) => mapColour(stop, entry.kind));
|
|
1568
1592
|
else if (entry.backdrop) {
|
|
1569
|
-
const
|
|
1593
|
+
const own = parse(entry.value);
|
|
1594
|
+
const brand = entry.accent && own && hslOf$1(own.hex)[1] < NEUTRAL_SATURATION;
|
|
1595
|
+
const fg = brand ? {
|
|
1596
|
+
hex: theme.primary,
|
|
1597
|
+
alpha: own.alpha
|
|
1598
|
+
} : mapHex(entry.value, entry.kind);
|
|
1570
1599
|
const bg = mapHex(entry.backdrop, "bg");
|
|
1571
1600
|
const original = {
|
|
1572
1601
|
fg: parse(entry.value)?.hex,
|
|
@@ -1574,7 +1603,7 @@ function createRecolourer() {
|
|
|
1574
1603
|
};
|
|
1575
1604
|
if (fg && bg && original.fg && original.bg) {
|
|
1576
1605
|
const aim = entry.role === "text" ? MIN_TEXT_CONTRAST : MIN_ICON_CONTRAST;
|
|
1577
|
-
const target = Math.min(aim, contrastRatio(original.fg, original.bg));
|
|
1606
|
+
const target = brand ? aim : Math.min(aim, contrastRatio(original.fg, original.bg));
|
|
1578
1607
|
const lighter = luminance(original.fg) > luminance(original.bg);
|
|
1579
1608
|
value = format({
|
|
1580
1609
|
...fg,
|
|
@@ -1626,6 +1655,113 @@ function createRecolourer() {
|
|
|
1626
1655
|
};
|
|
1627
1656
|
}
|
|
1628
1657
|
//#endregion
|
|
1658
|
+
//#region src/settings.js
|
|
1659
|
+
/**
|
|
1660
|
+
* @typedef {Object} PanelSettings
|
|
1661
|
+
* @property {'light' | 'dark' | 'system'} mode Which version of each theme to list; the panel matches it
|
|
1662
|
+
* @property {boolean} showPicks
|
|
1663
|
+
* @property {boolean} showLibrary
|
|
1664
|
+
* @property {boolean} showCustom "Custom palettes" section
|
|
1665
|
+
* @property {boolean} showOverrides "Override a single colour" section
|
|
1666
|
+
* @property {boolean} showImportExport
|
|
1667
|
+
* @property {boolean} draggable Whether the colour button can be dragged
|
|
1668
|
+
* @property {boolean} animateDot Whether the colour button's dot cycles through the theme's colours
|
|
1669
|
+
* @property {number | null} panelWidth Pixels; null is the standard width
|
|
1670
|
+
* @property {number | 'full' | null} panelHeight Pixels, 'full' for all the room there is, or null to fit the content
|
|
1671
|
+
* @property {boolean} libraryCollapsed Whether the library's category chips are folded away
|
|
1672
|
+
* @property {boolean} colourLogo Whether themes re-colour the site's logo too (off keeps its own colours)
|
|
1673
|
+
* @property {boolean} hideButton Hidden on this device from the finish screen (Alt+Shift+C brings it back)
|
|
1674
|
+
*/
|
|
1675
|
+
/** @type {PanelSettings} */
|
|
1676
|
+
var DEFAULT_SETTINGS = {
|
|
1677
|
+
mode: "light",
|
|
1678
|
+
showPicks: true,
|
|
1679
|
+
showLibrary: true,
|
|
1680
|
+
showCustom: true,
|
|
1681
|
+
showOverrides: true,
|
|
1682
|
+
showImportExport: true,
|
|
1683
|
+
draggable: true,
|
|
1684
|
+
animateDot: true,
|
|
1685
|
+
panelWidth: null,
|
|
1686
|
+
panelHeight: null,
|
|
1687
|
+
libraryCollapsed: false,
|
|
1688
|
+
colourLogo: false,
|
|
1689
|
+
hideButton: false
|
|
1690
|
+
};
|
|
1691
|
+
/** Size presets offered in settings; dragging an edge gives a custom size instead. */
|
|
1692
|
+
var PANEL_PRESETS = [
|
|
1693
|
+
{
|
|
1694
|
+
id: "compact",
|
|
1695
|
+
label: "Compact",
|
|
1696
|
+
width: 340,
|
|
1697
|
+
height: null
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
id: "standard",
|
|
1701
|
+
label: "Standard",
|
|
1702
|
+
width: null,
|
|
1703
|
+
height: null
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
id: "large",
|
|
1707
|
+
label: "Large",
|
|
1708
|
+
width: 560,
|
|
1709
|
+
height: "full"
|
|
1710
|
+
}
|
|
1711
|
+
];
|
|
1712
|
+
/** @returns {PanelSettings} */
|
|
1713
|
+
function loadSettings(storageKey) {
|
|
1714
|
+
try {
|
|
1715
|
+
const saved = JSON.parse(window.localStorage.getItem(`${storageKey}:settings`) || "null");
|
|
1716
|
+
if (!saved || typeof saved !== "object") return DEFAULT_SETTINGS;
|
|
1717
|
+
const out = { ...DEFAULT_SETTINGS };
|
|
1718
|
+
for (const [key, fallback] of Object.entries(DEFAULT_SETTINGS)) if (typeof saved[key] === typeof fallback) out[key] = saved[key];
|
|
1719
|
+
if (![
|
|
1720
|
+
"light",
|
|
1721
|
+
"dark",
|
|
1722
|
+
"system"
|
|
1723
|
+
].includes(out.mode)) out.mode = DEFAULT_SETTINGS.mode;
|
|
1724
|
+
const size = (v) => typeof v === "number" && v > 0 && v < 1e4;
|
|
1725
|
+
out.panelWidth = size(saved.panelWidth) ? saved.panelWidth : null;
|
|
1726
|
+
out.panelHeight = size(saved.panelHeight) || saved.panelHeight === "full" ? saved.panelHeight : null;
|
|
1727
|
+
return out;
|
|
1728
|
+
} catch {
|
|
1729
|
+
return DEFAULT_SETTINGS;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
function saveSettings(storageKey, settings) {
|
|
1733
|
+
try {
|
|
1734
|
+
window.localStorage.setItem(`${storageKey}:settings`, JSON.stringify(settings));
|
|
1735
|
+
} catch {}
|
|
1736
|
+
}
|
|
1737
|
+
/** True while the device prefers dark colours. */
|
|
1738
|
+
function usePrefersDark() {
|
|
1739
|
+
const query = "(prefers-color-scheme: dark)";
|
|
1740
|
+
const [dark, setDark] = useState(() => window.matchMedia(query).matches);
|
|
1741
|
+
useEffect(() => {
|
|
1742
|
+
const mq = window.matchMedia(query);
|
|
1743
|
+
const update = () => setDark(mq.matches);
|
|
1744
|
+
mq.addEventListener("change", update);
|
|
1745
|
+
return () => mq.removeEventListener("change", update);
|
|
1746
|
+
}, []);
|
|
1747
|
+
return dark;
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* @typedef {Object} SettingsApi
|
|
1751
|
+
* @property {PanelSettings} settings
|
|
1752
|
+
* @property {(patch: Partial<PanelSettings>) => void} update
|
|
1753
|
+
* @property {() => void} reset
|
|
1754
|
+
* @property {'light' | 'dark'} mode `settings.mode` with "system" resolved
|
|
1755
|
+
* @property {(() => void) | null} resetButton Moves the colour button back to its corner; null when it's there
|
|
1756
|
+
*/
|
|
1757
|
+
/** @type {import('react').Context<SettingsApi | null>} */
|
|
1758
|
+
var SettingsContext = createContext(null);
|
|
1759
|
+
function useSettings() {
|
|
1760
|
+
const ctx = useContext(SettingsContext);
|
|
1761
|
+
if (!ctx) throw new Error("useSettings must be used inside the colorsbymax switcher");
|
|
1762
|
+
return ctx;
|
|
1763
|
+
}
|
|
1764
|
+
//#endregion
|
|
1629
1765
|
//#region src/storage.js
|
|
1630
1766
|
var DEFAULT_STORAGE_KEY = "colorsbymax";
|
|
1631
1767
|
var VERSION = 1;
|
|
@@ -1772,6 +1908,8 @@ var newId = () => `custom-${Date.now().toString(36)}-${Math.random().toString(36
|
|
|
1772
1908
|
* Where each token is used on this site, shown in the editors
|
|
1773
1909
|
* @property {boolean} [scrollbars] Colour the page's scrollbars from the theme (default true)
|
|
1774
1910
|
* @property {() => Promise<any>} [pdf] Enables PDF uploads: pass `loadPdf` from 'colorsbymax/pdf'
|
|
1911
|
+
* @property {boolean} [intro] Bring the colour button in with a short pop and burst of the theme's
|
|
1912
|
+
* colours, a moment after the page loads (default true). Reduced motion fades it in instead.
|
|
1775
1913
|
* @property {boolean} [hidden] Hide the colour button and panel; the theme still applies. Use
|
|
1776
1914
|
* `hidden: import.meta.env.PROD` to keep it out of production once the colours are chosen.
|
|
1777
1915
|
* @property {'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'} [position] Where the colour
|
|
@@ -1929,8 +2067,78 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
1929
2067
|
}
|
|
1930
2068
|
}))(s);
|
|
1931
2069
|
}), []);
|
|
2070
|
+
/** Selects a theme by id; pass the theme itself for library themes and dark twins. */
|
|
2071
|
+
const selectTheme = useCallback((id, theme) => setState((s) => ({
|
|
2072
|
+
...s,
|
|
2073
|
+
activeId: id,
|
|
2074
|
+
snapshot: theme?.library || theme?.derived ? {
|
|
2075
|
+
id: theme.id,
|
|
2076
|
+
name: theme.name,
|
|
2077
|
+
tokens: theme.tokens
|
|
2078
|
+
} : s.snapshot
|
|
2079
|
+
})), []);
|
|
2080
|
+
const [modeSetting, setModeSetting] = useState(() => loadSettings(storageKey).mode);
|
|
2081
|
+
const prefersDark = usePrefersDark();
|
|
2082
|
+
const mode = modeSetting === "system" ? prefersDark ? "dark" : "light" : modeSetting;
|
|
2083
|
+
const modeRef = useRef(mode);
|
|
2084
|
+
modeRef.current = mode;
|
|
2085
|
+
const setMode = useCallback((next) => {
|
|
2086
|
+
if (![
|
|
2087
|
+
"light",
|
|
2088
|
+
"dark",
|
|
2089
|
+
"system"
|
|
2090
|
+
].includes(next)) return;
|
|
2091
|
+
setModeSetting(next);
|
|
2092
|
+
saveSettings(storageKey, {
|
|
2093
|
+
...loadSettings(storageKey),
|
|
2094
|
+
mode: next
|
|
2095
|
+
});
|
|
2096
|
+
}, [storageKey]);
|
|
2097
|
+
useLayoutEffect(() => {
|
|
2098
|
+
if (base.custom) return;
|
|
2099
|
+
if (mode === "dark" && !isDarkTheme(base.tokens)) {
|
|
2100
|
+
const twin = toDark(base);
|
|
2101
|
+
selectTheme(twin.id, twin);
|
|
2102
|
+
} else if (mode === "light" && base.id.endsWith("~dark")) {
|
|
2103
|
+
const lightId = base.id.slice(0, -DARK_SUFFIX.length);
|
|
2104
|
+
const local = allThemes.find((t) => t.id === lightId);
|
|
2105
|
+
if (local) selectTheme(local.id, local);
|
|
2106
|
+
else loadLibrary().then((lib) => {
|
|
2107
|
+
const t = lib.themes.find((x) => x.id === lightId);
|
|
2108
|
+
if (t) selectTheme(t.id, t);
|
|
2109
|
+
}, () => {});
|
|
2110
|
+
}
|
|
2111
|
+
}, [
|
|
2112
|
+
mode,
|
|
2113
|
+
base,
|
|
2114
|
+
allThemes,
|
|
2115
|
+
selectTheme
|
|
2116
|
+
]);
|
|
2117
|
+
useLayoutEffect(() => {
|
|
2118
|
+
const html = document.documentElement;
|
|
2119
|
+
html.dataset.colorsbymaxScheme = mode;
|
|
2120
|
+
html.style.colorScheme = mode;
|
|
2121
|
+
for (const el of document.querySelectorAll("[data-colorsbymax-mode]")) {
|
|
2122
|
+
const want = el.getAttribute("data-colorsbymax-mode");
|
|
2123
|
+
el.setAttribute("aria-pressed", String(want === "toggle" ? mode === "dark" : want === modeSetting));
|
|
2124
|
+
}
|
|
2125
|
+
}, [mode, modeSetting]);
|
|
2126
|
+
useEffect(() => {
|
|
2127
|
+
const onClick = (e) => {
|
|
2128
|
+
const el = e.target.closest?.("[data-colorsbymax-mode]");
|
|
2129
|
+
if (!el) return;
|
|
2130
|
+
const want = el.getAttribute("data-colorsbymax-mode");
|
|
2131
|
+
setMode(want === "toggle" ? modeRef.current === "dark" ? "light" : "dark" : want);
|
|
2132
|
+
};
|
|
2133
|
+
document.addEventListener("click", onClick);
|
|
2134
|
+
return () => document.removeEventListener("click", onClick);
|
|
2135
|
+
}, [setMode]);
|
|
1932
2136
|
const api = {
|
|
1933
2137
|
state,
|
|
2138
|
+
/** The mode in effect ('light' or 'dark'), the visitor's setting ('system' follows the device), and a setter. */
|
|
2139
|
+
mode,
|
|
2140
|
+
modeSetting,
|
|
2141
|
+
setMode,
|
|
1934
2142
|
storageKey,
|
|
1935
2143
|
siteName,
|
|
1936
2144
|
usage: initialConfig.usage ?? {},
|
|
@@ -1938,6 +2146,8 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
1938
2146
|
position: initialConfig.position ?? "bottom-right",
|
|
1939
2147
|
/** True when the config hides the switcher (e.g. in production); the theme still applies. */
|
|
1940
2148
|
hidden: Boolean(initialConfig.hidden),
|
|
2149
|
+
/** Whether the colour button makes an entrance when it first appears. */
|
|
2150
|
+
intro: initialConfig.intro !== false,
|
|
1941
2151
|
/** True when colorsbymax is swapping the page's own colours (the site isn't wired to tokens). */
|
|
1942
2152
|
recolouring: Boolean(pageColours),
|
|
1943
2153
|
/** Turns re-colouring of the site's logo on or off (the switcher's "Colour the logo" setting). */
|
|
@@ -1950,16 +2160,7 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
1950
2160
|
active: base,
|
|
1951
2161
|
tokens,
|
|
1952
2162
|
issues,
|
|
1953
|
-
|
|
1954
|
-
selectTheme: (id, theme) => setState((s) => ({
|
|
1955
|
-
...s,
|
|
1956
|
-
activeId: id,
|
|
1957
|
-
snapshot: theme?.library || theme?.derived ? {
|
|
1958
|
-
id: theme.id,
|
|
1959
|
-
name: theme.name,
|
|
1960
|
-
tokens: theme.tokens
|
|
1961
|
-
} : s.snapshot
|
|
1962
|
-
})),
|
|
2163
|
+
selectTheme,
|
|
1963
2164
|
createCustom: (name) => {
|
|
1964
2165
|
const id = newId();
|
|
1965
2166
|
setState((s) => ({
|
|
@@ -2300,6 +2501,30 @@ var Contrast = icon("contrast", [["circle", {
|
|
|
2300
2501
|
"cy": "12",
|
|
2301
2502
|
"r": "10"
|
|
2302
2503
|
}], ["path", { "d": "M12 18a6 6 0 0 0 0-12v12z" }]]);
|
|
2504
|
+
var ClipboardPaste = icon("clipboard-paste", [
|
|
2505
|
+
["path", { "d": "M11 14h10" }],
|
|
2506
|
+
["path", { "d": "M16 4h2a2 2 0 0 1 2 2v1.344" }],
|
|
2507
|
+
["path", { "d": "m17 18 4-4-4-4" }],
|
|
2508
|
+
["path", { "d": "M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113" }],
|
|
2509
|
+
["rect", {
|
|
2510
|
+
"x": "8",
|
|
2511
|
+
"y": "2",
|
|
2512
|
+
"width": "8",
|
|
2513
|
+
"height": "4",
|
|
2514
|
+
"rx": "1"
|
|
2515
|
+
}]
|
|
2516
|
+
]);
|
|
2517
|
+
var ToggleRight = icon("toggle-right", [["circle", {
|
|
2518
|
+
"cx": "15",
|
|
2519
|
+
"cy": "12",
|
|
2520
|
+
"r": "3"
|
|
2521
|
+
}], ["rect", {
|
|
2522
|
+
"width": "20",
|
|
2523
|
+
"height": "14",
|
|
2524
|
+
"x": "2",
|
|
2525
|
+
"y": "5",
|
|
2526
|
+
"rx": "7"
|
|
2527
|
+
}]]);
|
|
2303
2528
|
/** Images are scaled down to at most this many pixels on their longest side before sampling. */
|
|
2304
2529
|
var SAMPLE_SIZE = 200;
|
|
2305
2530
|
/** PDF pages read, from the first. */
|
|
@@ -2520,112 +2745,118 @@ var cssSnippet = (tokens) => `:root {\n${TOKEN_KEYS.map((k) => ` --color-${k}:
|
|
|
2520
2745
|
var keepPrompt = (name, tokens) => `Update my colorsbymax setup so the colours I chose become my site's default and the colour switcher is hidden in production. In the colorsbymax config (the autoMount({...}) call, or the config passed to <ThemeProvider>), set defaultTheme to { name: ${JSON.stringify(name)}, tokens: ${JSON.stringify(tokens)} } and set hidden: ${VITE_PROD} (use ${NODE_PROD} if this isn't a Vite project). If the site uses import 'colorsbymax/auto', replace it with import { autoMount } from 'colorsbymax/auto' and an autoMount({...}) call with that config. Don't change anything else.`;
|
|
2521
2746
|
/** @param {boolean} recolouring true when colorsbymax was swapping the site's hard-coded colours */
|
|
2522
2747
|
var removePrompt = (tokens, recolouring) => recolouring ? `Remove colorsbymax from this project but keep the colours it currently shows. The site's CSS uses hard-coded colours that colorsbymax was swapping at runtime, so update the site's own CSS to use this palette instead (primary is the main brand colour, background the page, ink the text): ${JSON.stringify(tokens)}. Then uninstall the colorsbymax package and delete its import (import 'colorsbymax/auto', autoMount, ThemeProvider or ThemeSwitcher) and any colorsbymax pre-paint script in index.html.` : `Remove colorsbymax from this project but keep my colours: add these CSS variables to my global stylesheet, replacing any existing --color-* values: ${cssSnippet(tokens)} Then uninstall the colorsbymax package and delete its usage (ThemeProvider, ThemeSwitcher, autoMount or import 'colorsbymax/auto') and any colorsbymax pre-paint script in index.html. Keep colorsbymax/tokens.css only if nothing else needs it.`;
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
*/
|
|
2541
|
-
/** @type {PanelSettings} */
|
|
2542
|
-
var DEFAULT_SETTINGS = {
|
|
2543
|
-
mode: "light",
|
|
2544
|
-
showPicks: true,
|
|
2545
|
-
showLibrary: true,
|
|
2546
|
-
showCustom: true,
|
|
2547
|
-
showOverrides: true,
|
|
2548
|
-
showImportExport: true,
|
|
2549
|
-
draggable: true,
|
|
2550
|
-
animateDot: true,
|
|
2551
|
-
panelWidth: null,
|
|
2552
|
-
panelHeight: null,
|
|
2553
|
-
libraryCollapsed: false,
|
|
2554
|
-
colourLogo: false,
|
|
2555
|
-
hideButton: false
|
|
2556
|
-
};
|
|
2557
|
-
/** Size presets offered in settings; dragging an edge gives a custom size instead. */
|
|
2558
|
-
var PANEL_PRESETS = [
|
|
2559
|
-
{
|
|
2560
|
-
id: "compact",
|
|
2561
|
-
label: "Compact",
|
|
2562
|
-
width: 340,
|
|
2563
|
-
height: null
|
|
2564
|
-
},
|
|
2565
|
-
{
|
|
2566
|
-
id: "standard",
|
|
2567
|
-
label: "Standard",
|
|
2568
|
-
width: null,
|
|
2569
|
-
height: null
|
|
2570
|
-
},
|
|
2571
|
-
{
|
|
2572
|
-
id: "large",
|
|
2573
|
-
label: "Large",
|
|
2574
|
-
width: 560,
|
|
2575
|
-
height: "full"
|
|
2576
|
-
}
|
|
2577
|
-
];
|
|
2578
|
-
/** @returns {PanelSettings} */
|
|
2579
|
-
function loadSettings(storageKey) {
|
|
2580
|
-
try {
|
|
2581
|
-
const saved = JSON.parse(window.localStorage.getItem(`${storageKey}:settings`) || "null");
|
|
2582
|
-
if (!saved || typeof saved !== "object") return DEFAULT_SETTINGS;
|
|
2583
|
-
const out = { ...DEFAULT_SETTINGS };
|
|
2584
|
-
for (const [key, fallback] of Object.entries(DEFAULT_SETTINGS)) if (typeof saved[key] === typeof fallback) out[key] = saved[key];
|
|
2585
|
-
if (![
|
|
2586
|
-
"light",
|
|
2587
|
-
"dark",
|
|
2588
|
-
"system"
|
|
2589
|
-
].includes(out.mode)) out.mode = DEFAULT_SETTINGS.mode;
|
|
2590
|
-
const size = (v) => typeof v === "number" && v > 0 && v < 1e4;
|
|
2591
|
-
out.panelWidth = size(saved.panelWidth) ? saved.panelWidth : null;
|
|
2592
|
-
out.panelHeight = size(saved.panelHeight) || saved.panelHeight === "full" ? saved.panelHeight : null;
|
|
2593
|
-
return out;
|
|
2594
|
-
} catch {
|
|
2595
|
-
return DEFAULT_SETTINGS;
|
|
2596
|
-
}
|
|
2597
|
-
}
|
|
2598
|
-
function saveSettings(storageKey, settings) {
|
|
2599
|
-
try {
|
|
2600
|
-
window.localStorage.setItem(`${storageKey}:settings`, JSON.stringify(settings));
|
|
2601
|
-
} catch {}
|
|
2748
|
+
/** The switch's markup: a sun in light mode, a moon in dark. */
|
|
2749
|
+
var TOGGLE_HTML = `<button type="button" class="cbm-mode-toggle" data-colorsbymax-mode="toggle" aria-label="Switch between light and dark mode">
|
|
2750
|
+
<svg class="cbm-mode-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><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"/></svg>
|
|
2751
|
+
<svg class="cbm-mode-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
|
|
2752
|
+
</button>`;
|
|
2753
|
+
/** Its styles. It takes the colour of the text around it, so it suits any top bar. */
|
|
2754
|
+
var TOGGLE_CSS = `.cbm-mode-toggle {
|
|
2755
|
+
display: inline-grid;
|
|
2756
|
+
place-items: center;
|
|
2757
|
+
width: 2.25rem;
|
|
2758
|
+
height: 2.25rem;
|
|
2759
|
+
border: 1px solid currentColor;
|
|
2760
|
+
border-radius: 9999px;
|
|
2761
|
+
background: transparent;
|
|
2762
|
+
color: inherit;
|
|
2763
|
+
opacity: 0.85;
|
|
2764
|
+
cursor: pointer;
|
|
2602
2765
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2766
|
+
.cbm-mode-toggle:hover { opacity: 1; }
|
|
2767
|
+
.cbm-mode-toggle .cbm-mode-moon,
|
|
2768
|
+
html[data-colorsbymax-scheme="dark"] .cbm-mode-toggle .cbm-mode-sun { display: none; }
|
|
2769
|
+
html[data-colorsbymax-scheme="dark"] .cbm-mode-toggle .cbm-mode-moon { display: block; }`;
|
|
2770
|
+
/** The same switch as a React component. */
|
|
2771
|
+
var TOGGLE_REACT = `// ModeToggle.jsx: put <ModeToggle /> in your top bar. colorsbymax makes it work.
|
|
2772
|
+
export function ModeToggle() {
|
|
2773
|
+
return (
|
|
2774
|
+
<button type="button" className="cbm-mode-toggle" data-colorsbymax-mode="toggle" aria-label="Switch between light and dark mode">
|
|
2775
|
+
<svg className="cbm-mode-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
2776
|
+
<circle cx="12" cy="12" r="4" />
|
|
2777
|
+
<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" />
|
|
2778
|
+
</svg>
|
|
2779
|
+
<svg className="cbm-mode-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
2780
|
+
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
|
|
2781
|
+
</svg>
|
|
2782
|
+
</button>
|
|
2783
|
+
)
|
|
2784
|
+
}`;
|
|
2785
|
+
/** A prompt for Claude, Cursor or Copilot that adds the switch to the site's top bar. */
|
|
2786
|
+
var TOGGLE_PROMPT = `Add a light and dark mode switch to my site's top navigation bar, next to its other links or buttons. The site uses colorsbymax, which makes the switch work: any element with data-colorsbymax-mode="toggle" switches the whole site between light and dark and remembers the visitor's choice. Use this button (as a React component if the nav is React):
|
|
2787
|
+
|
|
2788
|
+
${TOGGLE_HTML}
|
|
2789
|
+
|
|
2790
|
+
and add these styles to the global stylesheet, adjusting size and spacing to match the nav:
|
|
2791
|
+
|
|
2792
|
+
${TOGGLE_CSS}
|
|
2793
|
+
|
|
2794
|
+
Don't change anything else.`;
|
|
2795
|
+
var PREVIEW = "data-colorsbymax-preview";
|
|
2796
|
+
/** Where a switch would go: the page's top bar, or null if it has none. */
|
|
2797
|
+
function topBar() {
|
|
2798
|
+
for (const selector of [
|
|
2799
|
+
"header nav",
|
|
2800
|
+
"nav",
|
|
2801
|
+
"[role=\"navigation\"]",
|
|
2802
|
+
"header",
|
|
2803
|
+
"[role=\"banner\"]"
|
|
2804
|
+
]) for (const el of document.querySelectorAll(selector)) {
|
|
2805
|
+
if (el.closest("colorsbymax-root")) continue;
|
|
2806
|
+
const r = el.getBoundingClientRect();
|
|
2807
|
+
if (r.width > 200 && r.height > 12 && r.top < window.innerHeight / 2) return el;
|
|
2808
|
+
}
|
|
2809
|
+
return null;
|
|
2614
2810
|
}
|
|
2811
|
+
/** Whether a preview switch is on the page. */
|
|
2812
|
+
var isPreviewing = () => Boolean(document.querySelector(`[${PREVIEW}]`));
|
|
2615
2813
|
/**
|
|
2616
|
-
*
|
|
2617
|
-
*
|
|
2618
|
-
* @property {(patch: Partial<PanelSettings>) => void} update
|
|
2619
|
-
* @property {() => void} reset
|
|
2620
|
-
* @property {'light' | 'dark'} mode `settings.mode` with "system" resolved
|
|
2621
|
-
* @property {(() => void) | null} resetButton Moves the colour button back to its corner; null when it's there
|
|
2814
|
+
* Puts a switch in the page's top bar (or the top-left corner if it has none) until the next
|
|
2815
|
+
* reload. Returns where it went: 'nav' or 'corner'.
|
|
2622
2816
|
*/
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2817
|
+
function previewToggle() {
|
|
2818
|
+
removePreview();
|
|
2819
|
+
const style = document.createElement("style");
|
|
2820
|
+
style.setAttribute(PREVIEW, "");
|
|
2821
|
+
style.textContent = TOGGLE_CSS;
|
|
2822
|
+
document.head.append(style);
|
|
2823
|
+
const holder = document.createElement("span");
|
|
2824
|
+
holder.innerHTML = TOGGLE_HTML;
|
|
2825
|
+
const button = holder.firstElementChild;
|
|
2826
|
+
button.setAttribute(PREVIEW, "");
|
|
2827
|
+
button.setAttribute("aria-pressed", String(document.documentElement.dataset.colorsbymaxScheme === "dark"));
|
|
2828
|
+
const bar = topBar();
|
|
2829
|
+
if (bar) {
|
|
2830
|
+
button.style.marginInline = "0.5rem";
|
|
2831
|
+
bar.append(button);
|
|
2832
|
+
} else {
|
|
2833
|
+
Object.assign(button.style, {
|
|
2834
|
+
position: "fixed",
|
|
2835
|
+
top: "16px",
|
|
2836
|
+
left: "16px",
|
|
2837
|
+
zIndex: "59",
|
|
2838
|
+
background: "Canvas"
|
|
2839
|
+
});
|
|
2840
|
+
document.body.append(button);
|
|
2841
|
+
}
|
|
2842
|
+
button.animate?.([
|
|
2843
|
+
{
|
|
2844
|
+
transform: "scale(0.4)",
|
|
2845
|
+
opacity: 0
|
|
2846
|
+
},
|
|
2847
|
+
{ transform: "scale(1.15)" },
|
|
2848
|
+
{
|
|
2849
|
+
transform: "scale(1)",
|
|
2850
|
+
opacity: 1
|
|
2851
|
+
}
|
|
2852
|
+
], {
|
|
2853
|
+
duration: 450,
|
|
2854
|
+
easing: "ease-out"
|
|
2855
|
+
});
|
|
2856
|
+
return bar ? "nav" : "corner";
|
|
2857
|
+
}
|
|
2858
|
+
function removePreview() {
|
|
2859
|
+
for (const el of document.querySelectorAll(`[${PREVIEW}]`)) el.remove();
|
|
2629
2860
|
}
|
|
2630
2861
|
//#endregion
|
|
2631
2862
|
//#region src/ThemePanel.jsx
|
|
@@ -2676,7 +2907,7 @@ function useSavedToYours() {
|
|
|
2676
2907
|
}
|
|
2677
2908
|
function ThemePanel() {
|
|
2678
2909
|
const theme = useTheme();
|
|
2679
|
-
const { settings, mode, audit } = useSettings();
|
|
2910
|
+
const { settings, mode, audit, update } = useSettings();
|
|
2680
2911
|
const overrideCount = Object.keys(theme.state.overrides).length;
|
|
2681
2912
|
const [view, setView] = useState("main");
|
|
2682
2913
|
const returnFocus = useRef(null);
|
|
@@ -2741,33 +2972,73 @@ function ThemePanel() {
|
|
|
2741
2972
|
children: "by mrmaxdesigns"
|
|
2742
2973
|
})] }), /* @__PURE__ */ jsxs("div", {
|
|
2743
2974
|
className: "flex items-center gap-1",
|
|
2744
|
-
children: [
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2975
|
+
children: [
|
|
2976
|
+
/* @__PURE__ */ jsx("div", {
|
|
2977
|
+
role: "radiogroup",
|
|
2978
|
+
"aria-label": "Theme mode",
|
|
2979
|
+
className: "flex items-center rounded-lg border border-zinc-200 p-0.5",
|
|
2980
|
+
children: MODES.map(({ id, label, Icon }) => {
|
|
2981
|
+
const on = settings.mode === id;
|
|
2982
|
+
return /* @__PURE__ */ jsx("button", {
|
|
2983
|
+
type: "button",
|
|
2984
|
+
role: "radio",
|
|
2985
|
+
"aria-checked": on,
|
|
2986
|
+
"aria-label": label,
|
|
2987
|
+
"data-tip": id === "system" ? "Auto: follow this device" : label,
|
|
2988
|
+
onClick: () => update({ mode: id }),
|
|
2989
|
+
className: `grid h-7 w-7 place-items-center rounded-md cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${on ? "bg-zinc-900 text-white" : "text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900"}`,
|
|
2990
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
2991
|
+
className: "w-3.5 h-3.5",
|
|
2992
|
+
"aria-hidden": "true"
|
|
2993
|
+
})
|
|
2994
|
+
}, id);
|
|
2995
|
+
})
|
|
2996
|
+
}),
|
|
2997
|
+
/* @__PURE__ */ jsxs("button", {
|
|
2998
|
+
type: "button",
|
|
2999
|
+
onClick: (e) => view === "mode-toggle" ? setView("main") : showView("mode-toggle", e.currentTarget),
|
|
3000
|
+
className: `${btn} shrink-0 whitespace-nowrap px-1.5 @min-[500px]:px-2 ${view === "mode-toggle" ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
|
|
3001
|
+
"aria-pressed": view === "mode-toggle",
|
|
3002
|
+
"aria-label": "Add a light and dark switch to your site",
|
|
3003
|
+
"data-tip": "Add a light and dark switch to your site",
|
|
3004
|
+
children: [/* @__PURE__ */ jsx(ToggleRight, {
|
|
3005
|
+
className: "w-4 h-4",
|
|
3006
|
+
"aria-hidden": "true"
|
|
3007
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
3008
|
+
className: "hidden @min-[500px]:inline",
|
|
3009
|
+
children: "Add to site"
|
|
3010
|
+
})]
|
|
3011
|
+
}),
|
|
3012
|
+
/* @__PURE__ */ jsxs("button", {
|
|
3013
|
+
type: "button",
|
|
3014
|
+
onClick: audit.toggle,
|
|
3015
|
+
className: `${btn} px-2 ${audit.on ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
|
|
3016
|
+
"aria-pressed": audit.on,
|
|
3017
|
+
"data-tip": "Audit the page: point out what won’t look right with these colours",
|
|
3018
|
+
children: [/* @__PURE__ */ jsx(ScanSearch, {
|
|
3019
|
+
className: "w-4 h-4",
|
|
3020
|
+
"aria-hidden": "true"
|
|
3021
|
+
}), "Audit"]
|
|
3022
|
+
}),
|
|
3023
|
+
/* @__PURE__ */ jsx("button", {
|
|
3024
|
+
type: "button",
|
|
3025
|
+
onClick: (e) => view === "settings" ? setView("main") : showView("settings", e.currentTarget),
|
|
3026
|
+
className: `${btn} px-1.5 ${view === "settings" ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
|
|
3027
|
+
"aria-label": "Panel settings",
|
|
3028
|
+
"aria-pressed": view === "settings",
|
|
3029
|
+
"data-tip": "Panel settings",
|
|
3030
|
+
children: /* @__PURE__ */ jsx(Settings, {
|
|
3031
|
+
className: "w-4 h-4",
|
|
3032
|
+
"aria-hidden": "true"
|
|
3033
|
+
})
|
|
2764
3034
|
})
|
|
2765
|
-
|
|
3035
|
+
]
|
|
2766
3036
|
})]
|
|
2767
3037
|
}),
|
|
2768
3038
|
view === "contrast" && /* @__PURE__ */ jsx(ContrastView, { onBack: () => setView("main") }),
|
|
2769
3039
|
view === "settings" && /* @__PURE__ */ jsx(SettingsView, { onBack: () => setView("main") }),
|
|
2770
3040
|
view === "finish" && /* @__PURE__ */ jsx(FinishView, { onBack: () => setView("main") }),
|
|
3041
|
+
view === "mode-toggle" && /* @__PURE__ */ jsx(ModeToggleView, { onBack: () => setView("main") }),
|
|
2771
3042
|
/* @__PURE__ */ jsxs("div", {
|
|
2772
3043
|
hidden: view !== "main",
|
|
2773
3044
|
children: [/* @__PURE__ */ jsxs(ContrastNav.Provider, {
|
|
@@ -2882,6 +3153,137 @@ function Toast({ toast, onDismiss }) {
|
|
|
2882
3153
|
]
|
|
2883
3154
|
});
|
|
2884
3155
|
}
|
|
3156
|
+
/**
|
|
3157
|
+
* "Add to site": a light and dark switch for the site itself. Every theme has a dark version, so any
|
|
3158
|
+
* site can switch; this shows the switch, previews it in the page's top bar, and gives the code and
|
|
3159
|
+
* an AI-editor prompt to add it for good.
|
|
3160
|
+
*/
|
|
3161
|
+
function ModeToggleView({ onBack }) {
|
|
3162
|
+
const { settings, update } = useSettings();
|
|
3163
|
+
const { toast } = usePanel();
|
|
3164
|
+
const headingRef = useRef(null);
|
|
3165
|
+
const [previewing, setPreviewing] = useState(isPreviewing);
|
|
3166
|
+
const [tab, setTab] = useState("html");
|
|
3167
|
+
useEffect(() => headingRef.current?.focus(), []);
|
|
3168
|
+
const preview = () => {
|
|
3169
|
+
const where = previewToggle();
|
|
3170
|
+
setPreviewing(true);
|
|
3171
|
+
toast(where === "nav" ? "The switch is in your top bar. Try it; it’s gone when you reload." : "No top bar found, so the switch is in the top-left corner. It’s gone when you reload.");
|
|
3172
|
+
};
|
|
3173
|
+
const stop = () => {
|
|
3174
|
+
removePreview();
|
|
3175
|
+
setPreviewing(false);
|
|
3176
|
+
};
|
|
3177
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3178
|
+
className: "px-4 py-3 space-y-4",
|
|
3179
|
+
children: [
|
|
3180
|
+
/* @__PURE__ */ jsxs("button", {
|
|
3181
|
+
type: "button",
|
|
3182
|
+
className: `${btn} border-transparent px-1.5`,
|
|
3183
|
+
onClick: onBack,
|
|
3184
|
+
children: [/* @__PURE__ */ jsx(ArrowLeft, {
|
|
3185
|
+
className: "w-3.5 h-3.5",
|
|
3186
|
+
"aria-hidden": "true"
|
|
3187
|
+
}), " Back"]
|
|
3188
|
+
}),
|
|
3189
|
+
/* @__PURE__ */ jsx("h3", {
|
|
3190
|
+
ref: headingRef,
|
|
3191
|
+
tabIndex: -1,
|
|
3192
|
+
className: "text-sm font-semibold focus:outline-none",
|
|
3193
|
+
children: "Add a light and dark switch to your site"
|
|
3194
|
+
}),
|
|
3195
|
+
/* @__PURE__ */ jsx("p", {
|
|
3196
|
+
className: "text-xs text-zinc-600",
|
|
3197
|
+
children: "Every theme has a dark version, so your whole site can switch, even if it never had a dark mode. Put a switch in your top bar and visitors can choose; colorsbymax does the rest and remembers their choice. It keeps working when the colour button is hidden."
|
|
3198
|
+
}),
|
|
3199
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3200
|
+
className: "flex items-center justify-between gap-3 rounded-xl border border-zinc-200 bg-zinc-50 p-3",
|
|
3201
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
3202
|
+
className: "text-xs text-zinc-700",
|
|
3203
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
3204
|
+
className: "font-semibold text-zinc-900",
|
|
3205
|
+
children: "Try it here first"
|
|
3206
|
+
}), /* @__PURE__ */ jsx("p", { children: previewing ? "The switch is on this page. It’s gone when you reload." : "Puts a working switch in this page’s top bar." })]
|
|
3207
|
+
}), previewing ? /* @__PURE__ */ jsx("button", {
|
|
3208
|
+
type: "button",
|
|
3209
|
+
className: btn,
|
|
3210
|
+
onClick: stop,
|
|
3211
|
+
children: "Remove"
|
|
3212
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
3213
|
+
type: "button",
|
|
3214
|
+
className: btnPrimary,
|
|
3215
|
+
onClick: preview,
|
|
3216
|
+
children: [/* @__PURE__ */ jsx(ToggleRight, {
|
|
3217
|
+
className: "w-3.5 h-3.5",
|
|
3218
|
+
"aria-hidden": "true"
|
|
3219
|
+
}), " Preview it"]
|
|
3220
|
+
})]
|
|
3221
|
+
}),
|
|
3222
|
+
/* @__PURE__ */ jsx("div", {
|
|
3223
|
+
role: "radiogroup",
|
|
3224
|
+
"aria-label": "Mode",
|
|
3225
|
+
className: "grid grid-cols-3 gap-2",
|
|
3226
|
+
children: MODES.map(({ id, label, Icon }) => /* @__PURE__ */ jsxs("button", {
|
|
3227
|
+
type: "button",
|
|
3228
|
+
role: "radio",
|
|
3229
|
+
"aria-checked": settings.mode === id,
|
|
3230
|
+
onClick: () => update({ mode: id }),
|
|
3231
|
+
className: `flex h-8 items-center justify-center gap-1.5 rounded-lg border text-xs font-medium cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${settings.mode === id ? "border-zinc-900 bg-zinc-900 text-white" : "border-zinc-300 bg-white text-zinc-800 hover:bg-zinc-50"}`,
|
|
3232
|
+
children: [
|
|
3233
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
3234
|
+
className: "w-3.5 h-3.5",
|
|
3235
|
+
"aria-hidden": "true"
|
|
3236
|
+
}),
|
|
3237
|
+
" ",
|
|
3238
|
+
label
|
|
3239
|
+
]
|
|
3240
|
+
}, id))
|
|
3241
|
+
}),
|
|
3242
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3243
|
+
className: "space-y-2",
|
|
3244
|
+
children: [
|
|
3245
|
+
/* @__PURE__ */ jsx("p", {
|
|
3246
|
+
className: "text-xs font-semibold text-zinc-900",
|
|
3247
|
+
children: "Add it for good"
|
|
3248
|
+
}),
|
|
3249
|
+
/* @__PURE__ */ jsx("div", {
|
|
3250
|
+
role: "tablist",
|
|
3251
|
+
"aria-label": "Code for",
|
|
3252
|
+
className: "flex gap-1",
|
|
3253
|
+
children: [["html", "HTML"], ["react", "React"]].map(([id, label]) => /* @__PURE__ */ jsx("button", {
|
|
3254
|
+
type: "button",
|
|
3255
|
+
role: "tab",
|
|
3256
|
+
"aria-selected": tab === id,
|
|
3257
|
+
onClick: () => setTab(id),
|
|
3258
|
+
className: `rounded-md px-2 py-1 text-[11px] font-semibold cursor-pointer ${tab === id ? "bg-zinc-900 text-white" : "text-zinc-600 hover:bg-zinc-100"}`,
|
|
3259
|
+
children: label
|
|
3260
|
+
}, id))
|
|
3261
|
+
}),
|
|
3262
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3263
|
+
label: tab === "html" ? "In your top bar" : "ModeToggle.jsx",
|
|
3264
|
+
text: tab === "html" ? TOGGLE_HTML : TOGGLE_REACT
|
|
3265
|
+
}),
|
|
3266
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3267
|
+
label: "In your global stylesheet",
|
|
3268
|
+
text: TOGGLE_CSS
|
|
3269
|
+
}),
|
|
3270
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3271
|
+
label: "Or ask Claude, Cursor or Copilot",
|
|
3272
|
+
text: TOGGLE_PROMPT,
|
|
3273
|
+
copyLabel: "Copy prompt"
|
|
3274
|
+
})
|
|
3275
|
+
]
|
|
3276
|
+
}),
|
|
3277
|
+
/* @__PURE__ */ jsxs("p", {
|
|
3278
|
+
className: "rounded-lg bg-zinc-100 p-2.5 text-[11px] text-zinc-700",
|
|
3279
|
+
children: [/* @__PURE__ */ jsx("strong", {
|
|
3280
|
+
className: "font-semibold text-zinc-900",
|
|
3281
|
+
children: "Already have a dark mode?"
|
|
3282
|
+
}), " You don’t need this. The theme you pick colours your site well in light and dark, with contrast checked in both."]
|
|
3283
|
+
})
|
|
3284
|
+
]
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
2885
3287
|
/** Shows code with a copy button. */
|
|
2886
3288
|
function CopyBlock({ label, text, copyLabel = "Copy" }) {
|
|
2887
3289
|
const { toast } = usePanel();
|
|
@@ -3556,6 +3958,8 @@ function PresetGrid() {
|
|
|
3556
3958
|
const { toast, reveal } = usePanel();
|
|
3557
3959
|
const wrapRef = useRef(null);
|
|
3558
3960
|
const pendingScroll = useRef(false);
|
|
3961
|
+
const themesRef = useRef(null);
|
|
3962
|
+
const scrollToThemes = useRef(false);
|
|
3559
3963
|
useEffect(() => {
|
|
3560
3964
|
if (!reveal) return;
|
|
3561
3965
|
setCategory(reveal.group);
|
|
@@ -3567,6 +3971,14 @@ function PresetGrid() {
|
|
|
3567
3971
|
pendingScroll.current = true;
|
|
3568
3972
|
}, [reveal]);
|
|
3569
3973
|
useEffect(() => {
|
|
3974
|
+
if (scrollToThemes.current && themesRef.current) {
|
|
3975
|
+
scrollToThemes.current = false;
|
|
3976
|
+
const still = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3977
|
+
themesRef.current.scrollIntoView({
|
|
3978
|
+
block: "start",
|
|
3979
|
+
behavior: still ? "auto" : "smooth"
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3570
3982
|
if (!pendingScroll.current) return;
|
|
3571
3983
|
pendingScroll.current = false;
|
|
3572
3984
|
const root = wrapRef.current;
|
|
@@ -3654,10 +4066,11 @@ function PresetGrid() {
|
|
|
3654
4066
|
"yours"
|
|
3655
4067
|
].includes(category));
|
|
3656
4068
|
const catInfo = library?.categories.find((c) => c.id === category);
|
|
3657
|
-
const choose = (id) => {
|
|
4069
|
+
const choose = (id, { scroll = false } = {}) => {
|
|
3658
4070
|
setCategory(id);
|
|
3659
4071
|
setQuery("");
|
|
3660
4072
|
setLimit(PAGE_SIZE);
|
|
4073
|
+
scrollToThemes.current = scroll;
|
|
3661
4074
|
};
|
|
3662
4075
|
const pick = (t) => selectTheme(t.id, t);
|
|
3663
4076
|
const surprise = () => {
|
|
@@ -3789,7 +4202,7 @@ function PresetGrid() {
|
|
|
3789
4202
|
type: "button",
|
|
3790
4203
|
"aria-pressed": on,
|
|
3791
4204
|
"data-tip": c.description,
|
|
3792
|
-
onClick: () => choose(c.id),
|
|
4205
|
+
onClick: () => choose(c.id, { scroll: true }),
|
|
3793
4206
|
className: `flex h-8 min-w-0 items-center gap-1 rounded-lg border px-2 text-xs font-medium cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1 ${on ? "border-zinc-900 bg-zinc-900 text-white" : "border-zinc-300 bg-white text-zinc-800 hover:bg-zinc-50"}`,
|
|
3794
4207
|
children: [
|
|
3795
4208
|
on && /* @__PURE__ */ jsx(Check, {
|
|
@@ -3808,6 +4221,10 @@ function PresetGrid() {
|
|
|
3808
4221
|
}, c.id);
|
|
3809
4222
|
})
|
|
3810
4223
|
})] }),
|
|
4224
|
+
/* @__PURE__ */ jsx("div", {
|
|
4225
|
+
ref: themesRef,
|
|
4226
|
+
className: "scroll-mt-3"
|
|
4227
|
+
}),
|
|
3811
4228
|
!q && /* @__PURE__ */ jsx("p", {
|
|
3812
4229
|
className: "text-xs text-zinc-600",
|
|
3813
4230
|
children: (catInfo ?? chips.find((c) => c.id === category))?.description
|
|
@@ -4267,7 +4684,16 @@ var FROM_NOTE = {
|
|
|
4267
4684
|
pdf: "Picked from the colours on the first pages.",
|
|
4268
4685
|
"pdf-text": "Found colour codes written in the PDF."
|
|
4269
4686
|
};
|
|
4270
|
-
|
|
4687
|
+
var IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
4688
|
+
/** The paste shortcut, as the visitor's keyboard shows it. */
|
|
4689
|
+
var PASTE_KEYS = IS_MAC ? "⌘V" : "Ctrl+V";
|
|
4690
|
+
var PASTED = "Pasted image";
|
|
4691
|
+
/** The first image (or PDF) in a clipboard's files: a screenshot, or a copied picture. */
|
|
4692
|
+
var fileFromClipboard = (data) => [...data?.files ?? []].find((f) => f.type.startsWith("image/") || f.type === "application/pdf");
|
|
4693
|
+
/**
|
|
4694
|
+
* Builds a custom palette from the colours in an image (uploaded, dropped or pasted from the
|
|
4695
|
+
* clipboard), or a PDF where the site allows it.
|
|
4696
|
+
*/
|
|
4271
4697
|
function PaletteFromFile() {
|
|
4272
4698
|
const { addPalette, loadPdf } = useTheme();
|
|
4273
4699
|
const kinds = loadPdf ? "image or PDF" : "image";
|
|
@@ -4278,35 +4704,105 @@ function PaletteFromFile() {
|
|
|
4278
4704
|
const [off, setOff] = useState(() => /* @__PURE__ */ new Set());
|
|
4279
4705
|
const [name, setName] = useState("");
|
|
4280
4706
|
const [dragOver, setDragOver] = useState(false);
|
|
4707
|
+
const [picture, setPicture] = useState(null);
|
|
4708
|
+
const [flash, setFlash] = useState(false);
|
|
4281
4709
|
const fileRef = useRef(null);
|
|
4710
|
+
const zoneRef = useRef(null);
|
|
4282
4711
|
const nameId = useId();
|
|
4283
4712
|
const chosen = found ? found.colours.filter((c) => !off.has(c)) : [];
|
|
4284
4713
|
const preview = useMemo(() => chosen.length ? themeFromPalette(chosen) : null, [chosen.join()]);
|
|
4285
|
-
|
|
4714
|
+
useEffect(() => () => picture?.url && URL.revokeObjectURL(picture.url), [picture?.url]);
|
|
4715
|
+
useEffect(() => {
|
|
4716
|
+
if (!picture?.pasted) return;
|
|
4717
|
+
const still = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
4718
|
+
zoneRef.current?.scrollIntoView({
|
|
4719
|
+
block: "nearest",
|
|
4720
|
+
behavior: still ? "auto" : "smooth"
|
|
4721
|
+
});
|
|
4722
|
+
}, [picture?.url, picture?.pasted]);
|
|
4723
|
+
useEffect(() => {
|
|
4724
|
+
if (!flash) return;
|
|
4725
|
+
const t = setTimeout(() => setFlash(false), 1200);
|
|
4726
|
+
return () => clearTimeout(t);
|
|
4727
|
+
}, [flash]);
|
|
4728
|
+
/**
|
|
4729
|
+
* @param {string} [label] what to call the file; pasted images have no useful name
|
|
4730
|
+
* @param {boolean} [pasted]
|
|
4731
|
+
*/
|
|
4732
|
+
const read = async (file, label = file?.name, pasted = false) => {
|
|
4286
4733
|
if (!file) return;
|
|
4287
4734
|
setBusy(true);
|
|
4288
4735
|
setError(null);
|
|
4736
|
+
setFound(null);
|
|
4737
|
+
setPicture({
|
|
4738
|
+
url: file.type.startsWith("image/") ? URL.createObjectURL(file) : null,
|
|
4739
|
+
label,
|
|
4740
|
+
pasted
|
|
4741
|
+
});
|
|
4289
4742
|
try {
|
|
4290
4743
|
const { colours, from } = await coloursFromFile(file, { loadPdf });
|
|
4291
4744
|
if (!colours.length) throw new Error("Couldn’t find any colours in that file.");
|
|
4292
4745
|
setFound({
|
|
4293
|
-
fileName:
|
|
4746
|
+
fileName: label,
|
|
4294
4747
|
colours,
|
|
4295
4748
|
from
|
|
4296
4749
|
});
|
|
4297
4750
|
setOff(/* @__PURE__ */ new Set());
|
|
4298
|
-
setName(
|
|
4751
|
+
setName(label.replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ").trim().slice(0, 60) || "Uploaded palette");
|
|
4299
4752
|
} catch (e) {
|
|
4300
|
-
|
|
4753
|
+
setPicture(null);
|
|
4301
4754
|
setError(e.message || "Couldn’t read that file.");
|
|
4302
4755
|
} finally {
|
|
4303
4756
|
setBusy(false);
|
|
4304
4757
|
}
|
|
4305
4758
|
};
|
|
4759
|
+
const clear = () => {
|
|
4760
|
+
setFound(null);
|
|
4761
|
+
setPicture(null);
|
|
4762
|
+
};
|
|
4763
|
+
/** Reads a pasted image, opening Import / export first if it's collapsed, so the result shows. */
|
|
4764
|
+
const readPasted = (file) => {
|
|
4765
|
+
const section = zoneRef.current?.closest("details");
|
|
4766
|
+
if (section && !section.open) section.open = true;
|
|
4767
|
+
setFlash(true);
|
|
4768
|
+
read(file, PASTED, true);
|
|
4769
|
+
};
|
|
4770
|
+
const readPastedRef = useRef(readPasted);
|
|
4771
|
+
readPastedRef.current = readPasted;
|
|
4772
|
+
useEffect(() => {
|
|
4773
|
+
const root = zoneRef.current?.getRootNode();
|
|
4774
|
+
if (!root) return;
|
|
4775
|
+
const onPaste = (e) => {
|
|
4776
|
+
const file = fileFromClipboard(e.clipboardData);
|
|
4777
|
+
if (!file) return;
|
|
4778
|
+
const target = e.composedPath()[0];
|
|
4779
|
+
if (target instanceof HTMLElement && (target.isContentEditable || target.localName === "input" || target.localName === "textarea") && e.clipboardData.types.includes("text/plain")) return;
|
|
4780
|
+
e.preventDefault();
|
|
4781
|
+
readPastedRef.current(file);
|
|
4782
|
+
};
|
|
4783
|
+
root.addEventListener("paste", onPaste);
|
|
4784
|
+
return () => root.removeEventListener("paste", onPaste);
|
|
4785
|
+
}, []);
|
|
4786
|
+
const pasteFromClipboard = async () => {
|
|
4787
|
+
setError(null);
|
|
4788
|
+
if (!navigator.clipboard?.read) {
|
|
4789
|
+
setError(`This browser can’t paste from a button. Press ${PASTE_KEYS} instead.`);
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4792
|
+
try {
|
|
4793
|
+
for (const item of await navigator.clipboard.read()) {
|
|
4794
|
+
const type = item.types.find((t) => t.startsWith("image/"));
|
|
4795
|
+
if (type) return readPasted(new File([await item.getType(type)], PASTED, { type }));
|
|
4796
|
+
}
|
|
4797
|
+
setError("There’s no image on the clipboard. Take a screenshot or copy a picture, then paste.");
|
|
4798
|
+
} catch {
|
|
4799
|
+
setError(`Couldn’t read the clipboard. Press ${PASTE_KEYS} to paste instead.`);
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4306
4802
|
const create = () => {
|
|
4307
4803
|
addPalette(name, preview);
|
|
4308
4804
|
savedToYours(name.trim() || "Uploaded palette");
|
|
4309
|
-
|
|
4805
|
+
clear();
|
|
4310
4806
|
};
|
|
4311
4807
|
return /* @__PURE__ */ jsxs("div", {
|
|
4312
4808
|
className: "space-y-1.5",
|
|
@@ -4316,6 +4812,7 @@ function PaletteFromFile() {
|
|
|
4316
4812
|
children: ["Palette from an ", kinds]
|
|
4317
4813
|
}),
|
|
4318
4814
|
/* @__PURE__ */ jsxs("div", {
|
|
4815
|
+
ref: zoneRef,
|
|
4319
4816
|
onDragOver: (e) => {
|
|
4320
4817
|
e.preventDefault();
|
|
4321
4818
|
setDragOver(true);
|
|
@@ -4326,8 +4823,64 @@ function PaletteFromFile() {
|
|
|
4326
4823
|
setDragOver(false);
|
|
4327
4824
|
read(e.dataTransfer.files?.[0]);
|
|
4328
4825
|
},
|
|
4329
|
-
className: `rounded-xl border border-dashed p-3 text-center ${dragOver ? "border-zinc-900 bg-zinc-100" : "border-zinc-300 bg-zinc-50"}`,
|
|
4826
|
+
className: `rounded-xl border border-dashed p-3 text-center transition-shadow motion-reduce:transition-none ${dragOver || flash ? "border-zinc-900 bg-zinc-100" : "border-zinc-300 bg-zinc-50"} ${flash ? "ring-2 ring-zinc-900 ring-offset-1" : ""}`,
|
|
4330
4827
|
children: [
|
|
4828
|
+
picture && /* @__PURE__ */ jsxs("figure", {
|
|
4829
|
+
className: "mb-2.5 flex items-center gap-3 rounded-lg border border-zinc-200 bg-white p-2 text-left",
|
|
4830
|
+
children: [
|
|
4831
|
+
picture.url ? /* @__PURE__ */ jsx("img", {
|
|
4832
|
+
src: picture.url,
|
|
4833
|
+
alt: picture.label,
|
|
4834
|
+
className: "h-16 w-24 shrink-0 rounded-md border border-zinc-200 bg-zinc-100 object-contain",
|
|
4835
|
+
onLoad: (e) => {
|
|
4836
|
+
const { naturalWidth: w, naturalHeight: h, src } = e.currentTarget;
|
|
4837
|
+
setPicture((p) => p && p.url === src ? {
|
|
4838
|
+
...p,
|
|
4839
|
+
size: `${w} × ${h}`
|
|
4840
|
+
} : p);
|
|
4841
|
+
}
|
|
4842
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
4843
|
+
className: "grid h-16 w-24 shrink-0 place-items-center rounded-md border border-zinc-200 bg-zinc-100 font-mono text-xs font-semibold text-zinc-600",
|
|
4844
|
+
"aria-hidden": "true",
|
|
4845
|
+
children: "PDF"
|
|
4846
|
+
}),
|
|
4847
|
+
/* @__PURE__ */ jsxs("figcaption", {
|
|
4848
|
+
role: "status",
|
|
4849
|
+
className: "min-w-0 flex-1 space-y-0.5 text-[11px] text-zinc-600",
|
|
4850
|
+
children: [
|
|
4851
|
+
/* @__PURE__ */ jsxs("span", {
|
|
4852
|
+
className: "flex items-center gap-1 font-medium text-zinc-900",
|
|
4853
|
+
children: [busy ? /* @__PURE__ */ jsx(Loader2, {
|
|
4854
|
+
className: "w-3.5 h-3.5 animate-spin motion-reduce:animate-none",
|
|
4855
|
+
"aria-hidden": "true"
|
|
4856
|
+
}) : /* @__PURE__ */ jsx(CheckCircle2, {
|
|
4857
|
+
className: "w-3.5 h-3.5 text-emerald-800",
|
|
4858
|
+
"aria-hidden": "true"
|
|
4859
|
+
}), picture.pasted ? "Image pasted" : picture.url ? "Image added" : "PDF added"]
|
|
4860
|
+
}),
|
|
4861
|
+
/* @__PURE__ */ jsxs("span", {
|
|
4862
|
+
className: "block truncate",
|
|
4863
|
+
children: [picture.label, picture.size ? ` · ${picture.size}` : ""]
|
|
4864
|
+
}),
|
|
4865
|
+
/* @__PURE__ */ jsx("span", {
|
|
4866
|
+
className: "block",
|
|
4867
|
+
children: busy ? "Reading its colours…" : "Its colours are below."
|
|
4868
|
+
})
|
|
4869
|
+
]
|
|
4870
|
+
}),
|
|
4871
|
+
/* @__PURE__ */ jsx("button", {
|
|
4872
|
+
type: "button",
|
|
4873
|
+
className: `${btn} shrink-0 border-transparent px-1.5`,
|
|
4874
|
+
onClick: clear,
|
|
4875
|
+
"aria-label": `Remove ${picture.label}`,
|
|
4876
|
+
"data-tip": "Remove",
|
|
4877
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
4878
|
+
className: "w-3.5 h-3.5",
|
|
4879
|
+
"aria-hidden": "true"
|
|
4880
|
+
})
|
|
4881
|
+
})
|
|
4882
|
+
]
|
|
4883
|
+
}),
|
|
4331
4884
|
/* @__PURE__ */ jsxs("button", {
|
|
4332
4885
|
type: "button",
|
|
4333
4886
|
className: btn,
|
|
@@ -4340,12 +4893,29 @@ function PaletteFromFile() {
|
|
|
4340
4893
|
}) : /* @__PURE__ */ jsx(ImageUp, {
|
|
4341
4894
|
className: "w-3.5 h-3.5",
|
|
4342
4895
|
"aria-hidden": "true"
|
|
4343
|
-
}), busy ? "Reading colours…" : `Choose ${kinds}…`]
|
|
4896
|
+
}), busy ? "Reading colours…" : picture ? "Choose another…" : `Choose ${kinds}…`]
|
|
4344
4897
|
}),
|
|
4345
|
-
|
|
4898
|
+
" ",
|
|
4899
|
+
/* @__PURE__ */ jsxs("button", {
|
|
4900
|
+
type: "button",
|
|
4901
|
+
className: btn,
|
|
4902
|
+
onClick: pasteFromClipboard,
|
|
4903
|
+
disabled: busy,
|
|
4904
|
+
"aria-keyshortcuts": IS_MAC ? "Meta+V" : "Control+V",
|
|
4905
|
+
children: [/* @__PURE__ */ jsx(ClipboardPaste, {
|
|
4906
|
+
className: "w-3.5 h-3.5",
|
|
4907
|
+
"aria-hidden": "true"
|
|
4908
|
+
}), picture ? "Paste another" : "Paste image"]
|
|
4909
|
+
}),
|
|
4910
|
+
!picture && /* @__PURE__ */ jsxs("p", {
|
|
4346
4911
|
className: "mt-1.5 text-[11px] text-zinc-600",
|
|
4347
4912
|
children: [
|
|
4348
|
-
"or drop one here
|
|
4913
|
+
"or drop one here, or press ",
|
|
4914
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
4915
|
+
className: "font-mono",
|
|
4916
|
+
children: PASTE_KEYS
|
|
4917
|
+
}),
|
|
4918
|
+
" to paste a screenshot. A mood board, screenshot or photo",
|
|
4349
4919
|
loadPdf ? ", or a brand guide PDF," : "",
|
|
4350
4920
|
" works."
|
|
4351
4921
|
]
|
|
@@ -4431,7 +5001,7 @@ function PaletteFromFile() {
|
|
|
4431
5001
|
}), /* @__PURE__ */ jsx("button", {
|
|
4432
5002
|
type: "button",
|
|
4433
5003
|
className: btn,
|
|
4434
|
-
onClick:
|
|
5004
|
+
onClick: clear,
|
|
4435
5005
|
children: "Discard"
|
|
4436
5006
|
})]
|
|
4437
5007
|
})
|
|
@@ -5092,7 +5662,7 @@ function AuditBar({ anchorRect, open, hiddenCount, themeIssues, onRecheck, onClo
|
|
|
5092
5662
|
}
|
|
5093
5663
|
//#endregion
|
|
5094
5664
|
//#region src/panel-css.generated.js
|
|
5095
|
-
var panel_css_generated_default = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (display:block){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-900:oklch(39.1% .09 240.876);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-white:#fff;--spacing:4px;--text-xs:12px;--text-xs--line-height:calc(1 / .75);--text-sm:14px;--text-sm--line-height:calc(1.25 / .875);--text-base:16px;--text-base--line-height:calc(1.5 / 1);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-snug:1.375;--radius-md:6px;--radius-lg:8px;--radius-xl:12px;--radius-2xl:16px;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.theme-switcher{letter-spacing:normal;color:var(--color-zinc-900);font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:16px;line-height:1.5}}@layer components;@layer utilities{.\\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-5{inset-block:calc(var(--spacing) * 5)}.-top-2{top:calc(var(--spacing) * -2)}.-top-\\[6px\\]{top:-6px}.top-0{top:0}.top-1\\.5{top:calc(var(--spacing) * 1.5)}.top-1\\/2{top:50%}.top-5{top:calc(var(--spacing) * 5)}.top-\\[84px\\]{top:84px}.-right-0\\.5{right:calc(var(--spacing) * -.5)}.-right-2{right:calc(var(--spacing) * -2)}.right-1\\.5{right:calc(var(--spacing) * 1.5)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-full{right:100%}.-bottom-0\\.5{bottom:calc(var(--spacing) * -.5)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-\\[6px\\]{bottom:-6px}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.-left-2{left:calc(var(--spacing) * -2)}.left-5{left:calc(var(--spacing) * 5)}.left-full{left:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-\\[55\\]{z-index:55}.z-\\[56\\]{z-index:56}.z-\\[57\\]{z-index:57}.z-\\[60\\]{z-index:60}.z-\\[61\\]{z-index:61}.z-\\[62\\]{z-index:62}.z-\\[70\\]{z-index:70}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-px{margin-top:1px}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-2\\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-12{height:calc(var(--spacing) * 12)}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing) * 40)}.min-h-0{min-height:0}.w-2\\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-9{width:calc(var(--spacing) * 9)}.w-16{width:calc(var(--spacing) * 16)}.w-\\[5\\.5rem\\]{width:88px}.w-full{width:100%}.max-w-\\[240px\\]{max-width:240px}.max-w-\\[calc\\(100vw-80px\\)\\]{max-width:calc(100vw - 80px)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.animate-spin{animation:var(--animate-spin)}.cursor-ew-resize{cursor:ew-resize}.cursor-grabbing{cursor:grabbing}.cursor-nesw-resize{cursor:nesw-resize}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[inherit\\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-900{border-color:var(--color-zinc-900)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-white{background-color:var(--color-white)}.bg-white\\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.bg-zinc-900{background-color:var(--color-zinc-900)}.p-0\\.5{padding:calc(var(--spacing) * .5)}.p-2\\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.px-1{padding-inline:var(--spacing)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pr-1\\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.align-super{vertical-align:super}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-red-700{color:var(--color-red-700)}.text-sky-900{color:var(--color-sky-900)}.text-white{color:var(--color-white)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-zinc-900{accent-color:var(--color-zinc-900)}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-white{--tw-ring-color:var(--color-white)}.ring-zinc-900{--tw-ring-color:var(--color-zinc-900)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\\[color\\,background-color\\,box-shadow\\,scale\\]{transition-property:color,background-color,box-shadow,scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}.placeholder\\:text-zinc-500::placeholder{color:var(--color-zinc-500)}@media (hover:hover){.hover\\:bg-amber-200:hover{background-color:var(--color-amber-200)}.hover\\:bg-white:hover{background-color:var(--color-white)}.hover\\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\\:bg-zinc-400\\/30:hover{background-color:#9f9fa94d}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-zinc-400\\/30:hover{background-color:color-mix(in oklab, var(--color-zinc-400) 30%, transparent)}}.hover\\:bg-zinc-700:hover{background-color:var(--color-zinc-700)}.hover\\:text-amber-950:hover{color:var(--color-amber-950)}.hover\\:text-zinc-900:hover{color:var(--color-zinc-900)}.hover\\:opacity-100:hover{opacity:1}}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-white:focus-visible{--tw-ring-color:var(--color-white)}.focus-visible\\:ring-zinc-900:focus-visible{--tw-ring-color:var(--color-zinc-900)}.focus-visible\\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-inset:focus-visible{--tw-ring-inset:inset}.disabled\\:opacity-50:disabled{opacity:.5}@media (prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}.motion-reduce\\:transition-none{transition-property:none}}@media (min-width:1200px){.min-\\[1200px\\]\\:top-\\[27px\\]{top:27px}.min-\\[1200px\\]\\:right-4{right:calc(var(--spacing) * 4)}}@media (min-width:640px){.sm\\:inline{display:inline}}@container (min-width:380px){.\\@min-\\[380px\\]\\:h-10{height:calc(var(--spacing) * 10)}.\\@min-\\[380px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\\@min-\\[380px\\]\\:flex-row{flex-direction:row}.\\@min-\\[380px\\]\\:justify-start{justify-content:flex-start}.\\@min-\\[380px\\]\\:gap-1\\.5{gap:calc(var(--spacing) * 1.5)}}@container (min-width:440px){.\\@min-\\[440px\\]\\:inline{display:inline}.\\@min-\\[440px\\]\\:px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}}@container (min-width:520px){.\\@min-\\[520px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}:host{all:initial}.cbm-dark{color-scheme:dark;--color-white:oklch(21% .006 285.885);--color-zinc-50:oklch(24.5% .006 286);--color-zinc-100:oklch(27.4% .006 286.033);--color-zinc-200:oklch(33% .01 285.9);--color-zinc-300:oklch(42% .015 285.8);--color-zinc-400:oklch(52% .016 285.9);--color-zinc-500:oklch(68% .015 286.067);--color-zinc-600:oklch(74% .012 286);--color-zinc-700:oklch(82% .008 286.2);--color-zinc-800:oklch(88% .006 286.3);--color-zinc-900:oklch(96.7% .001 286.375);--color-zinc-950:oklch(98.5% 0 0);--color-amber-50:oklch(27.9% .077 45.635);--color-amber-100:oklch(33% .09 46);--color-amber-200:oklch(41.4% .112 45.904);--color-amber-300:oklch(55.5% .163 48.998);--color-amber-700:oklch(87.9% .169 91.605);--color-amber-800:oklch(92.4% .12 95.746);--color-amber-900:oklch(96.2% .059 95.617);--color-amber-950:oklch(98.7% .022 95.277);--color-emerald-50:oklch(26.2% .051 172.552);--color-emerald-800:oklch(90.5% .093 164.15);--color-emerald-900:oklch(95% .052 163.051);--color-sky-50:oklch(29.3% .066 243.157);--color-sky-100:oklch(39.1% .09 240.876);--color-sky-900:oklch(95.1% .026 236.824);--color-red-600:oklch(70.4% .191 22.216);--color-red-700:oklch(80.8% .114 19.571)}.cbm-dark *{--tw-ring-offset-color:oklch(21% .006 285.885)}.theme-scroll{--cbm-thumb:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 55%, var(--color-white))}}.theme-scroll{--cbm-thumb-hover:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb-hover:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 80%, var(--color-white))}}.theme-scroll{scrollbar-width:thin;scrollbar-color:var(--cbm-thumb) transparent}.theme-scroll:hover{scrollbar-color:var(--cbm-thumb-hover) transparent}.theme-scroll::-webkit-scrollbar{width:6px}.theme-scroll::-webkit-scrollbar-track{background:0 0}.theme-scroll::-webkit-scrollbar-thumb{background:var(--cbm-thumb);border-radius:9999px}.theme-scroll:hover::-webkit-scrollbar-thumb{background:var(--cbm-thumb-hover)}.theme-panel{transform-origin:100% 0;animation:.16s ease-out theme-panel-in}@keyframes theme-panel-in{0%{opacity:0;transform:translateY(-6px)scale(.98)}}.theme-hint{animation:.12s ease-out theme-hint-in}.theme-toast{animation:.18s ease-out theme-toast-in}@keyframes theme-toast-in{0%{opacity:0;transform:translateY(8px)}}@keyframes theme-hint-in{0%{opacity:0}}@media (prefers-reduced-motion:reduce){.theme-panel,.theme-hint,.theme-toast{animation:none}}.theme-panel summary{list-style:none}.theme-panel summary::-webkit-details-marker{display:none}.theme-panel details[open]>summary .theme-chevron{transform:rotate(90deg)}@keyframes spin{to{transform:rotate(360deg)}}";
|
|
5665
|
+
var panel_css_generated_default = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (display:block){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-900:oklch(39.1% .09 240.876);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-white:#fff;--spacing:4px;--text-xs:12px;--text-xs--line-height:calc(1 / .75);--text-sm:14px;--text-sm--line-height:calc(1.25 / .875);--text-base:16px;--text-base--line-height:calc(1.5 / 1);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-snug:1.375;--radius-md:6px;--radius-lg:8px;--radius-xl:12px;--radius-2xl:16px;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.theme-switcher{letter-spacing:normal;color:var(--color-zinc-900);font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:16px;line-height:1.5}}@layer components;@layer utilities{.\\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-5{inset-block:calc(var(--spacing) * 5)}.-top-2{top:calc(var(--spacing) * -2)}.-top-\\[6px\\]{top:-6px}.top-0{top:0}.top-1\\.5{top:calc(var(--spacing) * 1.5)}.top-1\\/2{top:50%}.top-5{top:calc(var(--spacing) * 5)}.top-\\[84px\\]{top:84px}.-right-0\\.5{right:calc(var(--spacing) * -.5)}.-right-2{right:calc(var(--spacing) * -2)}.right-1\\.5{right:calc(var(--spacing) * 1.5)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-full{right:100%}.-bottom-0\\.5{bottom:calc(var(--spacing) * -.5)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-\\[6px\\]{bottom:-6px}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.-left-2{left:calc(var(--spacing) * -2)}.left-5{left:calc(var(--spacing) * 5)}.left-full{left:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-\\[55\\]{z-index:55}.z-\\[56\\]{z-index:56}.z-\\[57\\]{z-index:57}.z-\\[60\\]{z-index:60}.z-\\[61\\]{z-index:61}.z-\\[62\\]{z-index:62}.z-\\[70\\]{z-index:70}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-px{margin-top:1px}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2\\.5{margin-bottom:calc(var(--spacing) * 2.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-2\\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing) * 40)}.min-h-0{min-height:0}.w-2\\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-9{width:calc(var(--spacing) * 9)}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-\\[5\\.5rem\\]{width:88px}.w-full{width:100%}.max-w-\\[240px\\]{max-width:240px}.max-w-\\[calc\\(100vw-80px\\)\\]{max-width:calc(100vw - 80px)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.animate-spin{animation:var(--animate-spin)}.cursor-ew-resize{cursor:ew-resize}.cursor-grabbing{cursor:grabbing}.cursor-nesw-resize{cursor:nesw-resize}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.scroll-mt-3{scroll-margin-top:calc(var(--spacing) * 3)}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-0\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[inherit\\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-900{border-color:var(--color-zinc-900)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-white{background-color:var(--color-white)}.bg-white\\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.bg-zinc-900{background-color:var(--color-zinc-900)}.object-contain{object-fit:contain}.p-0\\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.px-1{padding-inline:var(--spacing)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pr-1\\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.align-super{vertical-align:super}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-red-700{color:var(--color-red-700)}.text-sky-900{color:var(--color-sky-900)}.text-white{color:var(--color-white)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-zinc-900{accent-color:var(--color-zinc-900)}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-white{--tw-ring-color:var(--color-white)}.ring-zinc-900{--tw-ring-color:var(--color-zinc-900)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\\[color\\,background-color\\,box-shadow\\,scale\\]{transition-property:color,background-color,box-shadow,scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}.placeholder\\:text-zinc-500::placeholder{color:var(--color-zinc-500)}@media (hover:hover){.hover\\:bg-amber-200:hover{background-color:var(--color-amber-200)}.hover\\:bg-white:hover{background-color:var(--color-white)}.hover\\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\\:bg-zinc-400\\/30:hover{background-color:#9f9fa94d}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-zinc-400\\/30:hover{background-color:color-mix(in oklab, var(--color-zinc-400) 30%, transparent)}}.hover\\:bg-zinc-700:hover{background-color:var(--color-zinc-700)}.hover\\:text-amber-950:hover{color:var(--color-amber-950)}.hover\\:text-zinc-900:hover{color:var(--color-zinc-900)}.hover\\:opacity-100:hover{opacity:1}}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-white:focus-visible{--tw-ring-color:var(--color-white)}.focus-visible\\:ring-zinc-900:focus-visible{--tw-ring-color:var(--color-zinc-900)}.focus-visible\\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-inset:focus-visible{--tw-ring-inset:inset}.disabled\\:opacity-50:disabled{opacity:.5}@media (prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}.motion-reduce\\:transition-none{transition-property:none}}@media (min-width:1200px){.min-\\[1200px\\]\\:top-\\[27px\\]{top:27px}.min-\\[1200px\\]\\:right-4{right:calc(var(--spacing) * 4)}}@media (min-width:640px){.sm\\:inline{display:inline}}@container (min-width:380px){.\\@min-\\[380px\\]\\:h-10{height:calc(var(--spacing) * 10)}.\\@min-\\[380px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\\@min-\\[380px\\]\\:flex-row{flex-direction:row}.\\@min-\\[380px\\]\\:justify-start{justify-content:flex-start}.\\@min-\\[380px\\]\\:gap-1\\.5{gap:calc(var(--spacing) * 1.5)}}@container (min-width:440px){.\\@min-\\[440px\\]\\:inline{display:inline}.\\@min-\\[440px\\]\\:px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}}@container (min-width:500px){.\\@min-\\[500px\\]\\:inline{display:inline}.\\@min-\\[500px\\]\\:px-2{padding-inline:calc(var(--spacing) * 2)}}@container (min-width:520px){.\\@min-\\[520px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}:host{all:initial}.cbm-dark{color-scheme:dark;--color-white:oklch(21% .006 285.885);--color-zinc-50:oklch(24.5% .006 286);--color-zinc-100:oklch(27.4% .006 286.033);--color-zinc-200:oklch(33% .01 285.9);--color-zinc-300:oklch(42% .015 285.8);--color-zinc-400:oklch(52% .016 285.9);--color-zinc-500:oklch(68% .015 286.067);--color-zinc-600:oklch(74% .012 286);--color-zinc-700:oklch(82% .008 286.2);--color-zinc-800:oklch(88% .006 286.3);--color-zinc-900:oklch(96.7% .001 286.375);--color-zinc-950:oklch(98.5% 0 0);--color-amber-50:oklch(27.9% .077 45.635);--color-amber-100:oklch(33% .09 46);--color-amber-200:oklch(41.4% .112 45.904);--color-amber-300:oklch(55.5% .163 48.998);--color-amber-700:oklch(87.9% .169 91.605);--color-amber-800:oklch(92.4% .12 95.746);--color-amber-900:oklch(96.2% .059 95.617);--color-amber-950:oklch(98.7% .022 95.277);--color-emerald-50:oklch(26.2% .051 172.552);--color-emerald-800:oklch(90.5% .093 164.15);--color-emerald-900:oklch(95% .052 163.051);--color-sky-50:oklch(29.3% .066 243.157);--color-sky-100:oklch(39.1% .09 240.876);--color-sky-900:oklch(95.1% .026 236.824);--color-red-600:oklch(70.4% .191 22.216);--color-red-700:oklch(80.8% .114 19.571)}.cbm-dark *{--tw-ring-offset-color:oklch(21% .006 285.885)}.theme-scroll{--cbm-thumb:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 55%, var(--color-white))}}.theme-scroll{--cbm-thumb-hover:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb-hover:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 80%, var(--color-white))}}.theme-scroll{scrollbar-width:thin;scrollbar-color:var(--cbm-thumb) transparent}.theme-scroll:hover{scrollbar-color:var(--cbm-thumb-hover) transparent}.theme-scroll::-webkit-scrollbar{width:6px}.theme-scroll::-webkit-scrollbar-track{background:0 0}.theme-scroll::-webkit-scrollbar-thumb{background:var(--cbm-thumb);border-radius:9999px}.theme-scroll:hover::-webkit-scrollbar-thumb{background:var(--cbm-thumb-hover)}.theme-panel{transform-origin:100% 0;animation:.16s ease-out theme-panel-in}@keyframes theme-panel-in{0%{opacity:0;transform:translateY(-6px)scale(.98)}}.theme-hint{animation:.12s ease-out theme-hint-in}.theme-toast{animation:.18s ease-out theme-toast-in}@keyframes theme-toast-in{0%{opacity:0;transform:translateY(8px)}}@keyframes theme-hint-in{0%{opacity:0}}.theme-arrive{animation:.62s cubic-bezier(.34,1.56,.64,1) both theme-arrive}@keyframes theme-arrive{0%{opacity:0;transform:scale(.2)rotate(-45deg)}60%{opacity:1;transform:scale(1.16)rotate(8deg)}to{transform:scale(1)rotate(0)}}.theme-burst-piece{line-height:0;animation:.95s cubic-bezier(.15,.75,.3,1) both theme-burst-piece;display:block;position:absolute;top:50%;left:50%}@keyframes theme-burst-piece{0%{opacity:0;transform:translate(-50%, -50%) rotate(var(--r)) scale(.3)}12%{opacity:1}65%{opacity:1}to{opacity:0;transform:translate(calc(-50% + var(--x)), calc(-50% + var(--y))) rotate(calc(var(--r) + 150deg)) scale(1)}}.theme-piece-dot{background:currentColor;border-radius:9999px;width:6px;height:6px}.theme-piece-dash{background:currentColor;border-radius:9999px;width:10px;height:3px}@media (prefers-reduced-motion:reduce){.theme-panel,.theme-hint,.theme-toast{animation:none}.theme-arrive{animation:.3s ease-out both theme-hint-in}.theme-burst{display:none}}.theme-panel summary{list-style:none}.theme-panel summary::-webkit-details-marker{display:none}.theme-panel details[open]>summary .theme-chevron{transform:rotate(90deg)}@keyframes spin{to{transform:rotate(360deg)}}";
|
|
5096
5666
|
//#endregion
|
|
5097
5667
|
//#region src/ThemeSwitcher.jsx
|
|
5098
5668
|
var CORNERS = {
|
|
@@ -5111,6 +5681,8 @@ var MIN_PANEL_HEIGHT = 260;
|
|
|
5111
5681
|
var DRAG_THRESHOLD = 5;
|
|
5112
5682
|
var HINT_DELAY = 100;
|
|
5113
5683
|
var DOT_INTERVAL = 1500;
|
|
5684
|
+
var INTRO_DELAY = 900;
|
|
5685
|
+
var BURST_TIME = 1100;
|
|
5114
5686
|
var FOCUSABLE = "a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), summary, [tabindex]:not([tabindex=\"-1\"])";
|
|
5115
5687
|
/** Tag of the element that hosts the switcher's shadow root. The site scan skips it. */
|
|
5116
5688
|
var HOST_TAG = "colorsbymax-root";
|
|
@@ -5139,9 +5711,32 @@ function ThemeSwitcher() {
|
|
|
5139
5711
|
return mount && createPortal(/* @__PURE__ */ jsx(Switcher, {}), mount);
|
|
5140
5712
|
}
|
|
5141
5713
|
function Switcher() {
|
|
5142
|
-
const { issues, storageKey, tokens,
|
|
5714
|
+
const { issues, storageKey, tokens, position: requested, setLogoColouring, intro, mode, modeSetting, setMode } = useTheme();
|
|
5143
5715
|
const position = CORNERS[requested] ? requested : "bottom-right";
|
|
5144
5716
|
const [open, setOpen] = useState(false);
|
|
5717
|
+
const [arrived, setArrived] = useState(!intro);
|
|
5718
|
+
const [entering, setEntering] = useState(false);
|
|
5719
|
+
const [burst, setBurst] = useState(0);
|
|
5720
|
+
const celebrate = () => setBurst((n) => n + 1);
|
|
5721
|
+
useEffect(() => {
|
|
5722
|
+
if (arrived) return;
|
|
5723
|
+
const timer = setTimeout(() => {
|
|
5724
|
+
setArrived(true);
|
|
5725
|
+
setEntering(true);
|
|
5726
|
+
celebrate();
|
|
5727
|
+
}, INTRO_DELAY);
|
|
5728
|
+
return () => clearTimeout(timer);
|
|
5729
|
+
}, []);
|
|
5730
|
+
const [bursting, setBursting] = useState(false);
|
|
5731
|
+
useEffect(() => {
|
|
5732
|
+
if (!burst) return;
|
|
5733
|
+
setBursting(true);
|
|
5734
|
+
const timer = setTimeout(() => {
|
|
5735
|
+
setBursting(false);
|
|
5736
|
+
setEntering(false);
|
|
5737
|
+
}, BURST_TIME);
|
|
5738
|
+
return () => clearTimeout(timer);
|
|
5739
|
+
}, [burst]);
|
|
5145
5740
|
const [settings, setSettings] = useState(() => loadSettings(storageKey));
|
|
5146
5741
|
useEffect(() => setLogoColouring(settings.colourLogo), [settings.colourLogo, setLogoColouring]);
|
|
5147
5742
|
const [auditOn, setAuditOn] = useState(false);
|
|
@@ -5179,8 +5774,6 @@ function Switcher() {
|
|
|
5179
5774
|
window.addEventListener("keydown", onKey);
|
|
5180
5775
|
return () => window.removeEventListener("keydown", onKey);
|
|
5181
5776
|
}, []);
|
|
5182
|
-
const prefersDark = usePrefersDark();
|
|
5183
|
-
const mode = settings.mode === "system" ? prefersDark ? "dark" : "light" : settings.mode;
|
|
5184
5777
|
const buttonRef = useRef(null);
|
|
5185
5778
|
const panelRef = useRef(null);
|
|
5186
5779
|
const layerRef = useRef(null);
|
|
@@ -5248,6 +5841,7 @@ function Switcher() {
|
|
|
5248
5841
|
};
|
|
5249
5842
|
useEffect(() => {
|
|
5250
5843
|
const button = buttonRef.current;
|
|
5844
|
+
if (!button) return;
|
|
5251
5845
|
const onEnter = (e) => {
|
|
5252
5846
|
if (e.pointerType === "touch" || e.buttons || open || !settings.draggable) return;
|
|
5253
5847
|
clearTimeout(hintTimer.current);
|
|
@@ -5264,7 +5858,12 @@ function Switcher() {
|
|
|
5264
5858
|
button.removeEventListener("pointerenter", onEnter);
|
|
5265
5859
|
button.removeEventListener("pointerleave", onLeave);
|
|
5266
5860
|
};
|
|
5267
|
-
}, [
|
|
5861
|
+
}, [
|
|
5862
|
+
open,
|
|
5863
|
+
settings.draggable,
|
|
5864
|
+
arrived,
|
|
5865
|
+
settings.hideButton
|
|
5866
|
+
]);
|
|
5268
5867
|
const close = useCallback(() => {
|
|
5269
5868
|
setOpen(false);
|
|
5270
5869
|
buttonRef.current?.focus();
|
|
@@ -5289,7 +5888,6 @@ function Switcher() {
|
|
|
5289
5888
|
if (!d || d.id !== e.pointerId) return;
|
|
5290
5889
|
if (!(e.buttons & 1)) return onDragEnd(e);
|
|
5291
5890
|
if (!d.moved && Math.hypot(e.clientX - d.x, e.clientY - d.y) < DRAG_THRESHOLD) return;
|
|
5292
|
-
if (!d.moved) setOpen(false);
|
|
5293
5891
|
d.moved = true;
|
|
5294
5892
|
d.point = clampToViewport({
|
|
5295
5893
|
left: e.clientX - d.dx,
|
|
@@ -5313,11 +5911,17 @@ function Switcher() {
|
|
|
5313
5911
|
saveButtonPosition(storageKey, null);
|
|
5314
5912
|
};
|
|
5315
5913
|
const storeSettings = (next) => {
|
|
5316
|
-
saveSettings(storageKey,
|
|
5914
|
+
saveSettings(storageKey, {
|
|
5915
|
+
...next,
|
|
5916
|
+
mode: loadSettings(storageKey).mode
|
|
5917
|
+
});
|
|
5317
5918
|
return next;
|
|
5318
5919
|
};
|
|
5319
5920
|
const settingsApi = {
|
|
5320
|
-
settings
|
|
5921
|
+
settings: {
|
|
5922
|
+
...settings,
|
|
5923
|
+
mode: modeSetting
|
|
5924
|
+
},
|
|
5321
5925
|
mode,
|
|
5322
5926
|
audit: {
|
|
5323
5927
|
on: auditOn,
|
|
@@ -5331,36 +5935,20 @@ function Switcher() {
|
|
|
5331
5935
|
}
|
|
5332
5936
|
}
|
|
5333
5937
|
},
|
|
5334
|
-
update: (patch
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5938
|
+
update: ({ mode: nextMode, ...patch }) => {
|
|
5939
|
+
if (nextMode) setMode(nextMode);
|
|
5940
|
+
if (Object.keys(patch).length) setSettings((s) => storeSettings({
|
|
5941
|
+
...s,
|
|
5942
|
+
...patch,
|
|
5943
|
+
mode: nextMode ?? modeSetting
|
|
5944
|
+
}));
|
|
5945
|
+
},
|
|
5946
|
+
reset: () => {
|
|
5947
|
+
setMode(DEFAULT_SETTINGS.mode);
|
|
5948
|
+
setSettings(storeSettings(DEFAULT_SETTINGS));
|
|
5949
|
+
},
|
|
5339
5950
|
resetButton: saved ? resetPosition : null
|
|
5340
5951
|
};
|
|
5341
|
-
const lastMode = useRef(mode);
|
|
5342
|
-
useEffect(() => {
|
|
5343
|
-
if (lastMode.current === mode) return;
|
|
5344
|
-
lastMode.current = mode;
|
|
5345
|
-
if (active.custom) return;
|
|
5346
|
-
if (mode === "dark" && !isDarkTheme(active.tokens)) {
|
|
5347
|
-
const twin = toDark(active);
|
|
5348
|
-
selectTheme(twin.id, twin);
|
|
5349
|
-
} else if (mode === "light" && active.id.endsWith("~dark")) {
|
|
5350
|
-
const lightId = active.id.slice(0, -DARK_SUFFIX.length);
|
|
5351
|
-
const local = themes.find((t) => t.id === lightId);
|
|
5352
|
-
if (local) selectTheme(local.id, local);
|
|
5353
|
-
else loadLibrary().then((lib) => {
|
|
5354
|
-
const t = lib.themes.find((x) => x.id === lightId);
|
|
5355
|
-
if (t) selectTheme(t.id, t);
|
|
5356
|
-
}, () => {});
|
|
5357
|
-
}
|
|
5358
|
-
}, [
|
|
5359
|
-
mode,
|
|
5360
|
-
active,
|
|
5361
|
-
themes,
|
|
5362
|
-
selectTheme
|
|
5363
|
-
]);
|
|
5364
5952
|
useEffect(() => {
|
|
5365
5953
|
if (!open) return;
|
|
5366
5954
|
const panel = panelRef.current;
|
|
@@ -5401,7 +5989,7 @@ function Switcher() {
|
|
|
5401
5989
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
5402
5990
|
};
|
|
5403
5991
|
}, [open, close]);
|
|
5404
|
-
if (settings.hideButton) return null;
|
|
5992
|
+
if (settings.hideButton || !arrived) return null;
|
|
5405
5993
|
return /* @__PURE__ */ jsx(SettingsContext.Provider, {
|
|
5406
5994
|
value: settingsApi,
|
|
5407
5995
|
children: /* @__PURE__ */ jsxs("div", {
|
|
@@ -5421,6 +6009,7 @@ function Switcher() {
|
|
|
5421
6009
|
return;
|
|
5422
6010
|
}
|
|
5423
6011
|
hideHint();
|
|
6012
|
+
celebrate();
|
|
5424
6013
|
if (open) close();
|
|
5425
6014
|
else setOpen(true);
|
|
5426
6015
|
},
|
|
@@ -5430,7 +6019,7 @@ function Switcher() {
|
|
|
5430
6019
|
onPointerCancel: onDragEnd,
|
|
5431
6020
|
onLostPointerCapture: onDragEnd,
|
|
5432
6021
|
style: placed ?? void 0,
|
|
5433
|
-
className: `theme-switcher fixed z-[60] ${placed ? "" : CORNERS[position]} grid place-items-center w-9 h-9 rounded-full border border-zinc-200 bg-white/90 text-zinc-700 backdrop-blur hover:bg-white hover:text-zinc-900 transition-[color,background-color,box-shadow,scale] select-none ${settings.draggable ? "touch-none" : ""} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-2 ${dragPoint ? "scale-110 shadow-xl cursor-grabbing" : "shadow-md cursor-pointer"}`,
|
|
6022
|
+
className: `theme-switcher fixed z-[60] ${placed ? "" : CORNERS[position]} grid place-items-center w-9 h-9 rounded-full border border-zinc-200 bg-white/90 text-zinc-700 backdrop-blur hover:bg-white hover:text-zinc-900 transition-[color,background-color,box-shadow,scale] select-none ${settings.draggable ? "touch-none" : ""} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-2 ${dragPoint ? "scale-110 shadow-xl cursor-grabbing" : "shadow-md cursor-pointer"} ${entering ? "theme-arrive" : ""}`,
|
|
5434
6023
|
children: [
|
|
5435
6024
|
/* @__PURE__ */ jsx(Palette, {
|
|
5436
6025
|
className: "w-4 h-4",
|
|
@@ -5440,6 +6029,7 @@ function Switcher() {
|
|
|
5440
6029
|
tokens,
|
|
5441
6030
|
animate: settings.animateDot
|
|
5442
6031
|
}),
|
|
6032
|
+
bursting && /* @__PURE__ */ jsx(Burst, { tokens }, burst),
|
|
5443
6033
|
hint && !open && !dragPoint && /* @__PURE__ */ jsx("span", {
|
|
5444
6034
|
"aria-hidden": "true",
|
|
5445
6035
|
className: `theme-hint pointer-events-none absolute top-1/2 -translate-y-1/2 whitespace-nowrap rounded-md bg-zinc-900 px-2 py-1 text-[11px] font-medium text-white shadow-lg ${hintOnLeft ? "right-full mr-2" : "left-full ml-2"}`,
|
|
@@ -5517,6 +6107,59 @@ function CyclingDot({ tokens, animate }) {
|
|
|
5517
6107
|
style: { backgroundColor: still ? tokens.primary : colours[index % colours.length] }
|
|
5518
6108
|
});
|
|
5519
6109
|
}
|
|
6110
|
+
var BURST_KEYS = [
|
|
6111
|
+
"primary",
|
|
6112
|
+
"primary-alt",
|
|
6113
|
+
"data-1",
|
|
6114
|
+
"data-2",
|
|
6115
|
+
"data-3",
|
|
6116
|
+
"data-4",
|
|
6117
|
+
"warning",
|
|
6118
|
+
"data-5"
|
|
6119
|
+
];
|
|
6120
|
+
var BURST_PIECES = Array.from({ length: 12 }, (_, i) => {
|
|
6121
|
+
const angle = (i * 30 + (i % 2 ? 9 : -6)) * (Math.PI / 180);
|
|
6122
|
+
const distance = 30 + i % 3 * 9;
|
|
6123
|
+
return {
|
|
6124
|
+
x: Math.round(Math.cos(angle) * distance),
|
|
6125
|
+
y: Math.round(Math.sin(angle) * distance),
|
|
6126
|
+
r: Math.round(angle * 180 / Math.PI),
|
|
6127
|
+
shape: [
|
|
6128
|
+
"squiggle",
|
|
6129
|
+
"dot",
|
|
6130
|
+
"dash"
|
|
6131
|
+
][i % 3],
|
|
6132
|
+
delay: i % 4 * 25
|
|
6133
|
+
};
|
|
6134
|
+
});
|
|
6135
|
+
function Burst({ tokens }) {
|
|
6136
|
+
return /* @__PURE__ */ jsx("span", {
|
|
6137
|
+
"aria-hidden": "true",
|
|
6138
|
+
className: "theme-burst pointer-events-none absolute inset-0",
|
|
6139
|
+
children: BURST_PIECES.map((p, i) => /* @__PURE__ */ jsx("span", {
|
|
6140
|
+
className: `theme-burst-piece ${p.shape === "squiggle" ? "" : `theme-piece-${p.shape}`}`,
|
|
6141
|
+
style: {
|
|
6142
|
+
"--x": `${p.x}px`,
|
|
6143
|
+
"--y": `${p.y}px`,
|
|
6144
|
+
"--r": `${p.r}deg`,
|
|
6145
|
+
color: tokens[BURST_KEYS[i % BURST_KEYS.length]],
|
|
6146
|
+
animationDelay: `${p.delay}ms`
|
|
6147
|
+
},
|
|
6148
|
+
children: p.shape === "squiggle" && /* @__PURE__ */ jsx("svg", {
|
|
6149
|
+
width: "16",
|
|
6150
|
+
height: "10",
|
|
6151
|
+
viewBox: "0 0 16 10",
|
|
6152
|
+
fill: "none",
|
|
6153
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
6154
|
+
d: "M1 5c1.6-3.6 3.4-3.6 4.6 0s3 3.6 4.6 0 3-3.6 4.4 0",
|
|
6155
|
+
stroke: "currentColor",
|
|
6156
|
+
strokeWidth: "2.2",
|
|
6157
|
+
strokeLinecap: "round"
|
|
6158
|
+
})
|
|
6159
|
+
})
|
|
6160
|
+
}, i))
|
|
6161
|
+
});
|
|
6162
|
+
}
|
|
5520
6163
|
function usePrefersReducedMotion() {
|
|
5521
6164
|
const query = "(prefers-reduced-motion: reduce)";
|
|
5522
6165
|
const [reduce, setReduce] = useState(() => window.matchMedia(query).matches);
|
|
@@ -5758,4 +6401,4 @@ function ResizeHandles({ free, onStart, onMove, onEnd, onReset, active }) {
|
|
|
5758
6401
|
}, Object.keys(edges).join("-")));
|
|
5759
6402
|
}
|
|
5760
6403
|
//#endregion
|
|
5761
|
-
export { PAIRINGS as A, TOKEN_KEYS as C, MIN_CONTRAST_NON_TEXT as D, MIN_CONTRAST_LARGE_TEXT as E, contrastRatio as F, normalizeHex as I, checkTheme as M, fixAll as N, MIN_CONTRAST_TEXT as O, suggestFix as P, TOKEN_GROUPS as S, deriveAppTokens as T, darkTokens as _,
|
|
6404
|
+
export { PAIRINGS as A, TOKEN_KEYS as C, MIN_CONTRAST_NON_TEXT as D, MIN_CONTRAST_LARGE_TEXT as E, contrastRatio as F, normalizeHex as I, checkTheme as M, fixAll as N, MIN_CONTRAST_TEXT as O, suggestFix as P, TOKEN_GROUPS as S, deriveAppTokens as T, darkTokens as _, themeFromPalette as a, BASE_TOKENS as b, useTheme as c, DEFAULT_SETTINGS as d, collectColors as f, themeFromRoles as g, suggestThemes as h, rolesFromPalette as i, checkRamp as j, MIN_RAMP_STEP_DELTA_E as k, DEFAULT_STORAGE_KEY as l, inferRoles as m, coloursFromFile as n, ThemeProvider as o, detectSiteName as p, dominantColours as r, applyTokens as s, ThemeSwitcher as t, prePaintScript as u, isDarkTheme as v, completeTokens as w, PRESETS as x, toDark as y };
|