sveltejs / svelte

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

Scope css styles to a different Svelte component #8635

Open robertadamsonsmith opened 1 year ago

robertadamsonsmith commented 1 year ago

Describe the problem

The existing ways of scoping css styles to a child component don't feel right. Either they require customisable components to expose a lot of properties for styling purposes, which does not feel right, or for :global() to be used in a way that can cause styles that are designed to target a child component to leak into other descendant components.

Describe the proposed solution

Just as :global() is used to strip the prefix from selectors, we could allow the name of a component such as :MyButton() to be used to add the appropriate prefix to a selector, so that it can target elements in a descendant component. It would look like this:

<script>
    import MyButton from "./MyButton.svelte"
</script>

<div>
    <MyButton>Click Me!</MyButton>
</div>

<style>
    div :MyButton(button){
        background-color:red;
    }
</style>

I think that a containing element would usually be necessary, to avoid the style from being applied 'globally' to all instances of MyButton (much like how :global() is generally used).

Alternatives considered

The usual approach is to use :global(), but that can cause a descendant component to be accidentally targeted as well, such as illustrated here, since the selector in the :global() section cannot be restricted to just the component that we really want to style:

<script>
    import MySheet from "./MySheet.svelte"
    import MySection from "./MySection.svelte"
</script>

<div>
    <MySheet>
        <MySection>First Section</MySection>
        <MySection>Second Section</MySection>
    </MySheet>
</div>

<style>
    div :global(div){
        background-color:red;
    }
</style>

A child selector can stop the style leaking beyond the child (i.e. div > :global(div)), but then it makes the relationship more fragile. Another way to stop the style leaking is to add uncommon class names to elements so that they can be more precisely targeted from ancestors (i.e. div > :global(.mysheet-div)), but that feels very verbose.

Another approach is to expose styles through attributes, but that requires a lot of repetitive work, and prevents styling from being expressed through css, and so doesn't feel like the correct approach.

Importance

would make my life easier

karimfromjordan commented 1 year ago

Related issue: #4661

Tyrenn commented 1 year ago

Kinda related to #8538

langsys commented 8 months ago

I love this solution, very svelteish, keeps things clean, and dodges the ugly use of :global