@jsenv/dom 0.7.4 → 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 +93 -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,15 +504,16 @@ 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
  };
@@ -589,21 +589,45 @@ const normalizeStyle = (value, propertyName, context = "js") => {
589
589
  return value;
590
590
  }
591
591
 
592
- if (pxProperties.includes(propertyName)) {
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)) {
593
617
  return normalizeNumber(value, {
594
618
  propertyName,
595
619
  unit: "px",
596
620
  preferedType: context === "js" ? "number" : "string",
597
621
  });
598
622
  }
599
- if (degProperties.includes(propertyName)) {
623
+ if (degPropertySet.has(propertyName)) {
600
624
  return normalizeNumber(value, {
601
625
  propertyName,
602
626
  unit: "deg",
603
627
  preferedType: "string",
604
628
  });
605
629
  }
606
- if (unitlessProperties.includes(propertyName)) {
630
+ if (unitlessPropertySet.has(propertyName)) {
607
631
  return normalizeNumber(value, {
608
632
  propertyName,
609
633
  unit: "",
@@ -617,7 +641,7 @@ const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
617
641
  if (typeof value === "string") {
618
642
  // Keep strings as-is (including %, em, rem, auto, none, etc.)
619
643
  if (preferedType === "string") {
620
- if (unit && !hasUnit(value) && !isKeyword(value)) {
644
+ if (unit && isUnitless(value) && !isKeyword(value)) {
621
645
  return `${value}${unit}`;
622
646
  }
623
647
  return value;
@@ -664,6 +688,9 @@ const normalizeStyles = (styles, context = "js", mutate = false) => {
664
688
  const normalized = {};
665
689
  for (const key of Object.keys(styles)) {
666
690
  const value = styles[key];
691
+ if (value === undefined) {
692
+ continue;
693
+ }
667
694
  normalized[key] = normalizeStyle(value, key, context);
668
695
  }
669
696
  return normalized;
@@ -878,7 +905,7 @@ const parseSimple2DMatrix = (a, b, c, d, e, f) => {
878
905
  };
879
906
 
880
907
  // Merge two style objects, handling special cases like transform
881
- const mergeStyles = (stylesA, stylesB, context = "js") => {
908
+ const mergeTwoStyles = (stylesA, stylesB, context = "js") => {
882
909
  if (!stylesA) {
883
910
  return normalizeStyles(stylesB, context);
884
911
  }
@@ -1118,7 +1145,7 @@ const createStyleController = (name = "anonymous") => {
1118
1145
  }
1119
1146
 
1120
1147
  const { styles, animation } = elementData;
1121
- const mergedStyles = mergeStyles(styles, stylesToSet);
1148
+ const mergedStyles = mergeTwoStyles(styles, stylesToSet);
1122
1149
  elementData.styles = mergedStyles;
1123
1150
  updateAnimationStyles(animation, mergedStyles);
1124
1151
  };
@@ -1348,8 +1375,12 @@ const createStyleController = (name = "anonymous") => {
1348
1375
  return controller;
1349
1376
  };
1350
1377
 
1378
+ const getStyleForKeyframe = (styles) => {
1379
+ const cssStyles = normalizeStyles(styles, "css");
1380
+ return cssStyles;
1381
+ };
1351
1382
  const createAnimationForStyles = (element, styles, id) => {
1352
- const cssStylesToSet = normalizeStyles(styles, "css");
1383
+ const cssStylesToSet = getStyleForKeyframe(styles);
1353
1384
  const animation = element.animate([cssStylesToSet], {
1354
1385
  duration: 0,
1355
1386
  fill: "forwards",
@@ -1361,7 +1392,7 @@ const createAnimationForStyles = (element, styles, id) => {
1361
1392
  };
1362
1393
 
1363
1394
  const updateAnimationStyles = (animation, styles) => {
1364
- const cssStyles = normalizeStyles(styles, "css");
1395
+ const cssStyles = getStyleForKeyframe(styles);
1365
1396
  animation.effect.setKeyframes([cssStyles]);
1366
1397
  animation.play();
1367
1398
  animation.pause();
@@ -2149,6 +2180,11 @@ const resolveCSSColor = (color, element, context = "js") => {
2149
2180
  }
2150
2181
  }
2151
2182
 
2183
+ if (color.startsWith("--")) {
2184
+ console.warn(`found "${color}". Use "var(${color})" instead.`);
2185
+ return null;
2186
+ }
2187
+
2152
2188
  // Parse the resolved color and return in the requested format
2153
2189
  const rgba = parseCSSColor(resolvedColor);
2154
2190
 
@@ -2161,18 +2197,24 @@ const resolveCSSColor = (color, element, context = "js") => {
2161
2197
 
2162
2198
  /**
2163
2199
  * Chooses between light and dark colors based on which provides better contrast against a background
2164
- * @param {Element} element - DOM element to resolve CSS variables against
2165
- * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.)
2166
- * @param {string} lightColor - Light color option (typically for dark backgrounds)
2167
- * @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
2168
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"
2169
2211
  */
2170
2212
 
2171
2213
  const pickLightOrDark = (
2172
- element,
2173
2214
  backgroundColor,
2174
2215
  lightColor = "white",
2175
2216
  darkColor = "black",
2217
+ element,
2176
2218
  ) => {
2177
2219
  const resolvedBgColor = resolveCSSColor(backgroundColor, element);
2178
2220
  const resolvedLightColor = resolveCSSColor(lightColor, element);
@@ -2192,6 +2234,31 @@ const pickLightOrDark = (
2192
2234
  return contrastWithLight > contrastWithDark ? lightColor : darkColor;
2193
2235
  };
2194
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
+
2195
2262
  const findAncestor = (node, predicate) => {
2196
2263
  let ancestor = node.parentNode;
2197
2264
  while (ancestor) {
@@ -11803,4 +11870,4 @@ const notifyTransitionOverflow = (element, transitionId) => {
11803
11870
  };
11804
11871
  };
11805
11872
 
11806
- 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.4",
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"