diff --git a/CHANGELOG.md b/CHANGELOG.md index 0104c2321..b4e928a4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure classes containing numbers followed by dash or underscore are extracted correctly ([#16980](https://github.com/tailwindlabs/tailwindcss/pull/16980)) - Ensure arbitrary container queries are extracted correctly ([#16984](https://github.com/tailwindlabs/tailwindcss/pull/16984)) - Ensure classes ending in `[` are extracted in Slim templating language ([#16985](https://github.com/tailwindlabs/tailwindcss/pull/16985)) +- Ensure arbitrary variables with data types are extracted correctly ([#16986](https://github.com/tailwindlabs/tailwindcss/pull/16986)) ## [4.0.10] - 2025-03-05 diff --git a/crates/oxide/src/extractor/arbitrary_value_machine.rs b/crates/oxide/src/extractor/arbitrary_value_machine.rs index aa493c624..c99f7f54a 100644 --- a/crates/oxide/src/extractor/arbitrary_value_machine.rs +++ b/crates/oxide/src/extractor/arbitrary_value_machine.rs @@ -176,6 +176,8 @@ mod tests { "[&>[data-slot=icon]:last-child]", vec!["[&>[data-slot=icon]:last-child]"], ), + // With data types + ("[length:32rem]", vec!["[length:32rem]"]), // Spaces are not allowed ("[ #0088cc ]", vec![]), // Unbalanced brackets are not allowed diff --git a/crates/oxide/src/extractor/arbitrary_variable_machine.rs b/crates/oxide/src/extractor/arbitrary_variable_machine.rs index a56df24ed..65ba5d12a 100644 --- a/crates/oxide/src/extractor/arbitrary_variable_machine.rs +++ b/crates/oxide/src/extractor/arbitrary_variable_machine.rs @@ -36,6 +36,14 @@ enum State { #[default] Idle, + /// Currently parsing the data type of the arbitrary variable + /// + /// ```text + /// (length:--my-opacity) + /// ^^^^^^^ + /// ``` + ParsingDataType, + /// Currently parsing the inside of the arbitrary variable /// /// ```text @@ -81,6 +89,13 @@ impl Machine for ArbitraryVariableMachine { self.next(cursor) } + Class::AlphaLower => { + self.start_pos = cursor.pos; + self.state = State::ParsingDataType; + cursor.advance(); + self.next(cursor) + } + _ => MachineState::Idle, }, @@ -89,6 +104,38 @@ impl Machine for ArbitraryVariableMachine { _ => MachineState::Idle, }, + State::ParsingDataType => { + while cursor.pos < len { + match cursor.curr.into() { + // Valid data type characters + // + // E.g.: `(length:--my-length)` + // ^ + Class::AlphaLower | Class::Dash => { + cursor.advance(); + } + + // End of the data type + // + // E.g.: `(length:--my-length)` + // ^ + Class::Colon => match cursor.next.into() { + Class::Dash => { + self.state = State::Parsing; + cursor.advance(); + return self.next(cursor); + } + + _ => return self.restart(), + }, + + // Anything else is not a valid character + _ => return self.restart(), + }; + } + self.restart() + } + State::Parsing => match self.css_variable_machine.next(cursor) { MachineState::Idle => self.restart(), MachineState::Done(_) => match cursor.next.into() { @@ -286,6 +333,8 @@ mod tests { "(--my-img,url('https://example.com?q=(][)'))", vec!["(--my-img,url('https://example.com?q=(][)'))"], ), + // With a type hint + ("(length:--my-length)", vec!["(length:--my-length)"]), // -------------------------------------------------------- // Exceptions: diff --git a/crates/oxide/src/extractor/mod.rs b/crates/oxide/src/extractor/mod.rs index de78f4154..8f71f27eb 100644 --- a/crates/oxide/src/extractor/mod.rs +++ b/crates/oxide/src/extractor/mod.rs @@ -846,7 +846,7 @@ mod tests { ], ); } - + // https://github.com/tailwindlabs/tailwindcss/issues/16978 #[test] fn test_classes_containing_number_followed_by_dash_or_underscore() { @@ -856,6 +856,15 @@ mod tests { ); } + // https://github.com/tailwindlabs/tailwindcss/issues/16983 + #[test] + fn test_arbitrary_variable_with_data_type() { + assert_extract_sorted_candidates( + r#"
"#, + vec!["bg-(length:--my-length)", "bg-[color:var(--my-color)]"], + ); + } + #[test] fn test_extract_css_variables() { for (input, expected) in [