sassc4 2.4.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a455f81f07c892972d2629d8dd1772beb98bcd5c4380309f891f512d49f4e6f
4
- data.tar.gz: cc986690a8cd688316bc46525d856f0a158dac259706276d4a697d2229358077
3
+ metadata.gz: 39063f8c86eb4a6f48ae21e121d6ae96593e336c861dadd2b3c0734d66f8b200
4
+ data.tar.gz: ee927ad9e16142bcac97d25fbc8b451d57706e30f1990f5837bfe9e356eefd75
5
5
  SHA512:
6
- metadata.gz: fa4e4a16e5ff9227f133dc99ab61303eab1ed63e2acf2d88324d2c22416df3350c447787406a2cd74b4901944f015a3d7e629289b73937f354b34a4d11bd5b95
7
- data.tar.gz: 251ce370592156f87ab3d4962e3a5984b82b3c9f35df94d1770d84544ab8b25b5e3d7e1d2d8ab421c0fbb0865c87f3003f3fc92c6acebc7120c0153bfb47a3c5
6
+ metadata.gz: 9348e6f46dcb7170a8114ffad46093a0093f84c4f2bc1d4eef4b60640c5b43542b28fef5ce027849e76b88549ac30a2bc86a2ec0d2403abef0abd4033c75119b
7
+ data.tar.gz: e9e80f3d0e2fd227eb382ee53ced7e2f00acb4b72d2d63575ec39d1eaf89af83fbec7df04834fee2031baf02decef5de9ea81eefd946d231504dacf56c927a1b
data/ext/libsass/VERSION CHANGED
@@ -1 +1 @@
1
- 4.0.1
1
+ 4.0.3
@@ -13,46 +13,46 @@ namespace Sass {
13
13
  // ============================================================================
14
14
  // HWB Conversions
15
15
  // ============================================================================
16
-
16
+
17
17
  Color_RGBA* HWB::toRGBA() const {
18
18
  // From https://www.w3.org/TR/css-color-4/#hwb-to-rgb
19
19
  double hue = std::fmod(h, 360.0);
20
20
  if (hue < 0) hue += 360.0;
21
21
  double scaledHue = hue / 360.0;
22
-
22
+
23
23
  double white = w / 100.0;
24
24
  double black = b / 100.0;
25
-
25
+
26
26
  // Normalize if sum > 1
27
27
  double sum = white + black;
28
28
  if (sum > 1.0) {
29
29
  white /= sum;
30
30
  black /= sum;
31
31
  }
32
-
32
+
33
33
  double factor = 1.0 - white - black;
34
-
34
+
35
35
  // Convert to RGB using HSL-like formula
36
36
  auto toRgb = [&](double hue) -> double {
37
37
  return ColorSpaces::hueToRgb(0, 1, hue) * factor + white;
38
38
  };
39
-
39
+
40
40
  double r = toRgb(scaledHue + 1.0 / 3.0) * 255.0;
41
41
  double g = toRgb(scaledHue) * 255.0;
42
42
  double b = toRgb(scaledHue - 1.0 / 3.0) * 255.0;
43
-
43
+
44
44
  return SASS_MEMORY_NEW(Color_RGBA, SourceSpan("[hwb]"), r, g, b, a);
45
45
  }
46
-
46
+
47
47
  HWB HWB::fromRGBA(const Color_RGBA* rgb) {
48
48
  double r = rgb->r() / 255.0;
49
49
  double g = rgb->g() / 255.0;
50
50
  double b = rgb->b() / 255.0;
51
-
51
+
52
52
  double max = std::max({r, g, b});
53
53
  double min = std::min({r, g, b});
54
54
  double chroma = max - min;
55
-
55
+
56
56
  double hue = 0;
57
57
  if (chroma != 0) {
58
58
  if (max == r) {
@@ -65,177 +65,177 @@ namespace Sass {
65
65
  hue *= 60.0;
66
66
  if (hue < 0) hue += 360.0;
67
67
  }
68
-
68
+
69
69
  double whiteness = min * 100.0;
70
70
  double blackness = (1.0 - max) * 100.0;
71
-
71
+
72
72
  return HWB(hue, whiteness, blackness, rgb->a());
73
73
  }
74
-
74
+
75
75
  // ============================================================================
76
76
  // Lab Conversions
77
77
  // ============================================================================
78
-
78
+
79
79
  Color_RGBA* Lab::toRGBA() const {
80
80
  // Lab -> XYZ D50
81
81
  double fy = (l + 16.0) / 116.0;
82
82
  double fx = a / 500.0 + fy;
83
83
  double fz = fy - b / 200.0;
84
-
84
+
85
85
  double xr = ColorSpaces::labFinv(fx);
86
- double yr = l > ColorSpaces::LAB_KAPPA * ColorSpaces::LAB_EPSILON
87
- ? std::pow((l + 16.0) / 116.0, 3.0)
86
+ double yr = l > ColorSpaces::LAB_KAPPA * ColorSpaces::LAB_EPSILON
87
+ ? std::pow((l + 16.0) / 116.0, 3.0)
88
88
  : l / ColorSpaces::LAB_KAPPA;
89
89
  double zr = ColorSpaces::labFinv(fz);
90
-
90
+
91
91
  double x = xr * ColorSpaces::D50[0];
92
92
  double y = yr * ColorSpaces::D50[1];
93
93
  double z = zr * ColorSpaces::D50[2];
94
-
94
+
95
95
  // XYZ D50 -> Linear sRGB
96
96
  auto linear = ColorSpaces::multiplyMatrix(ColorSpaces::XYZ_D50_TO_LINEAR_SRGB, x, y, z);
97
-
97
+
98
98
  // Linear sRGB -> sRGB (gamma encoding)
99
99
  double r = ColorSpaces::gammaEncode(linear[0]) * 255.0;
100
100
  double g = ColorSpaces::gammaEncode(linear[1]) * 255.0;
101
101
  double b = ColorSpaces::gammaEncode(linear[2]) * 255.0;
102
-
102
+
103
103
  // Clamp to valid range
104
104
  r = clip(r, 0.0, 255.0);
105
105
  g = clip(g, 0.0, 255.0);
106
106
  b = clip(b, 0.0, 255.0);
107
-
107
+
108
108
  return SASS_MEMORY_NEW(Color_RGBA, SourceSpan("[lab]"), r, g, b, alpha);
109
109
  }
110
-
110
+
111
111
  Lab Lab::fromRGBA(const Color_RGBA* rgb) {
112
112
  // sRGB -> Linear sRGB (gamma decoding)
113
113
  double r = ColorSpaces::gammaDecode(rgb->r() / 255.0);
114
114
  double g = ColorSpaces::gammaDecode(rgb->g() / 255.0);
115
115
  double b = ColorSpaces::gammaDecode(rgb->b() / 255.0);
116
-
116
+
117
117
  // Linear sRGB -> XYZ D50
118
118
  auto xyz = ColorSpaces::multiplyMatrix(ColorSpaces::LINEAR_SRGB_TO_XYZ_D50, r, g, b);
119
-
119
+
120
120
  // Normalize by D50 white point
121
121
  double xr = xyz[0] / ColorSpaces::D50[0];
122
122
  double yr = xyz[1] / ColorSpaces::D50[1];
123
123
  double zr = xyz[2] / ColorSpaces::D50[2];
124
-
124
+
125
125
  // XYZ D50 -> Lab
126
126
  double fx = ColorSpaces::labF(xr);
127
127
  double fy = ColorSpaces::labF(yr);
128
128
  double fz = ColorSpaces::labF(zr);
129
-
129
+
130
130
  double L = 116.0 * fy - 16.0;
131
131
  double a = 500.0 * (fx - fy);
132
132
  double b_val = 200.0 * (fy - fz);
133
-
133
+
134
134
  return Lab(L, a, b_val, rgb->a());
135
135
  }
136
-
136
+
137
137
  // ============================================================================
138
138
  // LCH Conversions
139
139
  // ============================================================================
140
-
140
+
141
141
  Color_RGBA* LCH::toRGBA() const {
142
142
  Lab lab = toLab();
143
143
  return lab.toRGBA();
144
144
  }
145
-
145
+
146
146
  LCH LCH::fromRGBA(const Color_RGBA* rgb) {
147
147
  Lab lab = Lab::fromRGBA(rgb);
148
148
  return fromLab(lab);
149
149
  }
150
-
150
+
151
151
  Lab LCH::toLab() const {
152
152
  double L, a, b;
153
153
  ColorSpaces::lchToLab(l, c, h, L, a, b);
154
154
  return Lab(L, a, b, alpha);
155
155
  }
156
-
156
+
157
157
  LCH LCH::fromLab(const Lab& lab) {
158
158
  double l, c, h;
159
159
  ColorSpaces::labToLch(lab.l, lab.a, lab.b, l, c, h);
160
160
  return LCH(l, c, h, lab.alpha);
161
161
  }
162
-
162
+
163
163
  // ============================================================================
164
164
  // OKLab Conversions
165
165
  // ============================================================================
166
-
166
+
167
167
  Color_RGBA* OKLab::toRGBA() const {
168
168
  // OKLab -> LMS
169
169
  auto lms = ColorSpaces::multiplyMatrix(ColorSpaces::OKLAB_TO_LMS, l, a, b);
170
-
170
+
171
171
  // Cube LMS values
172
172
  double lCubed = lms[0] * lms[0] * lms[0];
173
173
  double mCubed = lms[1] * lms[1] * lms[1];
174
174
  double sCubed = lms[2] * lms[2] * lms[2];
175
-
175
+
176
176
  // LMS -> Linear sRGB
177
177
  auto linear = ColorSpaces::multiplyMatrix(ColorSpaces::LMS_TO_LINEAR_SRGB, lCubed, mCubed, sCubed);
178
-
178
+
179
179
  // Linear sRGB -> sRGB (gamma encoding)
180
180
  double r = ColorSpaces::gammaEncode(linear[0]) * 255.0;
181
181
  double g = ColorSpaces::gammaEncode(linear[1]) * 255.0;
182
182
  double b = ColorSpaces::gammaEncode(linear[2]) * 255.0;
183
-
183
+
184
184
  // Clamp to valid range
185
185
  r = clip(r, 0.0, 255.0);
186
186
  g = clip(g, 0.0, 255.0);
187
187
  b = clip(b, 0.0, 255.0);
188
-
188
+
189
189
  return SASS_MEMORY_NEW(Color_RGBA, SourceSpan("[oklab]"), r, g, b, alpha);
190
190
  }
191
-
191
+
192
192
  OKLab OKLab::fromRGBA(const Color_RGBA* rgb) {
193
193
  // sRGB -> Linear sRGB (gamma decoding)
194
194
  double r = ColorSpaces::gammaDecode(rgb->r() / 255.0);
195
195
  double g = ColorSpaces::gammaDecode(rgb->g() / 255.0);
196
196
  double b = ColorSpaces::gammaDecode(rgb->b() / 255.0);
197
-
197
+
198
198
  // Linear sRGB -> LMS
199
199
  auto lms = ColorSpaces::multiplyMatrix(ColorSpaces::LINEAR_SRGB_TO_LMS, r, g, b);
200
-
200
+
201
201
  // Cube root LMS values
202
202
  double lCbrt = std::cbrt(lms[0]);
203
203
  double mCbrt = std::cbrt(lms[1]);
204
204
  double sCbrt = std::cbrt(lms[2]);
205
-
205
+
206
206
  // LMS -> OKLab
207
207
  auto oklab = ColorSpaces::multiplyMatrix(ColorSpaces::LMS_TO_OKLAB, lCbrt, mCbrt, sCbrt);
208
-
208
+
209
209
  return OKLab(oklab[0], oklab[1], oklab[2], rgb->a());
210
210
  }
211
-
211
+
212
212
  // ============================================================================
213
213
  // OKLCH Conversions
214
214
  // ============================================================================
215
-
215
+
216
216
  Color_RGBA* OKLCH::toRGBA() const {
217
217
  OKLab oklab = toOKLab();
218
218
  return oklab.toRGBA();
219
219
  }
220
-
220
+
221
221
  OKLCH OKLCH::fromRGBA(const Color_RGBA* rgb) {
222
222
  OKLab oklab = OKLab::fromRGBA(rgb);
223
223
  return fromOKLab(oklab);
224
224
  }
225
-
225
+
226
226
  OKLab OKLCH::toOKLab() const {
227
227
  double hRad = h * M_PI / 180.0;
228
228
  double a = c * std::cos(hRad);
229
229
  double b = c * std::sin(hRad);
230
230
  return OKLab(l, a, b, alpha);
231
231
  }
232
-
232
+
233
233
  OKLCH OKLCH::fromOKLab(const OKLab& oklab) {
234
234
  double c = std::sqrt(oklab.a * oklab.a + oklab.b * oklab.b);
235
235
  double h = std::atan2(oklab.b, oklab.a) * 180.0 / M_PI;
236
236
  if (h < 0) h += 360.0;
237
237
  return OKLCH(oklab.l, c, h, oklab.alpha);
238
238
  }
239
-
239
+
240
240
  } // namespace Sass
241
241
 
@@ -14,14 +14,14 @@ namespace Sass {
14
14
 
15
15
  // Color space constants for CSS Color Level 4
16
16
  namespace ColorSpaces {
17
-
17
+
18
18
  // Constants from https://www.w3.org/TR/css-color-4/
19
19
  constexpr double D50[3] = {0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585};
20
-
20
+
21
21
  // Lab constants
22
22
  constexpr double LAB_KAPPA = 24389.0 / 27.0; // 29^3/3^3
23
23
  constexpr double LAB_EPSILON = 216.0 / 24389.0; // 6^3/29^3
24
-
24
+
25
25
  // Conversion matrices
26
26
  // Matrix for converting linear sRGB to XYZ D50
27
27
  constexpr double LINEAR_SRGB_TO_XYZ_D50[9] = {
@@ -29,56 +29,56 @@ namespace Sass {
29
29
  0.22249317711056518, 0.71688701309448240, 0.06061980979495235,
30
30
  0.01392392146316939, 0.09708132423141015, 0.71409935681588070
31
31
  };
32
-
32
+
33
33
  // Matrix for converting XYZ D50 to linear sRGB
34
34
  constexpr double XYZ_D50_TO_LINEAR_SRGB[9] = {
35
35
  3.13413585290011780, -1.61738599801804200, -0.49066221791109754,
36
36
  -0.97879547655577770, 1.91625437739598840, 0.03344287339036693,
37
37
  0.07195539255794733, -0.22897675981518200, 1.40538603511311820
38
38
  };
39
-
39
+
40
40
  // Matrix for converting linear sRGB to XYZ D65
41
41
  constexpr double LINEAR_SRGB_TO_XYZ_D65[9] = {
42
42
  0.41239079926595950, 0.35758433938387796, 0.18048078840183430,
43
43
  0.21263900587151036, 0.71516867876775590, 0.07219231536073371,
44
44
  0.01933081871559185, 0.11919477979462598, 0.95053215224966060
45
45
  };
46
-
46
+
47
47
  // Matrix for converting XYZ D65 to linear sRGB
48
48
  constexpr double XYZ_D65_TO_LINEAR_SRGB[9] = {
49
49
  3.24096994190452130, -1.53738317757009350, -0.49861076029300330,
50
50
  -0.96924363628087980, 1.87596750150772060, 0.04155505740717561,
51
51
  0.05563007969699360, -0.20397695888897657, 1.05697151424287860
52
52
  };
53
-
53
+
54
54
  // Matrix for converting linear sRGB to LMS
55
55
  constexpr double LINEAR_SRGB_TO_LMS[9] = {
56
56
  0.41222146947076300, 0.53633253726173480, 0.05144599326750220,
57
57
  0.21190349581782520, 0.68069955064523420, 0.10739695353694050,
58
58
  0.08830245919005641, 0.28171883913612150, 0.62997870167382210
59
59
  };
60
-
60
+
61
61
  // Matrix for converting LMS to linear sRGB
62
62
  constexpr double LMS_TO_LINEAR_SRGB[9] = {
63
63
  4.07674163607595800, -3.30771153925806200, 0.23096990318210417,
64
64
  -1.26843797328503200, 2.60975734928768900, -0.34131937600265710,
65
65
  -0.00419607613867551, -0.70341861793593630, 1.70761469407461200
66
66
  };
67
-
67
+
68
68
  // Matrix for converting LMS to OKLab
69
69
  constexpr double LMS_TO_OKLAB[9] = {
70
70
  0.21045426830931400, 0.79361777470230540, -0.00407204301161930,
71
71
  1.97799853243116840, -2.42859224204858000, 0.45059370961741100,
72
72
  0.02590404246554780, 0.78277171245752960, -0.80867575492307740
73
73
  };
74
-
74
+
75
75
  // Matrix for converting OKLab to LMS
76
76
  constexpr double OKLAB_TO_LMS[9] = {
77
77
  1.00000000000000020, 0.39633777737617490, 0.21580375730991360,
78
78
  0.99999999999999980, -0.10556134581565854, -0.06385417282581334,
79
79
  0.99999999999999990, -0.08948417752981180, -1.29148554801940940
80
80
  };
81
-
81
+
82
82
  // Utility functions for matrix multiplication
83
83
  inline std::array<double, 3> multiplyMatrix(const double matrix[9], double a, double b, double c) {
84
84
  return {
@@ -87,7 +87,7 @@ namespace Sass {
87
87
  matrix[6] * a + matrix[7] * b + matrix[8] * c
88
88
  };
89
89
  }
90
-
90
+
91
91
  // sRGB gamma encoding/decoding
92
92
  inline double gammaEncode(double linear) {
93
93
  if (linear <= 0.0031308) {
@@ -95,14 +95,14 @@ namespace Sass {
95
95
  }
96
96
  return 1.055 * std::pow(linear, 1.0 / 2.4) - 0.055;
97
97
  }
98
-
98
+
99
99
  inline double gammaDecode(double encoded) {
100
100
  if (encoded <= 0.04045) {
101
101
  return encoded / 12.92;
102
102
  }
103
103
  return std::pow((encoded + 0.055) / 1.055, 2.4);
104
104
  }
105
-
105
+
106
106
  // HWB conversion helpers
107
107
  inline double hueToRgb(double m1, double m2, double h) {
108
108
  h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
@@ -111,7 +111,7 @@ namespace Sass {
111
111
  if (h * 3 < 2) return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6;
112
112
  return m1;
113
113
  }
114
-
114
+
115
115
  // Lab conversion helpers
116
116
  inline double labF(double t) {
117
117
  if (t > LAB_EPSILON) {
@@ -119,7 +119,7 @@ namespace Sass {
119
119
  }
120
120
  return (LAB_KAPPA * t + 16) / 116;
121
121
  }
122
-
122
+
123
123
  inline double labFinv(double t) {
124
124
  double cubed = t * t * t;
125
125
  if (cubed > LAB_EPSILON) {
@@ -127,7 +127,7 @@ namespace Sass {
127
127
  }
128
128
  return (116 * t - 16) / LAB_KAPPA;
129
129
  }
130
-
130
+
131
131
  // LCH/Lab conversion
132
132
  inline void labToLch(double L, double a, double b, double& l, double& c, double& h) {
133
133
  l = L;
@@ -135,92 +135,92 @@ namespace Sass {
135
135
  h = std::atan2(b, a) * 180.0 / M_PI;
136
136
  if (h < 0) h += 360;
137
137
  }
138
-
138
+
139
139
  inline void lchToLab(double l, double c, double h, double& L, double& a, double& b) {
140
140
  L = l;
141
141
  double hRad = h * M_PI / 180.0;
142
142
  a = c * std::cos(hRad);
143
143
  b = c * std::sin(hRad);
144
144
  }
145
-
145
+
146
146
  } // namespace ColorSpaces
147
-
147
+
148
148
  // New color classes for CSS Color Level 4
149
-
149
+
150
150
  // HWB Color
151
151
  struct HWB {
152
152
  double h; // Hue (0-360)
153
153
  double w; // Whiteness (0-100)
154
154
  double b; // Blackness (0-100)
155
155
  double a; // Alpha (0-1)
156
-
156
+
157
157
  HWB(double hue = 0, double white = 0, double black = 0, double alpha = 1.0)
158
158
  : h(hue), w(white), b(black), a(alpha) {}
159
-
159
+
160
160
  Color_RGBA* toRGBA() const;
161
161
  static HWB fromRGBA(const Color_RGBA* rgb);
162
162
  };
163
-
163
+
164
164
  // Lab Color
165
165
  struct Lab {
166
166
  double l; // Lightness (0-100)
167
167
  double a; // a-axis (-125 to 125)
168
168
  double b; // b-axis (-125 to 125)
169
169
  double alpha; // Alpha (0-1)
170
-
170
+
171
171
  Lab(double lightness = 0, double aAxis = 0, double bAxis = 0, double alphaVal = 1.0)
172
172
  : l(lightness), a(aAxis), b(bAxis), alpha(alphaVal) {}
173
-
173
+
174
174
  Color_RGBA* toRGBA() const;
175
175
  static Lab fromRGBA(const Color_RGBA* rgb);
176
176
  };
177
-
177
+
178
178
  // LCH Color
179
179
  struct LCH {
180
180
  double l; // Lightness (0-100)
181
181
  double c; // Chroma (0-150)
182
182
  double h; // Hue (0-360)
183
183
  double alpha; // Alpha (0-1)
184
-
184
+
185
185
  LCH(double lightness = 0, double chroma = 0, double hue = 0, double alphaVal = 1.0)
186
186
  : l(lightness), c(chroma), h(hue), alpha(alphaVal) {}
187
-
187
+
188
188
  Color_RGBA* toRGBA() const;
189
189
  static LCH fromRGBA(const Color_RGBA* rgb);
190
190
  Lab toLab() const;
191
191
  static LCH fromLab(const Lab& lab);
192
192
  };
193
-
193
+
194
194
  // OKLab Color
195
195
  struct OKLab {
196
196
  double l; // Lightness (0-1)
197
197
  double a; // a-axis (-0.4 to 0.4)
198
198
  double b; // b-axis (-0.4 to 0.4)
199
199
  double alpha; // Alpha (0-1)
200
-
200
+
201
201
  OKLab(double lightness = 0, double aAxis = 0, double bAxis = 0, double alphaVal = 1.0)
202
202
  : l(lightness), a(aAxis), b(bAxis), alpha(alphaVal) {}
203
-
203
+
204
204
  Color_RGBA* toRGBA() const;
205
205
  static OKLab fromRGBA(const Color_RGBA* rgb);
206
206
  };
207
-
207
+
208
208
  // OKLCH Color
209
209
  struct OKLCH {
210
210
  double l; // Lightness (0-1)
211
211
  double c; // Chroma (0-0.4)
212
212
  double h; // Hue (0-360)
213
213
  double alpha; // Alpha (0-1)
214
-
214
+
215
215
  OKLCH(double lightness = 0, double chroma = 0, double hue = 0, double alphaVal = 1.0)
216
216
  : l(lightness), c(chroma), h(hue), alpha(alphaVal) {}
217
-
217
+
218
218
  Color_RGBA* toRGBA() const;
219
219
  static OKLCH fromRGBA(const Color_RGBA* rgb);
220
220
  OKLab toOKLab() const;
221
221
  static OKLCH fromOKLab(const OKLab& oklab);
222
222
  };
223
-
223
+
224
224
  } // namespace Sass
225
225
 
226
226
  #endif
@@ -103,6 +103,13 @@ namespace Sass {
103
103
  extern const char progid_kwd[] = "progid";
104
104
  extern const char expression_kwd[] = "expression";
105
105
  extern const char calc_fn_kwd[] = "calc";
106
+ // CSS special function keywords
107
+ extern const char var_fn_kwd[] = "var";
108
+ extern const char env_fn_kwd[] = "env";
109
+ extern const char attr_fn_kwd[] = "attr";
110
+ extern const char clamp_fn_kwd[] = "clamp";
111
+ extern const char min_fn_kwd[] = "min";
112
+ extern const char max_fn_kwd[] = "max";
106
113
 
107
114
  extern const char almost_any_value_class[] = "\"'#!;{}";
108
115
 
@@ -101,6 +101,13 @@ namespace Sass {
101
101
  extern const char progid_kwd[];
102
102
  extern const char expression_kwd[];
103
103
  extern const char calc_fn_kwd[];
104
+ // CSS special function keywords (for CSS custom properties, etc.)
105
+ extern const char var_fn_kwd[];
106
+ extern const char env_fn_kwd[];
107
+ extern const char attr_fn_kwd[];
108
+ extern const char clamp_fn_kwd[];
109
+ extern const char min_fn_kwd[];
110
+ extern const char max_fn_kwd[];
104
111
 
105
112
  // char classes for "regular expressions"
106
113
  extern const char almost_any_value_class[];
@@ -14,12 +14,30 @@ namespace Sass {
14
14
 
15
15
  namespace Functions {
16
16
 
17
+ // Check if argument is a "special number" according to CSS Color Level 4
18
+ // Special numbers include: calc(), var(), env(), attr(), clamp(), min(), max()
19
+ // These are CSS functions that may return a number and should be passed through as-is
17
20
  bool string_argument(AST_Node_Obj obj) {
18
21
  String_Constant* s = Cast<String_Constant>(obj);
19
22
  if (s == nullptr) return false;
20
23
  const sass::string& str = s->value();
24
+
25
+ // Check for CSS function prefixes (case-insensitive check would be better, but this works)
21
26
  return starts_with(str, "calc(") ||
22
- starts_with(str, "var(");
27
+ starts_with(str, "var(") ||
28
+ starts_with(str, "env(") ||
29
+ starts_with(str, "attr(") ||
30
+ starts_with(str, "clamp(") ||
31
+ starts_with(str, "min(") ||
32
+ starts_with(str, "max(") ||
33
+ // Case variations
34
+ starts_with(str, "CALC(") ||
35
+ starts_with(str, "VAR(") ||
36
+ starts_with(str, "ENV(") ||
37
+ starts_with(str, "ATTR(") ||
38
+ starts_with(str, "CLAMP(") ||
39
+ starts_with(str, "MIN(") ||
40
+ starts_with(str, "MAX(");
23
41
  }
24
42
 
25
43
  void hsla_alpha_percent_deprecation(const SourceSpan& pstate, const sass::string val)
@@ -633,11 +651,15 @@ namespace Sass {
633
651
  Signature hwb_sig = "hwb($hue, $whiteness, $blackness, $alpha: 1)";
634
652
  BUILT_IN(hwb)
635
653
  {
654
+ bool has_alpha_arg = env.has("$alpha");
655
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
656
+
636
657
  // Check for string arguments (CSS custom properties, calc, var, etc.)
637
658
  if (
638
659
  string_argument(env["$hue"]) ||
639
660
  string_argument(env["$whiteness"]) ||
640
- string_argument(env["$blackness"])
661
+ string_argument(env["$blackness"]) ||
662
+ alpha_is_string
641
663
  ) {
642
664
  sass::string result = "hwb("
643
665
  + env["$hue"]->to_string()
@@ -646,8 +668,12 @@ namespace Sass {
646
668
  + " "
647
669
  + env["$blackness"]->to_string();
648
670
 
649
- if (env.has("$alpha")) {
650
- result += " / " + env["$alpha"]->to_string();
671
+ // Add alpha if present and not default 1
672
+ if (has_alpha_arg) {
673
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
674
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
675
+ result += " / " + env["$alpha"]->to_string();
676
+ }
651
677
  }
652
678
  result += ")";
653
679
 
@@ -657,7 +683,7 @@ namespace Sass {
657
683
  double hue = ARGVAL("$hue");
658
684
  double whiteness = DARG_U_PRCT("$whiteness");
659
685
  double blackness = DARG_U_PRCT("$blackness");
660
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
686
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
661
687
 
662
688
  HWB hwb_color(hue, whiteness, blackness, alpha);
663
689
  return hwb_color.toRGBA();
@@ -666,10 +692,14 @@ namespace Sass {
666
692
  Signature lab_sig = "lab($lightness, $a, $b, $alpha: 1)";
667
693
  BUILT_IN(lab)
668
694
  {
695
+ bool has_alpha_arg = env.has("$alpha");
696
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
697
+
669
698
  if (
670
699
  string_argument(env["$lightness"]) ||
671
700
  string_argument(env["$a"]) ||
672
- string_argument(env["$b"])
701
+ string_argument(env["$b"]) ||
702
+ alpha_is_string
673
703
  ) {
674
704
  sass::string result = "lab("
675
705
  + env["$lightness"]->to_string()
@@ -678,8 +708,12 @@ namespace Sass {
678
708
  + " "
679
709
  + env["$b"]->to_string();
680
710
 
681
- if (env.has("$alpha")) {
682
- result += " / " + env["$alpha"]->to_string();
711
+ // Add alpha if present and not default 1
712
+ if (has_alpha_arg) {
713
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
714
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
715
+ result += " / " + env["$alpha"]->to_string();
716
+ }
683
717
  }
684
718
  result += ")";
685
719
 
@@ -689,7 +723,7 @@ namespace Sass {
689
723
  double lightness = DARG_U_PRCT("$lightness");
690
724
  double a = ARGVAL("$a");
691
725
  double b = ARGVAL("$b");
692
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
726
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
693
727
 
694
728
  Lab lab_color(lightness, a, b, alpha);
695
729
  return lab_color.toRGBA();
@@ -698,10 +732,14 @@ namespace Sass {
698
732
  Signature lch_sig = "lch($lightness, $chroma, $hue, $alpha: 1)";
699
733
  BUILT_IN(lch)
700
734
  {
735
+ bool has_alpha_arg = env.has("$alpha");
736
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
737
+
701
738
  if (
702
739
  string_argument(env["$lightness"]) ||
703
740
  string_argument(env["$chroma"]) ||
704
- string_argument(env["$hue"])
741
+ string_argument(env["$hue"]) ||
742
+ alpha_is_string
705
743
  ) {
706
744
  sass::string result = "lch("
707
745
  + env["$lightness"]->to_string()
@@ -710,8 +748,12 @@ namespace Sass {
710
748
  + " "
711
749
  + env["$hue"]->to_string();
712
750
 
713
- if (env.has("$alpha")) {
714
- result += " / " + env["$alpha"]->to_string();
751
+ // Add alpha if present and not default 1
752
+ if (has_alpha_arg) {
753
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
754
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
755
+ result += " / " + env["$alpha"]->to_string();
756
+ }
715
757
  }
716
758
  result += ")";
717
759
 
@@ -721,7 +763,7 @@ namespace Sass {
721
763
  double lightness = DARG_U_PRCT("$lightness");
722
764
  double chroma = ARGVAL("$chroma");
723
765
  double hue = ARGVAL("$hue");
724
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
766
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
725
767
 
726
768
  LCH lch_color(lightness, chroma, hue, alpha);
727
769
  return lch_color.toRGBA();
@@ -730,10 +772,14 @@ namespace Sass {
730
772
  Signature oklab_sig = "oklab($lightness, $a, $b, $alpha: 1)";
731
773
  BUILT_IN(oklab)
732
774
  {
775
+ bool has_alpha_arg = env.has("$alpha");
776
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
777
+
733
778
  if (
734
779
  string_argument(env["$lightness"]) ||
735
780
  string_argument(env["$a"]) ||
736
- string_argument(env["$b"])
781
+ string_argument(env["$b"]) ||
782
+ alpha_is_string
737
783
  ) {
738
784
  sass::string result = "oklab("
739
785
  + env["$lightness"]->to_string()
@@ -742,8 +788,12 @@ namespace Sass {
742
788
  + " "
743
789
  + env["$b"]->to_string();
744
790
 
745
- if (env.has("$alpha")) {
746
- result += " / " + env["$alpha"]->to_string();
791
+ // Add alpha if present and not default 1
792
+ if (has_alpha_arg) {
793
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
794
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
795
+ result += " / " + env["$alpha"]->to_string();
796
+ }
747
797
  }
748
798
  result += ")";
749
799
 
@@ -754,7 +804,7 @@ namespace Sass {
754
804
  double lightness = DARG_U_FACT("$lightness");
755
805
  double a = ARGVAL("$a");
756
806
  double b = ARGVAL("$b");
757
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
807
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
758
808
 
759
809
  OKLab oklab_color(lightness, a, b, alpha);
760
810
  return oklab_color.toRGBA();
@@ -763,10 +813,14 @@ namespace Sass {
763
813
  Signature oklch_sig = "oklch($lightness, $chroma, $hue, $alpha: 1)";
764
814
  BUILT_IN(oklch)
765
815
  {
816
+ bool has_alpha_arg = env.has("$alpha");
817
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
818
+
766
819
  if (
767
820
  string_argument(env["$lightness"]) ||
768
821
  string_argument(env["$chroma"]) ||
769
- string_argument(env["$hue"])
822
+ string_argument(env["$hue"]) ||
823
+ alpha_is_string
770
824
  ) {
771
825
  sass::string result = "oklch("
772
826
  + env["$lightness"]->to_string()
@@ -775,8 +829,12 @@ namespace Sass {
775
829
  + " "
776
830
  + env["$hue"]->to_string();
777
831
 
778
- if (env.has("$alpha")) {
779
- result += " / " + env["$alpha"]->to_string();
832
+ // Add alpha if present and not default 1
833
+ if (has_alpha_arg) {
834
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
835
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
836
+ result += " / " + env["$alpha"]->to_string();
837
+ }
780
838
  }
781
839
  result += ")";
782
840
 
@@ -787,7 +845,7 @@ namespace Sass {
787
845
  double lightness = DARG_U_FACT("$lightness");
788
846
  double chroma = ARGVAL("$chroma");
789
847
  double hue = ARGVAL("$hue");
790
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
848
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
791
849
 
792
850
  OKLCH oklch_color(lightness, chroma, hue, alpha);
793
851
  return oklch_color.toRGBA();
@@ -803,28 +861,41 @@ namespace Sass {
803
861
  }
804
862
 
805
863
  sass::string space = space_str->value();
864
+
865
+ bool has_alpha_arg = env.has("$alpha");
866
+ bool alpha_is_string = has_alpha_arg && string_argument(env["$alpha"]);
867
+
868
+ // Check if any channel contains special numbers (calc, var, etc.)
869
+ bool has_special_number = string_argument(env["$channel1"]) ||
870
+ string_argument(env["$channel2"]) ||
871
+ string_argument(env["$channel3"]) ||
872
+ alpha_is_string;
806
873
 
807
874
  // For now, support the most common color spaces
808
875
  // Full implementation would support: srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb, rec2020, xyz, xyz-d50, xyz-d65
809
876
 
810
- if (space == "srgb" || space == "rgb") {
811
- // sRGB is the same as legacy RGB
877
+ if ((space == "srgb" || space == "rgb") && !has_special_number) {
878
+ // sRGB is the same as legacy RGB - only compute if no special numbers
812
879
  double r = ARGVAL("$channel1");
813
880
  double g = ARGVAL("$channel2");
814
881
  double b = ARGVAL("$channel3");
815
- double alpha = env.has("$alpha") ? ALPHA_NUM("$alpha") : 1.0;
882
+ double alpha = has_alpha_arg ? ALPHA_NUM("$alpha") : 1.0;
816
883
 
817
884
  return SASS_MEMORY_NEW(Color_RGBA, pstate, r * 255.0, g * 255.0, b * 255.0, alpha);
818
885
  }
819
886
 
820
- // For other color spaces, output as CSS string for browser support
887
+ // For other color spaces or when special numbers are present, output as CSS string for browser support
821
888
  sass::string result = "color(" + space + " "
822
889
  + env["$channel1"]->to_string() + " "
823
890
  + env["$channel2"]->to_string() + " "
824
891
  + env["$channel3"]->to_string();
825
892
 
826
- if (env.has("$alpha")) {
827
- result += " / " + env["$alpha"]->to_string();
893
+ // Add alpha if present and not default 1
894
+ if (has_alpha_arg) {
895
+ Number* alpha_num = Cast<Number>(env["$alpha"]);
896
+ if (alpha_is_string || !alpha_num || alpha_num->value() != 1.0) {
897
+ result += " / " + env["$alpha"]->to_string();
898
+ }
828
899
  }
829
900
  result += ")";
830
901
 
@@ -1364,6 +1364,9 @@ namespace Sass {
1364
1364
  else if (peek< sequence < calc_fn_call, exactly <'('> > >()) {
1365
1365
  return parse_calc_function();
1366
1366
  }
1367
+ else if (peek< sequence < css_special_fn_call, exactly <'('> > >()) {
1368
+ return parse_css_special_function();
1369
+ }
1367
1370
  else if (lex < functional_schema >()) {
1368
1371
  return parse_function_call_schema();
1369
1372
  }
@@ -1579,6 +1582,16 @@ namespace Sass {
1579
1582
  if (lex< kwd_null >())
1580
1583
  { return SASS_MEMORY_NEW(Null, pstate); }
1581
1584
 
1585
+ // Check for calc() function
1586
+ if (peek< sequence < calc_fn_call, exactly <'('> > >()) {
1587
+ return parse_css_special_function();
1588
+ }
1589
+
1590
+ // Check for CSS special functions (var, env, attr, clamp, min, max) before identifier
1591
+ if (peek< sequence < css_special_fn_call, exactly <'('> > >()) {
1592
+ return parse_css_special_function();
1593
+ }
1594
+
1582
1595
  if (lex< identifier >()) {
1583
1596
  return color_or_string(lexed);
1584
1597
  }
@@ -2001,6 +2014,51 @@ namespace Sass {
2001
2014
  return SASS_MEMORY_NEW(Function_Call, call_pos, name, args);
2002
2015
  }
2003
2016
 
2017
+ // CSS special functions (var, env, attr, clamp, min, max) should be passed through as strings
2018
+ String_Constant_Obj Parser::parse_css_special_function()
2019
+ {
2020
+ lex< identifier >();
2021
+ sass::string name(lexed);
2022
+ SourceSpan call_pos = pstate;
2023
+
2024
+ if (!lex< exactly<'('> >()) {
2025
+ css_error("Invalid CSS", " after ", ": expected \"(\", was ");
2026
+ }
2027
+
2028
+ // Find the matching closing parenthesis
2029
+ const char* arg_beg = position;
2030
+
2031
+ // Skip over the entire function content including nested parentheses
2032
+ // This will position us right before the closing ')'
2033
+ const char* p = arg_beg;
2034
+ int depth = 1;
2035
+ bool in_squote = false;
2036
+ bool in_dquote = false;
2037
+
2038
+ while (depth > 0 && *p) {
2039
+ if (!in_squote && !in_dquote) {
2040
+ if (*p == '(') depth++;
2041
+ else if (*p == ')') depth--;
2042
+ else if (*p == '"') in_dquote = true;
2043
+ else if (*p == '\'') in_squote = true;
2044
+ } else {
2045
+ if (in_dquote && *p == '"' && *(p-1) != '\\') in_dquote = false;
2046
+ else if (in_squote && *p == '\'' && *(p-1) != '\\') in_squote = false;
2047
+ }
2048
+ if (depth > 0) p++;
2049
+ }
2050
+
2051
+ const char* arg_end = p;
2052
+
2053
+ // Build the complete function string: name(content)
2054
+ sass::string result = name + "(" + sass::string(arg_beg, arg_end) + ")";
2055
+
2056
+ // Move position past the closing ')'
2057
+ position = arg_end + 1;
2058
+
2059
+ return SASS_MEMORY_NEW(String_Constant, call_pos, result);
2060
+ }
2061
+
2004
2062
  String_Obj Parser::parse_url_function_string()
2005
2063
  {
2006
2064
  sass::string prefix("");
@@ -2075,7 +2133,11 @@ namespace Sass {
2075
2133
  Arguments_Obj args;
2076
2134
 
2077
2135
  if (normalized_name == "rgb" || normalized_name == "rgba" ||
2078
- normalized_name == "hsl" || normalized_name == "hsla") {
2136
+ normalized_name == "hsl" || normalized_name == "hsla" ||
2137
+ normalized_name == "hwb" ||
2138
+ normalized_name == "lab" || normalized_name == "lch" ||
2139
+ normalized_name == "oklab" || normalized_name == "oklch" ||
2140
+ normalized_name == "color") {
2079
2141
  args = parse_color_arguments();
2080
2142
  } else {
2081
2143
  args = parse_arguments();
@@ -279,6 +279,7 @@ namespace Sass {
279
279
  ExpressionObj parse_factor();
280
280
  ExpressionObj parse_value();
281
281
  Function_Call_Obj parse_calc_function();
282
+ String_Constant_Obj parse_css_special_function();
282
283
  Function_Call_Obj parse_function_call();
283
284
  Function_Call_Obj parse_function_call_schema();
284
285
  String_Obj parse_url_function_string();
@@ -1226,6 +1226,19 @@ namespace Sass {
1226
1226
  >(src);
1227
1227
  }
1228
1228
 
1229
+ // Match CSS special functions: var, env, attr, clamp, min, max
1230
+ // These functions should be passed through to CSS as-is
1231
+ const char* css_special_fn_call(const char* src) {
1232
+ return alternatives <
1233
+ sequence < exactly < var_fn_kwd >, word_boundary >,
1234
+ sequence < exactly < env_fn_kwd >, word_boundary >,
1235
+ sequence < exactly < attr_fn_kwd >, word_boundary >,
1236
+ sequence < exactly < clamp_fn_kwd >, word_boundary >,
1237
+ sequence < exactly < min_fn_kwd >, word_boundary >,
1238
+ sequence < exactly < max_fn_kwd >, word_boundary >
1239
+ >(src);
1240
+ }
1241
+
1229
1242
  // Match Sass boolean keywords.
1230
1243
  const char* kwd_true(const char* src) {
1231
1244
  return word<true_kwd>(src);
@@ -345,6 +345,8 @@ namespace Sass {
345
345
  // Match SCSS variable names.
346
346
  const char* variable(const char* src);
347
347
  const char* calc_fn_call(const char* src);
348
+ // Match CSS special functions (var, env, attr, clamp, min, max)
349
+ const char* css_special_fn_call(const char* src);
348
350
 
349
351
  // IE stuff
350
352
  const char* ie_progid(const char* src);
data/lib/sassc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SassC
4
- VERSION = "2.4.0"
4
+ VERSION = "2.4.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sassc4
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Boland