@webstudio-is/css-engine 0.257.0 → 0.258.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.
package/lib/index.js CHANGED
@@ -90,6 +90,12 @@ var toValue = (styleValue, transformValue) => {
90
90
  let [c1, c2, c3] = value.components;
91
91
  const alpha = typeof value.alpha === "number" ? value.alpha : toValue(value.alpha);
92
92
  switch (value.colorSpace) {
93
+ case "hex": {
94
+ const toHex = (v) => Math.round(Math.min(Math.max(v, 0), 1) * 255).toString(16).padStart(2, "0");
95
+ const hex = `#${toHex(c1)}${toHex(c2)}${toHex(c3)}`;
96
+ const alphaNum = typeof alpha === "number" ? alpha : 1;
97
+ return alphaNum < 1 ? hex + toHex(alphaNum) : hex;
98
+ }
93
99
  case "srgb": {
94
100
  c1 = Math.round(c1 * 255);
95
101
  c2 = Math.round(c2 * 255);
@@ -109,7 +115,7 @@ var toValue = (styleValue, transformValue) => {
109
115
  case "oklch":
110
116
  return `oklch(${c1} ${c2} ${c3} / ${alpha})`;
111
117
  // Fall back to color() function for less common color spaces.
112
- // Webstudio uses colorjs internal names; map to CSS predefined color space names.
118
+ // colorjs uses internal short names that differ from CSS predefined color space identifiers.
113
119
  case "p3":
114
120
  return `color(display-p3 ${c1} ${c2} ${c3} / ${alpha})`;
115
121
  case "a98rgb":
@@ -212,6 +218,7 @@ var ColorValue = z.object({
212
218
  type: z.literal("color"),
213
219
  // all these color spaces are defined by design tokens specification
214
220
  colorSpace: z.union([
221
+ z.literal("hex"),
215
222
  z.literal("srgb"),
216
223
  z.literal("p3"),
217
224
  z.literal("srgb-linear"),
@@ -371,12 +378,12 @@ var isLonghandValue = (value) => {
371
378
  var mergeBorder = (styleMap, base) => {
372
379
  const width = styleMap.get(`${base}-width`);
373
380
  const style = styleMap.get(`${base}-style`);
374
- const color = styleMap.get(`${base}-color`);
375
- if (isLonghandValue(width) && isLonghandValue(style) && isLonghandValue(color)) {
381
+ const color2 = styleMap.get(`${base}-color`);
382
+ if (isLonghandValue(width) && isLonghandValue(style) && isLonghandValue(color2)) {
376
383
  styleMap.delete(`${base}-width`);
377
384
  styleMap.delete(`${base}-style`);
378
385
  styleMap.delete(`${base}-color`);
379
- styleMap.set(base, { type: "tuple", value: [width, style, color] });
386
+ styleMap.set(base, { type: "tuple", value: [width, style, color2] });
380
387
  }
381
388
  };
382
389
  var mergeBox = (styleMap, base) => {
@@ -1058,6 +1065,116 @@ var generateAtomic = (sheet, options) => {
1058
1065
  });
1059
1066
  return { cssText, classes };
1060
1067
  };
1068
+
1069
+ // src/color.ts
1070
+ import * as colorjs from "colorjs.io/fn";
1071
+ colorjs.ColorSpace.register(colorjs.sRGB);
1072
+ colorjs.ColorSpace.register(colorjs.sRGB_Linear);
1073
+ colorjs.ColorSpace.register(colorjs.HSL);
1074
+ colorjs.ColorSpace.register(colorjs.HWB);
1075
+ colorjs.ColorSpace.register(colorjs.Lab);
1076
+ colorjs.ColorSpace.register(colorjs.LCH);
1077
+ colorjs.ColorSpace.register(colorjs.OKLab);
1078
+ colorjs.ColorSpace.register(colorjs.OKLCH);
1079
+ colorjs.ColorSpace.register(colorjs.P3);
1080
+ colorjs.ColorSpace.register(colorjs.A98RGB);
1081
+ colorjs.ColorSpace.register(colorjs.ProPhoto);
1082
+ colorjs.ColorSpace.register(colorjs.REC_2020);
1083
+ colorjs.ColorSpace.register(colorjs.XYZ_D65);
1084
+ colorjs.ColorSpace.register(colorjs.XYZ_D50);
1085
+ colorjs.ColorSpace.registry["display-p3"] = colorjs.P3;
1086
+ colorjs.ColorSpace.registry["a98-rgb"] = colorjs.A98RGB;
1087
+ colorjs.ColorSpace.registry["prophoto-rgb"] = colorjs.ProPhoto;
1088
+ var color = colorjs;
1089
+ var registeredSpaces = /* @__PURE__ */ new Map([
1090
+ [colorjs.sRGB, "srgb"],
1091
+ [colorjs.sRGB_Linear, "srgb-linear"],
1092
+ [colorjs.HSL, "hsl"],
1093
+ [colorjs.HWB, "hwb"],
1094
+ [colorjs.Lab, "lab"],
1095
+ [colorjs.LCH, "lch"],
1096
+ [colorjs.OKLab, "oklab"],
1097
+ [colorjs.OKLCH, "oklch"],
1098
+ [colorjs.P3, "p3"],
1099
+ [colorjs.A98RGB, "a98rgb"],
1100
+ [colorjs.ProPhoto, "prophoto"],
1101
+ [colorjs.REC_2020, "rec2020"],
1102
+ [colorjs.XYZ_D65, "xyz-d65"],
1103
+ [colorjs.XYZ_D50, "xyz-d50"]
1104
+ ]);
1105
+ var toColorSpace = (space) => {
1106
+ const id = registeredSpaces.get(space);
1107
+ if (id === void 0) {
1108
+ throw new Error(`Unregistered color space: ${space.id}`);
1109
+ }
1110
+ return id;
1111
+ };
1112
+ var toColorComponent = (value) => Math.round((value ?? 0) * 1e4) / 1e4;
1113
+ var srgbColor = (r, g, b, alpha = 1) => ({
1114
+ space: colorjs.sRGB,
1115
+ coords: [r, g, b],
1116
+ alpha
1117
+ });
1118
+ var transparentColor = srgbColor(0, 0, 0, 0);
1119
+ var whiteColor = srgbColor(1, 1, 1, 1);
1120
+ var resolveColorViaCanvas = (colorString) => {
1121
+ if (typeof document === "undefined") {
1122
+ return;
1123
+ }
1124
+ const canvas = document.createElement("canvas");
1125
+ canvas.width = 1;
1126
+ canvas.height = 1;
1127
+ const ctx = canvas.getContext("2d");
1128
+ if (!ctx) {
1129
+ return;
1130
+ }
1131
+ ctx.fillStyle = colorString;
1132
+ ctx.fillRect(0, 0, 1, 1);
1133
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
1134
+ return srgbColor(
1135
+ (r ?? 0) / 255,
1136
+ (g ?? 0) / 255,
1137
+ (b ?? 0) / 255,
1138
+ (a ?? 255) / 255
1139
+ );
1140
+ };
1141
+ var parseColor = (cssString) => {
1142
+ try {
1143
+ return colorjs.to(cssString, "srgb");
1144
+ } catch {
1145
+ return resolveColorViaCanvas(cssString) ?? transparentColor;
1146
+ }
1147
+ };
1148
+ var colorDistance = (a, b) => {
1149
+ const [ar, ag, ab] = a.coords;
1150
+ const [br, bg, bb] = b.coords;
1151
+ return Math.sqrt(
1152
+ ((ar ?? 0) - (br ?? 0)) ** 2 + ((ag ?? 0) - (bg ?? 0)) ** 2 + ((ab ?? 0) - (bb ?? 0)) ** 2 + ((a.alpha ?? 1) - (b.alpha ?? 1)) ** 2
1153
+ );
1154
+ };
1155
+ var lerpColor = (a, b, ratio) => {
1156
+ const lerp = (start, end, t) => start * (1 - t) + end * t;
1157
+ return {
1158
+ space: a.space,
1159
+ coords: [
1160
+ lerp(a.coords[0] ?? 0, b.coords[0] ?? 0, ratio),
1161
+ lerp(a.coords[1] ?? 0, b.coords[1] ?? 0, ratio),
1162
+ lerp(a.coords[2] ?? 0, b.coords[2] ?? 0, ratio)
1163
+ ],
1164
+ alpha: lerp(a.alpha ?? 1, b.alpha ?? 1, ratio)
1165
+ };
1166
+ };
1167
+ var serializeColor = (color2) => {
1168
+ const [r, g, b] = color2.coords;
1169
+ const red = Math.round((r ?? 0) * 255);
1170
+ const green = Math.round((g ?? 0) * 255);
1171
+ const blue = Math.round((b ?? 0) * 255);
1172
+ const alpha = color2.alpha ?? 1;
1173
+ if (alpha < 1) {
1174
+ return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
1175
+ }
1176
+ return `rgb(${red}, ${green}, ${blue})`;
1177
+ };
1061
1178
  export {
1062
1179
  ColorValue,
1063
1180
  FakeStyleElement,
@@ -1074,6 +1191,8 @@ export {
1074
1191
  UnitValue,
1075
1192
  UnparsedValue,
1076
1193
  VarFallback,
1194
+ color,
1195
+ colorDistance,
1077
1196
  compareMedia,
1078
1197
  createRegularStyleSheet,
1079
1198
  cssWideKeywords,
@@ -1082,9 +1201,17 @@ export {
1082
1201
  generateAtomic,
1083
1202
  generateStyleMap,
1084
1203
  hyphenateProperty,
1204
+ lerpColor,
1085
1205
  matchMedia,
1086
1206
  mergeStyles,
1207
+ parseColor,
1087
1208
  prefixStyles,
1209
+ serializeColor,
1210
+ srgbColor,
1211
+ toColorComponent,
1212
+ toColorSpace,
1088
1213
  toValue,
1089
- toVarFallback
1214
+ toVarFallback,
1215
+ transparentColor,
1216
+ whiteColor
1090
1217
  };
package/lib/runtime.js CHANGED
@@ -63,6 +63,12 @@ var toValue = (styleValue, transformValue) => {
63
63
  let [c1, c2, c3] = value.components;
64
64
  const alpha = typeof value.alpha === "number" ? value.alpha : toValue(value.alpha);
65
65
  switch (value.colorSpace) {
66
+ case "hex": {
67
+ const toHex = (v) => Math.round(Math.min(Math.max(v, 0), 1) * 255).toString(16).padStart(2, "0");
68
+ const hex = `#${toHex(c1)}${toHex(c2)}${toHex(c3)}`;
69
+ const alphaNum = typeof alpha === "number" ? alpha : 1;
70
+ return alphaNum < 1 ? hex + toHex(alphaNum) : hex;
71
+ }
66
72
  case "srgb": {
67
73
  c1 = Math.round(c1 * 255);
68
74
  c2 = Math.round(c2 * 255);
@@ -82,7 +88,7 @@ var toValue = (styleValue, transformValue) => {
82
88
  case "oklch":
83
89
  return `oklch(${c1} ${c2} ${c3} / ${alpha})`;
84
90
  // Fall back to color() function for less common color spaces.
85
- // Webstudio uses colorjs internal names; map to CSS predefined color space names.
91
+ // colorjs uses internal short names that differ from CSS predefined color space identifiers.
86
92
  case "p3":
87
93
  return `color(display-p3 ${c1} ${c2} ${c3} / ${alpha})`;
88
94
  case "a98rgb":
@@ -0,0 +1,14 @@
1
+ import * as colorjs from "colorjs.io/fn";
2
+ export type { PlainColorObject, ColorConstructor, ColorSpace, Coords, } from "colorjs.io/fn";
3
+ import type { ColorSpace, PlainColorObject } from "colorjs.io/fn";
4
+ import type { ColorValue } from "./schema";
5
+ export declare const color: typeof colorjs;
6
+ export declare const toColorSpace: (space: ColorSpace) => ColorValue["colorSpace"];
7
+ export declare const toColorComponent: (value: undefined | null | number) => number;
8
+ export declare const srgbColor: (r: number, g: number, b: number, alpha?: number) => PlainColorObject;
9
+ export declare const transparentColor: import("colorjs.io/src/color.js").PlainColorObject;
10
+ export declare const whiteColor: import("colorjs.io/src/color.js").PlainColorObject;
11
+ export declare const parseColor: (cssString: string) => PlainColorObject;
12
+ export declare const colorDistance: (a: PlainColorObject, b: PlainColorObject) => number;
13
+ export declare const lerpColor: (a: PlainColorObject, b: PlainColorObject, ratio: number) => PlainColorObject;
14
+ export declare const serializeColor: (color: PlainColorObject) => string;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  export * from "./core/index";
2
2
  export * from "./schema";
3
3
  export * from "./css";
4
+ export * from "./color";
4
5
  export type { Unit as __Unit } from "./__generated__/types";
5
6
  export type { HyphenatedProperty as __HyphenatedProperty } from "./__generated__/types";
@@ -94,7 +94,7 @@ declare const RgbValue: z.ZodObject<{
94
94
  export type RgbValue = z.infer<typeof RgbValue>;
95
95
  export type ColorValue = {
96
96
  type: "color";
97
- colorSpace: "srgb" | "p3" | "srgb-linear" | "hsl" | "hwb" | "lab" | "lch" | "oklab" | "oklch" | "a98rgb" | "prophoto" | "rec2020" | "xyz-d65" | "xyz-d50";
97
+ colorSpace: "hex" | "srgb" | "p3" | "srgb-linear" | "hsl" | "hwb" | "lab" | "lch" | "oklab" | "oklch" | "a98rgb" | "prophoto" | "rec2020" | "xyz-d65" | "xyz-d50";
98
98
  components: [number, number, number];
99
99
  alpha: number | VarValue;
100
100
  hidden?: boolean;
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@webstudio-is/css-engine",
3
- "version": "0.257.0",
3
+ "version": "0.258.0",
4
4
  "description": "CSS Renderer for Webstudio",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
7
7
  "type": "module",
8
8
  "dependencies": {
9
9
  "@emotion/hash": "^0.9.2",
10
+ "colorjs.io": "^0.6.1",
10
11
  "zod": "^3.24.2",
11
- "@webstudio-is/fonts": "0.257.0"
12
+ "@webstudio-is/fonts": "0.258.0"
12
13
  },
13
14
  "devDependencies": {
14
15
  "@types/react": "^18.2.70",