svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Change body class via <svelte:body />

Open PaulMaly opened this issue 5 years ago β€’ 72 comments

It's just an idea, but it'll be very convenient if we'll able to switch classes on body element like this:

<svelte:body class:profile={isProfilePage} />

PaulMaly avatar Jun 25 '19 09:06 PaulMaly

I've considered this before - what should happen with this when compiling for SSR? There's nowhere in the current .render() response where we could say what classes are on <body>.

Conduitry avatar Jun 25 '19 13:06 Conduitry

Seems, we already have something similar with head:

const { head, html, css, body } = App.render({ ... });

And can be used in Sapper like this:

<head>
  ...
  %sapper.head%
</head>
<body %sapper.body%>

</body>

I really believe the body class for pages is a common case for many apps.

PaulMaly avatar Jun 25 '19 14:06 PaulMaly

In case someone wants to change the class of the body it's very easy.

Add this to the script part of your main layout file:

document.body.classList.add('my-class')

Then in a global css file like public/global.css add the styles for .my-class

jorgegorka avatar Aug 16 '19 17:08 jorgegorka

@jorgegorka Sorry, but everyone knows that. Here we're talking about a universal (client and server) and declarative way to set body classes. The way you described won't work with SSR and not declarative.

PaulMaly avatar Aug 16 '19 22:08 PaulMaly

The same kind of thing would be very useful for setting attributes on html, such as lang. react-helmet handles this by returning htmlAttributes and bodyAttributes when calling helmet.renderStatic()[link]. Maybe a similar approach would make sense here?

nsivertsen avatar Aug 27 '19 07:08 nsivertsen

vote positive on <svelte:body class:name={confition} is usefull

ramiroaisen avatar Sep 02 '19 20:09 ramiroaisen

+1 It would be great if we could set it like this, from the special component and with SSR.

Chaciej avatar Oct 09 '19 05:10 Chaciej

+1 for

<svelte:body class:name={confition}>

as well as

<svelte:html lang={lang}>

CanRau avatar Oct 10 '19 04:10 CanRau

If you want to change the whole background without having the content being taller than the fold, being able to change body class is pretty vital.

Is there currently a workaround?

If I'm not wrong, using document.body.classList won't work for SSR.

I want to give body different classes based on which page is active.

hgl avatar Oct 12 '19 06:10 hgl

BTW, I think this feature might somewhat relate to https://github.com/sveltejs/sapper/issues/374.

That issue is basically asking a way for svelte to render directly at the html/body level, instead of a child node, and that probably affects how components can manipulate {body, html} {classes,attributes}

hgl avatar Oct 21 '19 08:10 hgl

I don't know what is the status of <svelte:body class:name={condition} implementation, but in the meantime, I've managed to overpass this issue by using <svelte:head> instead. As my need was to add styling to my page's body.

