Skip to content

Add the ability to add classes to slots #4443

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

Closed
tiddle opened this issue Feb 21, 2020 · 11 comments
Closed

Add the ability to add classes to slots #4443

tiddle opened this issue Feb 21, 2020 · 11 comments

Comments

@tiddle
Copy link

tiddle commented Feb 21, 2020

Is your feature request related to a problem? Please describe.
I want to be able to add classes to slots so that i can style them directly.

Describe the solution you'd like

App.svelte

<MyComponent>
    <ul slot="list">
       ...
    </ul>
</MyComponent>

MyComponent.svelte

<slot name="list" class="my-list-style">
   <p>No items found</p>
</slot>

Describe alternatives you've considered
This alternative works, but i still can't style the ul in my example correctly and it feels like it renders the fallback slot functionality useless.

{#if list.length}
<div class="my-list-style">
   <slot name="list" />
</div>
{:else}
<p>No items found</p>
{/if}

How important is this feature to you?
There are work arounds, but nothing clean. I just feel like this should be functionality that should be part of the slot feature.

@jesseskinner
Copy link
Contributor

jesseskinner commented Feb 21, 2020

@tiddle The current way to achieve this is with let:class to expose the variable to be used on your element like this:

<MyComponent>
    <ul slot="list" let:class={className} class={className}>
       ...
    </ul>
</MyComponent>

I think this limitation makes sense because the component doesn't know anything about the child slots. It's up to the component user to figure out what to do with the slot props.

@ghost
Copy link

ghost commented Feb 22, 2020

@jesseskinner I think you don't understand the OP's problem. Just like mine, I don't know what child component will be passed. Regardless of the passed child component inserted into the slot, I need to style it. E.g. I can't add special props and keywords to every single component I have and will ever create for this to work.

@jesseskinner
Copy link
Contributor

@GitGangGuy Sure, you want to be able to control the element used for the slot from the parent. Right now the syntax I used above is valid, where slot props become available through the let: directive on the slot element.

My concern is that this new feature would not be backwards compatible, because it would break my example code above. Would class become a special case, where it interprets it as an attribute to be used on the slot element instead of a prop? What about style? id? data-* attributes? I think instead, there would need to be some special way to make the distinction of what is a slot attribute and what is a slot prop to be consumed with let:. Maybe a new directive like <slot attr:class="abc"/>?

@tiddle
Copy link
Author

tiddle commented Feb 23, 2020

@jesseskinner I understand your concerns and I don't have a solution, but another use case for passing attributes through, would be transitions. Currently, you can't do <slot in:fade>

@jesseskinner
Copy link
Contributor

jesseskinner commented Feb 23, 2020

@tiddle True.. actually maybe that could be a constraint of the new feature, that only directives get passed down to the slot element. That'd work for class too: instead of class="foo" you could use class:foo={true} and have that passed through. That would get around my concerns of ambiguity, I think?

@Conduitry
Copy link
Member

Named slots do currently always have a single root element, but this is a limitation of the current syntax and not a design feature. (Default slots, by contrast, already do not necessarily have a single root element.) We don't want to rely on there being a single root element - or prevent ourselves from ever implementing named slots without a single root node - and so there's not anything to apply the class to or the transition to.

It wasn't popular when treating class specially was nixed in #2870/#2888 and I don't expect it will be popular now either, but I think the core Svelte team really does feel strongly about this. Providing a blessed way to nicely use CSS variables like in this RFC is probably going to be the recommended way forward - or, alternatively, to use partially :global() selectors as an escape hatch to style descendant elements.

@ghost
Copy link

ghost commented Feb 23, 2020

@Conduitry I don’t think that the possibility of multiple root elements is a problem here. In my case, if there are multiple root elements, I would like to apply the class to all those root elements with which the <slot> is replaced.
Currently, I can only do this with some ugly JS, by wrapping the <slot> in a div and then selecting all direct children of that div (div>*).
But this fix/solution also makes me face a problem: The <div> partially destroys the layout/design compared to when not using that div wrapper.
I hope we can continue implementing this feature again.
Syntax-wise, I would like to be able to pass id, style and class DOM attributes as well as (ideally) svelte props to whatever the slot was replaced with, so prefixing everything with attr in the slot that should be passed sounds like a good idea.
Examples: <slot attr:class=“test” attr:class:active={true} /> or <slot attr:style=“color: red” attr:id=“henlo” />

@johannesmutter
Copy link

johannesmutter commented Dec 1, 2020

Instead of using a class one can also use the css attribute selector with the slot name in combination with the :global() modifier.

Example:

<MyComponent>
  <p slot="mySlot">world</p>
</MyComponent>

MyComponent.svelte

<div>
  Hello
  <slot name="mySlot" />
</div>

<style>
  :global( [slot="mySlot"] ) { ... }
</style>

This makes the additional wrapper around the slot obsolete

@8483
Copy link

8483 commented Dec 19, 2020

@jesseskinner This seems to do the job. Just wrap the slot in a div with the desired class.

<div class="my-list-style">
    <slot name="list" >
        <p>No items found</p>
    </slot>
</div>

@gianmarco27
Copy link

gianmarco27 commented Jun 30, 2022

@jesseskinner This seems to do the job. Just wrap the slot in a div with the desired class.

<div class="my-list-style">
    <slot name="list" >
        <p>No items found</p>
    </slot>
</div>

this isn't possible tho when using named slots, which only allow for one subelement to be passed

@rodrigodagostino
Copy link

rodrigodagostino commented Jan 10, 2024

@gianmarco27 but what is your point, mate? He is actually passing a single element to the named slot in the provided example. It might not be useful for every single scenario, but in my case it solved my problem.

Being able to only pass a single element to a named slot is not a limitation of the solution he suggested, but of Svelte itself.

EDIT: I just remembered that in those cases where you need to add more that one element to a named slot but don’t want an additional element wrapping them up, you can use a <svelte:fragment> element, like so:

<svelte:fragment name="my-slot">
    <span>Element 1</span>
    <span>Element 2</span>
    <span>Element 3</span>
</svelte:fragment>

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

7 participants