sveltejs / kit

web development, streamlined
https://svelte.dev/docs/kit
MIT License
18.73k stars 1.94k forks source link

Load css files on `+layout.svelte` conditionally based on server-side data #9943

Open martgnz opened 1 year ago

martgnz commented 1 year ago

Describe the problem

We are trying to import a.css on a layout file, and when a prop coming from +layout.server.js is met, load b.css.

CSS files imported from components load regardless of the svelte component getting rendered on the markup. This is a problem because we haven't found another way to load global css files based on SSR props.

Describe the proposed solution

Ideally we could simply do the following:

<script>
  // +layout.svelte
  import './styles/a.css';
  import Theme  from './Theme.svelte';

  export let data;
</script>

<!-- b.css is not loaded when condition is false  -->
{#if data.condition}
  <Theme />
{/if}

<slot />
<script>
  // Theme.svelte
  import './styles/b.css';
</script>
<script>
  // +layout.server.js
  export async function load(event) {
    return { condition: false }
  }
</script>

Alternatives considered

We have taken a look at the layout system. Unfortunately it seems to designed around routes and not really around props coming from a server.

Importance

would make my life easier

Additional Information

We are not sure how to proceed maintaining SSR. Would appreciate any suggestions or ideas!

DhyeyMoliya commented 1 year ago

You can use <svelte:head> to include your stylesheets conditionally using <link rel="stylesheet" .../>.

Example :

<script>
    export let data;
</script>

<svelte:head>
    {#if data.condition}
    <link rel="stylesheet" href="/styles/a.css" />
    {:else}
        <link rel="stylesheet" href="/styles/b.css" />
    {/if}
</svelte:head>

Note : You need to have these files publicly accessible in /static directory of the project, i.e. /static/styles/a.css or something like that.

OR

You can use Advanced Layouts to achieve similar results. See here : https://kit.svelte.dev/docs/advanced-routing#advanced-layouts

martgnz commented 1 year ago

Thanks Dhyey. In this case we would prefer to avoid loading stylesheets from the static folder. Right now we have every style in the same folder and it's very easy to keep track of them. Moving only some of them elsewhere is something we want to avoid.

I was taking a look at the advanced layouts but in this case we are only working with one index.svelte page that loads props from a server and ideally loads different css based on that. The theming doesn't depend on a path, it depends on a prop loaded from a server and as far as I know this behaviour is always based on pages being under different routes.

As for the last example the problem is that importing CSS on a component and then loading that on a layout file automatically applies all the styles, even when that component is not being rendered on the markup. You don't have a way to control when they're loaded.

DhyeyMoliya commented 1 year ago

Yes that's the hard truth which I am facing since the inception of SvelteKit. Conditional Styling is a pain to implement with SvelteKit.

benmccann commented 1 year ago

I'd guess this is related to https://github.com/vitejs/vite/issues/4389

Can you change it to do a dynamic import of the component inside the if check? That will probably work instead

martgnz commented 1 year ago

@benmccann A dynamic import inside an await block works but the problem is that you lose SSR. Is there a way to have this keeping SSR?

benmccann commented 1 year ago

I don't think you would lose SSR with that

martgnz commented 1 year ago

Unfortunately I am losing SSR in the following example (CSS appears to load client-side): https://stackblitz.com/edit/sveltejs-kit-template-default-eun2mo?file=src%2Froutes%2F%2Bpage.svelte

david-plugge commented 1 year ago

You can try to load the theme component in layout.js and pass it to layout.svelte

samlfair commented 4 months ago

My workaround for this is to use <svelte:head>:

// DarkMode.svelte

<svelte:head>
  <style>
    body {
      color: white;
      background: black
    }
  </style>
</svelte:head>
// +page.svelte

<script>
  import DarkMode from "./DarkMode.svelte"

  let darkMode = false
</script>

{#if darkMode}
  <DarkMode />
{/if}