@@ -608,14 +608,35 @@ final class _SerializeVisitor
608
608
_maybeWriteSlashAlpha (value);
609
609
_buffer.writeCharCode ($rparen);
610
610
611
- // case ColorSpace.lab ||
612
- // ColorSpace.oklab ||
613
- // ColorSpace.lch ||
614
- // ColorSpace.oklch when fuzzyInRange(value.channel0, 0, 100) && !value.isChannel1Missing && !value.isChannel2Missing:
615
- // case ColorSpace.lch ||
616
- // ColorSpace.oklch when fuzzyLessThan(value.channel1, 0) && !value.isChannel0Missing && !value.isChannel1Missing:
617
- // // color-mix() is currently more widely supported than relative color
618
- // // syntax, so we use it to serialize out-of-gamut
611
+ case ColorSpace .lab ||
612
+ ColorSpace .oklab ||
613
+ ColorSpace .lch ||
614
+ ColorSpace .oklch
615
+ when ! _inspect &&
616
+ ! fuzzyInRange (value.channel0, 0 , 100 ) &&
617
+ ! value.isChannel1Missing &&
618
+ ! value.isChannel2Missing:
619
+ case ColorSpace .lch || ColorSpace .oklch
620
+ when ! _inspect &&
621
+ fuzzyLessThan (value.channel1, 0 ) &&
622
+ ! value.isChannel0Missing &&
623
+ ! value.isChannel1Missing:
624
+ // color-mix() is currently more widely supported than relative color
625
+ // syntax, so we use it to serialize out-of-gamut colors in a way that
626
+ // maintains the color space defined in Sass while (per spec) not
627
+ // clamping their values. In practice, all browsers clamp out-of-gamut
628
+ // values, but there's not much we can do about that at time of writing.
629
+ _buffer.write ('color-mix(in ' );
630
+ _buffer.write (value.space);
631
+ _buffer.write (_commaSeparator);
632
+ // The XYZ space has no gamut restrictions, so we use it to represent
633
+ // the out-of-gamut color before converting into the target space.
634
+ _writeColorFunction (value.toSpace (ColorSpace .xyzD65));
635
+ _writeOptionalSpace ();
636
+ _buffer.write ('100%' );
637
+ _buffer.write (_commaSeparator);
638
+ _buffer.write (_isCompressed ? 'red' : 'black' );
639
+ _buffer.writeCharCode ($rparen);
619
640
620
641
case ColorSpace .lab ||
621
642
ColorSpace .oklab ||
@@ -624,6 +645,21 @@ final class _SerializeVisitor
624
645
_buffer
625
646
..write (value.space)
626
647
..writeCharCode ($lparen);
648
+
649
+ // color-mix() can't represent out-of-bounds colors with missing
650
+ // channels, so in this case we use the less-supported but
651
+ // more-expressive relative color syntax instead. Relative color syntax
652
+ // never clamps channels.
653
+ var polar = value.space.channels[2 ].isPolarAngle;
654
+ if (! _inspect &&
655
+ (! fuzzyInRange (value.channel0, 0 , 100 ) ||
656
+ (polar && fuzzyLessThan (value.channel1, 0 )))) {
657
+ _buffer
658
+ ..write ('from ' )
659
+ ..write (_isCompressed ? 'red' : 'black' )
660
+ ..writeCharCode ($space);
661
+ }
662
+
627
663
if (! _isCompressed && ! value.isChannel0Missing) {
628
664
var max = (value.space.channels[0 ] as LinearChannel ).max;
629
665
_writeNumber (value.channel0 * 100 / max);
@@ -635,22 +671,14 @@ final class _SerializeVisitor
635
671
_writeChannel (value.channel1OrNull);
636
672
_buffer.writeCharCode ($space);
637
673
_writeChannel (value.channel2OrNull);
638
- if (! _isCompressed &&
639
- ! value.isChannel2Missing &&
640
- value.space.channels[2 ].isPolarAngle) {
674
+ if (! _isCompressed && ! value.isChannel2Missing && polar) {
641
675
_buffer.write ('deg' );
642
676
}
643
677
_maybeWriteSlashAlpha (value);
644
678
_buffer.writeCharCode ($rparen);
645
679
646
680
case _:
647
- _buffer
648
- ..write ('color(' )
649
- ..write (value.space)
650
- ..writeCharCode ($space);
651
- _writeBetween (value.channelsOrNull, ' ' , _writeChannel);
652
- _maybeWriteSlashAlpha (value);
653
- _buffer.writeCharCode ($rparen);
681
+ _writeColorFunction (value);
654
682
}
655
683
}
656
684
@@ -874,6 +902,26 @@ final class _SerializeVisitor
874
902
_buffer.writeCharCode ($rparen);
875
903
}
876
904
905
+ /// Writes [color] using the `color()` function syntax.
906
+ void _writeColorFunction (SassColor color) {
907
+ assert (! {
908
+ ColorSpace .rgb,
909
+ ColorSpace .hsl,
910
+ ColorSpace .hwb,
911
+ ColorSpace .lab,
912
+ ColorSpace .oklab,
913
+ ColorSpace .lch,
914
+ ColorSpace .oklch
915
+ }.contains (color.space));
916
+ _buffer
917
+ ..write ('color(' )
918
+ ..write (color.space)
919
+ ..writeCharCode ($space);
920
+ _writeBetween (color.channelsOrNull, ' ' , _writeChannel);
921
+ _maybeWriteSlashAlpha (color);
922
+ _buffer.writeCharCode ($rparen);
923
+ }
924
+
877
925
/// Returns whether [color] 's hex pair representation is symmetrical (e.g.
878
926
/// `FF` ).
879
927
bool _isSymmetricalHex (int color) => color & 0xF == color >> 4 ;
0 commit comments