Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Way to Do CSS #8421

Closed
ebrenner-code opened this issue Mar 25, 2023 · 4 comments
Closed

Better Way to Do CSS #8421

ebrenner-code opened this issue Mar 25, 2023 · 4 comments

Comments

@ebrenner-code
Copy link

Describe the problem

In my svelte builds. I consistency come up against the same problems with writing CSS for which I have found work arounds, but am always left feeling like there could be a better implementation to make CSS a first class citizen in svelte and bring features no other framework can provide without a compiler.

It seems to me that there is room for improvement by way of consistency, syntax and features that would take very little work to implement and create almost no technical debt.

Outlined below are all several ways currently to add css and how I see them them being improved.

1. Scoped styles

This is the default. Simply adding a style element to the file creates scoped css. I see no problem with this.

2. Global, using the :global() selector

I've never liked this method. This selector feels like neither svelte nor CSS. Its anatomy is that of a css pseudo-selector, but there are no pseudo selectors that "float" like this, unattached to an element selector. It's also very likely that one would want several lines of css to be global and this provides no solution. I implemented nesting via post css to try to get the desired effect, e.g.

 :global {
    &(body){
        background: blue;
    }
    &(header){
        background: black;
    }
}

This works, but is unnecessarily clunky.

The Solution?

I'd recommend a very simple change here. Keep :global() for the sake of backwards compatibility and add a "global" attribute that can be used on style block very similar to the "scoped" attr you can add in Vue. So a new bit of styling would look something like this:

<style>
    section{
        max-width: 80rem;
    }
</style>

<style global>
    header{
        background: black;
    }
</style>

3. Global and unmounted

Currently, if you add css using :global(), when navigating away from a page, that css will persist in the app. For example, if I want the header to be a different color on a specific page, I could use the :global() selector inside the page css to affect the header, but when I navigate to another page, that css will still be affecting the header. I could move the header color to a svelte store and use js to change the color, but it's an unnecessary burden. There is a simple workaround within svelte that is kind of a hack--that is to include this css in a style block inside of the component markup, not at the top level. There's nothing wrong with this except readability. It's not officially mentioned in the svelte docs. I always include a comment when I do this because without a comment, it will leave the next dev wondering... why? I'm recommending again adding a flag to the style block that could achieve this effect, have it's purpose be immediately clear, and be part of the official documentation. Something like:

<style global temp>
    header{
        background: black;
    }
</style>

4. Via JS

When the class directive isn't quite right for the job, we can use JS to add styles to an element. I'd much rather write css in css than in JS, so I often use JS to set CSS variables on my elements and then use CSS to do the actual styling. This usually works and I think is a better convention.

I believe svelte is currently missing out on a huge opportunity created by the fact that it is a compiler--the ability to use svelte variables within css and make the css itself reactive. The standard svelte syntax would need to change slightly as single brackets wouldn't work, but it could use double brackets or use the built in css var() function to look something like:

header{
   background: {{headerBgColor}}
}

-OR-

header{
   background: var({headerBgColor})
}

This is by far the biggest change I am recommending but would easily become one more reason devs love svelte.

In summary, I am suggesting a simple but profound overhaul to styling in svelte by phasing out the :global() selector in favor of 'global' and 'temp' flag attributes on <style> tags as well as allowing the use of reactive variables inside style tags.

Describe the proposed solution

See the main thread

Alternatives considered

See the main thread

Importance

would make my life easier

@Prinzhorn
Copy link
Contributor

  1. Global, using the :global() selector

Possible via svelte-preprocess:

https://github.com/sveltejs/svelte-preprocess#global-style

  1. Global and unmounted

I think this would greatly benefit from a reproducible example. It sounds like this is specific to SvelteKit? Not sure how pages work in contrast to normal components. You make it sound like "unmounting" a page doesn't remove it's CSS, which sounds like a bug.

  1. Via JS

Discussed in:

#758
sveltejs/rfcs#51

And somewhat related:

sveltejs/rfcs#13
https://svelte.dev/docs#template-syntax-component-directives---style-props

@ebrenner-code
Copy link
Author

I've added a repo demonstrating the problem: https://github.com/ebrenner-code/css-scoping-example.git

@Conduitry
Copy link
Member

There are other issues about global styles persisting when components are unmounted. This is a known limitation. There's not a good way to handle this without making each component responsible for counting the number of instances of it that are currently created, which is unnecessary work in nearly all cases.

It's generally inadvisable to create global styles in components, for several reasons, and this is one of them.

One of the often underappreciated powerful features of scoped styles is putting :global() around just part of the selector for a given style. This would resolve issues with styles still being present when components are unmounted, because these styles would no longer apply to any existing elements in the document.

I don't think we want to encourage people to create entirely global styles with a built-in syntax like <style global>. People shouldn't in general be creating global styles anyway.

Regarding dynamic styles: Our answer to this was the style props / CSS variables feature mentioned above. We also don't want to be writing or maintaining our own CSS parser, and so we're limited to things that are also valid CSS. :global { .selector { color: red; } } is not valid CSS, and nor is .selector { color: {{someValue}}; }.

In summary, I don't think there's anything we want to do here, and I'm closing this issue.

@Conduitry Conduitry closed this as not planned Won't fix, can't repro, duplicate, stale Mar 28, 2023
@ebrenner-code
Copy link
Author

ebrenner-code commented Mar 28, 2023

I'd like to point out that everything i suggested is really intended for pages in svelte-kit. In that case you would always know the number of instances (one). Perhaps that will make a difference in your decision.

I think your comment outlines the problem with :global(). It doesn't work as expected, it isn't recommended, and yet it's a necessary tool. I have full faith in the team's judgement and hope they will continue to explore improvements to this aspect of svelte. Thanks for the consideration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants