From 5a55e839063a9b802de4280ab15f70ab244ec4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pokrywka?= Date: Sat, 2 May 2026 14:02:13 +0200 Subject: [PATCH] Fixed `TwClass` in dynamic attribute --- .../src/html_parser/component.rs | 20 +++++++---- crates/vertigo/src/dom/dom_element.rs | 1 + .../src/tests/dom/component_dynamic.rs | 36 ++++++++++++++++++- docs/CHANGELOG.md | 1 + 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/crates/vertigo-macro/src/html_parser/component.rs b/crates/vertigo-macro/src/html_parser/component.rs index 90d4df12..2b2a43eb 100644 --- a/crates/vertigo-macro/src/html_parser/component.rs +++ b/crates/vertigo-macro/src/html_parser/component.rs @@ -277,13 +277,19 @@ fn punctuated_attribute_to_tokens( .trim_start_matches(&format!("{}:", group.to_token_stream())) .to_string(); - // If value name is 'tw' then register tailwind classes - if key == "tw" - && let Err(err) = add_to_tailwind(possible_value.to_token_stream()) - { - emit_error!(possible_value.span(), err); - return None; - }; + // If value name is 'tw' and it's a string literal, register tailwind classes. + // Block expressions like div:tw={my_tw_class} hold a pre-existing TwClass whose + // classes were already registered when tw!() was called — skip bundler tracing. + if key == "tw" { + let (block, _lit) = + take_block_or_literal_expr(possible_value, COMPONENT_ATTR_FORMAT_ERROR); + if block.is_none() + && let Err(err) = add_to_tailwind(possible_value.to_token_stream()) + { + emit_error!(possible_value.span(), err); + return None; + } + } match group { Some(group) => { diff --git a/crates/vertigo/src/dom/dom_element.rs b/crates/vertigo/src/dom/dom_element.rs index 0935ce9a..518d4436 100644 --- a/crates/vertigo/src/dom/dom_element.rs +++ b/crates/vertigo/src/dom/dom_element.rs @@ -127,6 +127,7 @@ impl DomElement { } ("on_submit", AttrGroupValue::OnSubmit(on_submit)) | ("form", AttrGroupValue::OnSubmit(on_submit)) => self.on_submit_rc(on_submit), + ("tw", AttrGroupValue::AttrValue(value)) => self.attr("class", value), (_, AttrGroupValue::AttrValue(value)) => self.attr(key, value), (_, _) => { crate::log::error!("Invalid attribute type for key {key}"); diff --git a/crates/vertigo/src/tests/dom/component_dynamic.rs b/crates/vertigo/src/tests/dom/component_dynamic.rs index 1550705e..2773a4cd 100644 --- a/crates/vertigo/src/tests/dom/component_dynamic.rs +++ b/crates/vertigo/src/tests/dom/component_dynamic.rs @@ -1,7 +1,7 @@ use crate::{ self as vertigo, AttrGroup, Computed, EmbedDom, component, css, dev::inspect::{DomDebugFragment, log_start}, - dom, + dom, tw, }; #[test] @@ -99,6 +99,40 @@ fn test_css_attrs_grouping_and_spreading() { ); } +#[test] +fn test_tw_attrs_grouping_and_spreading() { + #[component] + fn Hello<'a>(name: &'a str, div: AttrGroup, span: AttrGroup) { + dom! { +
+ + "Hello " {name} + +
+ } + } + + let red_css = tw!("px-3 bg-red-500"); + let green_css = tw!("py-2 bg-green-500"); + + log_start(); + + let _el1 = dom! { + + }; + + let el_str = DomDebugFragment::from_log().to_pseudo_html(); + + assert_eq!( + el_str, + "
Hello world
" + ); +} + #[test] fn test_css_extending() { use vertigo::{AttrGroup, Value, component, css, dom}; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e3e4c3af..bd816a7a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixed * Partially fixed SVG tags rendering, for full fix trace [#539] +* `TwClass` in dynamic attribute (`dyn:tw={my_tw_class}`) ## 0.11.4 - 2026-04-18