colorsbymax 0.4.0 → 0.4.1

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.
@@ -6121,11 +6121,28 @@ function PaletteFromFile() {
6121
6121
  ]
6122
6122
  });
6123
6123
  }
6124
+ var EXPORT_FORMATS = [{
6125
+ id: "json",
6126
+ label: "JSON",
6127
+ type: "application/json",
6128
+ ext: "theme.json",
6129
+ what: "theme JSON"
6130
+ }, {
6131
+ id: "css",
6132
+ label: "CSS",
6133
+ type: "text/css",
6134
+ ext: "theme.css",
6135
+ what: "theme CSS"
6136
+ }];
6124
6137
  function ImportExport() {
6125
- const { exportTheme, importTheme } = useTheme();
6138
+ const { exportTheme, importTheme, tokens } = useTheme();
6126
6139
  const { toast } = usePanel();
6127
6140
  const savedToYours = useSavedToYours();
6128
6141
  const json = exportTheme();
6142
+ const themeName = JSON.parse(json).name;
6143
+ const [format, setFormat] = useState("json");
6144
+ const out = EXPORT_FORMATS.find((f) => f.id === format);
6145
+ const text = format === "css" ? `/* ${themeName.replace(/\*\//g, "")}, from colorsbymax */\n${cssSnippet(tokens)}\n` : json;
6129
6146
  const [status, setStatus] = useState(null);
6130
6147
  const [input, setInput] = useState("");
6131
6148
  const exportId = useId();
@@ -6152,15 +6169,30 @@ function ImportExport() {
6152
6169
  /* @__PURE__ */ jsxs("div", {
6153
6170
  className: "space-y-1.5",
6154
6171
  children: [
6155
- /* @__PURE__ */ jsx("label", {
6156
- htmlFor: exportId,
6157
- className: "block text-xs font-medium text-zinc-700",
6158
- children: "Current theme as JSON"
6172
+ /* @__PURE__ */ jsxs("div", {
6173
+ className: "flex items-center justify-between gap-2",
6174
+ children: [/* @__PURE__ */ jsxs("label", {
6175
+ htmlFor: exportId,
6176
+ className: "block text-xs font-medium text-zinc-700",
6177
+ children: ["Current theme as ", out.label]
6178
+ }), /* @__PURE__ */ jsx("div", {
6179
+ role: "radiogroup",
6180
+ "aria-label": "Export as",
6181
+ className: "flex shrink-0 items-center rounded-lg border border-zinc-200 p-0.5",
6182
+ children: EXPORT_FORMATS.map((f) => /* @__PURE__ */ jsx("button", {
6183
+ type: "button",
6184
+ role: "radio",
6185
+ "aria-checked": format === f.id,
6186
+ onClick: () => setFormat(f.id),
6187
+ className: `h-6 rounded-md px-2 text-[11px] font-semibold cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${format === f.id ? "bg-zinc-900 text-white" : "text-zinc-700 hover:bg-zinc-100"}`,
6188
+ children: f.label
6189
+ }, f.id))
6190
+ })]
6159
6191
  }),
6160
6192
  /* @__PURE__ */ jsx("textarea", {
6161
6193
  id: exportId,
6162
6194
  readOnly: true,
6163
- value: json,
6195
+ value: text,
6164
6196
  rows: 5,
6165
6197
  className: `${field} font-mono text-[11px]`
6166
6198
  }),
@@ -6171,9 +6203,9 @@ function ImportExport() {
6171
6203
  className: btn,
6172
6204
  onClick: async () => {
6173
6205
  try {
6174
- await navigator.clipboard.writeText(json);
6206
+ await navigator.clipboard.writeText(text);
6175
6207
  setStatus(null);
6176
- toast("Copied the theme JSON to your clipboard.");
6208
+ toast(`Copied the ${out.what} to your clipboard.`);
6177
6209
  } catch {
6178
6210
  setStatus({
6179
6211
  ok: false,
@@ -6189,10 +6221,10 @@ function ImportExport() {
6189
6221
  type: "button",
6190
6222
  className: btn,
6191
6223
  onClick: () => {
6192
- const url = URL.createObjectURL(new Blob([json], { type: "application/json" }));
6224
+ const url = URL.createObjectURL(new Blob([text], { type: out.type }));
6193
6225
  const a = document.createElement("a");
6194
6226
  a.href = url;
6195
- a.download = `${JSON.parse(json).name.toLowerCase().replace(/[^a-z0-9]+/g, "-")}.theme.json`;
6227
+ a.download = `${themeName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}.${out.ext}`;
6196
6228
  a.click();
6197
6229
  toast(`Downloaded ${a.download}.`);
6198
6230
  URL.revokeObjectURL(url);
@@ -6898,7 +6930,12 @@ function Switcher() {
6898
6930
  width: settings.panelWidth,
6899
6931
  height: settings.panelHeight
6900
6932
  };
6901
- const layout = panelLayout(placed ?? (position === "top-right" ? null : cornerPoint(position, viewport)), viewport, size);
6933
+ const offsetTop = useTopOffset();
6934
+ const corner = placed ?? (position === "top-right" ? null : cornerPoint(position, viewport));
6935
+ const layout = panelLayout(corner && !placed && position.startsWith("top") ? {
6936
+ ...corner,
6937
+ top: corner.top + offsetTop
6938
+ } : corner, viewport, size, offsetTop);
6902
6939
  const onResizeStart = (edges) => (e) => {
6903
6940
  if (e.button !== 0) return;
6904
6941
  e.preventDefault();
@@ -7135,7 +7172,7 @@ function Switcher() {
7135
7172
  onPointerUp: onDragEnd,
7136
7173
  onPointerCancel: onDragEnd,
7137
7174
  onLostPointerCapture: onDragEnd,
7138
- style: placed ?? void 0,
7175
+ style: placed ?? (offsetTop && position.startsWith("top") ? { marginTop: offsetTop } : void 0),
7139
7176
  className: `theme-switcher fixed z-[60] ${placed ? "" : CORNERS[position]} grid place-items-center w-9 h-9 rounded-full border border-zinc-200 bg-white/90 text-zinc-700 backdrop-blur hover:bg-white hover:text-zinc-900 transition-[color,background-color,box-shadow,scale] select-none ${settings.draggable ? "touch-none" : ""} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-2 ${dragPoint ? "scale-110 shadow-xl cursor-grabbing" : "shadow-md cursor-pointer"} ${entering ? "theme-arrive" : ""}`,
7140
7177
  children: [
7141
7178
  /* @__PURE__ */ jsx(Palette, {
@@ -7313,6 +7350,29 @@ function TooltipLayer({ rootRef }) {
7313
7350
  });
7314
7351
  }
7315
7352
  /** Viewport size without scrollbars: the area fixed elements are placed in. */
7353
+ /**
7354
+ * How far a bar the site pins to the top of the page (above its nav, e.g. an announcement) pushes
7355
+ * the colour button and panel down: the CSS variable --colorsbymax-offset-top on <html>, in px.
7356
+ */
7357
+ function useTopOffset() {
7358
+ const read = () => parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--colorsbymax-offset-top")) || 0;
7359
+ const [offset, setOffset] = useState(read);
7360
+ useLayoutEffect(() => {
7361
+ const update = () => setOffset(read());
7362
+ const observer = new MutationObserver(update);
7363
+ observer.observe(document.documentElement, {
7364
+ attributes: true,
7365
+ attributeFilter: ["style", "class"]
7366
+ });
7367
+ window.addEventListener("resize", update);
7368
+ update();
7369
+ return () => {
7370
+ observer.disconnect();
7371
+ window.removeEventListener("resize", update);
7372
+ };
7373
+ }, []);
7374
+ return offset;
7375
+ }
7316
7376
  function useViewport() {
7317
7377
  const read = () => ({
7318
7378
  width: document.documentElement.clientWidth,
@@ -7367,7 +7427,7 @@ function cornerPoint(position, { width, height }) {
7367
7427
  * @param {{ left: number, top: number } | null} button
7368
7428
  * @param {{ width: number | null, height: number | 'full' | null }} size
7369
7429
  */
7370
- function panelLayout(button, { width: vw, height: vh }, size) {
7430
+ function panelLayout(button, { width: vw, height: vh }, size, offsetTop = 0) {
7371
7431
  const narrow = vw < 640;
7372
7432
  const style = {};
7373
7433
  const free = {
@@ -7380,7 +7440,7 @@ function panelLayout(button, { width: vw, height: vh }, size) {
7380
7440
  let maxHeight;
7381
7441
  if (!button) {
7382
7442
  const wide = vw >= 1200;
7383
- style.top = wide ? 72 : 128;
7443
+ style.top = (wide ? 72 : 128) + offsetTop;
7384
7444
  style.right = narrow ? EDGE : wide ? 16 : 24;
7385
7445
  maxHeight = vh - style.top - EDGE;
7386
7446
  free.bottom = true;
package/dist/auto.js CHANGED
@@ -1,4 +1,4 @@
1
- import { o as ThemeProvider, t as ThemeSwitcher } from "./ThemeSwitcher-DB_-2YB8.js";
1
+ import { o as ThemeProvider, t as ThemeSwitcher } from "./ThemeSwitcher-BFOWqFUZ.js";
2
2
  import { createElement } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
4
  //#region src/auto.js
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as PAIRINGS, C as TOKEN_KEYS, D as MIN_CONTRAST_NON_TEXT, E as MIN_CONTRAST_LARGE_TEXT, F as contrastRatio, I as normalizeHex, M as checkTheme, N as fixAll, O as MIN_CONTRAST_TEXT, P as suggestFix, S as TOKEN_GROUPS, T as deriveAppTokens, _ as darkTokens, a as themeFromPalette, b as BASE_TOKENS, c as useTheme, d as DEFAULT_SETTINGS, f as collectColors, g as themeFromRoles, h as suggestThemes, i as rolesFromPalette, j as checkRamp, k as MIN_RAMP_STEP_DELTA_E, l as DEFAULT_STORAGE_KEY, m as inferRoles, n as coloursFromFile, o as ThemeProvider, p as detectSiteName, r as dominantColours, s as applyTokens, t as ThemeSwitcher, u as prePaintScript, v as isDarkTheme, w as completeTokens, x as PRESETS, y as toDark } from "./ThemeSwitcher-DB_-2YB8.js";
1
+ import { A as PAIRINGS, C as TOKEN_KEYS, D as MIN_CONTRAST_NON_TEXT, E as MIN_CONTRAST_LARGE_TEXT, F as contrastRatio, I as normalizeHex, M as checkTheme, N as fixAll, O as MIN_CONTRAST_TEXT, P as suggestFix, S as TOKEN_GROUPS, T as deriveAppTokens, _ as darkTokens, a as themeFromPalette, b as BASE_TOKENS, c as useTheme, d as DEFAULT_SETTINGS, f as collectColors, g as themeFromRoles, h as suggestThemes, i as rolesFromPalette, j as checkRamp, k as MIN_RAMP_STEP_DELTA_E, l as DEFAULT_STORAGE_KEY, m as inferRoles, n as coloursFromFile, o as ThemeProvider, p as detectSiteName, r as dominantColours, s as applyTokens, t as ThemeSwitcher, u as prePaintScript, v as isDarkTheme, w as completeTokens, x as PRESETS, y as toDark } from "./ThemeSwitcher-BFOWqFUZ.js";
2
2
  export { BASE_TOKENS, DEFAULT_SETTINGS, DEFAULT_STORAGE_KEY, MIN_CONTRAST_LARGE_TEXT, MIN_CONTRAST_NON_TEXT, MIN_CONTRAST_TEXT, MIN_RAMP_STEP_DELTA_E, PAIRINGS, PRESETS, TOKEN_GROUPS, TOKEN_KEYS, ThemeProvider, ThemeSwitcher, applyTokens, checkRamp, checkTheme, collectColors, coloursFromFile, completeTokens, contrastRatio, darkTokens, deriveAppTokens, detectSiteName, dominantColours, fixAll, inferRoles, isDarkTheme, normalizeHex, prePaintScript, rolesFromPalette, suggestFix, suggestThemes, themeFromPalette, themeFromRoles, toDark, useTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "colorsbymax",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "A live theme switcher for any website: site themes, 700+ palettes, for light/dark modes and WCAG contrast checks.",
5
5
  "keywords": [
6
6
  "theme",