Skip to content

Introduce <style module> — update: forward directive #6972

Open
@aradalvand

Description

@aradalvand

Also see the update

Describe the problem

Currently, Svelte's default mechanism for handling scoped styles is limited in many ways, and literally dozens if not hundreds of issues have been created over the years complaining about different aspects of it. One thing that stands out however, is the inability to pass down scoped classes to child components.

Here are just a number of issues surrounding this:

Stack Overflow questions:

Some notable comments that beautifully describe this problem:

Arguably, the biggest pain point right now when it comes to Svelte's scoped styling is precisely this, namely the inability to send scoped classes down to child components, and this is a big deal.
This is something that has made me (and I'm pretty sure many others) seriously consider using Vue (although I love other aspects of Svelte too much I can't bring myself to do that), so this isn't trivial, and the workarounds are almost universally terrible.

There's an argument that has been repeatedly made by some (which has also been consistently downvoted by users, the number of thumbs-downs on those comments is very telling) claiming that this would break encapsulation. This is a horrible argument, if you have this in mind, I refer you to the comments I linked above, please take some time to read those and reflect. They debunk this myth eloquently. I'm not going to boringly reiterate what's already been said and discussed ad nauseam; instead, I'm going to focus on the solution I think would solve the aforementioned shortcomings.

Describe the proposed solution

I would propose Svelte add a feature that Vue has had for some time, namely <style module>, which solves nearly all of those problems that users have been suffering over for years now in the Svelte community.
Note that this would be an opt-in feature, meaning that it would be entirely backward-compatible and optional.

<script>
    import Icon from './Icon.svelte';
</script>

<button class={$$styles.button}>
    <!-- This is a component that receives a "class" prop and adds the classes to the root element: -->
    <Icon class={$$styles.icon} />
    <slot />
</button>

<style module>
    .button {
        /* styles */
    }
    .icon {
        /* styles */
    }
</style>

Note the special $$styles variables, this fits Svelte's syntax neatly as we already have things like $$props, $$slots, etc. The name could be anything, $$classes is another option, $$styles is just similar to what Vue uses ($styles). See this in Vue docs.

The other important bit here is that this only works with classes (and also animation names., for that matter), complying with the CSS Modules specs. So any other selectors (e.g. element selectors, IDs, attribute selectors) will be left untouched (or could cause an error, we can decide on this later), and will also not be included in $$styles.

This would give developers the freedom that's currently lacking, it:

  • Enables developers to pass down scoped classes to child components. (FINALLY!)
  • Requires no special treatment for the class prop on components - which was the solution most people were suggesting to solve the problem mentioned above, but this was consistently opposed by the maintainers.

Alternatives considered

Currently, I and many others have had to ditch Svelte's scoped styles entirely because of this limitation.

The alternative approach I've personally taken is I'm essentially using BEM naming and always making the styles global, this of course works, but obviously I'm deprived of all the advantages of using scoped styles; and I also have to worry about name clashes and such all over again. So, it's a huge bummer to have to do this.

I also want to ask you guys not to dismiss this proposal out of hand, please consider the fact that the problem this intends to solve is a real one, which has affected countless users, and you can tell by simply looking at the sheer number of issues created over the years surrounding this very topic.
The problem we're solving here is by no means uncommon or niche, and it's already been solved in other popular frameworks like Vue and React for years, so it must be taken with due seriousness, in my opinion. The current Svelte CSS scoping mechanism is effectively unusable for many people and projects precisely because of this particular limitation.

As I mentioned above, the solution I'm proposing is not a breaking change, as it doesn't intend to modify the current behavior of scoped styles, which means it won't interfere with the workflow of the users who are already using the default scoped styles. They can continue to do so if so they wish. So this is a big pro as well. As far as I've seen, most proposals addressing this problem involves some form of a breaking-change.

Would love to know your thoughts and opinions on this.
Thank you in advance.

Importance

crucial, big pain point

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions