abishekatp / stylers

Fully compile time scoped CSS for Leptos components
MIT License
128 stars 11 forks source link

Add a way to merge and or inherit styles #13

Closed WafflePersonThing closed 1 year ago

WafflePersonThing commented 1 year ago

The class argument of the view macro only accepts one class name, so there is no way to modularize the styling of components with the stylers macro, instead having to re-declare a style that we want to reuse for a single change (in a highly component-based project this becomes especially unwieldy). This is exacerbated by the fact that the generated stylesheets have class markers like "l-123456" attached to the html they are styling, so they only work in the views they are attached to, making it impossible to define a global stylesheet and have per component style overrides with the macro alone. It would also be nice if the macro optionally propagated and merged classes defined in a parent view to a child component's view, but maybe that's better handled upstream.

abishekatp commented 1 year ago

Yeah it is good idea to add modularization to increase the resuability. I am thinking some way to achieve this since we are creating css file for each macro usage and storing the corresponding css in that file. we need some way to coordinate between these things. can we use some macro like style_str!{} which will retrurn the style string instead storing it in file. then we can embed that string using tag.

So we can use style! for commons css used by more than one component. Then we can override that using style_str and html style tag.

gbj commented 1 year ago

By the way see my comment about multiple class names in this issue over in the Leptos repo — this is totally doable for us to support and not a very big deal, so don't feel constrained by the need only to have a single class name.

abishekatp commented 1 year ago

[component]

pub fn GreenButton(cx: Scope) -> impl IntoView { let (local_class, style_val) = style_str! { button{ background-color: green; } button:hover{ background-color: yellow; color: green; } }; let common_class = button_style(); let class_name = format!("{} {}", common_class, local_class);

view! {cx, class = {class_name.clone()},
    <style>{style_val}</style>
    <button>"I am green button"</button>
}

}

[component]

pub fn BlueButton(cx: Scope) -> impl IntoView { let (local_class, style_val) = style_str! {"BlueButton", button{ background-color: blue; } button:hover{ background-color: yellow; color: blue; } }; let common_class = button_style(); let class_name = format!("{} {}", common_class, local_class);

view! {cx, class = {class_name.clone()},
    <style>{style_val}</style>
    <button>"I am blue button"</button>
}

}

pub fn button_style() -> String { //note: we can even use style_str and get the style string wherever we use this style // but that will populate same style in multiple places at the DOM. style! {"ButtonStyle", button { background-color: #EA4C89; border-radius: 8px; border-style: none; box-sizing: border-box; color: yellow; cursor: pointer; display: inline-block; font-family: r#"Haas Grot Text R Web"#, r#"Helvetica Neue"#, Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 500; height: 40px; line-height: 20px; list-style: none; margin: 0; outline: none; padding: 10px 16px; position: relative; text-align: center; text-decoration: none; transition: color 100ms; vertical-align: baseline; user-select: none; -webkit-user-select: none; } button:hover{ background-color: yellow; } }.to_string() }


- For that second problem I think if we want child componets to use styles declared in a parent component that can be achieved by passing the class_name to the child componet view macro.  the code will look something like below. Working example is [here](https://github.com/abishekatp/stylers/blob/style_modularized/examples/style_parent_child/src/lib.rs).
```rust
use leptos::*;
use stylers::{style, style_str};

#[component]
pub fn Parent(cx: Scope) -> impl IntoView {
    let class_name = style! {"Parent",
        button {
            background-color: green;
            border-radius: 8px;
            border-style: none;
            box-sizing: border-box;
            color: yellow;
            cursor: pointer;
            display: inline-block;
            font-family: r#"Haas Grot Text R Web"#, r#"Helvetica Neue"#, Helvetica, Arial, sans-serif;
            font-size: 14px;
            font-weight: 500;
            height: 40px;
            line-height: 20px;
            list-style: none;
            margin: 0;
            outline: none;
            padding: 10px 16px;
            position: relative;
            text-align: center;
            text-decoration: none;
            transition: color 100ms;
            vertical-align: baseline;
            user-select: none;
            -webkit-user-select: none;
        }
        button:hover{
            background-color: yellow;
            color: green;
        }
    };

    view! {cx, class = {class_name},
        <button>"I am green button"</button>
        <Child class_name={class_name.to_string()}/>
    }
}

#[component]
fn Child(cx: Scope, class_name: String) -> impl IntoView {
    let (local_class, style_val) = style_str! {
        button{
            background-color: blue;
        }
        button:hover{
            background-color: yellow;
            color: blue;
        }
    };
    let class_name = format!("{} {}", class_name, local_class);
    view! {cx, class = {class_name.clone()},
        <style>{style_val}</style>
        <button>"I am blue button"</button>
    }
}

Check out these examples. Please feel free to add your comments.

WafflePersonThing commented 1 year ago

One minor suggestion, https://docs.rs/const_format/latest/const_format/macro.formatcp.html might be worthwhile to use in the doc examples as it allows you to skip cloning and the curly braces in the class argument. Or @gbj would it be worth it to add a check for whether the class argument is a group/surrounded by delimiters so that a list of styles could be passed as an array instead of having to be pre-formatted. Other than that, looks good to me. Thanks.

abishekatp commented 1 year ago

Thank you for your suggestion, This has some more constraints like variables should be const. So as of now we can with this clone approch.

abishekatp commented 1 year ago

Fixed this issue in version v0.3.0.