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

Add support for styling third-party components #1808

Closed
antony opened this issue Oct 26, 2018 · 26 comments
Closed

Add support for styling third-party components #1808

antony opened this issue Oct 26, 2018 · 26 comments
Labels
awaiting submitter needs a reproduction, or clarification

Comments

@antony
Copy link
Member

antony commented Oct 26, 2018

The problem

One of the things I come across repeatedly whilst working with Svelte is that scoped styles, whilst absolutely excellent, also prevent efficient style overrides of child components whose css you don't control.

For instance, if I have some code like this:

<div>
  <ThirdPartyComponent />
</div>

This renders html similar to the following:

<div class="svelte-l34ns">
  <!-- <ThirdPartyComponent /> -->
  <div class="third-party-owned-div svelte-b33fs">
     stuff
  </div>
</div>

Which in itself is fine, however, I am unable then to style the third-party-owned-div from my parent component, since all styles are prefixed - my CSS ends up like:

[svelte-l34ns] .third-party-owned-div {
  background-color: blue;
}

There are some solutions, but they have their own issues

:global(.third-party-owned-div) *

This works, but attempts to style every instance of .third-party-owned-div in the application. This global style conflicts with other components (especially if they don't have unique names), unless you prefix it with a local element selector, however, you can't use :global in the middle of a selector either, so you need to wrap the entire remainder of the selector inside :global.

Effectively, you can, in a rather awkward way, address a deep child of a third-party component as follows:

.parent :global(.some-component-div div ul li a) {
  text-decoration: none;
}

However, when using a preprocessor such as scss, the above is no longer usable, since global modifiers are only allowed at the beginning or end of a selector:

.parent :global(.some-component-div) {
  ... other styles
  div ul li {
    ... other styles
    a {
       text-decoration: none;
    }
  }
}

include a separate global css file for styling the component:

This also works, but circumvents the scoped style system, and takes component styles away from their component, which breaks the whole component model.

Proposed solutions:

I've been thinking around a few solutions for this, and I think the cleanest one which fits the Svelte model the best is to allow a section of styles to be scoped to a different scope, which can be specified with a 'scope' attribute in the style tag, which specifies a ref, or similar. For example:

<div>
  <ThirdPartyComponent ref:thirdparty />
</div>

<style>
  div {
    color: green;
  }
</style>

<style scope="thirdparty">
  .third-party-owned-div {
    color: red;
  }
</style>
@Conduitry Conduitry added awaiting submitter needs a reproduction, or clarification proposal labels Oct 26, 2018
@gavinmcfarland
Copy link

gavinmcfarland commented Nov 2, 2018

I find it a bit odd that we can't style imported components. Is this just something that's not been thought of yet?

<Component/>

<script>
	export default {
		components: {
			Component: './Component.html'
		}
	};
</script>

<style>
	.component {
		color: green;
	}
</style>

https://svelte.technology/repl?version=2.15.1&gist=353a4f670a3b9e111530bad1aa2d2d5f

Maybe it's because I am used to using web components, and if you import a web component you can style using its element name my-component however with Svelte the components can be transparent and doesn't necessarily have to have a "container" as such.

Perhaps a class name or attribute could be reserved for components for a hook that allows you to style them.

.Component {} or cmp:component {}

Something to that effect.

@gavinmcfarland
Copy link

gavinmcfarland commented Nov 2, 2018

I had considered creating a Postcss plugin which converts a Component {} to another selector so that it made more sense when styling imported components.

<Component/>

<script>
	export default {
		components: {
			Component: './Component.html'
		}
	};
</script>

<style>
	Component { /* would be preprocessed to .Component or something similar */
		color: green;
	}
</style>

@nikku
Copy link
Contributor

nikku commented May 28, 2019

Could you check if #2870 issue is a similar feature request? If that is the case #2888 will probably address this one, too (for svelte@3).

Essentially #2888 implements support for preserving classes passed to child components and passing them with the parents scoping selector included.

@antony
Copy link
Member Author

antony commented May 29, 2019

@nikku I like the suggestion referenced in #2870 - as per PR #2888.

It will certainly address my concerns/issues. How do you feel about it @limitlessloop ?

@Conduitry
Copy link
Member

As I commented on #2888, this general feature is something we want to support, but I don't think that PR is the way we want to go about it.

image

@stalkerg
Copy link
Contributor

stalkerg commented Jun 5, 2019

@Conduitry I suppose idea with scopes looks very good. I am not sure about binding to ref: here but the idea itself very good.
#2888 it's too unnatural and you can't send custom css rules into the component. If we won't make new syntax for css (I hope no) we should use something like in this proposal.

@nikku
Copy link
Contributor

nikku commented Jun 6, 2019

#2888 it's too unnatural and you can't send custom css rules into the component.

Could you elaborate on this? How would that limit you in your desired abilities? The whole aim of passing classes down (as you would in HTML) is to not be required to implement any new syntax.

@PaulMaly
Copy link
Contributor

PaulMaly commented Jun 6, 2019

Yep, seems it's a problem. I heard about it many times. In addition to the solution from initial comment, perhaps we can use Special selectors syntax from Svelte 2:

<div>
  <ThirdPartyComponent bind:this={comp}/>
</div>

<script>
  let comp;
</script>

<style>
  div {
    color: green;
  }

  ref:comp .third-party-owned-div {
    color: red;
  }
</style>

@antony
Copy link
Member Author

antony commented Jun 27, 2019

Interesting, just spotted this in the source code.

https://github.com/sveltejs/svelte/blob/master/src/compiler/parse/index.ts#L211

@stalkerg
Copy link
Contributor

it's also should help with sapper cases, multiple style tags will be the best implementation IMHO.

@nikku
Copy link
Contributor

nikku commented Aug 14, 2019

@Conduitry What exactly is pending clarification regarding this issue?

@Conduitry
Copy link
Member

Conduitry commented Aug 15, 2019

What's pending clarification is how we want this feature to work. Treating a prop called class specially as in #2888 doesn't seem to be the right answer, and is probably also a breaking change.

@nikku
Copy link
Contributor

nikku commented Aug 15, 2019

Treating a prop called class specially as in #2888 doesn't seem to be the right answer, and is probably also a breaking change.

My personal input is: Make things work intuitively, out of the box. What are concrete examples where #2888 would not be the desired behavior? I'm probably not thinking in Svelte enough here.

@antony
Copy link
Member Author

antony commented Aug 15, 2019

@nikku I personally don't see the value in passing some magic class attribute to components, when a reference to the svelte namespaced svelte-xyzzy would work the same way, if only we had access to it's future value.

@Introvertuous
Copy link

@antony I mean is that not the point of the class attribute, to somehow derive the scoped selector for the child.

@Conduitry
Copy link
Member

It's not a class attribute, it's a class prop on a component. There are no other props currently whose operation is at all inspired by the attribute of the same name.

@Introvertuous
Copy link

right okay, magic prop.

@antony
Copy link
Member Author

antony commented Dec 14, 2019

Closing this issue in absolute favour of sveltejs/rfcs#13

@antony antony closed this as completed Dec 14, 2019
@gamelaster
Copy link

gamelaster commented Feb 17, 2021

I think closing this in favor of sveltejs/rfcs#13 doesn't giving a sense.
It's still not resolving of styling the component from parent.
The current solution is only for small changes of parent's CSS,
although for some other scenarios, where you need to change
a lot of properties, it's not usable.
(And :global isn't much good solution)

@antony
Copy link
Member Author

antony commented Feb 17, 2021

@gamelaster sorry to hear that you think it's crap. We are of course open to better ideas which aren't crap.

If you have the need to make mass changes to the style of child components from their parents, then you perhaps need to reconsider your architecture, or just use a global stylesheet for these styles, as you would in any other JS app.

@gamelaster
Copy link

gamelaster commented Feb 17, 2021

Hi @antony , firstly, I'm very sorry if term I used crap sounded offensive, I meant it more like it doesn't giving a sense. (I edited the post)

To the topic, I don't exactly have any ideas how to improve it (since I don't know how exactly svelte handles all this under the hood), but I think there should be a better way than using global CSS file.
My current issue is, that I have component, which have slot inside, and I need to detect with CSS if components "div" has hovered, and then modify the content. At the moment, only doing it via global CSS file is possible.
Example:
page.svelte

<script>import Component from './component.svelte'</script>
<body>
  <Component>
    <p>some text</p>
  </Component>
</body>

<style>
.component:hover p {
  color: red;
}
</style>

component.svelte:

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

Any ideas how I can solve this?
Thanks.

@antony
Copy link
Member Author

antony commented Feb 17, 2021

Yes, there probably should be a better way, but none of the comments in this issue (or any issue) provide it, which is why it has been closed.

I'm afraid I can't provide support in a github issue. However if you come to the discord you will find a number of people who have solved this in a variety of different ways.

@gamelaster
Copy link

gamelaster commented Feb 17, 2021

I asked on the Discord, thanks!

Although, maybe I have an idea. Every component will able to have "root" element (by default first element or selected by parameter?). This element will be able to be modified by CSS from parent. Example:

<script>import Component from './component.svelte'</script>
<body>
  <Component>
    <p>some text</p>
  </Component>
</body>

<style>
Component:hover p {
  color: red;
}
</style>

component.svelte

<div bind:root class="component">
  <slot></slot>
</div>

Well, it's just idea, and as I said, I'm not sure how Svelte is processing all this under-the-hood, so even I'm not sure if realization is possible.

@davidmroth
Copy link

davidmroth commented Feb 20, 2021

@antony The problem with using systems like Discord is the solutions are not searchable or readily accessible. Keeping productive discussions regarding missing features should be done in Github. After it has been solved, then it doesn't matter what other channels are used.

I just started playing with Svelte, and I find it much more enjoyable and more efficient than React and Angular. However, there seems to be some obvious missing features (which is expected with such a new language). I think those features discussions should be kept in Github with the original issue. And if there IS as solution, it should be posted here before closing the issue.

Keep up the GREAT work! I'm predicting Svelte will surpass most of the other popular libraries due to being more frictionless and feeling more like natural fit for developing interfaces in Javascript. Svelte has me very excited about the future of Javascript!

EDIT: BTW, what is the solution for targeting Components with CSS styles that feels natural? i.e.:

<NavBar class="navbar"/>

<style>
  Navbar > header {
     position: sticky;
     top: 0;
  }
</style>

@antony
Copy link
Member Author

antony commented Feb 20, 2021

Hi @davidmroth

Thank you for your thoughts, and I'm glad you are enjoying Svelte.

To put the reasons for my closing this issue and trying to discourage discussion into context:

The issue of styling child components has been discussed literally, and I mean quite literally, to death in a large number of different github issues. The discussions are endless, unproductive, and as demonstrated here, the alleged searchability of github issues does not prevent yet another ticket being opened for the same issue, the same endless discussion, and the same ill-considered 3 line "solutions" being brought up time and time again.

I hope you can understand therefore why my actions may appear obtuse. We are tired by these discussions, we are fatigued by these discussions and frustrated by these discussions to the point where I may be the only maintainer who even participates in them any more. And I don't intend to keep it up.

So therefore this issue is closed, not up for further discussion here, and the way to continue or propose as solution to this issue boils down two two options. Either:

  • participate in the existing RFC
  • propose a new, considered, well thought out and articulated solution as a new RFC.

Discussions leading up to either event should be precipitated by a realtime discussion on discord, where we will all benefit from its ephemeral nature.

Hope this clarifies my actions for you, and please accept my apologies for being so blunt despite you being new here. I just want to provide the background for my firm decisions in this process.

@gamelaster
Copy link

Hi @antony , thank you for writing the context of whole situation, it helped also me to understand the reasons and background of this issue. I was thinking about this, so if I will have some spare time soon, I will read everything I will find about this and will try to implement my idea to Svelte, and then turn it into RFC. Thanks for your time and work on Svelte.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting submitter needs a reproduction, or clarification
Projects
None yet
Development

No branches or pull requests

9 participants