<svelte:head>
   {#if isModal}
      <style>
         body {
            overflow: hidden;
         }
      </style>
   {/if}
</svelte:head>	

3tmaan avatar Feb 10 '20 09:02 3tmaan

Simple theme chooser.

<script>
  const themes = ['', 'dark-theme', 'light-theme']
  const icons = ['πŸŒ™', '🌞', '⭐']
  let index = 0
</script>

<button on:click={() => { index = (index + 1) % themes.length }}>{icons[index]}</button>

<svelte:body class={themes[index]}/>

<svelte:head>
  <style>
    /* CSS file */
    :root {
      --background-color: #a19585;
      --text-color: #f7f7f7;
      --primal-color: #30a5a7;
    }

    .light-theme {
      --background-color: #fffaf4;
      --text-color: #201f20;
    }

    .dark-theme {
      --background-color: #201f20;
      --text-color: #fffaf4;
    }

    body {
      background-color: var(--background-color);
      color: var(--text-color);
    }
  </style>
</svelte:head>

techa avatar Feb 26 '20 10:02 techa

@Conduitry @Rich-Harris Any comments here? Seems this proposal is very popular.

PaulMaly avatar Mar 06 '20 12:03 PaulMaly

@Rich-Harris

ghost avatar May 10 '20 21:05 ghost

i have the same issue

AliBasicCoder avatar Jul 03 '20 22:07 AliBasicCoder

@sveltejs thanks for deleting my comment and i sure you delete this comment. but i should remember to you (who deleted my comment), Svelte isn’t single compiler around world, so keep your @sveltejs yourself. I just tried it and already has experience of deleting my comment

dalisoft avatar Jul 04 '20 12:07 dalisoft

i also find that dynamic class assignments to the body element are really needed, especially in the sapper environment with SSR

niklasgrewe avatar Jul 06 '20 16:07 niklasgrewe

where is the solution? Someone knows?

fran1990Web avatar Sep 05 '20 15:09 fran1990Web

I want this feature alongside <svelte:html /> . On server side it might look like this:

const { head, html, css, htmlAttrs, bodyAttrs } = App.render({ ... })

Sapper template:

<html %sapper.htmlAttrs%>
<head>
  ...
  %sapper.head%
</head>
<body %sapper.bodyAttrs%>
  ...
</body>
</html>

neurocmd avatar Sep 05 '20 22:09 neurocmd

+1 This is a a must. Of course this can be solved but why resort to hacks.

sharpcodepro avatar Sep 20 '20 13:09 sharpcodepro

+1 In the meantime in: _layout.svelte

<script>
import { stores } from '@sapper/app';
import { onMount } from 'svelte';

const { session } = stores();
let root;

onMount( () => {
    root = document.documentElement;
}
$: root && (hasUser => root.classList.toggle('loggedin', hasUser))(!!$session.user);
</script>

anito avatar Sep 29 '20 15:09 anito

+1 This would be nice to have!!

handfulofcats avatar Jan 05 '21 00:01 handfulofcats

Well I think it would be very helpful to be able to change a class or any other attribute on body simply by using <svetle:body>. I was a little bit surprised that it isn't currently supported.

kwiat1990 avatar Feb 14 '21 10:02 kwiat1990

Yea, this feature would be very very useful for many svelte devs! There's a solution in user-land (for example https://github.com/ghostdevv/svelte-body), but all they're not playing well with SSR. And it's very sad to me.

PaulMaly avatar Sep 09 '21 08:09 PaulMaly

Sometimes you don't mount the app directly to the body but rather some other element. Perhaps it would be nice if you could target this as well.

lemmon avatar Jan 21 '22 13:01 lemmon

All those document.body.classList.add didn't quite work for me, it was getting triggered too late in the render lifecycle.

I think I found a decent workaround for that:

<svelte:head>
	{#if isDarkTheme}
		<style>
			body {
				background-color: #121217;
			}
		</style>
	{:else}
		<style>
			body {
				background-color: #ececec;
			}
		</style>
	{/if}
</svelte:head>

Since this is getting rendered with SSR, the background is pretty much applied as soon as the page renders, which should prevent flickering :)

francoislg avatar Feb 11 '22 23:02 francoislg

Another way you could do dynamic changes on body styles from anywhere would be to do something like so:

<script lang="ts">
  function Color(hex, selected = false) {
    return {hex, selected}
  }
	
  const colors = [
    Color("#ff0000", true),
    Color("#00ff00"),
    Color("#0000ff"),
  ]

  $: backgroundColor = colors.find((c) => c.selected)
  $: bodyStyle = `
    <style>
      body {
        background-color: ${backgroundColor.hex}
      }
    </style>
  `;
</script>

<svelte:head>
  {@html bodyStyle}
</svelte:head>

For me the use case was that I wanted to dynamically change the background color of the <body> depending on something stored in my store.ts. This way you can have more control over what the style is and it could be move out is a function even. Not cluttering the html space, or doing multiple if/else statements with multiple <style> tags inside (which doesn't scale).

gspasov avatar Feb 15 '22 17:02 gspasov

Simple theme chooser.

  const themes = ['', 'dark-theme', 'light-theme']
  const icons = ['πŸŒ™', '🌞', '⭐']
  let index = 0
</script>

<button on:click={() => { index = (index + 1) % themes.length }}>{icons[index]}</button>

<svelte:body class={themes[index]}/>

@techa Is this aspirational? AFAICT, <svelte:body> does not support dynamic classes like that. (Although it would be cool)

oravecz-jpmc avatar Feb 21 '22 20:02 oravecz-jpmc

Are there any updates on this? I would really like this feature among with many others!

maurictg avatar Mar 02 '22 15:03 maurictg

I just needed styles on a single page, then removed when the page unmounts, so I did:

<svelte:head>
	<style>
		html,
		body,
		#svelte {
			height: 100%;
			overflow: hidden;
		}
	</style>
</svelte:head>

Now when I go to another page, it's not overflowed. Both :global() and a separate css file persisted and prevented me from scrolling on the other pages.

RomanistHere avatar May 05 '22 16:05 RomanistHere