diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs
index 5780de0a..839e1d92 100644
--- a/libs/extractor/src/lib.rs
+++ b/libs/extractor/src/lib.rs
@@ -8233,4 +8233,80 @@ keyframes({
.unwrap()
));
}
+
+ #[test]
+ #[serial]
+ fn test_dot_notation_theme_variables() {
+ // Test that dot notation theme variables (e.g., $primary.100) are correctly extracted
+ reset_class_map();
+ assert_debug_snapshot!(ToBTreeSet::from(
+ extract(
+ "test.tsx",
+ r#"import {Box} from '@devup-ui/core'
+
+ "#,
+ ExtractOption {
+ package: "@devup-ui/core".to_string(),
+ css_dir: "@devup-ui/core".to_string(),
+ single_css: true,
+ import_main_css: false
+ }
+ )
+ .unwrap()
+ ));
+
+ // Test multiple dot notation variables
+ reset_class_map();
+ assert_debug_snapshot!(ToBTreeSet::from(
+ extract(
+ "test.tsx",
+ r#"import {Box} from '@devup-ui/core'
+
+ "#,
+ ExtractOption {
+ package: "@devup-ui/core".to_string(),
+ css_dir: "@devup-ui/core".to_string(),
+ single_css: true,
+ import_main_css: false
+ }
+ )
+ .unwrap()
+ ));
+
+ // Test deep nested dot notation
+ reset_class_map();
+ assert_debug_snapshot!(ToBTreeSet::from(
+ extract(
+ "test.tsx",
+ r#"import {Box} from '@devup-ui/core'
+
+ "#,
+ ExtractOption {
+ package: "@devup-ui/core".to_string(),
+ css_dir: "@devup-ui/core".to_string(),
+ single_css: true,
+ import_main_css: false
+ }
+ )
+ .unwrap()
+ ));
+
+ // Test dot notation in border shorthand
+ reset_class_map();
+ assert_debug_snapshot!(ToBTreeSet::from(
+ extract(
+ "test.tsx",
+ r#"import {Box} from '@devup-ui/core'
+
+ "#,
+ ExtractOption {
+ package: "@devup-ui/core".to_string(),
+ css_dir: "@devup-ui/core".to_string(),
+ single_css: true,
+ import_main_css: false
+ }
+ )
+ .unwrap()
+ ));
+ }
}
diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap
new file mode 100644
index 00000000..c284a7c0
--- /dev/null
+++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-2.snap
@@ -0,0 +1,27 @@
+---
+source: libs/extractor/src/lib.rs
+expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())"
+---
+ToBTreeSet {
+ styles: {
+ Static(
+ ExtractStaticStyle {
+ property: "background",
+ value: "$gray.200",
+ level: 0,
+ selector: None,
+ style_order: None,
+ },
+ ),
+ Static(
+ ExtractStaticStyle {
+ property: "color",
+ value: "$blue.500",
+ level: 0,
+ selector: None,
+ style_order: None,
+ },
+ ),
+ },
+ code: "import \"@devup-ui/core/devup-ui.css\";\n
;\n",
+}
diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap
new file mode 100644
index 00000000..89adcef8
--- /dev/null
+++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-3.snap
@@ -0,0 +1,18 @@
+---
+source: libs/extractor/src/lib.rs
+expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())"
+---
+ToBTreeSet {
+ styles: {
+ Static(
+ ExtractStaticStyle {
+ property: "background",
+ value: "$color.brand.primary.100",
+ level: 0,
+ selector: None,
+ style_order: None,
+ },
+ ),
+ },
+ code: "import \"@devup-ui/core/devup-ui.css\";\n;\n",
+}
diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap
new file mode 100644
index 00000000..7f73c5f6
--- /dev/null
+++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables-4.snap
@@ -0,0 +1,18 @@
+---
+source: libs/extractor/src/lib.rs
+expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())"
+---
+ToBTreeSet {
+ styles: {
+ Static(
+ ExtractStaticStyle {
+ property: "border",
+ value: "1px solid $border.primary",
+ level: 0,
+ selector: None,
+ style_order: None,
+ },
+ ),
+ },
+ code: "import \"@devup-ui/core/devup-ui.css\";\n;\n",
+}
diff --git a/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap
new file mode 100644
index 00000000..f13e9ac6
--- /dev/null
+++ b/libs/extractor/src/snapshots/extractor__tests__dot_notation_theme_variables.snap
@@ -0,0 +1,18 @@
+---
+source: libs/extractor/src/lib.rs
+expression: "ToBTreeSet::from(extract(\"test.tsx\",\nr#\"import {Box} from '@devup-ui/core'\n \n \"#,\nExtractOption\n{\n package: \"@devup-ui/core\".to_string(), css_dir:\n \"@devup-ui/core\".to_string(), single_css: true, import_main_css: false\n}).unwrap())"
+---
+ToBTreeSet {
+ styles: {
+ Static(
+ ExtractStaticStyle {
+ property: "background",
+ value: "$primary.100",
+ level: 0,
+ selector: None,
+ style_order: None,
+ },
+ ),
+ },
+ code: "import \"@devup-ui/core/devup-ui.css\";\n;\n",
+}
diff --git a/libs/sheet/src/lib.rs b/libs/sheet/src/lib.rs
index 9ffe2f67..d9c7bb7a 100644
--- a/libs/sheet/src/lib.rs
+++ b/libs/sheet/src/lib.rs
@@ -72,7 +72,7 @@ impl ExtractStyle for StyleSheetProperty {
}
}
-static VAR_RE: Lazy = Lazy::new(|| Regex::new(r"\$\w+").unwrap());
+static VAR_RE: Lazy = Lazy::new(|| Regex::new(r"\$[\w.]+").unwrap());
static INTERFACE_KEY_RE: Lazy =
Lazy::new(|| Regex::new(r"^[a-zA-Z_$][a-zA-Z0-9_$]*$").unwrap());
@@ -88,7 +88,7 @@ fn convert_theme_variable_value(value: &str) -> String {
if value.contains("$") {
VAR_RE
.replace_all(value, |caps: ®ex::Captures| {
- format!("var(--{})", &caps[0][1..])
+ format!("var(--{})", &caps[0][1..].replace('.', "-"))
})
.to_string()
} else {
@@ -690,20 +690,22 @@ mod tests {
use extractor::{ExtractOption, extract};
use insta::assert_debug_snapshot;
- #[test]
- fn test_convert_theme_variable_value() {
- assert_eq!(convert_theme_variable_value("1px"), "1px");
- assert_eq!(convert_theme_variable_value("$var"), "var(--var)");
-
- assert_eq!(
- convert_theme_variable_value("$var $var"),
- "var(--var) var(--var)"
- );
-
- assert_eq!(
- convert_theme_variable_value("1px solid $red"),
- "1px solid var(--red)"
- );
+ use rstest::rstest;
+
+ #[rstest]
+ #[case("1px", "1px")]
+ #[case("$var", "var(--var)")]
+ #[case("$var $var", "var(--var) var(--var)")]
+ #[case("1px solid $red", "1px solid var(--red)")]
+ // Test dot notation theme variables (e.g., $primary.100)
+ // Dots should be converted to dashes for CSS variable names
+ #[case("$primary.100", "var(--primary-100)")]
+ #[case("$gray.200 $blue.500", "var(--gray-200) var(--blue-500)")]
+ #[case("1px solid $border.primary", "1px solid var(--border-primary)")]
+ // Test deep nested dot notation
+ #[case("$color.brand.primary.100", "var(--color-brand-primary-100)")]
+ fn test_convert_theme_variable_value(#[case] input: &str, #[case] expected: &str) {
+ assert_eq!(convert_theme_variable_value(input), expected);
}
#[test]