@jsenv/dom 0.7.3 → 0.8.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.
Files changed (2) hide show
  1. package/dist/jsenv_dom.js +111 -26
  2. package/package.json +3 -3
package/dist/jsenv_dom.js CHANGED
@@ -398,7 +398,7 @@ const setStyles = createSetMany$1(setStyle);
398
398
  const forceStyles = createSetMany$1(forceStyle);
399
399
 
400
400
  // Properties that need px units
401
- const pxProperties = [
401
+ const pxPropertySet = new Set([
402
402
  "width",
403
403
  "height",
404
404
  "top",
@@ -415,14 +415,13 @@ const pxProperties = [
415
415
  "paddingRight",
416
416
  "paddingBottom",
417
417
  "paddingLeft",
418
- "border",
419
418
  "borderWidth",
420
419
  "borderTopWidth",
421
420
  "borderRightWidth",
422
421
  "borderBottomWidth",
423
422
  "borderLeftWidth",
424
423
  "fontSize",
425
- "lineHeight",
424
+ // lineHeight intentionally excluded - it should remain unitless when no unit is specified
426
425
  "letterSpacing",
427
426
  "wordSpacing",
428
427
  "translateX",
@@ -436,10 +435,10 @@ const pxProperties = [
436
435
  "gap",
437
436
  "rowGap",
438
437
  "columnGap",
439
- ];
438
+ ]);
440
439
 
441
440
  // Properties that need deg units
442
- const degProperties = [
441
+ const degPropertySet = new Set([
443
442
  "rotate",
444
443
  "rotateX",
445
444
  "rotateY",
@@ -447,10 +446,10 @@ const degProperties = [
447
446
  "skew",
448
447
  "skewX",
449
448
  "skewY",
450
- ];
449
+ ]);
451
450
 
452
451
  // Properties that should remain unitless
453
- const unitlessProperties = [
452
+ const unitlessPropertySet = new Set([
454
453
  "opacity",
455
454
  "zIndex",
456
455
  "flexGrow",
@@ -461,7 +460,7 @@ const unitlessProperties = [
461
460
  "scaleX",
462
461
  "scaleY",
463
462
  "scaleZ",
464
- ];
463
+ ]);
465
464
 
466
465
  // Well-known CSS units and keywords that indicate a value already has proper formatting
467
466
  const cssSizeUnitSet = new Set([
@@ -505,19 +504,25 @@ const cssKeywordSet = new Set([
505
504
  "revert",
506
505
  ]);
507
506
 
508
- // Check if value already has a unit or is a keyword
509
- const hasUnit = (value) => {
507
+ const getUnit = (value) => {
510
508
  for (const cssUnit of cssUnitSet) {
511
509
  if (value.endsWith(cssUnit)) {
512
- return true;
510
+ return cssUnit;
513
511
  }
514
512
  }
515
- return false;
513
+ return "";
516
514
  };
515
+ // Check if value already has a unit
516
+ const isUnitless = (value) => getUnit(value) === "";
517
517
  const isKeyword = (value) => {
518
518
  return cssKeywordSet.has(value);
519
519
  };
520
520
 
521
+ // url(
522
+ // linear-gradient(
523
+ // radial-gradient(
524
+ // ...
525
+ const STARTS_WITH_CSS_IMAGE_FUNCTION_REGEX = /^[a-z-]+\(/;
521
526
  // Normalize a single style value
522
527
  const normalizeStyle = (value, propertyName, context = "js") => {
523
528
  if (propertyName === "transform") {
@@ -571,21 +576,58 @@ const normalizeStyle = (value, propertyName, context = "js") => {
571
576
  return undefined;
572
577
  }
573
578
 
574
- if (pxProperties.includes(propertyName)) {
579
+ if (propertyName === "backgroundImage") {
580
+ if (context === "css") {
581
+ if (
582
+ typeof value === "string" &&
583
+ !isKeyword(value) &&
584
+ !STARTS_WITH_CSS_IMAGE_FUNCTION_REGEX.test(value)
585
+ ) {
586
+ return `url(${value})`;
587
+ }
588
+ }
589
+ return value;
590
+ }
591
+
592
+ if (propertyName === "lineHeight") {
593
+ if (context === "js") {
594
+ if (typeof value === "string") {
595
+ const unit = getUnit(value);
596
+ if (unit === "px") {
597
+ const float = parseFloat(value);
598
+ return float;
599
+ }
600
+ if (unit === "") {
601
+ return `${value}em`;
602
+ }
603
+ return value;
604
+ }
605
+ }
606
+ if (context === "css") {
607
+ if (typeof value === "number") {
608
+ // When line height is converted to a number it means
609
+ // it was in pixels, we must restore the unit
610
+ return `${value}px`;
611
+ }
612
+ }
613
+ return value;
614
+ }
615
+
616
+ if (pxPropertySet.has(propertyName)) {
575
617
  return normalizeNumber(value, {
576
618
  propertyName,
577
619
  unit: "px",
578
620
  preferedType: context === "js" ? "number" : "string",
579
621
  });
580
622
  }
581
- if (degProperties.includes(propertyName)) {
623
+ if (degPropertySet.has(propertyName)) {
582
624
  return normalizeNumber(value, {
583
625
  propertyName,
584
626
  unit: "deg",
585
627
  preferedType: "string",
586
628
  });
587
629
  }
588
- if (unitlessProperties.includes(propertyName)) {
630
+ if (unitlessPropertySet.has(propertyName)) {
589
631
  return normalizeNumber(value, {
590
632
  propertyName,
591
633
  unit: "",
@@ -599,7 +641,7 @@ const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
599
641
  if (typeof value === "string") {
600
642
  // Keep strings as-is (including %, em, rem, auto, none, etc.)
601
643
  if (preferedType === "string") {
602
- if (unit && !hasUnit(value) && !isKeyword(value)) {
644
+ if (unit && isUnitless(value) && !isKeyword(value)) {
603
645
  return `${value}${unit}`;
604
646
  }
605
647
  return value;
@@ -646,6 +688,9 @@ const normalizeStyles = (styles, context = "js", mutate = false) => {
646
688
  const normalized = {};
647
689
  for (const key of Object.keys(styles)) {
648
690
  const value = styles[key];
691
+ if (value === undefined) {
692
+ continue;
693
+ }
649
694
  normalized[key] = normalizeStyle(value, key, context);
650
695
  }
651
696
  return normalized;
@@ -860,7 +905,7 @@ const parseSimple2DMatrix = (a, b, c, d, e, f) => {
860
905
  };
861
906
 
862
907
  // Merge two style objects, handling special cases like transform
863
- const mergeStyles = (stylesA, stylesB, context = "js") => {
908
+ const mergeTwoStyles = (stylesA, stylesB, context = "js") => {
864
909
  if (!stylesA) {
865
910
  return normalizeStyles(stylesB, context);
866
911
  }
@@ -1100,7 +1145,7 @@ const createStyleController = (name = "anonymous") => {
1100
1145
  }
1101
1146
 
1102
1147
  const { styles, animation } = elementData;
1103
- const mergedStyles = mergeStyles(styles, stylesToSet);
1148
+ const mergedStyles = mergeTwoStyles(styles, stylesToSet);
1104
1149
  elementData.styles = mergedStyles;
1105
1150
  updateAnimationStyles(animation, mergedStyles);
1106
1151
  };
@@ -1330,8 +1375,12 @@ const createStyleController = (name = "anonymous") => {
1330
1375
  return controller;
1331
1376
  };
1332
1377
 
1378
+ const getStyleForKeyframe = (styles) => {
1379
+ const cssStyles = normalizeStyles(styles, "css");
1380
+ return cssStyles;
1381
+ };
1333
1382
  const createAnimationForStyles = (element, styles, id) => {
1334
- const cssStylesToSet = normalizeStyles(styles, "css");
1383
+ const cssStylesToSet = getStyleForKeyframe(styles);
1335
1384
  const animation = element.animate([cssStylesToSet], {
1336
1385
  duration: 0,
1337
1386
  fill: "forwards",
@@ -1343,7 +1392,7 @@ const createAnimationForStyles = (element, styles, id) => {
1343
1392
  };
1344
1393
 
1345
1394
  const updateAnimationStyles = (animation, styles) => {
1346
- const cssStyles = normalizeStyles(styles, "css");
1395
+ const cssStyles = getStyleForKeyframe(styles);
1347
1396
  animation.effect.setKeyframes([cssStyles]);
1348
1397
  animation.play();
1349
1398
  animation.pause();
@@ -2131,6 +2180,11 @@ const resolveCSSColor = (color, element, context = "js") => {
2131
2180
  }
2132
2181
  }
2133
2182
 
2183
+ if (color.startsWith("--")) {
2184
+ console.warn(`found "${color}". Use "var(${color})" instead.`);
2185
+ return null;
2186
+ }
2187
+
2134
2188
  // Parse the resolved color and return in the requested format
2135
2189
  const rgba = parseCSSColor(resolvedColor);
2136
2190
 
@@ -2143,18 +2197,24 @@ const resolveCSSColor = (color, element, context = "js") => {
2143
2197
 
2144
2198
  /**
2145
2199
  * Chooses between light and dark colors based on which provides better contrast against a background
2146
- * @param {Element} element - DOM element to resolve CSS variables against
2147
- * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.)
2148
- * @param {string} lightColor - Light color option (typically for dark backgrounds)
2149
- * @param {string} darkColor - Dark color option (typically for light backgrounds)
2200
+ * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.) to test against
2201
+ * @param {string} [lightColor="white"] - Light color option (typically for dark backgrounds)
2202
+ * @param {string} [darkColor="black"] - Dark color option (typically for light backgrounds)
2203
+ * @param {Element} [element] - DOM element to resolve CSS variables against
2150
2204
  * @returns {string} The color that provides better contrast (lightColor or darkColor)
2205
+ * @example
2206
+ * // Choose text color for a dark blue background
2207
+ * pickLightOrDark("#1a202c") // returns "white"
2208
+ *
2209
+ * // Choose text color for a light background with CSS variable
2210
+ * pickLightOrDark("var(--bg-color)", "white", "black", element) // returns "black" or "white"
2151
2211
  */
2152
2212
 
2153
2213
  const pickLightOrDark = (
2154
- element,
2155
2214
  backgroundColor,
2156
2215
  lightColor = "white",
2157
2216
  darkColor = "black",
2217
+ element,
2158
2218
  ) => {
2159
2219
  const resolvedBgColor = resolveCSSColor(backgroundColor, element);
2160
2220
  const resolvedLightColor = resolveCSSColor(lightColor, element);
@@ -2174,6 +2234,31 @@ const pickLightOrDark = (
2174
2234
  return contrastWithLight > contrastWithDark ? lightColor : darkColor;
2175
2235
  };
2176
2236
 
2237
+ /**
2238
+ * Resolves the luminance value of a CSS color
2239
+ * @param {string} color - CSS color value (hex, rgb, hsl, CSS variable, etc.)
2240
+ * @param {Element} [element] - DOM element to resolve CSS variables against
2241
+ * @returns {number|undefined} Relative luminance (0-1) according to WCAG formula, or undefined if color cannot be resolved
2242
+ * @example
2243
+ * // Get luminance of a hex color
2244
+ * resolveColorLuminance("#ff0000") // returns ~0.213 (red)
2245
+ *
2246
+ * // Get luminance of a CSS variable
2247
+ * resolveColorLuminance("var(--primary-color)", element) // returns luminance value or undefined
2248
+ *
2249
+ * // Use for light/dark classification
2250
+ * const luminance = resolveColorLuminance("#2ecc71");
2251
+ * const isLight = luminance > 0.3; // true for light colors, false for dark
2252
+ */
2253
+ const resolveColorLuminance = (color, element) => {
2254
+ const resolvedColor = resolveCSSColor(color, element);
2255
+ if (!resolvedColor) {
2256
+ return undefined;
2257
+ }
2258
+ const [r, g, b] = resolvedColor;
2259
+ return getLuminance(r, g, b);
2260
+ };
2261
+
2177
2262
  const findAncestor = (node, predicate) => {
2178
2263
  let ancestor = node.parentNode;
2179
2264
  while (ancestor) {
@@ -11785,4 +11870,4 @@ const notifyTransitionOverflow = (element, transitionId) => {
11785
11870
  };
11786
11871
  };
11787
11872
 
11788
- export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateY, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeStyles, normalizeStyle, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
11873
+ export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateY, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeTwoStyles, normalizeStyle, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, resolveColorLuminance, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "description": "DOM utilities for writing frontend code",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,8 +36,8 @@
36
36
  "devDependencies": {
37
37
  "@jsenv/core": "../../../",
38
38
  "@jsenv/navi": "../navi",
39
- "@preact/signals": "^2.3.2",
40
- "preact": "^10.27.2"
39
+ "@preact/signals": "2.4.0",
40
+ "preact": "11.0.0-beta.0"
41
41
  },
42
42
  "publishConfig": {
43
43
  "access": "public"