colorsbymax 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +212 -37
- package/dist/{ThemeSwitcher-CnxUWdBP.js → ThemeSwitcher-L1GXY55E.js} +1139 -51
- package/dist/auto.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +113 -112
- package/types/index.d.ts +13 -0
|
@@ -1170,7 +1170,7 @@ function inferRoles(colors) {
|
|
|
1170
1170
|
const readable = (c, min) => contrastRatio(c.hex, background) >= min;
|
|
1171
1171
|
const ink = (byText.find((c) => readable(c, 4.5)) ?? byText[0])?.hex ?? "#1f2937";
|
|
1172
1172
|
const inkSecondary = byText.find((c) => c.hex !== ink && deltaE(c.hex, ink) > 8 && readable(c, 3))?.hex;
|
|
1173
|
-
const accents = colors.map((c) => {
|
|
1173
|
+
const accents = colors.filter((c) => c.hex !== ink && c.hex !== inkSecondary).map((c) => {
|
|
1174
1174
|
const [h, s, l] = toHsl(c.hex);
|
|
1175
1175
|
const share = c.bg / BG + c.text / TEXT + c.border / BORDER * .5;
|
|
1176
1176
|
return {
|
|
@@ -1289,7 +1289,25 @@ function suggestThemes(scan, siteName, library = []) {
|
|
|
1289
1289
|
//#region src/recolour.js
|
|
1290
1290
|
var ATTR = "data-cbm";
|
|
1291
1291
|
/** Elements the engine never touches: the switcher itself. */
|
|
1292
|
-
var SKIP = "colorsbymax-root";
|
|
1292
|
+
var SKIP$1 = "colorsbymax-root";
|
|
1293
|
+
/**
|
|
1294
|
+
* How colorsbymax finds a site's logo, to keep it in its own colours: an explicit
|
|
1295
|
+
* data-colorsbymax-logo, "logo" in a class, id or label (but not "logout"), or common brand classes.
|
|
1296
|
+
*/
|
|
1297
|
+
var LOGO_SELECTOR = [
|
|
1298
|
+
"[data-colorsbymax-logo]",
|
|
1299
|
+
"[class~=\"logo\" i]",
|
|
1300
|
+
"[class*=\"logo-\" i]:not([class*=\"logout\" i])",
|
|
1301
|
+
"[class*=\"-logo\" i]",
|
|
1302
|
+
"[class*=\"_logo\" i]",
|
|
1303
|
+
"[class*=\"Logo\"]:not([class*=\"Logout\"])",
|
|
1304
|
+
"[id*=\"logo\" i]:not([id*=\"logout\" i])",
|
|
1305
|
+
"[aria-label*=\"logo\" i]",
|
|
1306
|
+
".brand",
|
|
1307
|
+
".navbar-brand",
|
|
1308
|
+
".site-title",
|
|
1309
|
+
".site-brand"
|
|
1310
|
+
].join(", ");
|
|
1293
1311
|
/** Below this HSL saturation a colour counts as a grey and follows the background→text scale. */
|
|
1294
1312
|
var NEUTRAL_SATURATION = .12;
|
|
1295
1313
|
/** Colours within this many degrees of a brand hue are shades of it and follow the theme's version. */
|
|
@@ -1320,9 +1338,28 @@ var SIDES = [
|
|
|
1320
1338
|
"bottom",
|
|
1321
1339
|
"left"
|
|
1322
1340
|
];
|
|
1341
|
+
/**
|
|
1342
|
+
* Contrast a swapped pair aims for (WCAG AA): 4.5:1 for text, 3:1 for icons. Never more than the
|
|
1343
|
+
* site's own original pair had, so deliberately soft text and icons stay soft.
|
|
1344
|
+
*/
|
|
1345
|
+
var MIN_TEXT_CONTRAST = 4.5;
|
|
1346
|
+
var MIN_ICON_CONTRAST = 3;
|
|
1347
|
+
/** Painted colours that sit on a background, so are checked against it. */
|
|
1348
|
+
var FOREGROUND = /* @__PURE__ */ new Set([
|
|
1349
|
+
"color",
|
|
1350
|
+
"fill",
|
|
1351
|
+
"stroke"
|
|
1352
|
+
]);
|
|
1323
1353
|
/** Whether a background-image has any colours in it (gradients), rather than just images. */
|
|
1324
1354
|
var HAS_COLOUR = new RegExp(COLOR_IN_GRADIENT.source, "i");
|
|
1325
1355
|
var clamp$1 = (n, lo = 0, hi = 1) => Math.min(hi, Math.max(lo, n));
|
|
1356
|
+
/** Whether an element has text of its own, rather than only icons or child elements. */
|
|
1357
|
+
var hasOwnText = (el) => [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim());
|
|
1358
|
+
/**
|
|
1359
|
+
* Whether a background image covers the element, so it counts as what's behind the element's
|
|
1360
|
+
* content. Decorative strips (e.g. an animated underline sized "0% 2px") don't.
|
|
1361
|
+
*/
|
|
1362
|
+
var fillsElement = (cs) => Boolean(cs.backgroundImage && cs.backgroundImage !== "none") && cs.backgroundSize.split(",").some((size) => /^(auto|cover|contain|100% 100%|auto auto|100%)$/.test(size.trim()));
|
|
1326
1363
|
var hueDist = (a, b) => Math.min(Math.abs(a - b), 360 - Math.abs(a - b));
|
|
1327
1364
|
/** How far hue `a` is from `b`, signed, in -180..180. */
|
|
1328
1365
|
var signedHue = (a, b) => (a - b + 540) % 360 - 180;
|
|
@@ -1369,29 +1406,46 @@ function createRecolourer() {
|
|
|
1369
1406
|
const site = themeFromRoles(inferRoles(collectColors()).roles);
|
|
1370
1407
|
const siteBgL = hslOf$1(site.background)[2];
|
|
1371
1408
|
const siteInkL = hslOf$1(site.ink)[2];
|
|
1372
|
-
/** Each distinct painted value, keyed "prop|value". */
|
|
1409
|
+
/** Each distinct painted value, keyed "prop|value", plus the background under text and icons. */
|
|
1373
1410
|
const entries = /* @__PURE__ */ new Map();
|
|
1374
1411
|
let theme = null;
|
|
1375
|
-
const idFor = (prop, value, kind) => {
|
|
1376
|
-
const key = `${prop}|${value}`;
|
|
1412
|
+
const idFor = (prop, value, kind, backdrop = null, role = null) => {
|
|
1413
|
+
const key = `${prop}|${value}|${backdrop ?? ""}|${role ?? ""}`;
|
|
1377
1414
|
let entry = entries.get(key);
|
|
1378
1415
|
if (!entry) {
|
|
1379
1416
|
entry = {
|
|
1380
1417
|
id: `c${entries.size.toString(36)}`,
|
|
1381
1418
|
prop,
|
|
1382
1419
|
value,
|
|
1383
|
-
kind
|
|
1420
|
+
kind,
|
|
1421
|
+
backdrop,
|
|
1422
|
+
role
|
|
1384
1423
|
};
|
|
1385
1424
|
entries.set(key, entry);
|
|
1386
|
-
if (theme)
|
|
1425
|
+
if (theme) pendingRules += rule(entry);
|
|
1387
1426
|
}
|
|
1388
1427
|
return entry.id;
|
|
1389
1428
|
};
|
|
1429
|
+
let backdrops = /* @__PURE__ */ new WeakMap();
|
|
1430
|
+
const backdropOf = (el) => {
|
|
1431
|
+
if (!el || el.nodeType !== 1) return "rgb(255, 255, 255)";
|
|
1432
|
+
if (backdrops.has(el)) return backdrops.get(el);
|
|
1433
|
+
const cs = getComputedStyle(el);
|
|
1434
|
+
const own = parse(cs.backgroundColor);
|
|
1435
|
+
let value;
|
|
1436
|
+
if (own && own.alpha >= .5) value = cs.backgroundColor;
|
|
1437
|
+
else if (fillsElement(cs) && HAS_COLOUR.test(cs.backgroundImage)) value = cs.backgroundImage.match(HAS_COLOUR)[0];
|
|
1438
|
+
else value = el === document.documentElement ? "rgb(255, 255, 255)" : backdropOf(el.parentElement);
|
|
1439
|
+
backdrops.set(el, value);
|
|
1440
|
+
return value;
|
|
1441
|
+
};
|
|
1390
1442
|
const read = (el) => {
|
|
1391
1443
|
const cs = getComputedStyle(el);
|
|
1392
1444
|
const ids = [];
|
|
1445
|
+
const role = el instanceof SVGElement || !hasOwnText(el) ? "icon" : "text";
|
|
1393
1446
|
const colour = (prop, css, kind) => {
|
|
1394
|
-
if (parse(css))
|
|
1447
|
+
if (!parse(css)) return;
|
|
1448
|
+
ids.push(FOREGROUND.has(prop) ? idFor(prop, css, kind, backdropOf(el), prop === "color" ? role : "icon") : idFor(prop, css, kind));
|
|
1395
1449
|
};
|
|
1396
1450
|
colour("background-color", cs.backgroundColor, "bg");
|
|
1397
1451
|
colour("color", cs.color, "text");
|
|
@@ -1408,23 +1462,33 @@ function createRecolourer() {
|
|
|
1408
1462
|
return ids;
|
|
1409
1463
|
};
|
|
1410
1464
|
/** Tags `elements` (and optionally their descendants) with the colours they paint. */
|
|
1465
|
+
let pendingRules = "";
|
|
1411
1466
|
const tag = (roots, deep) => {
|
|
1467
|
+
backdrops = /* @__PURE__ */ new WeakMap();
|
|
1412
1468
|
withoutAppliedTheme(() => {
|
|
1413
1469
|
for (const root of roots) {
|
|
1414
|
-
if (!(root instanceof Element) || root.closest(
|
|
1470
|
+
if (!(root instanceof Element) || root.closest(skip)) continue;
|
|
1415
1471
|
const all = deep ? [root, ...root.querySelectorAll("*")] : [root];
|
|
1416
1472
|
for (const el of all) {
|
|
1417
|
-
if (el.localName === SKIP) continue;
|
|
1473
|
+
if (el.localName === SKIP$1) continue;
|
|
1474
|
+
if (el.closest(skip)) {
|
|
1475
|
+
el.removeAttribute(ATTR);
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1418
1478
|
const ids = read(el);
|
|
1419
1479
|
if (ids.length) el.setAttribute(ATTR, ids.join(" "));
|
|
1420
1480
|
else el.removeAttribute(ATTR);
|
|
1421
1481
|
}
|
|
1422
1482
|
}
|
|
1423
1483
|
});
|
|
1484
|
+
if (pendingRules) {
|
|
1485
|
+
sheet.textContent += pendingRules;
|
|
1486
|
+
pendingRules = "";
|
|
1487
|
+
}
|
|
1424
1488
|
};
|
|
1425
|
-
const
|
|
1489
|
+
const mapHex = (css, kind) => {
|
|
1426
1490
|
const c = parse(css);
|
|
1427
|
-
if (!c) return
|
|
1491
|
+
if (!c) return null;
|
|
1428
1492
|
const [, s, l] = hslOf$1(c.hex);
|
|
1429
1493
|
let hex;
|
|
1430
1494
|
if (s < NEUTRAL_SATURATION) {
|
|
@@ -1462,14 +1526,65 @@ function createRecolourer() {
|
|
|
1462
1526
|
clamp$1(lightness)
|
|
1463
1527
|
]));
|
|
1464
1528
|
}
|
|
1465
|
-
|
|
1529
|
+
return {
|
|
1530
|
+
hex,
|
|
1531
|
+
alpha: c.alpha
|
|
1532
|
+
};
|
|
1533
|
+
};
|
|
1534
|
+
const format = ({ hex, alpha }) => {
|
|
1535
|
+
if (alpha >= 1) return hex;
|
|
1466
1536
|
const [r, g, b] = hexToRgb(hex);
|
|
1467
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
1537
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
1538
|
+
};
|
|
1539
|
+
const mapColour = (css, kind) => {
|
|
1540
|
+
const mapped = mapHex(css, kind);
|
|
1541
|
+
return mapped ? format(mapped) : css;
|
|
1542
|
+
};
|
|
1543
|
+
const readable = (fg, bg, target, lighter) => {
|
|
1544
|
+
const ratio = (c) => contrastRatio(c, bg);
|
|
1545
|
+
if (ratio(fg) >= target - .05) return fg;
|
|
1546
|
+
const pool = [
|
|
1547
|
+
fg,
|
|
1548
|
+
theme.background,
|
|
1549
|
+
theme.surface,
|
|
1550
|
+
theme.ink,
|
|
1551
|
+
theme["on-primary"],
|
|
1552
|
+
theme["on-secondary"],
|
|
1553
|
+
"#ffffff",
|
|
1554
|
+
"#000000"
|
|
1555
|
+
];
|
|
1556
|
+
const sameSide = pool.filter((c) => luminance(c) > luminance(bg) === lighter);
|
|
1557
|
+
const nearest = (list) => list.reduce((a, c) => deltaE(c, fg) < deltaE(a, fg) ? c : a);
|
|
1558
|
+
const strongest = (list) => list.reduce((a, c) => ratio(c) > ratio(a) ? c : a);
|
|
1559
|
+
const passing = sameSide.filter((c) => ratio(c) >= target);
|
|
1560
|
+
if (passing.length) return nearest(passing);
|
|
1561
|
+
if (sameSide.length && ratio(strongest(sameSide)) >= Math.min(target, MIN_ICON_CONTRAST)) return strongest(sameSide);
|
|
1562
|
+
const flipped = pool.filter((c) => ratio(c) >= target);
|
|
1563
|
+
return flipped.length ? nearest(flipped) : strongest(pool);
|
|
1468
1564
|
};
|
|
1469
1565
|
const rule = (entry) => {
|
|
1470
|
-
|
|
1566
|
+
let value;
|
|
1567
|
+
if (entry.prop === "background-image") value = entry.value.replace(COLOR_IN_GRADIENT, (stop) => mapColour(stop, entry.kind));
|
|
1568
|
+
else if (entry.backdrop) {
|
|
1569
|
+
const fg = mapHex(entry.value, entry.kind);
|
|
1570
|
+
const bg = mapHex(entry.backdrop, "bg");
|
|
1571
|
+
const original = {
|
|
1572
|
+
fg: parse(entry.value)?.hex,
|
|
1573
|
+
bg: parse(entry.backdrop)?.hex
|
|
1574
|
+
};
|
|
1575
|
+
if (fg && bg && original.fg && original.bg) {
|
|
1576
|
+
const aim = entry.role === "text" ? MIN_TEXT_CONTRAST : MIN_ICON_CONTRAST;
|
|
1577
|
+
const target = Math.min(aim, contrastRatio(original.fg, original.bg));
|
|
1578
|
+
const lighter = luminance(original.fg) > luminance(original.bg);
|
|
1579
|
+
value = format({
|
|
1580
|
+
...fg,
|
|
1581
|
+
hex: readable(fg.hex, bg.hex, target, lighter)
|
|
1582
|
+
});
|
|
1583
|
+
} else value = mapColour(entry.value, entry.kind);
|
|
1584
|
+
} else value = mapColour(entry.value, entry.kind);
|
|
1471
1585
|
return `[${ATTR}~="${entry.id}"]{${entry.prop}:${value}!important}`;
|
|
1472
1586
|
};
|
|
1587
|
+
let skip = `${SKIP$1}, ${LOGO_SELECTOR}`;
|
|
1473
1588
|
tag([document.body], true);
|
|
1474
1589
|
const observer = new MutationObserver((records) => {
|
|
1475
1590
|
const added = [];
|
|
@@ -1481,7 +1596,7 @@ function createRecolourer() {
|
|
|
1481
1596
|
if (r.type === "attributes") changed.add(r.target);
|
|
1482
1597
|
}
|
|
1483
1598
|
if (added.length) tag(added, true);
|
|
1484
|
-
if (changed.size) tag([...changed],
|
|
1599
|
+
if (changed.size) tag([...changed], true);
|
|
1485
1600
|
});
|
|
1486
1601
|
observer.observe(document.body, {
|
|
1487
1602
|
subtree: true,
|
|
@@ -1496,6 +1611,13 @@ function createRecolourer() {
|
|
|
1496
1611
|
theme = tokens;
|
|
1497
1612
|
sheet.textContent = tokens ? [...entries.values()].map(rule).join("") : "";
|
|
1498
1613
|
},
|
|
1614
|
+
/** Whether the logo is re-coloured with the rest (off keeps it in its own colours). */
|
|
1615
|
+
setLogoColouring(on) {
|
|
1616
|
+
const next = on ? SKIP$1 : `${SKIP$1}, ${LOGO_SELECTOR}`;
|
|
1617
|
+
if (next === skip) return;
|
|
1618
|
+
skip = next;
|
|
1619
|
+
tag([document.body], true);
|
|
1620
|
+
},
|
|
1499
1621
|
stop() {
|
|
1500
1622
|
observer.disconnect();
|
|
1501
1623
|
sheet.remove();
|
|
@@ -1650,6 +1772,8 @@ var newId = () => `custom-${Date.now().toString(36)}-${Math.random().toString(36
|
|
|
1650
1772
|
* Where each token is used on this site, shown in the editors
|
|
1651
1773
|
* @property {boolean} [scrollbars] Colour the page's scrollbars from the theme (default true)
|
|
1652
1774
|
* @property {() => Promise<any>} [pdf] Enables PDF uploads: pass `loadPdf` from 'colorsbymax/pdf'
|
|
1775
|
+
* @property {boolean} [hidden] Hide the colour button and panel; the theme still applies. Use
|
|
1776
|
+
* `hidden: import.meta.env.PROD` to keep it out of production once the colours are chosen.
|
|
1653
1777
|
* @property {'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'} [position] Where the colour
|
|
1654
1778
|
* button starts (default 'bottom-right'). 'top-right' sits under a floating nav bar.
|
|
1655
1779
|
* @property {boolean | 'auto'} [recolour] Re-colour a site that doesn't paint with the --color-*
|
|
@@ -1675,20 +1799,28 @@ function resolveSite(config, pageColours) {
|
|
|
1675
1799
|
tokens: completeTokens(sanitizeTokens(t.tokens), defaultTheme.tokens)
|
|
1676
1800
|
}));
|
|
1677
1801
|
const fixes = fixAll(defaultTheme.tokens);
|
|
1802
|
+
const accessible = Object.keys(fixes).length ? [{
|
|
1803
|
+
id: "site-accessible",
|
|
1804
|
+
name: `${defaultTheme.name} (accessible)`,
|
|
1805
|
+
site: true,
|
|
1806
|
+
tokens: {
|
|
1807
|
+
...defaultTheme.tokens,
|
|
1808
|
+
...fixes
|
|
1809
|
+
}
|
|
1810
|
+
}] : [];
|
|
1811
|
+
const pageOriginal = pageColours && config.defaultTheme ? [{
|
|
1812
|
+
id: "site-original",
|
|
1813
|
+
name: `${siteName} original`,
|
|
1814
|
+
site: true,
|
|
1815
|
+
tokens: pageColours
|
|
1816
|
+
}] : [];
|
|
1678
1817
|
return {
|
|
1679
1818
|
siteName,
|
|
1680
1819
|
defaultTheme,
|
|
1681
1820
|
siteThemes: [
|
|
1682
1821
|
defaultTheme,
|
|
1683
|
-
...
|
|
1684
|
-
|
|
1685
|
-
name: `${defaultTheme.name} (accessible)`,
|
|
1686
|
-
site: true,
|
|
1687
|
-
tokens: {
|
|
1688
|
-
...defaultTheme.tokens,
|
|
1689
|
-
...fixes
|
|
1690
|
-
}
|
|
1691
|
-
}] : [],
|
|
1822
|
+
...accessible,
|
|
1823
|
+
...pageOriginal,
|
|
1692
1824
|
...extra
|
|
1693
1825
|
]
|
|
1694
1826
|
};
|
|
@@ -1739,7 +1871,23 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
1739
1871
|
recolourer.current = null;
|
|
1740
1872
|
};
|
|
1741
1873
|
}, [recolourMode]);
|
|
1742
|
-
const
|
|
1874
|
+
const [logoColouring, setLogoColouring] = useState(false);
|
|
1875
|
+
useLayoutEffect(() => {
|
|
1876
|
+
recolourer.current?.setLogoColouring(logoColouring);
|
|
1877
|
+
}, [logoColouring, pageColours]);
|
|
1878
|
+
useLayoutEffect(() => {
|
|
1879
|
+
if (logoColouring || pageColours) return;
|
|
1880
|
+
const style = document.createElement("style");
|
|
1881
|
+
style.dataset.colorsbymax = "logo";
|
|
1882
|
+
style.textContent = `:is(${LOGO_SELECTOR}){${TOKEN_KEYS.map((k) => `--color-${k}:${defaultTheme.tokens[k]}`).join(";")}}`;
|
|
1883
|
+
document.head.append(style);
|
|
1884
|
+
return () => style.remove();
|
|
1885
|
+
}, [
|
|
1886
|
+
logoColouring,
|
|
1887
|
+
pageColours,
|
|
1888
|
+
defaultTheme
|
|
1889
|
+
]);
|
|
1890
|
+
const original = (base.id === "site-original" || base.id === defaultTheme.id && !initialConfig.defaultTheme) && !Object.keys(state.overrides).length;
|
|
1743
1891
|
useLayoutEffect(() => {
|
|
1744
1892
|
recolourer.current?.apply(original ? null : tokens);
|
|
1745
1893
|
}, [
|
|
@@ -1788,8 +1936,12 @@ function ThemeProvider({ config = {}, children }) {
|
|
|
1788
1936
|
usage: initialConfig.usage ?? {},
|
|
1789
1937
|
loadPdf: initialConfig.pdf ?? null,
|
|
1790
1938
|
position: initialConfig.position ?? "bottom-right",
|
|
1939
|
+
/** True when the config hides the switcher (e.g. in production); the theme still applies. */
|
|
1940
|
+
hidden: Boolean(initialConfig.hidden),
|
|
1791
1941
|
/** True when colorsbymax is swapping the page's own colours (the site isn't wired to tokens). */
|
|
1792
1942
|
recolouring: Boolean(pageColours),
|
|
1943
|
+
/** Turns re-colouring of the site's logo on or off (the switcher's "Colour the logo" setting). */
|
|
1944
|
+
setLogoColouring,
|
|
1793
1945
|
siteThemes,
|
|
1794
1946
|
defaultTheme,
|
|
1795
1947
|
presets: PRESETS,
|
|
@@ -2131,6 +2283,23 @@ var Wand2 = icon("wand-sparkles", [
|
|
|
2131
2283
|
["path", { "d": "M11 3H9" }]
|
|
2132
2284
|
]);
|
|
2133
2285
|
var X = icon("x", [["path", { "d": "M18 6 6 18" }], ["path", { "d": "m6 6 12 12" }]]);
|
|
2286
|
+
var ScanSearch = icon("scan-search", [
|
|
2287
|
+
["path", { "d": "M3 7V5a2 2 0 0 1 2-2h2" }],
|
|
2288
|
+
["path", { "d": "M17 3h2a2 2 0 0 1 2 2v2" }],
|
|
2289
|
+
["path", { "d": "M21 17v2a2 2 0 0 1-2 2h-2" }],
|
|
2290
|
+
["path", { "d": "M7 21H5a2 2 0 0 1-2-2v-2" }],
|
|
2291
|
+
["circle", {
|
|
2292
|
+
"cx": "12",
|
|
2293
|
+
"cy": "12",
|
|
2294
|
+
"r": "3"
|
|
2295
|
+
}],
|
|
2296
|
+
["path", { "d": "m16 16-1.9-1.9" }]
|
|
2297
|
+
]);
|
|
2298
|
+
var Contrast = icon("contrast", [["circle", {
|
|
2299
|
+
"cx": "12",
|
|
2300
|
+
"cy": "12",
|
|
2301
|
+
"r": "10"
|
|
2302
|
+
}], ["path", { "d": "M12 18a6 6 0 0 0 0-12v12z" }]]);
|
|
2134
2303
|
/** Images are scaled down to at most this many pixels on their longest side before sampling. */
|
|
2135
2304
|
var SAMPLE_SIZE = 200;
|
|
2136
2305
|
/** PDF pages read, from the first. */
|
|
@@ -2332,6 +2501,26 @@ function themeFromPalette(colours) {
|
|
|
2332
2501
|
};
|
|
2333
2502
|
}
|
|
2334
2503
|
//#endregion
|
|
2504
|
+
//#region src/finish.js
|
|
2505
|
+
/** The production check for Vite projects; other bundlers use NODE_ENV. */
|
|
2506
|
+
var VITE_PROD = "import.meta.env.PROD";
|
|
2507
|
+
var NODE_PROD = "process.env.NODE_ENV === 'production'";
|
|
2508
|
+
var tokenLines = (tokens, indent) => TOKEN_KEYS.map((k) => `${indent}'${k}': '${tokens[k]}',`).join("\n");
|
|
2509
|
+
/**
|
|
2510
|
+
* Code that makes the chosen colours the site's default and hides the switcher in production.
|
|
2511
|
+
* @param {'auto' | 'provider'} kind the one-line setup (autoMount) or ThemeProvider
|
|
2512
|
+
*/
|
|
2513
|
+
function keepSnippet(kind, name, tokens, prod = VITE_PROD) {
|
|
2514
|
+
const theme = `defaultTheme: {\n name: ${JSON.stringify(name)},\n tokens: {\n${tokenLines(tokens, " ")}\n },\n },\n // Hides the colour button in production; set to false to bring it back.\n hidden: ${prod},`;
|
|
2515
|
+
if (kind === "auto") return `// Replace \`import 'colorsbymax/auto'\` with:\nimport { autoMount } from 'colorsbymax/auto'\n\nautoMount({\n ${theme}\n})`;
|
|
2516
|
+
return `// Add to the config you pass to <ThemeProvider>:\n<ThemeProvider config={{\n ...config,\n ${theme}\n}}>`;
|
|
2517
|
+
}
|
|
2518
|
+
/** The theme as `--color-*` variables on :root. */
|
|
2519
|
+
var cssSnippet = (tokens) => `:root {\n${TOKEN_KEYS.map((k) => ` --color-${k}: ${tokens[k]};`).join("\n")}\n}`;
|
|
2520
|
+
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
|
+
/** @param {boolean} recolouring true when colorsbymax was swapping the site's hard-coded colours */
|
|
2522
|
+
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
|
+
//#endregion
|
|
2335
2524
|
//#region src/settings.js
|
|
2336
2525
|
/**
|
|
2337
2526
|
* @typedef {Object} PanelSettings
|
|
@@ -2346,6 +2535,8 @@ function themeFromPalette(colours) {
|
|
|
2346
2535
|
* @property {number | null} panelWidth Pixels; null is the standard width
|
|
2347
2536
|
* @property {number | 'full' | null} panelHeight Pixels, 'full' for all the room there is, or null to fit the content
|
|
2348
2537
|
* @property {boolean} libraryCollapsed Whether the library's category chips are folded away
|
|
2538
|
+
* @property {boolean} colourLogo Whether themes re-colour the site's logo too (off keeps its own colours)
|
|
2539
|
+
* @property {boolean} hideButton Hidden on this device from the finish screen (Alt+Shift+C brings it back)
|
|
2349
2540
|
*/
|
|
2350
2541
|
/** @type {PanelSettings} */
|
|
2351
2542
|
var DEFAULT_SETTINGS = {
|
|
@@ -2359,7 +2550,9 @@ var DEFAULT_SETTINGS = {
|
|
|
2359
2550
|
animateDot: true,
|
|
2360
2551
|
panelWidth: null,
|
|
2361
2552
|
panelHeight: null,
|
|
2362
|
-
libraryCollapsed: false
|
|
2553
|
+
libraryCollapsed: false,
|
|
2554
|
+
colourLogo: false,
|
|
2555
|
+
hideButton: false
|
|
2363
2556
|
};
|
|
2364
2557
|
/** Size presets offered in settings; dragging an edge gives a custom size instead. */
|
|
2365
2558
|
var PANEL_PRESETS = [
|
|
@@ -2483,7 +2676,7 @@ function useSavedToYours() {
|
|
|
2483
2676
|
}
|
|
2484
2677
|
function ThemePanel() {
|
|
2485
2678
|
const theme = useTheme();
|
|
2486
|
-
const { settings, mode } = useSettings();
|
|
2679
|
+
const { settings, mode, audit } = useSettings();
|
|
2487
2680
|
const overrideCount = Object.keys(theme.state.overrides).length;
|
|
2488
2681
|
const [view, setView] = useState("main");
|
|
2489
2682
|
const returnFocus = useRef(null);
|
|
@@ -2546,21 +2739,35 @@ function ThemePanel() {
|
|
|
2546
2739
|
}), /* @__PURE__ */ jsx("p", {
|
|
2547
2740
|
className: "text-[11px] text-zinc-500",
|
|
2548
2741
|
children: "by mrmaxdesigns"
|
|
2549
|
-
})] }), /* @__PURE__ */
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2742
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
2743
|
+
className: "flex items-center gap-1",
|
|
2744
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
2745
|
+
type: "button",
|
|
2746
|
+
onClick: audit.toggle,
|
|
2747
|
+
className: `${btn} px-2 ${audit.on ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
|
|
2748
|
+
"aria-pressed": audit.on,
|
|
2749
|
+
"data-tip": "Audit the page: point out what won’t look right with these colours",
|
|
2750
|
+
children: [/* @__PURE__ */ jsx(ScanSearch, {
|
|
2751
|
+
className: "w-4 h-4",
|
|
2752
|
+
"aria-hidden": "true"
|
|
2753
|
+
}), "Audit"]
|
|
2754
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2755
|
+
type: "button",
|
|
2756
|
+
onClick: (e) => view === "settings" ? setView("main") : showView("settings", e.currentTarget),
|
|
2757
|
+
className: `${btn} px-1.5 ${view === "settings" ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
|
|
2758
|
+
"aria-label": "Panel settings",
|
|
2759
|
+
"aria-pressed": view === "settings",
|
|
2760
|
+
"data-tip": "Panel settings",
|
|
2761
|
+
children: /* @__PURE__ */ jsx(Settings, {
|
|
2762
|
+
className: "w-4 h-4",
|
|
2763
|
+
"aria-hidden": "true"
|
|
2764
|
+
})
|
|
2765
|
+
})]
|
|
2560
2766
|
})]
|
|
2561
2767
|
}),
|
|
2562
2768
|
view === "contrast" && /* @__PURE__ */ jsx(ContrastView, { onBack: () => setView("main") }),
|
|
2563
2769
|
view === "settings" && /* @__PURE__ */ jsx(SettingsView, { onBack: () => setView("main") }),
|
|
2770
|
+
view === "finish" && /* @__PURE__ */ jsx(FinishView, { onBack: () => setView("main") }),
|
|
2564
2771
|
/* @__PURE__ */ jsxs("div", {
|
|
2565
2772
|
hidden: view !== "main",
|
|
2566
2773
|
children: [/* @__PURE__ */ jsxs(ContrastNav.Provider, {
|
|
@@ -2587,14 +2794,25 @@ function ThemePanel() {
|
|
|
2587
2794
|
]
|
|
2588
2795
|
}), /* @__PURE__ */ jsx("footer", {
|
|
2589
2796
|
className: "border-t border-zinc-200 px-4 py-3",
|
|
2590
|
-
children: /* @__PURE__ */ jsxs("
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2797
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2798
|
+
className: "flex gap-2",
|
|
2799
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
2800
|
+
type: "button",
|
|
2801
|
+
className: `${btn} flex-1`,
|
|
2802
|
+
onClick: resetToDefault,
|
|
2803
|
+
children: [/* @__PURE__ */ jsx(RotateCcw, {
|
|
2804
|
+
className: "w-3.5 h-3.5",
|
|
2805
|
+
"aria-hidden": "true"
|
|
2806
|
+
}), "Reset to default"]
|
|
2807
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
2808
|
+
type: "button",
|
|
2809
|
+
className: `${btnPrimary} flex-1`,
|
|
2810
|
+
onClick: (e) => showView("finish", e.currentTarget),
|
|
2811
|
+
children: [/* @__PURE__ */ jsx(Check, {
|
|
2812
|
+
className: "w-3.5 h-3.5",
|
|
2813
|
+
"aria-hidden": "true"
|
|
2814
|
+
}), "I’m done"]
|
|
2815
|
+
})]
|
|
2598
2816
|
})
|
|
2599
2817
|
})]
|
|
2600
2818
|
})
|
|
@@ -2664,6 +2882,296 @@ function Toast({ toast, onDismiss }) {
|
|
|
2664
2882
|
]
|
|
2665
2883
|
});
|
|
2666
2884
|
}
|
|
2885
|
+
/** Shows code with a copy button. */
|
|
2886
|
+
function CopyBlock({ label, text, copyLabel = "Copy" }) {
|
|
2887
|
+
const { toast } = usePanel();
|
|
2888
|
+
const id = useId();
|
|
2889
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2890
|
+
className: "space-y-1",
|
|
2891
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2892
|
+
className: "flex items-center justify-between gap-2",
|
|
2893
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2894
|
+
id,
|
|
2895
|
+
className: "text-[11px] font-semibold text-zinc-700",
|
|
2896
|
+
children: label
|
|
2897
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
2898
|
+
type: "button",
|
|
2899
|
+
className: `${btn} px-2 py-1`,
|
|
2900
|
+
onClick: async () => {
|
|
2901
|
+
try {
|
|
2902
|
+
await navigator.clipboard.writeText(text);
|
|
2903
|
+
toast(`Copied: ${label.toLowerCase()}.`);
|
|
2904
|
+
} catch {
|
|
2905
|
+
toast("Couldn’t reach the clipboard. Select the text and copy it instead.");
|
|
2906
|
+
}
|
|
2907
|
+
},
|
|
2908
|
+
children: [
|
|
2909
|
+
/* @__PURE__ */ jsx(Copy, {
|
|
2910
|
+
className: "w-3.5 h-3.5",
|
|
2911
|
+
"aria-hidden": "true"
|
|
2912
|
+
}),
|
|
2913
|
+
" ",
|
|
2914
|
+
copyLabel
|
|
2915
|
+
]
|
|
2916
|
+
})]
|
|
2917
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
2918
|
+
"aria-labelledby": id,
|
|
2919
|
+
tabIndex: 0,
|
|
2920
|
+
className: "max-h-40 overflow-auto rounded-lg border border-zinc-200 bg-zinc-50 p-2.5 font-mono text-[11px] leading-snug text-zinc-800 whitespace-pre-wrap break-all",
|
|
2921
|
+
children: text
|
|
2922
|
+
})]
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
var FINISH_OPTIONS = [
|
|
2926
|
+
{
|
|
2927
|
+
id: "keep",
|
|
2928
|
+
label: "Keep these colours and hide it in production",
|
|
2929
|
+
note: "Recommended. Everyone sees your colours; the button still shows while you develop."
|
|
2930
|
+
},
|
|
2931
|
+
{
|
|
2932
|
+
id: "local",
|
|
2933
|
+
label: "Hide it on this device only",
|
|
2934
|
+
note: "Quick and undoable. Nothing changes for anyone else."
|
|
2935
|
+
},
|
|
2936
|
+
{
|
|
2937
|
+
id: "remove",
|
|
2938
|
+
label: "Remove colorsbymax",
|
|
2939
|
+
note: "Uninstall it and keep the colours in your own CSS."
|
|
2940
|
+
}
|
|
2941
|
+
];
|
|
2942
|
+
/**
|
|
2943
|
+
* After "I'm done": how to keep the chosen colours for every visitor and hide the switcher, hide it
|
|
2944
|
+
* here only, or remove colorsbymax, each with code and a prompt for an AI editor. Cancel goes back.
|
|
2945
|
+
*/
|
|
2946
|
+
function FinishView({ onBack }) {
|
|
2947
|
+
const { tokens, active, recolouring } = useTheme();
|
|
2948
|
+
const { update } = useSettings();
|
|
2949
|
+
const [choice, setChoice] = useState("keep");
|
|
2950
|
+
const [kind, setKind] = useState(() => document.querySelector("[data-colorsbymax=\"auto\"]") ? "auto" : "provider");
|
|
2951
|
+
const headingRef = useRef(null);
|
|
2952
|
+
useEffect(() => headingRef.current?.focus(), []);
|
|
2953
|
+
const name = active.name.replace(/ \(dark\)$/, " dark");
|
|
2954
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2955
|
+
className: "px-4 py-3 space-y-4",
|
|
2956
|
+
children: [
|
|
2957
|
+
/* @__PURE__ */ jsxs("button", {
|
|
2958
|
+
type: "button",
|
|
2959
|
+
className: `${btn} border-transparent px-1.5`,
|
|
2960
|
+
onClick: onBack,
|
|
2961
|
+
children: [/* @__PURE__ */ jsx(ArrowLeft, {
|
|
2962
|
+
className: "w-3.5 h-3.5",
|
|
2963
|
+
"aria-hidden": "true"
|
|
2964
|
+
}), " Back"]
|
|
2965
|
+
}),
|
|
2966
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2967
|
+
className: "space-y-1.5",
|
|
2968
|
+
children: [
|
|
2969
|
+
/* @__PURE__ */ jsx("h3", {
|
|
2970
|
+
ref: headingRef,
|
|
2971
|
+
tabIndex: -1,
|
|
2972
|
+
className: "text-sm font-semibold focus:outline-none",
|
|
2973
|
+
children: "Happy with your colours?"
|
|
2974
|
+
}),
|
|
2975
|
+
/* @__PURE__ */ jsxs("p", {
|
|
2976
|
+
className: "text-[11px] text-zinc-600",
|
|
2977
|
+
children: [
|
|
2978
|
+
"Your pick, ",
|
|
2979
|
+
/* @__PURE__ */ jsx("strong", {
|
|
2980
|
+
className: "font-semibold text-zinc-900",
|
|
2981
|
+
children: active.name
|
|
2982
|
+
}),
|
|
2983
|
+
", is only saved in this browser. To show it to every visitor and keep the button out of production, put it in your code:"
|
|
2984
|
+
]
|
|
2985
|
+
}),
|
|
2986
|
+
/* @__PURE__ */ jsx(Swatches, { tokens })
|
|
2987
|
+
]
|
|
2988
|
+
}),
|
|
2989
|
+
/* @__PURE__ */ jsx("div", {
|
|
2990
|
+
role: "radiogroup",
|
|
2991
|
+
"aria-label": "What to do",
|
|
2992
|
+
className: "space-y-1.5",
|
|
2993
|
+
children: FINISH_OPTIONS.map((o) => /* @__PURE__ */ jsxs("button", {
|
|
2994
|
+
type: "button",
|
|
2995
|
+
role: "radio",
|
|
2996
|
+
"aria-checked": choice === o.id,
|
|
2997
|
+
onClick: () => setChoice(o.id),
|
|
2998
|
+
className: `w-full rounded-xl border px-3 py-2 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${choice === o.id ? "border-zinc-900 bg-zinc-100" : "border-zinc-200 hover:bg-zinc-50"}`,
|
|
2999
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
3000
|
+
className: "block text-xs font-semibold text-zinc-900",
|
|
3001
|
+
children: o.label
|
|
3002
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
3003
|
+
className: "block text-[11px] text-zinc-600",
|
|
3004
|
+
children: o.note
|
|
3005
|
+
})]
|
|
3006
|
+
}, o.id))
|
|
3007
|
+
}),
|
|
3008
|
+
choice === "keep" && /* @__PURE__ */ jsxs("div", {
|
|
3009
|
+
className: "space-y-3",
|
|
3010
|
+
children: [
|
|
3011
|
+
/* @__PURE__ */ jsx("div", {
|
|
3012
|
+
role: "radiogroup",
|
|
3013
|
+
"aria-label": "How colorsbymax is set up",
|
|
3014
|
+
className: "grid grid-cols-2 gap-2",
|
|
3015
|
+
children: [["auto", "One-line import"], ["provider", "ThemeProvider"]].map(([id, label]) => /* @__PURE__ */ jsx("button", {
|
|
3016
|
+
type: "button",
|
|
3017
|
+
role: "radio",
|
|
3018
|
+
"aria-checked": kind === id,
|
|
3019
|
+
onClick: () => setKind(id),
|
|
3020
|
+
className: `h-8 rounded-lg border px-2 text-xs font-medium cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${kind === id ? "border-zinc-900 bg-zinc-900 text-white" : "border-zinc-300 bg-white text-zinc-800 hover:bg-zinc-50"}`,
|
|
3021
|
+
children: label
|
|
3022
|
+
}, id))
|
|
3023
|
+
}),
|
|
3024
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3025
|
+
label: "Code for your site",
|
|
3026
|
+
text: keepSnippet(kind, name, tokens)
|
|
3027
|
+
}),
|
|
3028
|
+
/* @__PURE__ */ jsxs("p", {
|
|
3029
|
+
className: "text-[11px] text-zinc-600",
|
|
3030
|
+
children: [
|
|
3031
|
+
"Not using Vite? Use ",
|
|
3032
|
+
/* @__PURE__ */ jsx("code", {
|
|
3033
|
+
className: "font-mono",
|
|
3034
|
+
children: "process.env.NODE_ENV === 'production'"
|
|
3035
|
+
}),
|
|
3036
|
+
" instead of ",
|
|
3037
|
+
/* @__PURE__ */ jsx("code", {
|
|
3038
|
+
className: "font-mono",
|
|
3039
|
+
children: "import.meta.env.PROD"
|
|
3040
|
+
}),
|
|
3041
|
+
" (Next.js, webpack)."
|
|
3042
|
+
]
|
|
3043
|
+
}),
|
|
3044
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3045
|
+
label: "Or ask Claude, Cursor or Copilot",
|
|
3046
|
+
text: keepPrompt(name, tokens),
|
|
3047
|
+
copyLabel: "Copy prompt"
|
|
3048
|
+
}),
|
|
3049
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3050
|
+
className: "rounded-lg bg-zinc-100 p-2.5 text-[11px] text-zinc-700 space-y-1.5",
|
|
3051
|
+
children: [/* @__PURE__ */ jsxs("p", { children: [
|
|
3052
|
+
/* @__PURE__ */ jsx("strong", {
|
|
3053
|
+
className: "font-semibold text-zinc-900",
|
|
3054
|
+
children: "Bring it back later:"
|
|
3055
|
+
}),
|
|
3056
|
+
" it still shows when you run the site locally, so you can keep iterating. To show it in production too, set ",
|
|
3057
|
+
/* @__PURE__ */ jsx("code", {
|
|
3058
|
+
className: "font-mono",
|
|
3059
|
+
children: "hidden: false"
|
|
3060
|
+
}),
|
|
3061
|
+
"."
|
|
3062
|
+
] }), /* @__PURE__ */ jsx(CopyBlock, {
|
|
3063
|
+
label: "Prompt to bring it back",
|
|
3064
|
+
text: "Show the colorsbymax colour switcher again in production: in its config (the autoMount({...}) call or the config passed to <ThemeProvider>), set hidden to false or remove the hidden line. Don't change anything else.",
|
|
3065
|
+
copyLabel: "Copy prompt"
|
|
3066
|
+
})]
|
|
3067
|
+
})
|
|
3068
|
+
]
|
|
3069
|
+
}),
|
|
3070
|
+
choice === "local" && /* @__PURE__ */ jsxs("div", {
|
|
3071
|
+
className: "space-y-2",
|
|
3072
|
+
children: [
|
|
3073
|
+
/* @__PURE__ */ jsx("p", {
|
|
3074
|
+
className: "text-[11px] text-zinc-600",
|
|
3075
|
+
children: "Hides the colour button in this browser only, and keeps showing your current colours here. It doesn’t change your code or what anyone else sees."
|
|
3076
|
+
}),
|
|
3077
|
+
/* @__PURE__ */ jsxs("p", {
|
|
3078
|
+
className: "rounded-lg bg-zinc-100 p-2.5 text-[11px] text-zinc-700",
|
|
3079
|
+
children: [
|
|
3080
|
+
/* @__PURE__ */ jsx("strong", {
|
|
3081
|
+
className: "font-semibold text-zinc-900",
|
|
3082
|
+
children: "To bring it back:"
|
|
3083
|
+
}),
|
|
3084
|
+
" press ",
|
|
3085
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
3086
|
+
className: "font-mono",
|
|
3087
|
+
children: "Alt"
|
|
3088
|
+
}),
|
|
3089
|
+
"+",
|
|
3090
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
3091
|
+
className: "font-mono",
|
|
3092
|
+
children: "Shift"
|
|
3093
|
+
}),
|
|
3094
|
+
"+",
|
|
3095
|
+
/* @__PURE__ */ jsx("kbd", {
|
|
3096
|
+
className: "font-mono",
|
|
3097
|
+
children: "C"
|
|
3098
|
+
}),
|
|
3099
|
+
" on the page, or open it with ",
|
|
3100
|
+
/* @__PURE__ */ jsx("code", {
|
|
3101
|
+
className: "font-mono",
|
|
3102
|
+
children: "?colorsbymax"
|
|
3103
|
+
}),
|
|
3104
|
+
" at the end of the address."
|
|
3105
|
+
]
|
|
3106
|
+
}),
|
|
3107
|
+
/* @__PURE__ */ jsx("button", {
|
|
3108
|
+
type: "button",
|
|
3109
|
+
className: `${btnPrimary} w-full`,
|
|
3110
|
+
onClick: () => update({ hideButton: true }),
|
|
3111
|
+
children: "Hide the button here"
|
|
3112
|
+
})
|
|
3113
|
+
]
|
|
3114
|
+
}),
|
|
3115
|
+
choice === "remove" && /* @__PURE__ */ jsxs("div", {
|
|
3116
|
+
className: "space-y-3",
|
|
3117
|
+
children: [
|
|
3118
|
+
/* @__PURE__ */ jsxs("ol", {
|
|
3119
|
+
className: "list-decimal space-y-1 pl-4 text-[11px] text-zinc-700",
|
|
3120
|
+
children: [
|
|
3121
|
+
/* @__PURE__ */ jsxs("li", { children: ["Uninstall it: ", /* @__PURE__ */ jsx("code", {
|
|
3122
|
+
className: "font-mono",
|
|
3123
|
+
children: "npm uninstall colorsbymax"
|
|
3124
|
+
})] }),
|
|
3125
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
3126
|
+
"Delete its import (",
|
|
3127
|
+
/* @__PURE__ */ jsx("code", {
|
|
3128
|
+
className: "font-mono",
|
|
3129
|
+
children: "import 'colorsbymax/auto'"
|
|
3130
|
+
}),
|
|
3131
|
+
", ",
|
|
3132
|
+
/* @__PURE__ */ jsx("code", {
|
|
3133
|
+
className: "font-mono",
|
|
3134
|
+
children: "autoMount"
|
|
3135
|
+
}),
|
|
3136
|
+
", or ",
|
|
3137
|
+
/* @__PURE__ */ jsx("code", {
|
|
3138
|
+
className: "font-mono",
|
|
3139
|
+
children: "ThemeProvider"
|
|
3140
|
+
}),
|
|
3141
|
+
" and ",
|
|
3142
|
+
/* @__PURE__ */ jsx("code", {
|
|
3143
|
+
className: "font-mono",
|
|
3144
|
+
children: "ThemeSwitcher"
|
|
3145
|
+
}),
|
|
3146
|
+
") and any colorsbymax pre-paint script."
|
|
3147
|
+
] }),
|
|
3148
|
+
/* @__PURE__ */ jsx("li", { children: recolouring ? "Keep the colours: your site’s CSS uses its own hard-coded colours, which colorsbymax was swapping as the page ran, so they need writing into your CSS. The prompt below asks your AI editor to do that." : "Keep the colours: paste these into your global stylesheet, replacing your current --color-* values." })
|
|
3149
|
+
]
|
|
3150
|
+
}),
|
|
3151
|
+
!recolouring && /* @__PURE__ */ jsx(CopyBlock, {
|
|
3152
|
+
label: "CSS for your colours",
|
|
3153
|
+
text: cssSnippet(tokens)
|
|
3154
|
+
}),
|
|
3155
|
+
/* @__PURE__ */ jsx(CopyBlock, {
|
|
3156
|
+
label: "Or ask Claude, Cursor or Copilot",
|
|
3157
|
+
text: removePrompt(tokens, recolouring),
|
|
3158
|
+
copyLabel: "Copy prompt"
|
|
3159
|
+
}),
|
|
3160
|
+
/* @__PURE__ */ jsx("p", {
|
|
3161
|
+
className: "text-[11px] text-zinc-600",
|
|
3162
|
+
children: "To bring it back later, install it again and follow the Quick start in the README."
|
|
3163
|
+
})
|
|
3164
|
+
]
|
|
3165
|
+
}),
|
|
3166
|
+
/* @__PURE__ */ jsx("button", {
|
|
3167
|
+
type: "button",
|
|
3168
|
+
className: `${btn} w-full`,
|
|
3169
|
+
onClick: onBack,
|
|
3170
|
+
children: "Cancel, keep using colorsbymax"
|
|
3171
|
+
})
|
|
3172
|
+
]
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
2667
3175
|
var MODES = [
|
|
2668
3176
|
{
|
|
2669
3177
|
id: "light",
|
|
@@ -2811,6 +3319,18 @@ function SettingsView({ onBack }) {
|
|
|
2811
3319
|
})
|
|
2812
3320
|
]
|
|
2813
3321
|
}),
|
|
3322
|
+
/* @__PURE__ */ jsxs("fieldset", {
|
|
3323
|
+
className: "space-y-1",
|
|
3324
|
+
children: [/* @__PURE__ */ jsx("legend", {
|
|
3325
|
+
className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-zinc-600",
|
|
3326
|
+
children: "Page"
|
|
3327
|
+
}), /* @__PURE__ */ jsx(Toggle, {
|
|
3328
|
+
checked: settings.colourLogo,
|
|
3329
|
+
onChange: (v) => update({ colourLogo: v }),
|
|
3330
|
+
label: "Colour the logo too",
|
|
3331
|
+
note: "Off keeps the site’s logo in its own colours whatever the theme"
|
|
3332
|
+
})]
|
|
3333
|
+
}),
|
|
2814
3334
|
/* @__PURE__ */ jsxs("fieldset", {
|
|
2815
3335
|
className: "space-y-1",
|
|
2816
3336
|
children: [
|
|
@@ -4070,8 +4590,509 @@ function ImportExport() {
|
|
|
4070
4590
|
});
|
|
4071
4591
|
}
|
|
4072
4592
|
//#endregion
|
|
4593
|
+
//#region src/audit.js
|
|
4594
|
+
/** Elements read, at most, so audits stay quick on huge pages. */
|
|
4595
|
+
var MAX_ELEMENTS = 3e3;
|
|
4596
|
+
var SKIP = "colorsbymax-root";
|
|
4597
|
+
var parser = () => {
|
|
4598
|
+
const ctx = Object.assign(document.createElement("canvas"), {
|
|
4599
|
+
width: 1,
|
|
4600
|
+
height: 1
|
|
4601
|
+
}).getContext("2d", { willReadFrequently: true });
|
|
4602
|
+
return (css) => {
|
|
4603
|
+
if (!css || css === "none" || css === "transparent") return null;
|
|
4604
|
+
ctx.clearRect(0, 0, 1, 1);
|
|
4605
|
+
ctx.fillStyle = "#000";
|
|
4606
|
+
ctx.fillStyle = css;
|
|
4607
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
4608
|
+
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
|
|
4609
|
+
return a < 128 ? null : rgbToHex([
|
|
4610
|
+
r,
|
|
4611
|
+
g,
|
|
4612
|
+
b
|
|
4613
|
+
]);
|
|
4614
|
+
};
|
|
4615
|
+
};
|
|
4616
|
+
var visible = (el) => {
|
|
4617
|
+
if (el.checkVisibility && !el.checkVisibility({
|
|
4618
|
+
opacityProperty: true,
|
|
4619
|
+
visibilityProperty: true
|
|
4620
|
+
})) return false;
|
|
4621
|
+
const r = el.getBoundingClientRect();
|
|
4622
|
+
return r.width > 2 && r.height > 2;
|
|
4623
|
+
};
|
|
4624
|
+
var ownText = (el) => [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim());
|
|
4625
|
+
/** A short quote of an element's text for its note, cut at a word. */
|
|
4626
|
+
var describe = (el) => {
|
|
4627
|
+
const text = (el.getAttribute("aria-label") || el.alt || el.textContent || "").trim().replace(/\s+/g, " ");
|
|
4628
|
+
if (text.length <= 48) return text;
|
|
4629
|
+
const cut = text.slice(0, 48);
|
|
4630
|
+
const space = cut.lastIndexOf(" ");
|
|
4631
|
+
return `${(space > 20 ? cut.slice(0, space) : cut).replace(/[,.;:]$/, "")}…`;
|
|
4632
|
+
};
|
|
4633
|
+
var fmt = (r) => `${(Math.floor(r * 10) / 10).toFixed(1)}:1`;
|
|
4634
|
+
/**
|
|
4635
|
+
* Samples an image's corners and middle (for a solid background and an overall colour). Returns
|
|
4636
|
+
* null when the browser won't allow reading it (a cross-origin image without CORS).
|
|
4637
|
+
*/
|
|
4638
|
+
function sampleImage(img) {
|
|
4639
|
+
try {
|
|
4640
|
+
const w = 24;
|
|
4641
|
+
const h = Math.max(1, Math.round(img.naturalHeight / Math.max(1, img.naturalWidth) * w));
|
|
4642
|
+
const ctx = Object.assign(document.createElement("canvas"), {
|
|
4643
|
+
width: w,
|
|
4644
|
+
height: h
|
|
4645
|
+
}).getContext("2d", { willReadFrequently: true });
|
|
4646
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
4647
|
+
const data = ctx.getImageData(0, 0, w, h).data;
|
|
4648
|
+
const at = (x, y) => {
|
|
4649
|
+
const i = (y * w + x) * 4;
|
|
4650
|
+
return data[i + 3] < 200 ? null : rgbToHex([
|
|
4651
|
+
data[i],
|
|
4652
|
+
data[i + 1],
|
|
4653
|
+
data[i + 2]
|
|
4654
|
+
]);
|
|
4655
|
+
};
|
|
4656
|
+
const corners = [
|
|
4657
|
+
at(0, 0),
|
|
4658
|
+
at(23, 0),
|
|
4659
|
+
at(0, h - 1),
|
|
4660
|
+
at(23, h - 1)
|
|
4661
|
+
];
|
|
4662
|
+
let r = 0, g = 0, b = 0, n = 0;
|
|
4663
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
4664
|
+
if (data[i + 3] < 128) continue;
|
|
4665
|
+
r += data[i];
|
|
4666
|
+
g += data[i + 1];
|
|
4667
|
+
b += data[i + 2];
|
|
4668
|
+
n++;
|
|
4669
|
+
}
|
|
4670
|
+
return {
|
|
4671
|
+
corners,
|
|
4672
|
+
average: n ? rgbToHex([
|
|
4673
|
+
r / n,
|
|
4674
|
+
g / n,
|
|
4675
|
+
b / n
|
|
4676
|
+
].map(Math.round)) : null,
|
|
4677
|
+
transparent: n < data.length / 4 * .9
|
|
4678
|
+
};
|
|
4679
|
+
} catch {
|
|
4680
|
+
return null;
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
/**
|
|
4684
|
+
* Audits the page as painted now.
|
|
4685
|
+
* @param {{ logoColouring: boolean }} options
|
|
4686
|
+
* @returns {{ id: string, el: Element, title: string, message: string, action?: 'colour-logo' | 'invert' }[]}
|
|
4687
|
+
*/
|
|
4688
|
+
function runAudit({ logoColouring }) {
|
|
4689
|
+
const parse = parser();
|
|
4690
|
+
const findings = [];
|
|
4691
|
+
const flagged = /* @__PURE__ */ new Set();
|
|
4692
|
+
const add = (el, finding) => {
|
|
4693
|
+
for (let n = el; n; n = n.parentElement) if (flagged.has(n)) return;
|
|
4694
|
+
flagged.add(el);
|
|
4695
|
+
findings.push({
|
|
4696
|
+
id: `${finding.kind}-${findings.length}`,
|
|
4697
|
+
el,
|
|
4698
|
+
...finding
|
|
4699
|
+
});
|
|
4700
|
+
};
|
|
4701
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
4702
|
+
const behind = (el) => {
|
|
4703
|
+
if (!el || el.nodeType !== 1) return parse(getComputedStyle(document.documentElement).backgroundColor) ?? "#ffffff";
|
|
4704
|
+
if (cache.has(el)) return cache.get(el);
|
|
4705
|
+
const cs = getComputedStyle(el);
|
|
4706
|
+
let out;
|
|
4707
|
+
const own = parse(cs.backgroundColor);
|
|
4708
|
+
if (own) out = own;
|
|
4709
|
+
else if (/url\(/.test(cs.backgroundImage)) out = null;
|
|
4710
|
+
else out = el === document.body ? parse(getComputedStyle(document.documentElement).backgroundColor) ?? "#ffffff" : behind(el.parentElement);
|
|
4711
|
+
cache.set(el, out);
|
|
4712
|
+
return out;
|
|
4713
|
+
};
|
|
4714
|
+
const pageBg = behind(document.body) ?? "#ffffff";
|
|
4715
|
+
const darkPage = luminance(pageBg) < .2;
|
|
4716
|
+
for (const logo of document.querySelectorAll(LOGO_SELECTOR)) {
|
|
4717
|
+
if (logo.closest(SKIP) || !visible(logo)) continue;
|
|
4718
|
+
if (logo.parentElement?.closest(LOGO_SELECTOR)) continue;
|
|
4719
|
+
const bg = behind(logo.parentElement) ?? pageBg;
|
|
4720
|
+
const img = logo.localName === "img" ? logo : logo.querySelector("img");
|
|
4721
|
+
if (img && img.complete && img.naturalWidth) {
|
|
4722
|
+
const sample = sampleImage(img);
|
|
4723
|
+
const ratio = sample?.average ? contrastRatio(sample.average, bg) : null;
|
|
4724
|
+
if (ratio !== null && ratio < 3) add(img, {
|
|
4725
|
+
kind: "logo-image",
|
|
4726
|
+
title: "Logo is hard to see here",
|
|
4727
|
+
message: `Your logo is a picture, which colorsbymax can’t re-colour, and it reads at only ${fmt(ratio)} on this background. Use a transparent SVG (which colorsbymax can colour), or add a version made for ${darkPage ? "dark" : "light"} backgrounds.`,
|
|
4728
|
+
action: "invert"
|
|
4729
|
+
});
|
|
4730
|
+
else if (sample === null) add(img, {
|
|
4731
|
+
kind: "logo-image",
|
|
4732
|
+
title: "Check your logo here",
|
|
4733
|
+
message: `Your logo is a picture, which colorsbymax can’t re-colour. Check it still reads on this ${darkPage ? "dark" : "light"} background, or use a transparent SVG instead.`,
|
|
4734
|
+
action: "invert"
|
|
4735
|
+
});
|
|
4736
|
+
continue;
|
|
4737
|
+
}
|
|
4738
|
+
const painted = ownText(logo) ? logo : [...logo.querySelectorAll("*")].find((n) => ownText(n) || n instanceof SVGElement) ?? logo;
|
|
4739
|
+
const cs = getComputedStyle(painted);
|
|
4740
|
+
const fg = parse(painted instanceof SVGElement ? cs.fill !== "none" ? cs.fill : cs.stroke : cs.color);
|
|
4741
|
+
if (!fg) continue;
|
|
4742
|
+
const ratio = contrastRatio(fg, bg);
|
|
4743
|
+
if (ratio < 3) add(logo, {
|
|
4744
|
+
kind: "logo",
|
|
4745
|
+
title: "Logo is hard to see here",
|
|
4746
|
+
message: logoColouring ? `Your logo reads at only ${fmt(ratio)} on this background. Try another theme, or give the logo its own colour.` : `Your logo keeps its own colours, which read at only ${fmt(ratio)} on this background. Let colorsbymax colour it to match the theme, or pick a theme it suits.`,
|
|
4747
|
+
action: logoColouring ? void 0 : "colour-logo"
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
for (const img of document.images) {
|
|
4751
|
+
if (img.closest(SKIP) || !img.complete || !img.naturalWidth || !visible(img)) continue;
|
|
4752
|
+
const rect = img.getBoundingClientRect();
|
|
4753
|
+
if (rect.width < 24 || rect.height < 24) continue;
|
|
4754
|
+
const sample = sampleImage(img);
|
|
4755
|
+
if (!sample || sample.transparent) continue;
|
|
4756
|
+
if (!sample.corners.every((c) => c && contrastRatio(c, sample.corners[0]) < 1.15)) continue;
|
|
4757
|
+
const bg = behind(img.parentElement) ?? pageBg;
|
|
4758
|
+
if (contrastRatio(sample.corners[0], bg) >= 1.6) {
|
|
4759
|
+
const light = luminance(sample.corners[0]) > luminance(bg);
|
|
4760
|
+
add(img, {
|
|
4761
|
+
kind: "image-box",
|
|
4762
|
+
title: `Picture shows as a ${light ? "light" : "dark"} box`,
|
|
4763
|
+
message: `This picture has a solid ${light ? "light" : "dark"} background that stands out against this ${luminance(bg) < .2 ? "dark" : "light"} page. A transparent PNG or an SVG would blend in with any theme.`,
|
|
4764
|
+
action: img.closest(LOGO_SELECTOR) ? "invert" : void 0
|
|
4765
|
+
});
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
const elements = [...document.body.querySelectorAll("*")].slice(0, MAX_ELEMENTS);
|
|
4769
|
+
for (const el of elements) {
|
|
4770
|
+
if (el.closest(SKIP) || el.closest(LOGO_SELECTOR)) continue;
|
|
4771
|
+
const svg = el.localName === "svg";
|
|
4772
|
+
if (!svg && !ownText(el)) continue;
|
|
4773
|
+
if (!visible(el)) continue;
|
|
4774
|
+
const cs = getComputedStyle(el);
|
|
4775
|
+
if (cs.backgroundClip === "text" || cs.webkitBackgroundClip === "text") continue;
|
|
4776
|
+
const fg = parse(svg ? cs.stroke !== "none" ? cs.stroke : cs.fill : cs.color);
|
|
4777
|
+
const bg = behind(el);
|
|
4778
|
+
if (!fg || !bg) continue;
|
|
4779
|
+
const size = parseFloat(cs.fontSize);
|
|
4780
|
+
const large = size >= 24 || size >= 18.66 && Number(cs.fontWeight) >= 700;
|
|
4781
|
+
const needed = svg || large ? 3 : 4.5;
|
|
4782
|
+
const ratio = contrastRatio(fg, bg);
|
|
4783
|
+
if (ratio >= needed) continue;
|
|
4784
|
+
add(el, {
|
|
4785
|
+
kind: svg ? "icon" : "text",
|
|
4786
|
+
title: `${svg ? "Icon" : "Text"} is hard to read`,
|
|
4787
|
+
message: `${describe(el) ? `“${describe(el)}” ` : ""}reads at ${fmt(ratio)} here; ${svg ? "icons" : large ? "large text" : "text"} needs ${needed}:1. Try another theme, or adjust it under “Override a single colour”.`
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
return findings;
|
|
4791
|
+
}
|
|
4792
|
+
//#endregion
|
|
4793
|
+
//#region src/AuditLayer.jsx
|
|
4794
|
+
var CARD_WIDTH = 264;
|
|
4795
|
+
var GAP = 10;
|
|
4796
|
+
var EDGE$1 = 12;
|
|
4797
|
+
/** Space kept between two notes. */
|
|
4798
|
+
var APART = 8;
|
|
4799
|
+
/** A note's height before it has been measured. */
|
|
4800
|
+
var GUESS_HEIGHT = 150;
|
|
4801
|
+
/** Below this width, only one note is open at a time; the numbered badges pick which. */
|
|
4802
|
+
var COMPACT_WIDTH = 640;
|
|
4803
|
+
/**
|
|
4804
|
+
* The audit on the page: each finding outlined where it is, with a numbered card pointing at it,
|
|
4805
|
+
* kept in place as the page scrolls; and a bar by the colour button to re-check or close.
|
|
4806
|
+
*/
|
|
4807
|
+
function AuditLayer({ findings, anchor, themeIssues, onRecheck, onClose, onAction }) {
|
|
4808
|
+
const [rects, setRects] = useState([]);
|
|
4809
|
+
const [dismissed, setDismissed] = useState(() => /* @__PURE__ */ new Set());
|
|
4810
|
+
const [inverted, setInverted] = useState(() => /* @__PURE__ */ new Set());
|
|
4811
|
+
const [selected, setSelected] = useState(null);
|
|
4812
|
+
const [anchorRect, setAnchorRect] = useState(null);
|
|
4813
|
+
const pinned = findings.filter((f) => !dismissed.has(f.id)).slice(0, 8);
|
|
4814
|
+
const hiddenCount = findings.filter((f) => !dismissed.has(f.id)).length - pinned.length;
|
|
4815
|
+
useEffect(() => setDismissed(/* @__PURE__ */ new Set()), [findings]);
|
|
4816
|
+
useLayoutEffect(() => {
|
|
4817
|
+
let frame = 0;
|
|
4818
|
+
const measure = () => {
|
|
4819
|
+
frame = 0;
|
|
4820
|
+
setRects(pinned.map((f) => f.el.isConnected ? f.el.getBoundingClientRect() : null));
|
|
4821
|
+
setAnchorRect(anchor.current?.getBoundingClientRect() ?? null);
|
|
4822
|
+
};
|
|
4823
|
+
const schedule = () => {
|
|
4824
|
+
if (!frame) frame = requestAnimationFrame(measure);
|
|
4825
|
+
};
|
|
4826
|
+
measure();
|
|
4827
|
+
window.addEventListener("scroll", schedule, true);
|
|
4828
|
+
window.addEventListener("resize", schedule);
|
|
4829
|
+
const timer = setInterval(schedule, 500);
|
|
4830
|
+
return () => {
|
|
4831
|
+
cancelAnimationFrame(frame);
|
|
4832
|
+
clearInterval(timer);
|
|
4833
|
+
window.removeEventListener("scroll", schedule, true);
|
|
4834
|
+
window.removeEventListener("resize", schedule);
|
|
4835
|
+
};
|
|
4836
|
+
}, [findings, dismissed]);
|
|
4837
|
+
useEffect(() => {
|
|
4838
|
+
for (const f of findings) f.el.toggleAttribute("data-colorsbymax-invert", inverted.has(f.id));
|
|
4839
|
+
return () => {
|
|
4840
|
+
for (const f of findings) f.el.removeAttribute("data-colorsbymax-invert");
|
|
4841
|
+
};
|
|
4842
|
+
}, [inverted, findings]);
|
|
4843
|
+
useEffect(() => {
|
|
4844
|
+
const style = document.createElement("style");
|
|
4845
|
+
style.dataset.colorsbymax = "audit";
|
|
4846
|
+
style.textContent = "[data-colorsbymax-invert]{filter:invert(1) hue-rotate(180deg)!important}";
|
|
4847
|
+
document.head.append(style);
|
|
4848
|
+
return () => style.remove();
|
|
4849
|
+
}, []);
|
|
4850
|
+
const vw = document.documentElement.clientWidth;
|
|
4851
|
+
const vh = document.documentElement.clientHeight;
|
|
4852
|
+
const open = findings.filter((f) => !dismissed.has(f.id)).length;
|
|
4853
|
+
const heights = useRef({});
|
|
4854
|
+
const layerRef = useRef(null);
|
|
4855
|
+
useLayoutEffect(() => {
|
|
4856
|
+
for (const note of layerRef.current?.querySelectorAll("[data-note]") ?? []) heights.current[note.dataset.note] = note.offsetHeight;
|
|
4857
|
+
});
|
|
4858
|
+
const placed = [];
|
|
4859
|
+
const compact = vw < COMPACT_WIDTH;
|
|
4860
|
+
const openId = pinned.some((f) => f.id === selected) ? selected : pinned[0]?.id;
|
|
4861
|
+
const showsNote = (f) => !compact || f.id === openId;
|
|
4862
|
+
const placements = pinned.map((f, i) => {
|
|
4863
|
+
const r = rects[i];
|
|
4864
|
+
if (!r || r.bottom < 0 || r.top > vh || !showsNote(f)) return null;
|
|
4865
|
+
const h = heights.current[f.id] ?? GUESS_HEIGHT;
|
|
4866
|
+
const below = r.bottom + GAP + h < vh || r.top < h + GAP;
|
|
4867
|
+
let top = below ? r.bottom + GAP : r.top - GAP - h;
|
|
4868
|
+
let left = Math.min(Math.max(r.left + r.width / 2 - CARD_WIDTH / 2, EDGE$1), vw - CARD_WIDTH - EDGE$1);
|
|
4869
|
+
for (let tries = 0; tries < placed.length + 1; tries++) {
|
|
4870
|
+
const hit = placed.find((p) => left < p.left + CARD_WIDTH + APART && left + CARD_WIDTH + APART > p.left && top < p.top + p.h + APART && top + h + APART > p.top);
|
|
4871
|
+
if (!hit) break;
|
|
4872
|
+
if (hit.left + 2 * CARD_WIDTH + APART + EDGE$1 <= vw) left = hit.left + CARD_WIDTH + APART;
|
|
4873
|
+
else top = hit.top + hit.h + APART;
|
|
4874
|
+
}
|
|
4875
|
+
placed.push({
|
|
4876
|
+
left,
|
|
4877
|
+
top,
|
|
4878
|
+
h
|
|
4879
|
+
});
|
|
4880
|
+
const arrow = Math.min(Math.max(r.left + r.width / 2 - left, 16), 248);
|
|
4881
|
+
const pointing = below ? Math.abs(top - (r.bottom + GAP)) < 2 : Math.abs(top + h - (r.top - GAP)) < 2;
|
|
4882
|
+
return {
|
|
4883
|
+
left: Math.round(left),
|
|
4884
|
+
top: Math.round(top),
|
|
4885
|
+
below,
|
|
4886
|
+
arrow: Math.round(arrow),
|
|
4887
|
+
pointing
|
|
4888
|
+
};
|
|
4889
|
+
});
|
|
4890
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4891
|
+
ref: layerRef,
|
|
4892
|
+
children: [pinned.map((f, i) => {
|
|
4893
|
+
const r = rects[i];
|
|
4894
|
+
if (!r || r.bottom < 0 || r.top > vh) return null;
|
|
4895
|
+
const place = placements[i];
|
|
4896
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
4897
|
+
/* @__PURE__ */ jsx("div", {
|
|
4898
|
+
"aria-hidden": "true",
|
|
4899
|
+
className: "pointer-events-none fixed z-[55] rounded-md border-2 border-dashed border-amber-400",
|
|
4900
|
+
style: {
|
|
4901
|
+
left: r.left - 4,
|
|
4902
|
+
top: r.top - 4,
|
|
4903
|
+
width: r.width + 8,
|
|
4904
|
+
height: r.height + 8
|
|
4905
|
+
}
|
|
4906
|
+
}),
|
|
4907
|
+
compact ? /* @__PURE__ */ jsx("button", {
|
|
4908
|
+
type: "button",
|
|
4909
|
+
"aria-label": `Show audit finding ${i + 1}: ${f.title}`,
|
|
4910
|
+
"aria-pressed": f.id === openId,
|
|
4911
|
+
onClick: () => setSelected(f.id),
|
|
4912
|
+
className: `fixed z-[57] grid h-6 w-6 place-items-center rounded-full bg-amber-400 text-[11px] font-bold text-zinc-900 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${f.id === openId ? "ring-2 ring-white" : ""}`,
|
|
4913
|
+
style: {
|
|
4914
|
+
left: r.left - 14,
|
|
4915
|
+
top: r.top - 14
|
|
4916
|
+
},
|
|
4917
|
+
children: i + 1
|
|
4918
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
4919
|
+
"aria-hidden": "true",
|
|
4920
|
+
className: "pointer-events-none fixed z-[56] grid h-5 w-5 place-items-center rounded-full bg-amber-400 text-[11px] font-bold text-zinc-900",
|
|
4921
|
+
style: {
|
|
4922
|
+
left: r.left - 12,
|
|
4923
|
+
top: r.top - 12
|
|
4924
|
+
},
|
|
4925
|
+
children: i + 1
|
|
4926
|
+
}),
|
|
4927
|
+
place && /* @__PURE__ */ jsxs("div", {
|
|
4928
|
+
role: "note",
|
|
4929
|
+
"data-note": f.id,
|
|
4930
|
+
"aria-label": `Audit finding ${i + 1}: ${f.title}`,
|
|
4931
|
+
className: "theme-switcher theme-hint fixed z-[62] rounded-xl border border-zinc-200 bg-white p-3 text-zinc-900 shadow-xl",
|
|
4932
|
+
style: {
|
|
4933
|
+
left: place.left,
|
|
4934
|
+
top: place.top,
|
|
4935
|
+
width: CARD_WIDTH
|
|
4936
|
+
},
|
|
4937
|
+
children: [
|
|
4938
|
+
place.pointing && /* @__PURE__ */ jsx("span", {
|
|
4939
|
+
"aria-hidden": "true",
|
|
4940
|
+
className: `absolute h-2.5 w-2.5 rotate-45 border-zinc-200 bg-white ${place.below ? "-top-[6px] border-l border-t" : "-bottom-[6px] border-b border-r"}`,
|
|
4941
|
+
style: { left: place.arrow - 5 }
|
|
4942
|
+
}),
|
|
4943
|
+
/* @__PURE__ */ jsxs("p", {
|
|
4944
|
+
className: "flex items-start gap-1.5 text-xs font-semibold",
|
|
4945
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4946
|
+
className: "grid h-4 w-4 shrink-0 place-items-center rounded-full bg-amber-400 text-[10px] font-bold text-zinc-900",
|
|
4947
|
+
children: i + 1
|
|
4948
|
+
}), f.title]
|
|
4949
|
+
}),
|
|
4950
|
+
/* @__PURE__ */ jsx("p", {
|
|
4951
|
+
className: "mt-1 text-[11px] leading-snug text-zinc-600",
|
|
4952
|
+
children: f.message
|
|
4953
|
+
}),
|
|
4954
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4955
|
+
className: "mt-2 flex flex-wrap gap-1.5",
|
|
4956
|
+
children: [
|
|
4957
|
+
f.action === "colour-logo" && /* @__PURE__ */ jsx("button", {
|
|
4958
|
+
type: "button",
|
|
4959
|
+
className: "rounded-lg bg-zinc-900 px-2 py-1 text-[11px] font-medium text-white hover:bg-zinc-700 cursor-pointer",
|
|
4960
|
+
onClick: () => onAction("colour-logo"),
|
|
4961
|
+
children: "Colour the logo"
|
|
4962
|
+
}),
|
|
4963
|
+
f.action === "invert" && /* @__PURE__ */ jsxs("button", {
|
|
4964
|
+
type: "button",
|
|
4965
|
+
"aria-pressed": inverted.has(f.id),
|
|
4966
|
+
className: "inline-flex items-center gap-1 rounded-lg border border-zinc-300 bg-white px-2 py-1 text-[11px] font-medium text-zinc-800 hover:bg-zinc-50 cursor-pointer",
|
|
4967
|
+
onClick: () => setInverted((s) => {
|
|
4968
|
+
const next = new Set(s);
|
|
4969
|
+
if (next.has(f.id)) next.delete(f.id);
|
|
4970
|
+
else next.add(f.id);
|
|
4971
|
+
return next;
|
|
4972
|
+
}),
|
|
4973
|
+
children: [/* @__PURE__ */ jsx(Contrast, {
|
|
4974
|
+
className: "w-3 h-3",
|
|
4975
|
+
"aria-hidden": "true"
|
|
4976
|
+
}), inverted.has(f.id) ? "Undo preview" : "Preview inverted"]
|
|
4977
|
+
}),
|
|
4978
|
+
/* @__PURE__ */ jsx("button", {
|
|
4979
|
+
type: "button",
|
|
4980
|
+
className: "rounded-lg px-2 py-1 text-[11px] font-medium text-zinc-600 hover:bg-zinc-100 cursor-pointer",
|
|
4981
|
+
onClick: () => setDismissed((s) => new Set(s).add(f.id)),
|
|
4982
|
+
children: "Got it"
|
|
4983
|
+
})
|
|
4984
|
+
]
|
|
4985
|
+
})
|
|
4986
|
+
]
|
|
4987
|
+
})
|
|
4988
|
+
] }, f.id);
|
|
4989
|
+
}), /* @__PURE__ */ jsx(AuditBar, {
|
|
4990
|
+
anchorRect,
|
|
4991
|
+
open,
|
|
4992
|
+
hiddenCount,
|
|
4993
|
+
themeIssues,
|
|
4994
|
+
onRecheck,
|
|
4995
|
+
onClose
|
|
4996
|
+
})]
|
|
4997
|
+
});
|
|
4998
|
+
}
|
|
4999
|
+
/** The audit's summary, next to the colour button: what it found, Re-check and Close. */
|
|
5000
|
+
function AuditBar({ anchorRect, open, hiddenCount, themeIssues, onRecheck, onClose }) {
|
|
5001
|
+
const ref = useRef(null);
|
|
5002
|
+
const [size, setSize] = useState({
|
|
5003
|
+
width: 0,
|
|
5004
|
+
height: 0
|
|
5005
|
+
});
|
|
5006
|
+
useLayoutEffect(() => {
|
|
5007
|
+
const r = ref.current?.getBoundingClientRect();
|
|
5008
|
+
if (r && (r.width !== size.width || r.height !== size.height)) setSize({
|
|
5009
|
+
width: r.width,
|
|
5010
|
+
height: r.height
|
|
5011
|
+
});
|
|
5012
|
+
});
|
|
5013
|
+
const vw = document.documentElement.clientWidth;
|
|
5014
|
+
const vh = document.documentElement.clientHeight;
|
|
5015
|
+
let style = {
|
|
5016
|
+
right: EDGE$1,
|
|
5017
|
+
bottom: EDGE$1
|
|
5018
|
+
};
|
|
5019
|
+
if (anchorRect) {
|
|
5020
|
+
const onRight = anchorRect.left + anchorRect.width / 2 > vw / 2;
|
|
5021
|
+
const top = anchorRect.top + anchorRect.height / 2 < vh / 2 ? Math.round(anchorRect.bottom + GAP) : Math.round(anchorRect.top - GAP - size.height);
|
|
5022
|
+
const left = onRight ? Math.round(anchorRect.right - size.width) : Math.round(anchorRect.left);
|
|
5023
|
+
style = {
|
|
5024
|
+
top,
|
|
5025
|
+
left: Math.min(Math.max(EDGE$1, left), vw - size.width - EDGE$1)
|
|
5026
|
+
};
|
|
5027
|
+
}
|
|
5028
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5029
|
+
ref,
|
|
5030
|
+
role: "status",
|
|
5031
|
+
"aria-live": "polite",
|
|
5032
|
+
className: "theme-switcher fixed z-[61] flex max-w-[calc(100vw-80px)] items-center gap-2 rounded-full border border-zinc-200 bg-white py-1.5 pl-3 pr-1.5 text-xs text-zinc-900 shadow-xl",
|
|
5033
|
+
style,
|
|
5034
|
+
children: [
|
|
5035
|
+
/* @__PURE__ */ jsx(ScanSearch, {
|
|
5036
|
+
className: "w-4 h-4 shrink-0",
|
|
5037
|
+
"aria-hidden": "true"
|
|
5038
|
+
}),
|
|
5039
|
+
/* @__PURE__ */ jsx("span", {
|
|
5040
|
+
className: "font-semibold",
|
|
5041
|
+
children: "Audit"
|
|
5042
|
+
}),
|
|
5043
|
+
open ? /* @__PURE__ */ jsxs("span", {
|
|
5044
|
+
className: "flex items-center gap-1 text-zinc-700",
|
|
5045
|
+
children: [
|
|
5046
|
+
/* @__PURE__ */ jsx(AlertTriangle, {
|
|
5047
|
+
className: "w-3.5 h-3.5 text-amber-700",
|
|
5048
|
+
"aria-hidden": "true"
|
|
5049
|
+
}),
|
|
5050
|
+
open,
|
|
5051
|
+
" to check",
|
|
5052
|
+
hiddenCount > 0 ? ` (${hiddenCount} more after these)` : ""
|
|
5053
|
+
]
|
|
5054
|
+
}) : /* @__PURE__ */ jsxs("span", {
|
|
5055
|
+
className: "flex items-center gap-1 text-zinc-700",
|
|
5056
|
+
children: [/* @__PURE__ */ jsx(CheckCircle2, {
|
|
5057
|
+
className: "w-3.5 h-3.5 text-emerald-800",
|
|
5058
|
+
"aria-hidden": "true"
|
|
5059
|
+
}), "Nothing to fix on the page"]
|
|
5060
|
+
}),
|
|
5061
|
+
themeIssues > 0 && /* @__PURE__ */ jsxs("span", {
|
|
5062
|
+
className: "hidden text-zinc-600 sm:inline",
|
|
5063
|
+
children: [
|
|
5064
|
+
"· theme has ",
|
|
5065
|
+
themeIssues,
|
|
5066
|
+
" contrast ",
|
|
5067
|
+
themeIssues === 1 ? "issue" : "issues",
|
|
5068
|
+
" (see the panel)"
|
|
5069
|
+
]
|
|
5070
|
+
}),
|
|
5071
|
+
/* @__PURE__ */ jsxs("button", {
|
|
5072
|
+
type: "button",
|
|
5073
|
+
onClick: onRecheck,
|
|
5074
|
+
className: "inline-flex items-center gap-1 rounded-full border border-zinc-300 px-2 py-1 text-[11px] font-medium hover:bg-zinc-50 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
|
|
5075
|
+
children: [/* @__PURE__ */ jsx(RotateCcw, {
|
|
5076
|
+
className: "w-3 h-3",
|
|
5077
|
+
"aria-hidden": "true"
|
|
5078
|
+
}), " Re-check"]
|
|
5079
|
+
}),
|
|
5080
|
+
/* @__PURE__ */ jsx("button", {
|
|
5081
|
+
type: "button",
|
|
5082
|
+
onClick: onClose,
|
|
5083
|
+
"aria-label": "Close the audit",
|
|
5084
|
+
className: "grid h-6 w-6 place-items-center rounded-full hover:bg-zinc-100 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
|
|
5085
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
5086
|
+
className: "w-3.5 h-3.5",
|
|
5087
|
+
"aria-hidden": "true"
|
|
5088
|
+
})
|
|
5089
|
+
})
|
|
5090
|
+
]
|
|
5091
|
+
});
|
|
5092
|
+
}
|
|
5093
|
+
//#endregion
|
|
4073
5094
|
//#region src/panel-css.generated.js
|
|
4074
|
-
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-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-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;--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-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-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-\\[60\\]{z-index:60}.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-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-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}.min-h-0{min-height:0}.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-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-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-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}.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-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-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.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-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-12{padding-right:calc(var(--spacing) * 12)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:var(--spacing)}.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-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)}.whitespace-nowrap{white-space:nowrap}.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-zinc-900{--tw-ring-color:var(--color-zinc-900)}.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-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)}}@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)}}";
|
|
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)}}";
|
|
4075
5096
|
//#endregion
|
|
4076
5097
|
//#region src/ThemeSwitcher.jsx
|
|
4077
5098
|
var CORNERS = {
|
|
@@ -4098,8 +5119,10 @@ var HOST_TAG = "colorsbymax-root";
|
|
|
4098
5119
|
* needs nothing from the host site's CSS and the host's CSS can't restyle it.
|
|
4099
5120
|
*/
|
|
4100
5121
|
function ThemeSwitcher() {
|
|
5122
|
+
const { hidden } = useTheme();
|
|
4101
5123
|
const [mount, setMount] = useState(null);
|
|
4102
5124
|
useEffect(() => {
|
|
5125
|
+
if (hidden) return;
|
|
4103
5126
|
const host = document.createElement(HOST_TAG);
|
|
4104
5127
|
const shadow = host.attachShadow({ mode: "open" });
|
|
4105
5128
|
const style = document.createElement("style");
|
|
@@ -4108,15 +5131,54 @@ function ThemeSwitcher() {
|
|
|
4108
5131
|
shadow.append(style, container);
|
|
4109
5132
|
document.body.append(host);
|
|
4110
5133
|
setMount(container);
|
|
4111
|
-
return () =>
|
|
4112
|
-
|
|
5134
|
+
return () => {
|
|
5135
|
+
host.remove();
|
|
5136
|
+
setMount(null);
|
|
5137
|
+
};
|
|
5138
|
+
}, [hidden]);
|
|
4113
5139
|
return mount && createPortal(/* @__PURE__ */ jsx(Switcher, {}), mount);
|
|
4114
5140
|
}
|
|
4115
5141
|
function Switcher() {
|
|
4116
|
-
const { issues, storageKey, tokens, active, themes, selectTheme, position: requested } = useTheme();
|
|
5142
|
+
const { issues, storageKey, tokens, active, themes, selectTheme, position: requested, setLogoColouring } = useTheme();
|
|
4117
5143
|
const position = CORNERS[requested] ? requested : "bottom-right";
|
|
4118
5144
|
const [open, setOpen] = useState(false);
|
|
4119
5145
|
const [settings, setSettings] = useState(() => loadSettings(storageKey));
|
|
5146
|
+
useEffect(() => setLogoColouring(settings.colourLogo), [settings.colourLogo, setLogoColouring]);
|
|
5147
|
+
const [auditOn, setAuditOn] = useState(false);
|
|
5148
|
+
const [findings, setFindings] = useState([]);
|
|
5149
|
+
const recheck = useCallback(() => {
|
|
5150
|
+
requestAnimationFrame(() => setFindings(runAudit({ logoColouring: settingsRef.current.colourLogo })));
|
|
5151
|
+
}, []);
|
|
5152
|
+
const settingsRef = useRef(settings);
|
|
5153
|
+
settingsRef.current = settings;
|
|
5154
|
+
useEffect(() => {
|
|
5155
|
+
if (!auditOn) return;
|
|
5156
|
+
const timer = setTimeout(recheck, 150);
|
|
5157
|
+
return () => clearTimeout(timer);
|
|
5158
|
+
}, [
|
|
5159
|
+
auditOn,
|
|
5160
|
+
tokens,
|
|
5161
|
+
settings.colourLogo,
|
|
5162
|
+
recheck
|
|
5163
|
+
]);
|
|
5164
|
+
useEffect(() => {
|
|
5165
|
+
const setHidden = (hide) => setSettings((s) => s.hideButton === hide ? s : storeSettings({
|
|
5166
|
+
...s,
|
|
5167
|
+
hideButton: hide
|
|
5168
|
+
}));
|
|
5169
|
+
if (new URLSearchParams(window.location.search).has("colorsbymax")) setHidden(false);
|
|
5170
|
+
const onKey = (e) => {
|
|
5171
|
+
if (e.altKey && e.shiftKey && e.code === "KeyC") {
|
|
5172
|
+
e.preventDefault();
|
|
5173
|
+
setSettings((s) => storeSettings({
|
|
5174
|
+
...s,
|
|
5175
|
+
hideButton: !s.hideButton
|
|
5176
|
+
}));
|
|
5177
|
+
}
|
|
5178
|
+
};
|
|
5179
|
+
window.addEventListener("keydown", onKey);
|
|
5180
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
5181
|
+
}, []);
|
|
4120
5182
|
const prefersDark = usePrefersDark();
|
|
4121
5183
|
const mode = settings.mode === "system" ? prefersDark ? "dark" : "light" : settings.mode;
|
|
4122
5184
|
const buttonRef = useRef(null);
|
|
@@ -4257,6 +5319,18 @@ function Switcher() {
|
|
|
4257
5319
|
const settingsApi = {
|
|
4258
5320
|
settings,
|
|
4259
5321
|
mode,
|
|
5322
|
+
audit: {
|
|
5323
|
+
on: auditOn,
|
|
5324
|
+
toggle() {
|
|
5325
|
+
if (auditOn) {
|
|
5326
|
+
setAuditOn(false);
|
|
5327
|
+
setFindings([]);
|
|
5328
|
+
} else {
|
|
5329
|
+
setOpen(false);
|
|
5330
|
+
setAuditOn(true);
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
},
|
|
4260
5334
|
update: (patch) => setSettings((s) => storeSettings({
|
|
4261
5335
|
...s,
|
|
4262
5336
|
...patch
|
|
@@ -4327,6 +5401,7 @@ function Switcher() {
|
|
|
4327
5401
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
4328
5402
|
};
|
|
4329
5403
|
}, [open, close]);
|
|
5404
|
+
if (settings.hideButton) return null;
|
|
4330
5405
|
return /* @__PURE__ */ jsx(SettingsContext.Provider, {
|
|
4331
5406
|
value: settingsApi,
|
|
4332
5407
|
children: /* @__PURE__ */ jsxs("div", {
|
|
@@ -4389,6 +5464,19 @@ function Switcher() {
|
|
|
4389
5464
|
active: Boolean(liveSize)
|
|
4390
5465
|
})]
|
|
4391
5466
|
}),
|
|
5467
|
+
auditOn && /* @__PURE__ */ jsx(AuditLayer, {
|
|
5468
|
+
findings,
|
|
5469
|
+
anchor: buttonRef,
|
|
5470
|
+
themeIssues: issues.length,
|
|
5471
|
+
onRecheck: recheck,
|
|
5472
|
+
onClose: () => {
|
|
5473
|
+
setAuditOn(false);
|
|
5474
|
+
setFindings([]);
|
|
5475
|
+
},
|
|
5476
|
+
onAction: (action) => {
|
|
5477
|
+
if (action === "colour-logo") settingsApi.update({ colourLogo: true });
|
|
5478
|
+
}
|
|
5479
|
+
}),
|
|
4392
5480
|
/* @__PURE__ */ jsx(TooltipLayer, { rootRef: layerRef })
|
|
4393
5481
|
]
|
|
4394
5482
|
})
|