From 0c8349e6869a5bc8fafdbf23f95dcee5b56c738e Mon Sep 17 00:00:00 2001 From: Ryan Walker Date: Thu, 20 Nov 2025 00:08:58 -0700 Subject: [PATCH] fix(css): updating formatting of non-lowercase CSS dimension units (#8175) Co-authored-by: Emanuele Stoppa --- .changeset/wise-dots-rush.md | 25 ++++++++ .../src/css/value/regular_dimension.rs | 4 +- .../src/css/value/unknown_dimension.rs | 7 ++- .../src/utils/string_utils.rs | 58 +++++++++++++++++++ .../tests/specs/css/units.css | 5 +- .../tests/specs/css/units.css.snap | 16 +++-- 6 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 .changeset/wise-dots-rush.md diff --git a/.changeset/wise-dots-rush.md b/.changeset/wise-dots-rush.md new file mode 100644 index 0000000000..96bdd80d32 --- /dev/null +++ b/.changeset/wise-dots-rush.md @@ -0,0 +1,25 @@ +--- +"@biomejs/biome": patch +--- + +Fixed CSS formatting of dimension units to use correct casing for `Q`, `Hz` and `kHz`. + +**Before:** + +``` css +.cssUnits { + a: 1q; + b: 1hz; + c: 1khz; +} +``` + +**After:** + +``` css +.cssUnits { + a: 1Q; + b: 1Hz; + c: 1kHz; +} +``` diff --git a/crates/biome_css_formatter/src/css/value/regular_dimension.rs b/crates/biome_css_formatter/src/css/value/regular_dimension.rs index 01ec0f68f7..9c8ff79106 100644 --- a/crates/biome_css_formatter/src/css/value/regular_dimension.rs +++ b/crates/biome_css_formatter/src/css/value/regular_dimension.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, utils::string_utils::FormatTokenAsLowercase}; +use crate::{prelude::*, utils::string_utils::FormatDimensionUnit}; use biome_css_syntax::{CssRegularDimension, CssRegularDimensionFields}; use biome_formatter::{token::number::NumberFormatOptions, write}; @@ -15,7 +15,7 @@ impl FormatNodeRule for FormatCssRegularDimension { f, [ format_number_token(&value_token?, NumberFormatOptions::default()), - FormatTokenAsLowercase::from(unit_token?), + FormatDimensionUnit::from(unit_token?), ] ) } diff --git a/crates/biome_css_formatter/src/css/value/unknown_dimension.rs b/crates/biome_css_formatter/src/css/value/unknown_dimension.rs index 47f1963774..16b59fc970 100644 --- a/crates/biome_css_formatter/src/css/value/unknown_dimension.rs +++ b/crates/biome_css_formatter/src/css/value/unknown_dimension.rs @@ -1,4 +1,7 @@ -use crate::{prelude::*, utils::string_utils::FormatTokenAsLowercase}; +use crate::{ + prelude::*, + utils::string_utils::{FormatDimensionUnit, FormatTokenAsLowercase}, +}; use biome_css_syntax::{CssUnknownDimension, CssUnknownDimensionFields}; use biome_formatter::write; @@ -15,7 +18,7 @@ impl FormatNodeRule for FormatCssUnknownDimension { f, [ FormatTokenAsLowercase::from(value_token?), - FormatTokenAsLowercase::from(unit_token?), + FormatDimensionUnit::from(unit_token?), ] ); var_name diff --git a/crates/biome_css_formatter/src/utils/string_utils.rs b/crates/biome_css_formatter/src/utils/string_utils.rs index 84fa158a2d..1ce602be98 100644 --- a/crates/biome_css_formatter/src/utils/string_utils.rs +++ b/crates/biome_css_formatter/src/utils/string_utils.rs @@ -296,3 +296,61 @@ impl<'token> LiteralStringNormaliser<'token> { } } } + +pub(crate) struct FormatDimensionUnit { + token: SyntaxToken, +} + +impl From> for FormatDimensionUnit { + fn from(value: SyntaxToken) -> Self { + Self { token: value } + } +} + +impl Format for FormatDimensionUnit { + fn fmt(&self, f: &mut CssFormatter) -> FormatResult<()> { + let original = self.token.text_trimmed(); + + match original.to_ascii_lowercase_cow() { + Cow::Borrowed(lowercase) => { + if let Some(uppercase) = map_dimension_unit(lowercase) { + write!( + f, + [format_replaced( + &self.token, + &text(&uppercase, self.token.text_trimmed_range().start()), + )] + ) + } else { + write!(f, [self.token.format()]) + } + } + + Cow::Owned(lowercase) => { + write!( + f, + [format_replaced( + &self.token, + &text( + &map_dimension_unit(lowercase.as_str()).unwrap_or(lowercase), + self.token.text_trimmed_range().start() + ), + )] + ) + } + } + } +} + +/// Most CSS dimension units can be formatted as lower case, but there are +/// a few that are more commonly formatted with some uppercase characters. +/// This maps those few cases to the correct casing. This matches the +/// behavior of the Prettier formatter. +fn map_dimension_unit(unit: &str) -> Option { + match unit { + "hz" => Some(String::from("Hz")), + "khz" => Some(String::from("kHz")), + "q" => Some(String::from("Q")), + _ => None, + } +} diff --git a/crates/biome_css_formatter/tests/specs/css/units.css b/crates/biome_css_formatter/tests/specs/css/units.css index fe59451616..c60d1bb722 100644 --- a/crates/biome_css_formatter/tests/specs/css/units.css +++ b/crates/biome_css_formatter/tests/specs/css/units.css @@ -20,6 +20,7 @@ a: 5CM; a: 5MM; a: 5Q; + a: 5q; a: 5IN; a: 5PT; a: 5PC; @@ -30,7 +31,9 @@ a: 5S; a: 5MS; a: 5HZ; + a: 5hz; a: 5KHZ; + a: 5kHz; a: 5DPI; a: 5DPCM; a: 5DPPX; @@ -41,4 +44,4 @@ a: 5Unknown; /* http://browserbu.gs/css-hacks/media-min-width-0-backslash-0/ */ a: 0\0; -} \ No newline at end of file +} diff --git a/crates/biome_css_formatter/tests/specs/css/units.css.snap b/crates/biome_css_formatter/tests/specs/css/units.css.snap index 6dcdbf9db3..39e7cafc87 100644 --- a/crates/biome_css_formatter/tests/specs/css/units.css.snap +++ b/crates/biome_css_formatter/tests/specs/css/units.css.snap @@ -2,7 +2,6 @@ source: crates/biome_formatter_test/src/snapshot_builder.rs info: css/units.css --- - # Input ```css @@ -28,6 +27,7 @@ info: css/units.css a: 5CM; a: 5MM; a: 5Q; + a: 5q; a: 5IN; a: 5PT; a: 5PC; @@ -38,7 +38,9 @@ info: css/units.css a: 5S; a: 5MS; a: 5HZ; + a: 5hz; a: 5KHZ; + a: 5kHz; a: 5DPI; a: 5DPCM; a: 5DPPX; @@ -50,6 +52,7 @@ info: css/units.css /* http://browserbu.gs/css-hacks/media-min-width-0-backslash-0/ */ a: 0\0; } + ``` @@ -89,7 +92,8 @@ Quote style: Double Quotes a: 5vmax; a: 5cm; a: 5mm; - a: 5q; + a: 5Q; + a: 5Q; a: 5in; a: 5pt; a: 5pc; @@ -99,8 +103,10 @@ Quote style: Double Quotes a: 5rad; a: 5s; a: 5ms; - a: 5hz; - a: 5khz; + a: 5Hz; + a: 5Hz; + a: 5kHz; + a: 5kHz; a: 5dpi; a: 5dpcm; a: 5dppx; @@ -113,5 +119,3 @@ Quote style: Double Quotes a: 0\0; } ``` - -