@@ -1144,48 +1144,69 @@ impl Evaluator {
1144 let mut result_nodes =
1144 let mut result_nodes =
1145 replace_children_and_slots(body_nodes, &caller_children, &slot_contents);
1145 replace_children_and_slots(body_nodes, &caller_children, &slot_contents);
1147+ // Attributes that aren't parameters style the function's root
1148+ // element, and a scoped @style's class goes on it too.
1149+ let scope_class = ctx.scoped_functions.contains(name).then(|| format!("hl-{}", name));
1147− if !forwarded.is_empty() {
1150+ if !forwarded.is_empty() || scope_class.is_some() {
1148 let mut roots = result_nodes.iter_mut().filter_map(|n| match n {
1151 let mut roots = result_nodes.iter_mut().filter_map(|n| match n {
1149 Node::Element(e) => Some(e),
1152 Node::Element(e) => Some(e),
1150 _ => None,
1153 _ => None,
1152− match (roots.next(), roots.next()) {
1155+ let root = match (roots.next(), roots.next()) {
1156+ (Some(root), None) => Some(root),
1159+ let mut problems = Vec::new();
1153− (Some(root), None) => root.attrs.extend(forwarded),
1162+ root.attrs.extend(forwarded);
1163+ if let Some(class) = scope_class {
1164+ match root.attrs.iter_mut().find(|a| a.html && a.key == "class") {
1166+ let existing = attr.value.take().unwrap_or_default();
1167+ attr.value = Some(format!("{} {}", existing, class).trim().to_string());
1169+ None => root.attrs.push(Attribute {
1170+ key: "class".to_string(),
1171+ value: Some(class),
1178+ if !forwarded.is_empty() {
1179+ problems.push(format!(
1180+ "attributes {} on @{} are not parameters, and its body has no single \
1181+ root element to receive them",
1184+ .map(|a| format!("'{}'", a.key))
1185+ .collect::<Vec<_>>()
1190+ if scope_class.is_some() {
1191+ problems.push(format!(
1192+ "@{} has a scoped @style, but its body has no single root element \
1199+ for message in problems {
1154− _ => ctx.diagnostics.push(Diagnostic {
1200+ ctx.diagnostics.push(Diagnostic {
1155 line: line_num,
1201 line: line_num,
1156 column: None,
1202 column: None,
1157− message: format!(
1203+ message,
1158− "attributes {} on @{} are not parameters, and its body has no single \
1159− root element to receive them",
1162− .map(|a| format!("'{}'", a.key))
1163− .collect::<Vec<_>>()
1167 severity: Severity::Warning,
1204 severity: Severity::Warning,
1168 source_line: Some(content.to_string()),
1205 source_line: Some(content.to_string()),
1173− // A function with a scoped @style wraps its output in the scope class
1174− if ctx.scoped_functions.contains(name) {
1175− let wrapper = Element {
1176− kind: ElementKind::El,
1177− attrs: vec![Attribute {
1178− key: "class".to_string(),
1179− value: Some(format!("hl-{}", name)),
1183− children: result_nodes,
1186− result_nodes = vec![Node::Element(wrapper)];
1189 Ok(result_nodes)
1210 Ok(result_nodes)