Skip to content

Commit 0c8349e

Browse files
fix(css): updating formatting of non-lowercase CSS dimension units (#8175)
Co-authored-by: Emanuele Stoppa <[email protected]>
1 parent fb1458b commit 0c8349e

File tree

6 files changed

+104
-11
lines changed

6 files changed

+104
-11
lines changed

.changeset/wise-dots-rush.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed CSS formatting of dimension units to use correct casing for `Q`, `Hz` and `kHz`.
6+
7+
**Before:**
8+
9+
``` css
10+
.cssUnits {
11+
a: 1q;
12+
b: 1hz;
13+
c: 1khz;
14+
}
15+
```
16+
17+
**After:**
18+
19+
``` css
20+
.cssUnits {
21+
a: 1Q;
22+
b: 1Hz;
23+
c: 1kHz;
24+
}
25+
```

crates/biome_css_formatter/src/css/value/regular_dimension.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{prelude::*, utils::string_utils::FormatTokenAsLowercase};
1+
use crate::{prelude::*, utils::string_utils::FormatDimensionUnit};
22
use biome_css_syntax::{CssRegularDimension, CssRegularDimensionFields};
33
use biome_formatter::{token::number::NumberFormatOptions, write};
44

@@ -15,7 +15,7 @@ impl FormatNodeRule<CssRegularDimension> for FormatCssRegularDimension {
1515
f,
1616
[
1717
format_number_token(&value_token?, NumberFormatOptions::default()),
18-
FormatTokenAsLowercase::from(unit_token?),
18+
FormatDimensionUnit::from(unit_token?),
1919
]
2020
)
2121
}

crates/biome_css_formatter/src/css/value/unknown_dimension.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::{prelude::*, utils::string_utils::FormatTokenAsLowercase};
1+
use crate::{
2+
prelude::*,
3+
utils::string_utils::{FormatDimensionUnit, FormatTokenAsLowercase},
4+
};
25
use biome_css_syntax::{CssUnknownDimension, CssUnknownDimensionFields};
36
use biome_formatter::write;
47

@@ -15,7 +18,7 @@ impl FormatNodeRule<CssUnknownDimension> for FormatCssUnknownDimension {
1518
f,
1619
[
1720
FormatTokenAsLowercase::from(value_token?),
18-
FormatTokenAsLowercase::from(unit_token?),
21+
FormatDimensionUnit::from(unit_token?),
1922
]
2023
);
2124
var_name

crates/biome_css_formatter/src/utils/string_utils.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,61 @@ impl<'token> LiteralStringNormaliser<'token> {
296296
}
297297
}
298298
}
299+
300+
pub(crate) struct FormatDimensionUnit {
301+
token: SyntaxToken<CssLanguage>,
302+
}
303+
304+
impl From<SyntaxToken<CssLanguage>> for FormatDimensionUnit {
305+
fn from(value: SyntaxToken<CssLanguage>) -> Self {
306+
Self { token: value }
307+
}
308+
}
309+
310+
impl Format<CssFormatContext> for FormatDimensionUnit {
311+
fn fmt(&self, f: &mut CssFormatter) -> FormatResult<()> {
312+
let original = self.token.text_trimmed();
313+
314+
match original.to_ascii_lowercase_cow() {
315+
Cow::Borrowed(lowercase) => {
316+
if let Some(uppercase) = map_dimension_unit(lowercase) {
317+
write!(
318+
f,
319+
[format_replaced(
320+
&self.token,
321+
&text(&uppercase, self.token.text_trimmed_range().start()),
322+
)]
323+
)
324+
} else {
325+
write!(f, [self.token.format()])
326+
}
327+
}
328+
329+
Cow::Owned(lowercase) => {
330+
write!(
331+
f,
332+
[format_replaced(
333+
&self.token,
334+
&text(
335+
&map_dimension_unit(lowercase.as_str()).unwrap_or(lowercase),
336+
self.token.text_trimmed_range().start()
337+
),
338+
)]
339+
)
340+
}
341+
}
342+
}
343+
}
344+
345+
/// Most CSS dimension units can be formatted as lower case, but there are
346+
/// a few that are more commonly formatted with some uppercase characters.
347+
/// This maps those few cases to the correct casing. This matches the
348+
/// behavior of the Prettier formatter.
349+
fn map_dimension_unit(unit: &str) -> Option<String> {
350+
match unit {
351+
"hz" => Some(String::from("Hz")),
352+
"khz" => Some(String::from("kHz")),
353+
"q" => Some(String::from("Q")),
354+
_ => None,
355+
}
356+
}

crates/biome_css_formatter/tests/specs/css/units.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
a: 5CM;
2121
a: 5MM;
2222
a: 5Q;
23+
a: 5q;
2324
a: 5IN;
2425
a: 5PT;
2526
a: 5PC;
@@ -30,7 +31,9 @@
3031
a: 5S;
3132
a: 5MS;
3233
a: 5HZ;
34+
a: 5hz;
3335
a: 5KHZ;
36+
a: 5kHz;
3437
a: 5DPI;
3538
a: 5DPCM;
3639
a: 5DPPX;
@@ -41,4 +44,4 @@
4144
a: 5Unknown;
4245
/* http://browserbu.gs/css-hacks/media-min-width-0-backslash-0/ */
4346
a: 0\0;
44-
}
47+
}

crates/biome_css_formatter/tests/specs/css/units.css.snap

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
source: crates/biome_formatter_test/src/snapshot_builder.rs
33
info: css/units.css
44
---
5-
65
# Input
76

87
```css
@@ -28,6 +27,7 @@ info: css/units.css
2827
a: 5CM;
2928
a: 5MM;
3029
a: 5Q;
30+
a: 5q;
3131
a: 5IN;
3232
a: 5PT;
3333
a: 5PC;
@@ -38,7 +38,9 @@ info: css/units.css
3838
a: 5S;
3939
a: 5MS;
4040
a: 5HZ;
41+
a: 5hz;
4142
a: 5KHZ;
43+
a: 5kHz;
4244
a: 5DPI;
4345
a: 5DPCM;
4446
a: 5DPPX;
@@ -50,6 +52,7 @@ info: css/units.css
5052
/* http://browserbu.gs/css-hacks/media-min-width-0-backslash-0/ */
5153
a: 0\0;
5254
}
55+
5356
```
5457

5558

@@ -89,7 +92,8 @@ Quote style: Double Quotes
8992
a: 5vmax;
9093
a: 5cm;
9194
a: 5mm;
92-
a: 5q;
95+
a: 5Q;
96+
a: 5Q;
9397
a: 5in;
9498
a: 5pt;
9599
a: 5pc;
@@ -99,8 +103,10 @@ Quote style: Double Quotes
99103
a: 5rad;
100104
a: 5s;
101105
a: 5ms;
102-
a: 5hz;
103-
a: 5khz;
106+
a: 5Hz;
107+
a: 5Hz;
108+
a: 5kHz;
109+
a: 5kHz;
104110
a: 5dpi;
105111
a: 5dpcm;
106112
a: 5dppx;
@@ -113,5 +119,3 @@ Quote style: Double Quotes
113119
a: 0\0;
114120
}
115121
```
116-
117-

0 commit comments

Comments
 (0)