Can I set svelte style css attribute values using variables passed in to a component
Asked Answered
F

4

34

I want to create a svelte component that receives the name and path of an image. I want to have the component set the image as the "background-image" using CSS.

I've tried the following which does not seem to work...

Component called in App.svelte:

<Image image_url='./images/image1.jpg' />

Image.Svelte

<script>
export let image_url;
</script>

<style>
.image{
    position:relative;
    opacity: 0.70;
    background-position:bottom;
    background-size: cover;
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-image: url({image_url});
    min-height: 100%;
}
</style>

<div class="image">
  <p>some text</p>
</div>

When I inspect the component the css for background_image is:

background-image: url({image_url});

Is it possible to have the variable converted in the CSS?

Floorboard answered 24/7, 2019 at 2:21 Comment(1)
I guess the answers here are out of date. See svelte.dev/tutorial/style-directive .Unlawful
S
77

No. Component styles are shared between all instances of a component, either because they're statically extracted to a .css file, or because they're injected into a single <style> element that all components reference. If it were possible to put variables directly inside the component's <style>, it would mean that Svelte would need to create encapsulated styles per-instance, which would be detrimental to performance and would consume a lot more memory.

There are two ways to approach this. The first is to use inline styles for anything that can change per-instance:

<script>
export let image_url;
</script>

<style>
.image{
    position:relative;
    opacity: 0.70;
    background-position:bottom;
    background-size: cover;
    background-repeat: no-repeat;
    background-attachment: fixed;
    /* background-image: url({image_url}); */
    min-height: 100%;
}
</style>

<!-- <div class="image"> -->
<div class="image" style="background-image: url({image_url});">
  <p>some text</p>
</div>

The second, particularly if you need to use values in multiple places, is to use CSS variables:

<script>
export let image_url;
</script>

<style>
.image{
    position:relative;
    opacity: 0.70;
    background-position:bottom;
    background-size: cover;
    background-repeat: no-repeat;
    background-attachment: fixed;
    /* background-image: url({image_url}); */
    background-image: var(--image);
    min-height: 100%;
}
</style>

<!-- <div class="image"> -->
<div class="image" style="--image: url({image_url});">
  <p>some text</p>
</div>
Saros answered 24/7, 2019 at 12:41 Comment(4)
...or 3. use "framework-agnostic css-in-js solution" as core team suggests. it's kind more flexible since inline styles does not handle media queries, pseudoclasses and pseudoelements.Blus
btw I'm the creator of Svelte, I wrote that article ;) CSS-in-JS has its uses but I wouldn't generally recommend using it to solve this particular problem, it's probably overkillSaros
@RichHarris, if this is overkill, then what is the alternative? I want to delegate these changes to the component where the parent can deliver a 'context' to customize in the child component's css styling, the alternatives break the clean separationsRudman
I like the css variables approach betterIrrational
E
17

You can now pass css variables directly as props: https://svelte.dev/docs#template-syntax-component-directives---style-props

<Image --background-image='url(./images/image1.jpg)' />

In Image.svelte

background-image: var(--background-image, url(./images/default.jpg));
Emad answered 23/12, 2021 at 15:41 Comment(2)
@coyotte508, is there a way in the var function that takes the prop name and the variable value to add a default? How can some logic be attached to it? Is it possible to make this prop inclusion optional for the parent component in adding it?Rudman
@Vass, no. You would need to set it as a variable, e.g. "backgroundImage" where you would have access to the string in javascript.Beautician
C
0

Think of Svelte block as a CSS black box. You can't use javascript variables in the same way that you can't use them in a css file in the browser.

But...since it's a CSS box, you can always use scss and compile your block using a svelte preprocessor like this one. Then you can just do

<script>
export let image_url;
</script>

<style lang="scss">
@import "my/path/to/variables";

.image{
    position:relative;
    opacity: 0.70;
    background-position:bottom;
    background-size: cover;
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-image: url(#{$image_url});
    min-height: 100%;
}
</style>

<div class="image">
  <p>some text</p>
</div>
Congressional answered 8/11, 2019 at 17:40 Comment(0)
A
0

I was able to achieve what you are asking for in the following way:

<script>
  export let image_url;
</script>

<div class="image" style="--image_url: url({image_url})">
  <p>some text</p>
</div>

<style>
  .image{
    background-image: var(--image-url);
  }
</style>

The in the html part of the component has access to the props, so can pass the prop to style as a CSS variable. The CSS variable can then be used in the background_image directly.

Aphyllous answered 23/6, 2023 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.