Skip to content

<style bind:styles={styles}> and <style styles={styles}> - CSS Binding and Passing scoped styles between components and scoped svelte CSS import. #6422

Closed
@lukaszpolowczyk

Description

@lukaszpolowczyk

Is your feature request related to a problem? Please describe.
I want to have a common part of the CSS style, at the same time also I don't want to have to throw every little bit of CSS out of the *.svelte files and I don't want to use global CSS.

Describe the solution you'd like
CSS Binding and Passing.
It is the most intuitive, transparent and sveltish solution, at the same time referring to the CSS assert in custom element proposal.

Style set in App.svelte in style tag with bind:styles attribute, passed to the Component.svelte and Component2.svelte components in style with styles atribute. Scoped.

Example:

<!-- App.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 let styles;
</script>
<Component styles={styles}>
<Component2 styles={styles}>
<style bind:styles={styles}>
 /* simple styles */
</style>
<!-- Component.svelte  -->
<script>
export let styles;
</script>
<style styles={styles}/>
  • Style should not be global, but scoped at the point of use.

  • I can see that in every component with <style styles={styles} />, the same hash like svelte-n323vl is used for classes.

  • The variable styles is an object that cannot be modified, it is used only to pass the preset style to the component.

Questions:

  1. It should be possible to use multiple style bind:styles={styles}(both declaration and use) and plain <style> at the same time.
    Is something in the way?
    OR: <style bind:styles={ [styles, styles2] } /> or <style bind:styles={ {...styles, ...styles2} } /> - and only the declaration could be multiple.

  2. Should the binded style in App.svelte also be applied to the HTML elements in App.svelte?
    Or should it be optional? Attribute of type onlydeclaration (I don't have a good idea of the name).

<style bind:styles={styles} onlydeclaration>
 /* simple styles */
</style>

I don't know if it should work in App.svelte by default and need to be turned off with the attribute, or vice versa?

Another option - better? - at the same time simpler, but also a bit circular (OF COURSE, this is not usually used, only in exceptional cases):

<!-- App.svelte  -->
<style bind:styles={styles}>
 /* simple styles */
</style>
<style styles={styles}/>

or even just:

<!-- App.svelte  -->
<style bind:styles={styles} styles={styles}>
 /* simple styles */
</style>
  1. Should css custom properties work?:
<!-- App.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 let styles;
</script>
<Component styles={styles}>
<Component2 styles={styles}>
<style bind:styles={styles}>
 div {
  background-color: var(--css-var);
 }
</style>
<!-- Component.svelte  -->
<script>
export let styles;
</script>
<div style="--css-var: red;"></div>
<style styles={styles}/>

Probably yes, but maybe there will be some obstacles?

  1. Maybe in order for <style bind:styles={styles}></style> to not confuse with the standard <style></style> behavior (i.e. applying styles to App.svelte), you need to use <svelte:style bind:styles={styles}></svelte:style> ?

  2. Other idea...:
    Instead of requiring separate tags (<style bind:styles> or <svelte:style bind:styles>), I figured out a way to keep the number of <style> tags constant.

<!-- App.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 let styles;
</script>
<Component styles={styles}>
<Component2 styles={styles}>
<style bind:exposed={styles}>

:expose {
  /* exposed styles */
}

 /* App.svelte own styles  */
</style>
<!-- Component.svelte  -->
<script>
export let styles;
</script>
<style styles={styles}>
  
 /* Component.svelte own styles  */
</style>

Where:

  • everything contained in :expose {} has a separate hash
  • binds to the styles object is the hash of the elements in :expose {}
  • using the styles object in <style styles={style}> causes the hash to be used in Component.svelte elements - Component.svelte has its own hash, and the one passed is an additional hash, e.g .:
    <p class="svelte-15kka16 svelte-edszo3"></p>
    svelte-15kka16 is the hash added with styles={styles} and svelte-edszo3 is the hash Component.svelte.
  • styles in :expose {} default are NOT applied to App.svelte. To make them, you have to use:
    <style bind:exposed={styles} styles={styles}>

I think it would be nice to be able to name the displayed block styles and use more than one:

<!-- App.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 let styles;
</script>
<Component styles={styles.name1}>
<Component2 styles={styles.name1}>
<style bind:exposed={styles}>
  :expose(name1) {}
  :expose(name2) {}

Each of them would have a separate hash.

Most importantly, it is not global, and is controlled where this :expose {} block is to be used.

Summary:
The whole thing seems clear, refers to the upcoming standard, and at the same time is "sveltish".

Describe alternatives you've considered

<style>
:global(...)
</style>
<svelte:head>
   <link rel="stylesheet" href="styles.css">
</svelte:head>
<style src="./styles.css"></style>
<style>
	@import "./style.css";
</style>

Some do not work, none offer the type of possibilities I suggest, nor are they so sveltish.

If anyone wants to do RFC, please do.

How important is this feature to you?
The point is not to use global CSS or do some redundant CSS.
In fact, such something is needed all the time.

This is where my basic proposition ends.


FURTHER THINKING - Import CSS file and Passing Classes:

Import CSS file
Seems like can be used to... import CSS.
There is a proposition import styleSheet from "./styles.css" assert { type: "css" }; for WebComponents.

<!-- Component.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 import styles from" ./styles.css "assert {type: "css"};
</script>
<Component styles={styles}>
<Component2 styles={styles}>
<style styles={styles}/>

Such an imported svelte style would also be scoped.

I can see an easy use of this in <svelte:options tag="my-element" />.
I will not be surprised if such placing of imported CSS will be interesting for someone, and passing between components less...

Question:
Do you allow import ("./styles.css", {assert: {type: "css"}});? Probably not.

Passing Classes
Binding classes with the style tag and passing to components. Extracting classes from the binding variable style.

Example:

<!-- App.svelte  -->
<script>
 import Component from "$lib/components/Component.svelte";
 import Component2 from "$lib/components/Component2.svelte";
 let styles;
</script>
<Component el={styles.el}>
<Component2 styles={styles}>
<style bind:styles={styles}>
 .el {
  /* props */
 }
</style>
<!-- Component.svelte  -->
<script>
export let el;
</script>
<div class="el"></div>
<style styles={{el}}/>

...or as in css-modules?:

<!-- Component.svelte  -->
<script>
export let el;
</script>
<div class={el}></div>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions