sass4 4.0.0 → 4.0.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 +4 -4
- data/AGENTS.md +120 -67
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/lib/sass/script/functions.rb +52 -24
- data/lib/sass/script/value/base.rb +18 -0
- data/lib/sass/script/value/helpers.rb +45 -2
- data/lib/sass/script/value/string.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2532993143d7c789248e915c271040649e7e3c8c45f8d42ed2cf2863ce302a92
|
4
|
+
data.tar.gz: 389ead2b3f1bca0cff8146ec3b3ab86c68528f06305e1f3ada7ed838d93d37c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e37cc075482ea8f56e8bfd6e18f5f96cf9b9cfd83a06fa4505b570631e22eb2a4f9438ee3e86e22fbec17333138ea730f1236f57d4e93a4238d9547a25cad08
|
7
|
+
data.tar.gz: e1b0176044a695d57546d2ba441c21cc18e45c5cdf0bdfa391c76124cd2682a5ae05d98c295534d9f733839b303c803bb8a128cc6026410903963a9731e3a4e7
|
data/AGENTS.md
CHANGED
@@ -409,123 +409,176 @@ test/
|
|
409
409
|
|
410
410
|
## Реализация CSS Color Level 4 синтаксиса
|
411
411
|
|
412
|
-
### Обзор
|
412
|
+
### Обзор реализации
|
413
413
|
|
414
|
-
Была реализована поддержка CSS Color Level 4 синтаксиса для цветовых функций `rgb()`, `rgba()`, `hsl()`, `hsla()
|
414
|
+
Была реализована полная поддержка CSS Color Level 4 синтаксиса для цветовых функций `rgb()`, `rgba()`, `hsl()`, `hsla()`:
|
415
|
+
|
416
|
+
**Новый синтаксис:**
|
415
417
|
- **Space-separated значения**: `rgb(255 0 0)` вместо `rgb(255, 0, 0)`
|
416
418
|
- **Slash-separated alpha**: `rgb(255 0 0 / 0.5)` вместо `rgba(255, 0, 0, 0.5)`
|
417
419
|
|
418
|
-
|
419
|
-
|
420
|
-
|
420
|
+
**Поддержка special numbers (CSS функций):**
|
421
|
+
- `var()` - CSS Custom Properties
|
422
|
+
- `calc()` - математические вычисления
|
423
|
+
- `env()` - environment variables
|
424
|
+
- `attr()` - атрибуты элементов
|
425
|
+
- `clamp()` - ограничение значений
|
426
|
+
- `min()` / `max()` - минимум/максимум
|
427
|
+
|
428
|
+
**Примеры:**
|
429
|
+
```scss
|
430
|
+
// CSS Custom Properties
|
431
|
+
.element {
|
432
|
+
color: rgb(var(--r), var(--g), var(--b));
|
433
|
+
background: hsl(var(--hue) 50% 50%);
|
434
|
+
}
|
435
|
+
|
436
|
+
// Вычисления
|
437
|
+
.dynamic {
|
438
|
+
color: rgb(255 128 0 / calc(0.5 * 2));
|
439
|
+
border-color: hsl(calc(180deg + 10deg), 50%, 50%);
|
440
|
+
}
|
441
|
+
|
442
|
+
// Новый синтаксис
|
443
|
+
.modern {
|
444
|
+
color: rgb(255 128 0 / 0.5); // space-separated с alpha
|
445
|
+
}
|
446
|
+
```
|
421
447
|
|
422
|
-
|
448
|
+
### Архитектурное решение
|
423
449
|
|
424
|
-
####
|
450
|
+
#### Проблема 1: Парсинг slash-separated синтаксиса
|
425
451
|
|
426
|
-
|
452
|
+
Парсер Ruby Sass интерпретировал символ `/` как оператор деления в выражениях типа `rgb(255 0 0 / 0.5)`, создавая неправильную AST структуру.
|
427
453
|
|
454
|
+
**Решение:**
|
428
455
|
1. **Создан специализированный парсер** `color_fn_arglist` в `lib/sass/script/parser.rb`
|
429
456
|
2. **Модифицирован** метод `funcall` для использования `color_fn_arglist` для функций `rgb/rgba/hsl/hsla`
|
430
|
-
3.
|
457
|
+
3. **Парсинг на уровне `unary_plus`** вместо `equals` для избежания интерпретации `/` как деления
|
431
458
|
|
432
|
-
####
|
459
|
+
#### Проблема 2: Поддержка CSS special numbers
|
433
460
|
|
434
|
-
|
461
|
+
CSS функции типа `var()`, `calc()` должны передаваться в CSS без вычисления, но Ruby Sass пытался их обработать.
|
462
|
+
|
463
|
+
**Решение:**
|
464
|
+
1. **Добавлены методы проверки** в базовый класс `Value`:
|
465
|
+
- `is_special_number?` - проверяет, является ли значение CSS функцией
|
466
|
+
- `is_var?` - специальная проверка для `var()`
|
435
467
|
|
436
|
-
|
468
|
+
2. **Реализовано в `String` классе**:
|
437
469
|
```ruby
|
438
|
-
def
|
439
|
-
|
440
|
-
return raw unless tok
|
441
|
-
|
442
|
-
# Специальная обработка для CSS Color Level 4 функций
|
443
|
-
if %w[rgb rgba hsl hsla].include?(tok.value.downcase)
|
444
|
-
args, keywords, splat, kwarg_splat = color_fn_arglist
|
445
|
-
else
|
446
|
-
args, keywords, splat, kwarg_splat = fn_arglist
|
447
|
-
end
|
448
|
-
|
449
|
-
assert_tok(:rparen)
|
450
|
-
node(Script::Tree::Funcall.new(tok.value, args, keywords, splat, kwarg_splat),
|
451
|
-
tok.source_range.start_pos, source_position)
|
470
|
+
def is_special_number?
|
471
|
+
type == :identifier && value =~ /(calc|var|env|attr|clamp|min|max)\s*\(/i
|
452
472
|
end
|
453
473
|
```
|
454
474
|
|
455
|
-
|
475
|
+
3. **Обновлена вспомогательная функция** `special_number?` в `lib/sass/script/value/helpers.rb` для поддержки всех CSS функций
|
476
|
+
|
477
|
+
4. **Модифицированы цветовые функции** для проверки special numbers и возврата CSS-строки вместо вычисления
|
478
|
+
|
479
|
+
#### Детали реализации
|
480
|
+
|
481
|
+
##### 1. Парсер (lib/sass/script/parser.rb)
|
482
|
+
|
483
|
+
Создан метод `color_fn_arglist` который:
|
456
484
|
- Парсит аргументы используя `unary_plus` вместо `equals` чтобы избежать интерпретации `/` как деления
|
457
485
|
- Определяет тип синтаксиса по следующему токену:
|
458
|
-
- `:colon` → keyword arguments
|
459
|
-
- `:comma` → legacy syntax
|
460
|
-
- иначе → CSS Color Level 4
|
461
|
-
-
|
462
|
-
- Space-separated список RGB/HSL значений
|
463
|
-
- Slash-separated список для alpha (если присутствует)
|
486
|
+
- `:colon` → keyword arguments
|
487
|
+
- `:comma` → legacy syntax
|
488
|
+
- иначе → CSS Color Level 4 space/slash-separated
|
489
|
+
- Создает правильную AST структуру для slash-separated alpha
|
464
490
|
|
465
491
|
##### 2. Функции цвета (lib/sass/script/functions.rb)
|
466
492
|
|
467
|
-
|
468
|
-
```ruby
|
469
|
-
ListLiteral(space) с 2 элементами:
|
470
|
-
[0]: ListLiteral(space) = (255 0 0)
|
471
|
-
[1]: ListLiteral(slash) = (0.5)
|
472
|
-
```
|
493
|
+
Обновлены функции `rgb()`, `rgba()`, `hsl()`, `hsla()`:
|
473
494
|
|
474
|
-
**Обработка в функции:**
|
475
495
|
```ruby
|
476
496
|
def rgb(*args)
|
477
497
|
if args.length == 1 && args[0].is_a?(Sass::Script::Value::List)
|
478
498
|
list = args[0]
|
479
499
|
|
500
|
+
# Проверка на var() - передаем как есть
|
501
|
+
return unquoted_string("rgb(#{list})") if list.is_var?
|
502
|
+
|
480
503
|
if list.separator == :space
|
481
|
-
|
482
|
-
|
483
|
-
red, green, blue = list.value
|
484
|
-
return rgb(red, green, blue)
|
485
|
-
elsif list.value.length == 2
|
486
|
-
# rgb(255 0 0 / 0.5) - с alpha
|
504
|
+
# Новый синтаксис
|
505
|
+
if list.value.length == 2
|
487
506
|
rgb_list, alpha_list = list.value
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
return rgba(red, green, blue, alpha)
|
507
|
+
# Проверка на special numbers
|
508
|
+
if [red, green, blue, alpha].any? { |v| v.is_special_number? }
|
509
|
+
return format_color_function("rgb", list)
|
492
510
|
end
|
511
|
+
# Обычное вычисление цвета
|
493
512
|
end
|
494
513
|
end
|
495
514
|
end
|
496
|
-
|
497
515
|
# Legacy syntax...
|
498
516
|
end
|
499
517
|
```
|
500
518
|
|
519
|
+
##### 3. Форматирование вывода (lib/sass/script/value/helpers.rb)
|
520
|
+
|
521
|
+
Добавлена функция `format_color_function` для правильного форматирования slash-separated синтаксиса:
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
def format_color_function(function_name, list)
|
525
|
+
# Проверяет структуру списка и форматирует как "rgb(255 128 0 / 0.5)"
|
526
|
+
# вместо "rgb(255 128 0 0.5)"
|
527
|
+
end
|
528
|
+
```
|
529
|
+
|
501
530
|
### Результаты тестирования
|
502
531
|
|
503
|
-
####
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
- ✅
|
509
|
-
- ✅
|
532
|
+
#### Тесты CSS Color Level 4 (`test/sass/css_color_level4_test.rb`)
|
533
|
+
|
534
|
+
**29 тестов, все проходят:**
|
535
|
+
|
536
|
+
**Special numbers:**
|
537
|
+
- ✅ `rgb(var(--r), var(--g), var(--b))`
|
538
|
+
- ✅ `rgb(calc(200 + 55), 100, 50)`
|
539
|
+
- ✅ `rgb(env(--r), 128, 0)`
|
540
|
+
- ✅ `rgb(clamp(0, 255, 300), 128, 0)`
|
541
|
+
- ✅ `rgb(min(200, 255), max(100, 128), 0)`
|
542
|
+
|
543
|
+
**Новый синтаксис с special numbers:**
|
544
|
+
- ✅ `rgb(255 128 0 / var(--alpha))`
|
545
|
+
- ✅ `rgb(255 128 0 / calc(0.5 * 2))`
|
546
|
+
- ✅ `hsl(calc(180deg + 10deg), 50%, 50%)`
|
547
|
+
- ✅ `hsl(180 50% 50% / calc(0.5 + 0.3))`
|
548
|
+
|
549
|
+
**Обратная совместимость:**
|
550
|
+
- ✅ Legacy синтаксис работает: `rgb(255, 128, 0)`
|
551
|
+
- ✅ Новый синтаксис без special numbers: `rgb(255 128 0)`
|
552
|
+
- ✅ Sass переменные: `rgba($color, var(--alpha))`
|
510
553
|
|
511
554
|
#### Обратная совместимость
|
512
|
-
-
|
513
|
-
-
|
514
|
-
-
|
515
|
-
-
|
555
|
+
- Все существующие тесты проходят
|
556
|
+
- Legacy синтаксис полностью поддерживается
|
557
|
+
- Keyword arguments работают
|
558
|
+
- Sass переменные можно комбинировать с CSS функциями
|
516
559
|
|
517
560
|
### Ключевые уроки
|
518
561
|
|
519
|
-
1.
|
520
|
-
2.
|
521
|
-
3.
|
562
|
+
1. **Архитектурный подход** - правильное решение на уровне парсера лучше чем обходные пути в функциях
|
563
|
+
2. **Приоритет операторов** - парсинг на уровне `unary_plus` избегает проблем с `/` как делением
|
564
|
+
3. **Объектно-ориентированная проверка** - методы `is_special_number?` и `is_var?` в классах Value обеспечивают чистую архитектуру
|
565
|
+
4. **Обратная совместимость** - критически важно сохранять работу всего существующего кода
|
522
566
|
|
523
567
|
### Файлы изменений
|
524
568
|
|
569
|
+
**Основные изменения:**
|
525
570
|
- `lib/sass/script/parser.rb` - добавлен `color_fn_arglist`, модифицирован `funcall`
|
526
571
|
- `lib/sass/script/functions.rb` - обновлены `rgb`, `rgba`, `hsl`, `hsla`
|
527
|
-
- `lib/sass/script/
|
528
|
-
- `lib/sass/script/value/
|
572
|
+
- `lib/sass/script/value/base.rb` - добавлены методы `is_special_number?` и `is_var?`
|
573
|
+
- `lib/sass/script/value/string.rb` - реализованы методы проверки special numbers
|
574
|
+
- `lib/sass/script/value/helpers.rb` - обновлен `special_number?`, добавлен `format_color_function`
|
575
|
+
|
576
|
+
**Тесты:**
|
577
|
+
- `test/sass/css_color_level4_test.rb` - 29 тестов для всех новых возможностей
|
578
|
+
|
579
|
+
**Поддержка была добавлена ранее:**
|
580
|
+
- `lib/sass/script/tree/list_literal.rb` - поддержка `:slash` разделителя
|
581
|
+
- `lib/sass/script/value/list.rb` - поддержка `:slash` разделителя
|
529
582
|
|
530
583
|
## Заключение
|
531
584
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.0.
|
1
|
+
4.0.1
|
data/VERSION_NAME
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
CSS COLORS LEVEL 4
|
@@ -653,13 +653,16 @@ module Sass::Script
|
|
653
653
|
if args.length == 1 && args[0].is_a?(Sass::Script::Value::List)
|
654
654
|
list = args[0]
|
655
655
|
|
656
|
+
# Check if this is a var() call - pass through as-is
|
657
|
+
return unquoted_string("rgb(#{list})") if list.is_var?
|
658
|
+
|
656
659
|
if list.separator == :space
|
657
660
|
# New space-separated syntax
|
658
661
|
if list.value.length == 3
|
659
662
|
# rgb(0% 100% 0%) - simple space-separated without alpha
|
660
663
|
red, green, blue = list.value
|
661
664
|
# Check for special numbers (calc, var, etc.) and return as-is
|
662
|
-
if [red, green, blue].any? { |v|
|
665
|
+
if [red, green, blue].any? { |v| v.is_special_number? }
|
663
666
|
return unquoted_string("rgb(#{list})")
|
664
667
|
end
|
665
668
|
return color_with_hex(red, green, blue)
|
@@ -672,8 +675,8 @@ module Sass::Script
|
|
672
675
|
red, green, blue = rgb_list.value
|
673
676
|
alpha = alpha_list.value[0]
|
674
677
|
# Check for special numbers and return as-is
|
675
|
-
if [red, green, blue, alpha].any? { |v|
|
676
|
-
return
|
678
|
+
if [red, green, blue, alpha].any? { |v| v.is_special_number? }
|
679
|
+
return format_color_function("rgb", list)
|
677
680
|
end
|
678
681
|
return color_with_hex(red, green, blue, alpha)
|
679
682
|
end
|
@@ -685,14 +688,14 @@ module Sass::Script
|
|
685
688
|
# Legacy comma-separated syntax
|
686
689
|
red, green, blue = args
|
687
690
|
if green.nil?
|
688
|
-
return unquoted_string("rgb(#{red})") if
|
691
|
+
return unquoted_string("rgb(#{red})") if red.is_var?
|
689
692
|
raise ArgumentError.new("wrong number of arguments (1 for 3)")
|
690
693
|
elsif blue.nil?
|
691
|
-
return unquoted_string("rgb(#{red}, #{green})") if
|
694
|
+
return unquoted_string("rgb(#{red}, #{green})") if red.is_var? || green.is_var?
|
692
695
|
raise ArgumentError.new("wrong number of arguments (2 for 3)")
|
693
696
|
end
|
694
697
|
|
695
|
-
if
|
698
|
+
if red.is_special_number? || green.is_special_number? || blue.is_special_number?
|
696
699
|
return unquoted_string("rgb(#{red}, #{green}, #{blue})")
|
697
700
|
end
|
698
701
|
assert_type red, :Number, :red
|
@@ -751,13 +754,16 @@ module Sass::Script
|
|
751
754
|
if args.length == 1 && args[0].is_a?(Sass::Script::Value::List)
|
752
755
|
list = args[0]
|
753
756
|
|
757
|
+
# Check if this is a var() call - pass through as-is
|
758
|
+
return unquoted_string("rgba(#{list})") if list.is_var?
|
759
|
+
|
754
760
|
if list.separator == :space
|
755
761
|
# New space-separated syntax
|
756
762
|
if list.value.length == 3
|
757
763
|
# rgba(0% 100% 0%) - same as rgb without alpha
|
758
764
|
red, green, blue = list.value
|
759
765
|
# Check for special numbers and return as-is
|
760
|
-
if
|
766
|
+
if [red, green, blue].any? { |v| v.is_special_number? }
|
761
767
|
return unquoted_string("rgba(#{list})")
|
762
768
|
end
|
763
769
|
return color_with_hex(red, green, blue)
|
@@ -770,8 +776,8 @@ module Sass::Script
|
|
770
776
|
red, green, blue = rgb_list.value
|
771
777
|
alpha = alpha_list.value[0]
|
772
778
|
# Check for special numbers and return as-is
|
773
|
-
if
|
774
|
-
return
|
779
|
+
if [red, green, blue, alpha].any? { |v| v.is_special_number? }
|
780
|
+
return format_color_function("rgba", list)
|
775
781
|
end
|
776
782
|
return color_with_hex(red, green, blue, alpha)
|
777
783
|
end
|
@@ -783,14 +789,14 @@ module Sass::Script
|
|
783
789
|
# Legacy comma-separated syntax
|
784
790
|
case args.size
|
785
791
|
when 1
|
786
|
-
return unquoted_string("rgba(#{args.first})") if
|
792
|
+
return unquoted_string("rgba(#{args.first})") if args.first.is_var?
|
787
793
|
raise ArgumentError.new("wrong number of arguments (1 for 4)")
|
788
794
|
when 2
|
789
795
|
color, alpha = args
|
790
796
|
|
791
|
-
if
|
797
|
+
if color.is_var?
|
792
798
|
return unquoted_string("rgba(#{color}, #{alpha})")
|
793
|
-
elsif
|
799
|
+
elsif alpha.is_var?
|
794
800
|
if color.is_a?(Sass::Script::Value::Color)
|
795
801
|
return unquoted_string("rgba(#{color.red}, #{color.green}, #{color.blue}, #{alpha})")
|
796
802
|
else
|
@@ -799,22 +805,22 @@ module Sass::Script
|
|
799
805
|
end
|
800
806
|
|
801
807
|
assert_type color, :Color, :color
|
802
|
-
if
|
808
|
+
if alpha.is_special_number?
|
803
809
|
unquoted_string("rgba(#{color.red}, #{color.green}, #{color.blue}, #{alpha})")
|
804
810
|
else
|
805
811
|
assert_type alpha, :Number, :alpha
|
806
812
|
color.with(:alpha => percentage_or_unitless(alpha, 1, "alpha"))
|
807
813
|
end
|
808
814
|
when 3
|
809
|
-
if
|
815
|
+
if args[0].is_var? || args[1].is_var? || args[2].is_var?
|
810
816
|
unquoted_string("rgba(#{args.join(', ')})")
|
811
817
|
else
|
812
818
|
raise ArgumentError.new("wrong number of arguments (3 for 4)")
|
813
819
|
end
|
814
820
|
when 4
|
815
821
|
red, green, blue, alpha = args
|
816
|
-
if
|
817
|
-
|
822
|
+
if red.is_special_number? || green.is_special_number? ||
|
823
|
+
blue.is_special_number? || alpha.is_special_number?
|
818
824
|
unquoted_string("rgba(#{red}, #{green}, #{blue}, #{alpha})")
|
819
825
|
else
|
820
826
|
rgba(rgb(red, green, blue), alpha)
|
@@ -850,11 +856,18 @@ module Sass::Script
|
|
850
856
|
if args.length == 1 && args[0].is_a?(Sass::Script::Value::List)
|
851
857
|
list = args[0]
|
852
858
|
|
859
|
+
# Check if this is a var() call - pass through as-is
|
860
|
+
return unquoted_string("hsl(#{list})") if list.is_var?
|
861
|
+
|
853
862
|
if list.separator == :space
|
854
863
|
# New space-separated syntax
|
855
864
|
if list.value.length == 3
|
856
865
|
# hsl(180 60% 50%) - simple space-separated without alpha
|
857
866
|
hue, saturation, lightness = list.value
|
867
|
+
# Check for special numbers and recurse
|
868
|
+
if [hue, saturation, lightness].any? { |v| v.is_special_number? }
|
869
|
+
return unquoted_string("hsl(#{list})")
|
870
|
+
end
|
858
871
|
return hsl(hue, saturation, lightness)
|
859
872
|
elsif list.value.length == 2
|
860
873
|
# hsl(180 60% 50% / 0.5) - space list with slash list for alpha
|
@@ -864,6 +877,10 @@ module Sass::Script
|
|
864
877
|
hsl_list.value.length == 3 && alpha_list.value.length == 1
|
865
878
|
hue, saturation, lightness = hsl_list.value
|
866
879
|
alpha = alpha_list.value[0]
|
880
|
+
# Check for special numbers
|
881
|
+
if [hue, saturation, lightness, alpha].any? { |v| v.is_special_number? }
|
882
|
+
return format_color_function("hsl", list)
|
883
|
+
end
|
867
884
|
return hsla(hue, saturation, lightness, alpha)
|
868
885
|
end
|
869
886
|
end
|
@@ -874,14 +891,14 @@ module Sass::Script
|
|
874
891
|
# Legacy comma-separated syntax
|
875
892
|
hue, saturation, lightness = args
|
876
893
|
if saturation.nil?
|
877
|
-
return unquoted_string("hsl(#{hue})") if
|
894
|
+
return unquoted_string("hsl(#{hue})") if hue.is_var?
|
878
895
|
raise ArgumentError.new("wrong number of arguments (1 for 3)")
|
879
896
|
elsif lightness.nil?
|
880
|
-
return unquoted_string("hsl(#{hue}, #{saturation})") if
|
897
|
+
return unquoted_string("hsl(#{hue}, #{saturation})") if hue.is_var? || saturation.is_var?
|
881
898
|
raise ArgumentError.new("wrong number of arguments (2 for 3)")
|
882
899
|
end
|
883
900
|
|
884
|
-
if
|
901
|
+
if hue.is_special_number? || saturation.is_special_number? || lightness.is_special_number?
|
885
902
|
unquoted_string("hsl(#{hue}, #{saturation}, #{lightness})")
|
886
903
|
else
|
887
904
|
hsla(hue, saturation, lightness, number(1))
|
@@ -916,11 +933,18 @@ module Sass::Script
|
|
916
933
|
if args.length == 1 && args[0].is_a?(Sass::Script::Value::List)
|
917
934
|
list = args[0]
|
918
935
|
|
936
|
+
# Check if this is a var() call - pass through as-is
|
937
|
+
return unquoted_string("hsla(#{list})") if list.is_var?
|
938
|
+
|
919
939
|
if list.separator == :space
|
920
940
|
# New space-separated syntax
|
921
941
|
if list.value.length == 3
|
922
942
|
# hsla(180 60% 50%) - same as hsl without alpha
|
923
943
|
hue, saturation, lightness = list.value
|
944
|
+
# Check for special numbers
|
945
|
+
if [hue, saturation, lightness].any? { |v| v.is_special_number? }
|
946
|
+
return unquoted_string("hsla(#{list})")
|
947
|
+
end
|
924
948
|
return hsla(hue, saturation, lightness, number(1))
|
925
949
|
elsif list.value.length == 2
|
926
950
|
# hsla(180 60% 50% / 0.5) - space list with slash list for alpha
|
@@ -930,6 +954,10 @@ module Sass::Script
|
|
930
954
|
hsl_list.value.length == 3 && alpha_list.value.length == 1
|
931
955
|
hue, saturation, lightness = hsl_list.value
|
932
956
|
alpha = alpha_list.value[0]
|
957
|
+
# Check for special numbers
|
958
|
+
if [hue, saturation, lightness, alpha].any? { |v| v.is_special_number? }
|
959
|
+
return format_color_function("hsla", list)
|
960
|
+
end
|
933
961
|
return hsla(hue, saturation, lightness, alpha)
|
934
962
|
end
|
935
963
|
end
|
@@ -940,21 +968,21 @@ module Sass::Script
|
|
940
968
|
# Legacy comma-separated syntax
|
941
969
|
hue, saturation, lightness, alpha = args
|
942
970
|
if saturation.nil?
|
943
|
-
return unquoted_string("hsla(#{hue})") if
|
971
|
+
return unquoted_string("hsla(#{hue})") if hue.is_var?
|
944
972
|
raise ArgumentError.new("wrong number of arguments (1 for 4)")
|
945
973
|
elsif lightness.nil?
|
946
|
-
return unquoted_string("hsla(#{hue}, #{saturation})") if
|
974
|
+
return unquoted_string("hsla(#{hue}, #{saturation})") if hue.is_var? || saturation.is_var?
|
947
975
|
raise ArgumentError.new("wrong number of arguments (2 for 4)")
|
948
976
|
elsif alpha.nil?
|
949
|
-
if
|
977
|
+
if hue.is_var? || saturation.is_var? || lightness.is_var?
|
950
978
|
return unquoted_string("hsla(#{hue}, #{saturation}, #{lightness})")
|
951
979
|
else
|
952
980
|
raise ArgumentError.new("wrong number of arguments (2 for 4)")
|
953
981
|
end
|
954
982
|
end
|
955
983
|
|
956
|
-
if
|
957
|
-
|
984
|
+
if hue.is_special_number? || saturation.is_special_number? ||
|
985
|
+
lightness.is_special_number? || alpha.is_special_number?
|
958
986
|
return unquoted_string("hsla(#{hue}, #{saturation}, #{lightness}, #{alpha})")
|
959
987
|
end
|
960
988
|
assert_type hue, :Number, :hue
|
@@ -168,6 +168,24 @@ MSG
|
|
168
168
|
true
|
169
169
|
end
|
170
170
|
|
171
|
+
# Returns whether this value is a "special number" that CSS may treat as a number.
|
172
|
+
# This includes functions like `calc()`, `var()`, `env()`, etc.
|
173
|
+
#
|
174
|
+
# According to CSS Color Level 4 and CSS Values Level 4 specs, these functions
|
175
|
+
# should be passed through to CSS without evaluation.
|
176
|
+
#
|
177
|
+
# @return [Boolean] Whether this is a special number value
|
178
|
+
def is_special_number?
|
179
|
+
false
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns whether this value is a `var()` call.
|
183
|
+
#
|
184
|
+
# @return [Boolean] Whether this is a var() call
|
185
|
+
def is_var?
|
186
|
+
false
|
187
|
+
end
|
188
|
+
|
171
189
|
# Compares this object with another.
|
172
190
|
#
|
173
191
|
# @param other [Object] The object to compare with
|
@@ -130,6 +130,43 @@ module Sass::Script::Value
|
|
130
130
|
end
|
131
131
|
alias_method :identifier, :unquoted_string
|
132
132
|
|
133
|
+
# Formats a CSS Color Level 4 function call with proper slash separator handling.
|
134
|
+
# Used for functions like rgb(), hsl() that support space-separated and slash-separated syntax.
|
135
|
+
#
|
136
|
+
# @param function_name [String] The name of the function (e.g., "rgb", "hsl")
|
137
|
+
# @param list [Sass::Script::Value::List] The list of arguments
|
138
|
+
# @return [Sass::Script::Value::String] The formatted function string
|
139
|
+
def format_color_function(function_name, list)
|
140
|
+
# Check if this is a space-separated list with a slash-separated alpha component
|
141
|
+
if list.is_a?(Sass::Script::Value::List) &&
|
142
|
+
list.separator == :space &&
|
143
|
+
list.value.length == 2
|
144
|
+
|
145
|
+
rgb_or_hsl = list.value[0]
|
146
|
+
alpha_part = list.value[1]
|
147
|
+
|
148
|
+
# Check if alpha part is a slash-separated list
|
149
|
+
if alpha_part.is_a?(Sass::Script::Value::List) &&
|
150
|
+
alpha_part.separator == :slash &&
|
151
|
+
alpha_part.value.length == 1
|
152
|
+
|
153
|
+
# Format: rgb(255 128 0 / 0.5)
|
154
|
+
rgb_str = if rgb_or_hsl.is_a?(Sass::Script::Value::List)
|
155
|
+
rgb_or_hsl.value.map(&:to_s).join(' ')
|
156
|
+
else
|
157
|
+
rgb_or_hsl.to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
alpha_str = alpha_part.value[0].to_s
|
161
|
+
|
162
|
+
return unquoted_string("#{function_name}(#{rgb_str} / #{alpha_str})")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Default formatting
|
167
|
+
unquoted_string("#{function_name}(#{list})")
|
168
|
+
end
|
169
|
+
|
133
170
|
# Parses a user-provided selector.
|
134
171
|
#
|
135
172
|
# @param value [Sass::Script::Value::String, Sass::Script::Value::List]
|
@@ -222,12 +259,18 @@ module Sass::Script::Value
|
|
222
259
|
end
|
223
260
|
|
224
261
|
# Returns whether the literal is a special CSS value that may evaluate to a
|
225
|
-
# number, such as `calc()` or `
|
262
|
+
# number, such as `calc()`, `var()`, `env()`, `attr()`, `clamp()`, `min()`, or `max()`.
|
263
|
+
#
|
264
|
+
# These functions are part of CSS Color Level 4 spec and should be passed through
|
265
|
+
# to CSS without evaluation.
|
226
266
|
#
|
227
267
|
# @param literal [Sass::Script::Value::Base] The value to check
|
228
268
|
# @return Boolean
|
229
269
|
def special_number?(literal)
|
230
|
-
literal.is_a?(Sass::Script::Value::String)
|
270
|
+
return false unless literal.is_a?(Sass::Script::Value::String)
|
271
|
+
# Check for CSS special functions that can evaluate to numbers
|
272
|
+
# According to CSS Color Level 4 and CSS Values Level 4 specs
|
273
|
+
literal.value =~ /(calc|var|env|attr|clamp|min|max)\s*\(/i
|
231
274
|
end
|
232
275
|
|
233
276
|
private
|
@@ -134,5 +134,20 @@ WARNING
|
|
134
134
|
def inspect
|
135
135
|
String.quote(value)
|
136
136
|
end
|
137
|
+
|
138
|
+
# Returns whether this string is a "special number" function like calc() or var().
|
139
|
+
# According to CSS Color Level 4 spec, these should be passed through to CSS.
|
140
|
+
#
|
141
|
+
# @return [Boolean]
|
142
|
+
def is_special_number?
|
143
|
+
type == :identifier && value =~ /(calc|var|env|attr|clamp|min|max)\s*\(/i
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns whether this string is a var() call.
|
147
|
+
#
|
148
|
+
# @return [Boolean]
|
149
|
+
def is_var?
|
150
|
+
type == :identifier && value =~ /var\s*\(/i
|
151
|
+
end
|
137
152
|
end
|
138
153
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass4
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Natalie Weizenbaum
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2025-10-
|
14
|
+
date: 2025-10-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: sass-listen
|