Skip to content

Commit 835e5bc

Browse files
committed
Support number or percentage in lab, lch, oklab, and oklch colors
Fixes parcel-bundler#445
1 parent be440ef commit 835e5bc

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

src/lib.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13534,6 +13534,14 @@ mod tests {
1353413534
".foo { color: lab(29.2345% 39.3825 20.0664); }",
1353513535
".foo{color:lab(29.2345% 39.3825 20.0664)}",
1353613536
);
13537+
minify_test(
13538+
".foo { color: lab(29.2345 39.3825 20.0664); }",
13539+
".foo{color:lab(29.2345% 39.3825 20.0664)}",
13540+
);
13541+
minify_test(
13542+
".foo { color: lab(29.2345% 39.3825% 20.0664%); }",
13543+
".foo{color:lab(29.2345% 49.2281 25.083)}",
13544+
);
1353713545
minify_test(
1353813546
".foo { color: lab(29.2345% 39.3825 20.0664 / 100%); }",
1353913547
".foo{color:lab(29.2345% 39.3825 20.0664)}",
@@ -13546,6 +13554,14 @@ mod tests {
1354613554
".foo { color: lch(29.2345% 44.2 27); }",
1354713555
".foo{color:lch(29.2345% 44.2 27)}",
1354813556
);
13557+
minify_test(
13558+
".foo { color: lch(29.2345 44.2 27); }",
13559+
".foo{color:lch(29.2345% 44.2 27)}",
13560+
);
13561+
minify_test(
13562+
".foo { color: lch(29.2345% 44.2% 27deg); }",
13563+
".foo{color:lch(29.2345% 66.3 27)}",
13564+
);
1354913565
minify_test(
1355013566
".foo { color: lch(29.2345% 44.2 45deg); }",
1355113567
".foo{color:lch(29.2345% 44.2 45)}",
@@ -13566,10 +13582,26 @@ mod tests {
1356613582
".foo { color: oklab(40.101% 0.1147 0.0453); }",
1356713583
".foo{color:oklab(40.101% .1147 .0453)}",
1356813584
);
13585+
minify_test(
13586+
".foo { color: oklab(.40101 0.1147 0.0453); }",
13587+
".foo{color:oklab(40.101% .1147 .0453)}",
13588+
);
13589+
minify_test(
13590+
".foo { color: oklab(40.101% 0.1147% 0.0453%); }",
13591+
".foo{color:oklab(40.101% .0004588 .0001812)}",
13592+
);
1356913593
minify_test(
1357013594
".foo { color: oklch(40.101% 0.12332 21.555); }",
1357113595
".foo{color:oklch(40.101% .12332 21.555)}",
1357213596
);
13597+
minify_test(
13598+
".foo { color: oklch(.40101 0.12332 21.555); }",
13599+
".foo{color:oklch(40.101% .12332 21.555)}",
13600+
);
13601+
minify_test(
13602+
".foo { color: oklch(40.101% 0.12332% 21.555); }",
13603+
".foo{color:oklch(40.101% .00049328 21.555)}",
13604+
);
1357313605
minify_test(
1357413606
".foo { color: oklch(40.101% 0.12332 .5turn); }",
1357513607
".foo{color:oklch(40.101% .12332 180)}",

src/values/color.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,8 @@ impl ToCss for CssColor {
487487
Ok(())
488488
}
489489
CssColor::LAB(lab) => match &**lab {
490-
LABColor::LAB(lab) => write_components("lab", lab.l, lab.a, lab.b, lab.alpha, dest),
491-
LABColor::LCH(lch) => write_components("lch", lch.l, lch.c, lch.h, lch.alpha, dest),
490+
LABColor::LAB(lab) => write_components("lab", lab.l / 100.0, lab.a, lab.b, lab.alpha, dest),
491+
LABColor::LCH(lch) => write_components("lch", lch.l / 100.0, lch.c, lch.h, lch.alpha, dest),
492492
LABColor::OKLAB(lab) => write_components("oklab", lab.l, lab.a, lab.b, lab.alpha, dest),
493493
LABColor::OKLCH(lch) => write_components("oklch", lch.l, lch.c, lch.h, lch.alpha, dest),
494494
},
@@ -826,22 +826,22 @@ fn parse_color_function<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor,
826826

827827
match_ignore_ascii_case! {&*function,
828828
"lab" => {
829-
let (l, a, b, alpha) = parse_lab::<LAB>(input, &mut parser)?;
829+
let (l, a, b, alpha) = parse_lab::<LAB>(input, &mut parser, 100.0, 125.0)?;
830830
let lab = LABColor::LAB(LAB { l, a, b, alpha });
831831
Ok(CssColor::LAB(Box::new(lab)))
832832
},
833833
"oklab" => {
834-
let (l, a, b, alpha) = parse_lab::<OKLAB>(input, &mut parser)?;
834+
let (l, a, b, alpha) = parse_lab::<OKLAB>(input, &mut parser, 1.0, 0.4)?;
835835
let lab = LABColor::OKLAB(OKLAB { l, a, b, alpha });
836836
Ok(CssColor::LAB(Box::new(lab)))
837837
},
838838
"lch" => {
839-
let (l, c, h, alpha) = parse_lch::<LCH>(input, &mut parser)?;
839+
let (l, c, h, alpha) = parse_lch::<LCH>(input, &mut parser, 100.0, 150.0)?;
840840
let lab = LABColor::LCH(LCH { l, c, h, alpha });
841841
Ok(CssColor::LAB(Box::new(lab)))
842842
},
843843
"oklch" => {
844-
let (l, c, h, alpha) = parse_lch::<OKLCH>(input, &mut parser)?;
844+
let (l, c, h, alpha) = parse_lch::<OKLCH>(input, &mut parser, 1.0, 0.4)?;
845845
let lab = LABColor::OKLCH(OKLCH { l, c, h, alpha });
846846
Ok(CssColor::LAB(Box::new(lab)))
847847
},
@@ -875,15 +875,17 @@ fn parse_color_function<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor,
875875
fn parse_lab<'i, 't, T: From<CssColor> + ColorSpace>(
876876
input: &mut Parser<'i, 't>,
877877
parser: &mut ComponentParser,
878+
l_basis: f32,
879+
ab_basis: f32,
878880
) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
879881
// https://www.w3.org/TR/css-color-4/#funcdef-lab
880882
let res = input.parse_nested_block(|input| {
881883
parser.parse_relative::<T>(input)?;
882884

883885
// f32::max() does not propagate NaN, so use clamp for now until f32::maximum() is stable.
884-
let l = parser.parse_percentage(input)?.clamp(0.0, f32::MAX);
885-
let a = parser.parse_number(input)?;
886-
let b = parser.parse_number(input)?;
886+
let l = parse_number_or_percentage(input, parser, l_basis)?.clamp(0.0, f32::MAX);
887+
let a = parse_number_or_percentage(input, parser, ab_basis)?;
888+
let b = parse_number_or_percentage(input, parser, ab_basis)?;
887889
let alpha = parse_alpha(input, parser)?;
888890

889891
Ok((l, a, b, alpha))
@@ -897,6 +899,8 @@ fn parse_lab<'i, 't, T: From<CssColor> + ColorSpace>(
897899
fn parse_lch<'i, 't, T: From<CssColor> + ColorSpace>(
898900
input: &mut Parser<'i, 't>,
899901
parser: &mut ComponentParser,
902+
l_basis: f32,
903+
c_basis: f32,
900904
) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
901905
// https://www.w3.org/TR/css-color-4/#funcdef-lch
902906
let res = input.parse_nested_block(|input| {
@@ -910,8 +914,8 @@ fn parse_lch<'i, 't, T: From<CssColor> + ColorSpace>(
910914
}
911915
}
912916

913-
let l = parser.parse_percentage(input)?.clamp(0.0, f32::MAX);
914-
let c = parser.parse_number(input)?.clamp(0.0, f32::MAX);
917+
let l = parse_number_or_percentage(input, parser, l_basis)?.clamp(0.0, f32::MAX);
918+
let c = parse_number_or_percentage(input, parser, c_basis)?.clamp(0.0, f32::MAX);
915919
let h = parse_angle_or_number(input, parser)?;
916920
let alpha = parse_alpha(input, parser)?;
917921

@@ -956,13 +960,13 @@ fn parse_predefined<'i, 't>(
956960
// Out of gamut values should not be clamped, i.e. values < 0 or > 1 should be preserved.
957961
// The browser will gamut-map the color for the target device that it is rendered on.
958962
let a = input
959-
.try_parse(|input| parse_number_or_percentage(input, parser))
963+
.try_parse(|input| parse_number_or_percentage(input, parser, 1.0))
960964
.unwrap_or(0.0);
961965
let b = input
962-
.try_parse(|input| parse_number_or_percentage(input, parser))
966+
.try_parse(|input| parse_number_or_percentage(input, parser, 1.0))
963967
.unwrap_or(0.0);
964968
let c = input
965-
.try_parse(|input| parse_number_or_percentage(input, parser))
969+
.try_parse(|input| parse_number_or_percentage(input, parser, 1.0))
966970
.unwrap_or(0.0);
967971
let alpha = parse_alpha(input, parser)?;
968972

@@ -1088,10 +1092,11 @@ fn parse_angle_or_number<'i, 't>(
10881092
fn parse_number_or_percentage<'i, 't>(
10891093
input: &mut Parser<'i, 't>,
10901094
parser: &ComponentParser,
1095+
percent_basis: f32,
10911096
) -> Result<f32, ParseError<'i, ParserError<'i>>> {
10921097
Ok(match parser.parse_number_or_percentage(input)? {
10931098
NumberOrPercentage::Number { value } => value,
1094-
NumberOrPercentage::Percentage { unit_value } => unit_value,
1099+
NumberOrPercentage::Percentage { unit_value } => unit_value * percent_basis,
10951100
})
10961101
}
10971102

@@ -1101,7 +1106,7 @@ fn parse_alpha<'i, 't>(
11011106
parser: &ComponentParser,
11021107
) -> Result<f32, ParseError<'i, ParserError<'i>>> {
11031108
let res = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
1104-
parse_number_or_percentage(input, parser)?.clamp(0.0, 1.0)
1109+
parse_number_or_percentage(input, parser, 1.0)?.clamp(0.0, 1.0)
11051110
} else {
11061111
1.0
11071112
};
@@ -1125,6 +1130,7 @@ where
11251130
if a.is_nan() {
11261131
dest.write_str("none")?;
11271132
} else {
1133+
// Safari 15 only supports percentages.
11281134
Percentage(a).to_css(dest)?;
11291135
}
11301136
dest.write_char(' ')?;
@@ -1579,7 +1585,7 @@ impl From<LAB> for XYZd50 {
15791585
const E: f32 = 216.0 / 24389.0; // 6^3/29^3
15801586

15811587
let lab = lab.resolve_missing();
1582-
let l = lab.l * 100.0;
1588+
let l = lab.l;
15831589
let a = lab.a;
15841590
let b = lab.b;
15851591

@@ -1838,7 +1844,7 @@ impl From<XYZd50> for LAB {
18381844

18391845
let f2 = if z > E { z.cbrt() } else { (K * z + 16.0) / 116.0 };
18401846

1841-
let l = ((116.0 * f1) - 16.0) / 100.0;
1847+
let l = (116.0 * f1) - 16.0;
18421848
let a = 500.0 * (f0 - f1);
18431849
let b = 200.0 * (f1 - f2);
18441850
LAB {

0 commit comments

Comments
 (0)