From 4a0ab8d1a67c9a0f143ddbacd49cbebb86363f5b Mon Sep 17 00:00:00 2001 From: Ryan Balsdon Date: Wed, 12 Jul 2023 21:50:57 -0400 Subject: [PATCH] lessen restrictions on class names to support tailwindcss styling --- typed-html/src/types/class.rs | 60 ++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/typed-html/src/types/class.rs b/typed-html/src/types/class.rs index 6209120..d123e03 100644 --- a/typed-html/src/types/class.rs +++ b/typed-html/src/types/class.rs @@ -31,20 +31,25 @@ impl Class { } } +// w3.org defines a strict grammar for classes in the CSS parser at +// https://www.w3.org/TR/CSS21/grammar.html#scanner, as shown below where +// ident is the class name: +// ident -?{nmstart}{nmchar}* +// nmstart [_a-z]|{nonascii}|{escape} +// nmchar [_a-z0-9-]|{nonascii}|{escape} +// nonascii [\240-\377] //Note: this is 0xA0 to 0xFF in hex +// unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])? +// escape {unicode}|\\[^\r\n\f0-9a-f] +// w3.org defines a very different grammar for class names on the HTML side at +// https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes, which +// has no limits other than spaces not being allowed. This implementation is +// for typed html and will use the HTML grammar. impl FromStr for Class { type Err = &'static str; fn from_str(s: &str) -> Result { - let mut chars = s.chars(); - match chars.next() { - None => return Err("class name cannot be empty"), - Some(c) if !c.is_alphabetic() => { - return Err("class name must start with an alphabetic character") - } - _ => (), - } - for c in chars { - if !c.is_alphanumeric() && c != '_' && c != '-' && c != '.' { - return Err("class name can only contain alphanumerics, dash, dot and underscore"); + for c in s.chars() { + if c.is_whitespace() { + return Err("class name may not contain spaces"); } } Ok(Class(s.to_string())) @@ -76,3 +81,36 @@ impl Deref for Class { &self.0 } } + +#[test] +fn test_from_string() { + use crate::types::Class; + // This test covers some tricky examples from TailwindsCSS + // that don't quite pass the strict CSS-style grammar but + // are valid in HTML. + + assert_eq!( + Ok(Class(String::from("-bottom"))), + Class::from_str("-bottom") + ); + + assert_eq!( + Ok(Class(String::from("top-[3px]"))), + Class::from_str("top-[3px]") + ); + + assert_eq!( + Ok(Class(String::from("md:top-6"))), + Class::from_str("md:top-6") + ); + + assert_eq!( + Ok(Class(String::from("w-1/2"))), + Class::from_str("w-1/2") + ); + + assert_eq!( + Ok(Class(String::from("bg-[#50d71e]"))), + Class::from_str("bg-[#50d71e]") + ); +}