futursolo / stylist-rs

A CSS-in-Rust styling solution for WebAssembly Applications
https://crates.io/crates/stylist
MIT License
373 stars 22 forks source link

Data URL encoding breaks css! #59

Open Madoshakalaka opened 2 years ago

Madoshakalaka commented 2 years ago

I was following this tutorial and noticed something in base64 breaks the macro at compile time.

This passes:

css!(r#"
a[target="_blank"]::after{
    content: '+';
}
"#
)

This panics:

a[target="_blank"]::after {
  content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
}

Producing code

use yew::prelude::*;
use stylist::{
    css,
    yew::styled_component,
};

#[styled_component(App)]
fn app() -> Html {
    let class = css!(r#"

        a[target="_blank"]::after{
            content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
            margin: 0 3px 0 5px;
        }

    "#);
    html! {
        <div {class}>
            <a target="_blank">{"link"}</a>
        </div>

    }
}

fn main() {
    yew::start_app::<App>();
}
WorldSEnder commented 2 years ago

The parsing error is trigger not by the base 64, but by the semicolon after data:image/png;.... This is incorrectly recognized as the end of the attribute, instead of scanning for the closing bracket. As a work-around, the following is correctly parsed, note the url is now a string token. Haven't fully tested this across browsers.

    let class = css!(r#"
        a[target="_blank"]::after{
            content: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==");
            margin: 0 3px 0 5px;
        }
    "#);

url tokens are a special case in css syntax and are somewhat insane leftovers from long gone days of the early browser wars. Literally `url(any string not containing a closing parens)` is supposed to be accepted, instead of the much more sane `url("just write it as a string duh")`. This is only the case for the `url` function-token. No other token behaves that way. Sorry for not being able to literally copy-paste css :/
futursolo commented 2 years ago

The new parser should be coming relatively soon and should have a way to deal with url(...) a little bit better.

If there's a workaround that works at the moment, I think this issue can be solved with the new parser. (But if someone is interested in a PR, I will review it and publish a fix.)