aberdeen 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -252,23 +252,85 @@ export declare function merge<T extends object>(dst: T, dstKey: keyof T, value:
252
252
  */
253
253
  export declare const NO_COPY: unique symbol;
254
254
  /**
255
- * CSS variables that are output as native CSS custom properties.
255
+ * A reactive object containing CSS variable definitions.
256
256
  *
257
- * When a CSS value starts with `@`, it becomes `var(--name)` (or `var(--mN)` for numeric keys).
258
- * Pre-initialized with keys '1'-'12' mapping to an exponential rem scale (e.g., @1=0.25rem, @3=1rem).
257
+ * Any property you assign to `cssVars` becomes available as a CSS custom property throughout your application.
259
258
  *
260
- * Changes to cssVars are automatically reflected in a `<style>` tag in `<head>`, making updates
261
- * reactive across all elements using those variables.
259
+ * Use {@link setSpacingCssVars} to optionally initialize `cssVars[1]` through `cssVars[12]` with an exponential spacing scale.
260
+ *
261
+ * When you reference a CSS variable in Aberdeen using the `$` prefix (e.g., `$primary`), it automatically resolves to `var(--primary)`.
262
+ * For numeric keys (which can't be used directly as CSS custom property names), Aberdeen prefixes them with `m` (e.g., `$3` becomes `var(--m3)`).
263
+ *
264
+ * When you add the first property to cssVars, Aberdeen automatically creates a reactive `<style>` tag in `<head>`
265
+ * containing the `:root` CSS custom property declarations. The style tag is automatically removed if cssVars becomes empty.
262
266
  *
263
267
  * @example
264
- * ```typescript
268
+ * ```javascript
269
+ * import { cssVars, setSpacingCssVars, $ } from 'aberdeen';
270
+ *
271
+ * // Optionally initialize spacing scale
272
+ * setSpacingCssVars(); // Uses defaults: base=1, unit='rem'
273
+ *
274
+ * // Define custom colors - style tag is automatically created
265
275
  * cssVars.primary = '#3b82f6';
266
- * cssVars[3] = '16px'; // Override @3 to be 16px instead of 1rem
267
- * $('p color:@primary'); // Sets color to var(--primary)
268
- * $('div mt:@3'); // Sets margin-top to var(--m3)
276
+ * cssVars.danger = '#ef4444';
277
+ *
278
+ * // Use in elements with the $ prefix
279
+ * $('button bg:$primary fg:white');
280
+ *
281
+ * // Use spacing (if setSpacingCssVars() was called)
282
+ * $('div mt:$3'); // Sets margin-top to var(--m3)
269
283
  * ```
270
284
  */
271
285
  export declare const cssVars: Record<string, string>;
286
+ /**
287
+ * Initializes `cssVars[1]` through `cssVars[12]` with an exponential spacing scale.
288
+ *
289
+ * The scale is calculated as `2^(n-3) * base`, providing values from `0.25 * base` to `512 * base`.
290
+ *
291
+ * @param base - The base size for the spacing scale (default: 1). If unit is 'rem' or 'em', this is in that unit. If unit is 'px', this is the pixel value.
292
+ * @param unit - The CSS unit to use (default: 'rem'). Can be 'rem', 'em', 'px', or any other valid CSS unit.
293
+ *
294
+ * @example
295
+ * ```javascript
296
+ * // Use default scale (0.25rem to 512rem)
297
+ * setSpacingCssVars();
298
+ *
299
+ * // Use custom base size
300
+ * setSpacingCssVars(16, 'px'); // 4px to 8192px
301
+ *
302
+ * // Use em units
303
+ * setSpacingCssVars(1, 'em'); // 0.25em to 512em
304
+ * ```
305
+ */
306
+ export declare function setSpacingCssVars(base?: number, unit?: string): void;
307
+ /**
308
+ * Returns whether the user's browser prefers a dark color scheme.
309
+ *
310
+ * This function is reactive - scopes that call it will re-execute when the
311
+ * browser's color scheme preference changes (via the `prefers-color-scheme` media query).
312
+ *
313
+ * Use this in combination with {@link $} and {@link cssVars} to implement theme switching:
314
+ *
315
+ * @returns `true` if the browser prefers dark mode, `false` if it prefers light mode.
316
+ *
317
+ * @example
318
+ * ```javascript
319
+ * import { darkMode, cssVars, $, mount } from 'aberdeen';
320
+ *
321
+ * // Reactively set colors based on browser preference
322
+ * $(() => {
323
+ * if (darkMode()) { // Optionally override this with user settings
324
+ * cssVars.bg = '#1a1a1a';
325
+ * cssVars.fg = '#e5e5e5';
326
+ * } else {
327
+ * cssVars.bg = '#ffffff';
328
+ * cssVars.fg = '#000000';
329
+ * }
330
+ * });
331
+ * ```
332
+ */
333
+ export declare function darkMode(): boolean;
272
334
  /**
273
335
  * Clone an (optionally proxied) object or array.
274
336
  *
package/dist/aberdeen.js CHANGED
@@ -1017,7 +1017,7 @@ function copyRecursive(dst, src, flags) {
1017
1017
  const old = dst.get(k);
1018
1018
  dst.delete(k);
1019
1019
  if (flags & COPY_EMIT) {
1020
- emit(dst, k, undefined, old);
1020
+ emit(dst, k, EMPTY, old);
1021
1021
  }
1022
1022
  changed = true;
1023
1023
  }
@@ -1047,7 +1047,7 @@ function copyRecursive(dst, src, flags) {
1047
1047
  const old = dst[k];
1048
1048
  delete dst[k];
1049
1049
  if (flags & COPY_EMIT && old !== undefined) {
1050
- emit(dst, k, undefined, old);
1050
+ emit(dst, k, EMPTY, old);
1051
1051
  }
1052
1052
  changed = true;
1053
1053
  }
@@ -1064,14 +1064,52 @@ var COPY_EMIT = 64;
1064
1064
  var NO_COPY = Symbol("NO_COPY");
1065
1065
  Promise.prototype[NO_COPY] = true;
1066
1066
  var cssVars = optProxy({});
1067
- for (let i = 1;i <= 12; i++) {
1068
- cssVars[i] = 2 ** (i - 3) + "rem";
1067
+ function setSpacingCssVars(base = 1, unit = "rem") {
1068
+ for (let i = 1;i <= 12; i++) {
1069
+ cssVars[i] = 2 ** (i - 3) * base + unit;
1070
+ }
1069
1071
  }
1070
1072
  var DIGIT_FIRST = /^\d/;
1071
1073
  function cssVarRef(name) {
1072
1074
  const varName = DIGIT_FIRST.test(name) ? `m${name}` : name;
1073
1075
  return `var(--${varName})`;
1074
1076
  }
1077
+ if (typeof document !== "undefined") {
1078
+ leakScope(() => {
1079
+ $(() => {
1080
+ if (!isEmpty(cssVars)) {
1081
+ mount(document.head, () => {
1082
+ $("style", () => {
1083
+ let css = `:root {
1084
+ `;
1085
+ for (const [key, value] of Object.entries(cssVars)) {
1086
+ const varName = DIGIT_FIRST.test(String(key)) ? `m${key}` : key;
1087
+ css += ` --${varName}: ${value};
1088
+ `;
1089
+ }
1090
+ css += "}";
1091
+ $(`#${css}`);
1092
+ });
1093
+ });
1094
+ }
1095
+ });
1096
+ });
1097
+ }
1098
+ function darkMode() {
1099
+ if (typeof window === "undefined" || !window.matchMedia)
1100
+ return false;
1101
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
1102
+ const changed = proxy(false);
1103
+ changed.value;
1104
+ function onChange() {
1105
+ changed.value = true;
1106
+ }
1107
+ mediaQuery.addEventListener("change", onChange);
1108
+ clean(() => {
1109
+ mediaQuery.removeEventListener("change", onChange);
1110
+ });
1111
+ return mediaQuery.matches;
1112
+ }
1075
1113
  function clone(src) {
1076
1114
  if (NO_COPY in src)
1077
1115
  return src;
@@ -1310,7 +1348,7 @@ ${styleToCss(v, prefix)}}
1310
1348
  rules += styleToCss(v, k.includes("&") ? k.replace(/&/g, prefix) : `${prefix} ${k}`);
1311
1349
  }
1312
1350
  } else {
1313
- const val = v == null || v === false ? "" : typeof v === "string" ? v[0] === "@" ? cssVarRef(v.substring(1)) : v : String(v);
1351
+ const val = v == null || v === false ? "" : typeof v === "string" ? v[0] === "$" ? cssVarRef(v.substring(1)) : v : String(v);
1314
1352
  const expanded = CSS_SHORT[k] || k;
1315
1353
  for (const prop of Array.isArray(expanded) ? expanded : [expanded]) {
1316
1354
  props += `${prop.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}:${val};`;
@@ -1338,7 +1376,7 @@ function applyArg(el, key, value) {
1338
1376
  el.classList.remove(...classes);
1339
1377
  } else if (key[0] === "$") {
1340
1378
  key = key.substring(1);
1341
- const val = value == null || value === false ? "" : typeof value === "string" ? value[0] === "@" ? cssVarRef(value.substring(1)) : value : String(value);
1379
+ const val = value == null || value === false ? "" : typeof value === "string" ? value[0] === "$" ? cssVarRef(value.substring(1)) : value : String(value);
1342
1380
  const expanded = CSS_SHORT[key] || key;
1343
1381
  if (typeof expanded === "string") {
1344
1382
  el.style[expanded] = val;
@@ -1507,27 +1545,11 @@ function withEmitHandler(handler, func) {
1507
1545
  emit = oldEmitHandler;
1508
1546
  }
1509
1547
  }
1510
- if (typeof document !== "undefined") {
1511
- leakScope(() => {
1512
- mount(document.head, () => {
1513
- $("style", () => {
1514
- let css = `:root {
1515
- `;
1516
- for (const [key, value] of Object.entries(cssVars)) {
1517
- const varName = DIGIT_FIRST.test(String(key)) ? `m${key}` : key;
1518
- css += ` --${varName}: ${value};
1519
- `;
1520
- }
1521
- css += "}";
1522
- $(`#${css}`);
1523
- });
1524
- });
1525
- });
1526
- }
1527
1548
  export {
1528
1549
  withEmitHandler,
1529
1550
  unproxy,
1530
1551
  unmountAll,
1552
+ setSpacingCssVars,
1531
1553
  setErrorHandler,
1532
1554
  runQueue,
1533
1555
  ref,
@@ -1548,6 +1570,7 @@ export {
1548
1570
  dump,
1549
1571
  derive,
1550
1572
  defaultEmitHandler,
1573
+ darkMode,
1551
1574
  cssVars,
1552
1575
  count,
1553
1576
  copy,
@@ -1557,5 +1580,5 @@ export {
1557
1580
  $
1558
1581
  };
1559
1582
 
1560
- //# debugId=69BD2D56BBDAB97164756E2164756E21
1583
+ //# debugId=30E505D12852725E64756E2164756E21
1561
1584
  //# sourceMappingURL=aberdeen.js.map