sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.74k stars 4.13k forks source link

Proposal: Better Scoped CSS #2131

Closed bsssshhhhhhh closed 4 years ago

bsssshhhhhhh commented 5 years ago

In some situations, such as shipping a standalone widget for use in websites we do not control, it would be helpful if svelte went a bit further with scoped CSS to avoid any external styles with clashing class names from leaking into the component, even as the CSS scoping prevents any styles from leaking out.

If I define:

<div class="box"></div>
<style>
.box {
    width: 100px;
    height: 100px;
}
</style>

and svelte outputs the CSS:

.box.svelte-1lyv5pv{width:100px;height:100px}

...

function create_main_fragment(component, ctx) {
    var div, current;

    return {
        c: function create() {
            div = createElement("div");
            div.className = "box svelte-1lyv5pv";
            addLoc(div, file, 0, 0, 0);
        },

is it possible to add an option for svelte to completely replace .box with just the random .svelte-{id} classname in the output instead?

.svelte-1lyv5pv{width:100px;height:100px}

...

function create_main_fragment(component, ctx) {
    var div, current;

    return {
        c: function create() {
            div = createElement("div");
            div.className = "svelte-1lyv5pv";
            addLoc(div, file, 0, 0, 0);
        },

In the event there's an external style rule defining .box, the standalone code might not otherwise look as we would expect.

This idea wouldn't solve the problem of attribute or element name selectors leaking in, but I'm not sure if there's anything svelte can do to mitigate those cases.

thgh commented 5 years ago

I have had the same concern and namespaced all scoped css to avoid collissions.

Another way to overcome this is this: https://svelte.technology/guide#special-selectors

nikku commented 5 years ago

I'm currently building a small svelte application on with bootstrap as the basic CSS framework and can confirm @thgh's comment. Essentially the only way to workaround global style clashes right now is to manually prefix your selectors.

mtlewis commented 5 years ago

We're also really interested in this - it would be so great to be able to use totally generic classnames without fear of CSS external to our component leaking into it.

antony commented 5 years ago

I also like this, but I feel that it should be part of a svelte preprocessor.

Svelte now has support for multiple preprocessors, so you could obfuscate the CSS after compilation.

Another usecase for this is that of the google homepage - where css classnames are continually obfuscated to obstruct bots / scrapers / scripts.

coldbrewstudios commented 5 years ago

I have just run into a scoping issue too. I have been using svelte for small projects. Initially, I thought the scoping was cool but now I am seeing some caveats.

I have a component for example Alert.svelte

<div class="alert">
    <slot></slot>
</div>

<style>
  .alert {
    padding: 1em;
  }
</style>

I use this component on a page with page specific css:

import Alert from ....

    <div inline right>
      <a>copy output</a>
      <Alert>Hello</Alert>
    </div>

<style>
  [inline] > * {
    display: inline-block;
  }
  [inline][right] {
    text-align: right;
  }
  [inline][right] > * {
    margin-left: 0.5em;
  }

</style>

Funny enough when it comes to the alert component the first line of css, the [inline] > * does not get applied, but the [inline][right] does get applied and the last line does not get applied either.

Because of the css scoping. I get that this might be default functionality and it might help in future development but at the same token, the css that is on a page that includes other components does not affect those components.... does not really make sense.

Immortalin commented 4 years ago

If implemented, this should be configurable. Frameworks such as Tailwindcss.com expects to be imported globally else there will be a ton of duplicated code.

antony commented 4 years ago

Closing as whilst this is desirable, I feel that this doesn't belong in Svelte core, but as some sort of obfuscation plugin (rollup, webpack etc) which runs after Svelte has compiled the components.