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

Injecting styles into components #12802

Closed
XIYO opened this issue Aug 12, 2024 · 4 comments
Closed

Injecting styles into components #12802

XIYO opened this issue Aug 12, 2024 · 4 comments

Comments

@XIYO
Copy link
Contributor

XIYO commented Aug 12, 2024

Describe the problem

Hello,

I always style my components with a no-JS environment in mind. I only use scripts to control the on/off state of styles when absolutely necessary.

As a result, I end up using :global quite a lot when applying styles to componentized elements.

Border.svelte

<script>
	let { classesOuter, classes } = $props();
</script>

<div class="outer" bind:class={classesOuter}>
	<div class="inner" bind:class={classes}>
		{@render children()}
	</div>
</div>

<style>
    .outer {
        border: 1px solid black;
        padding: 10px;
    }

    .inner {
        background-color: blanchedalmond;
    }
</style>

+page.svelte

<script>
	import Border from '$lib/Border.svelte';
</script>

<label>Show Menu
	<input type="checkbox"/>
</label>
<Border classes="nav">
	<nav>~~~</nav>
</Border>

<style>
	:global(.nav) {
			position: absolute;
			right: 0;
			top: 0;
	}

  input:not(:checked) + :global(.nav) {
      visibility: hidden;
  }
</style>

Describe the proposed solution

If components were designed to allow for style injection, the use of :global could be minimized, and the code would become more robust against errors since styles wouldn't rely on text literals for selection.

Border.svelte

<div class="outer" handlableElement>
	<div class="inner" >
		{@render children()}
		</div>
</div>

<style>
	.outer {
			border: 1px solid black;
			padding: 10px;
	}

	.inner {
			background-color: blanchedalmond;
	}
</style>

+page.svelte

<script>
	import Border from '$lib/Border.svelte';
</script>

<label>Show Menu
<input type="checkbox"/>
</label>
<Border><nav>~~~</nav></Border>

<style>
	$Border {
			position: absolute;
			right: 0;
			top: 0;
	}

	input:not(:checked) + $Border {
			visibility: hidden;
	}
</style>

Importance

nice to have

@XIYO XIYO changed the title Styling Components in a No-JS Environment Injecting styles into components Aug 12, 2024
@paoloricciuti
Copy link
Member

Not every component has a single wrapper around them so in a component like this

<label></label>
<input />

on which element does the style goes?

However maintainers are kinda thinking about some solution that could help with this situation but definitely for svelte 5.x

@XIYO
Copy link
Contributor Author

XIYO commented Aug 12, 2024

Here's the translation of your Korean text into English, formatted in a more readable Markdown style:

Injecting Styles into Components

There are a few different cases to consider:

Idea when returning a single element:

Styles are always injected into the top-level element. You cannot inject styles into other elements because Border.svelte itself is a fully styled tool.

  1. Single return component
<Border><nav>~~</nav></Border>

<style>
$Border {position:absolute;}
</style>

You can specify it in this format.

  1. Single return component 2, when the component dynamically generates the DOM

Border.svelte

<script>
  const { tag = 'div', classes } = $props();
</script>

<div class="outer">
  <svelte:element this={tag} class="inner" class={ classes } >
    {@render children()}
  </svelte:element>
</div>

+page.svelete

<Border tag='nav'>~~</Border>

<style>
$Border {position:absolute;}
$Border nav {width:200px; height: 100lvh;} // You need to know that 'nav' exists inside through the usage of the Border component.
</style>
  1. Single return component 3, when there are multiple DOMs inside the component

Border.svelte

<script>
  const { tag = 'div', classes, icon } = $props();
</script>

<div class="outer">
    <svg>
      {icon ?? <text>no-icon</text>}
    </svg>
  <svelte:element this={tag} class="inner" class={ classes } >
    {@render children()}
  </svelte:element>
</div>

+page.svelete

<Border tag='nav'>~~</Border>

<style>
$Border {position:absolute;}
$Border > *:last-child {{width:200px; height: 100lvh;} // 'nav' will be styled.
</style>
  1. Multiple return components, the element to be placed inside the border is further componentized.

BorderElement.svelte

<div>
  header
</div>

<div class='inner'>
  {@render children()}
</div>

<div>
  footer
</div>

+page.svelete

<Border>
  <BorderElement>ul~li~~</BorderElement>
</Border>

<style>
$BorderElement{ color : pink } // all style
$BorderElement:first-child { color: red; }
$BorderElement:last-child { color: green; }
$BorderElement:nth-child(2) { color:blue }
</style>

However, the current approach seems a bit awkward.
Svelte's style syntax allows you to use actual CSS directly, so it feels like a bad direction if Svelte's unique style-independent syntax comes out like this.

It needs to be supplemented with better syntax...

@7nik
Copy link

7nik commented Aug 12, 2024

IMHO, it's a duplicate of #2870 and #7776 (there are a whole bunch of issues about this topic).

@paoloricciuti
Copy link
Member

IMHO, it's a duplicate of #2870 and #7776 (there are a whole bunch of issues about this topic).

Oh yeah it's definitely a duplicate

@Conduitry Conduitry closed this as not planned Won't fix, can't repro, duplicate, stale Aug 12, 2024
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

4 participants