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

Global styles or scripts #3127

Closed
ghostdevv opened this issue Dec 29, 2021 · 32 comments
Closed

Global styles or scripts #3127

ghostdevv opened this issue Dec 29, 2021 · 32 comments
Labels
feature request New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc.
Milestone

Comments

@ghostdevv
Copy link
Member

ghostdevv commented Dec 29, 2021

Describe the problem

Currently in Svelte Kit the recommended way to import global styles or scripts is in your root layout, this is a problem for the following reasons:

  • Slower connects will receive a FOUC (Flash Of Unstyled Content)
  • Resets will remove these applied style sheets
  • Styles won't be applied if js isn't functioning correctly (in some contexts)
  • (opinion) it's weird

Describe the proposed solution

I was reading up in the discord and issues here and I believe one of the following is the best solution:

  1. A global js/ts file and global css/scss file

    this would be my initial guess at a solution and it was even shown here Allow inclusion of global styles #1948 but the downsides are that users might want more than provided options such as less or another javascript superset that isn't provided and adding a option might add extra complexity

  2. A global js/ts file

    Vite already allows for this easily and can just be treated as a secondary entry point I guess. This is already what you do in vanilla vite, for example on my personal site which is currently in routify

  3. A global svelte file

    This might be familiar for those using vanilla svelte, having a App.svelte (this case would probably be better for a global.svelte file). This can be where you import your global styles but also global scripts and logic. Again this is how things are done on sites in Routify, for example global keybinds

  4. Processing app.html

    It might be also possible to process the scripts and links inside of app.html and have this the place your global files are placed (processing is necessary since the script or style might be in a superset such as typescript or sass)

Alternatives considered

Writing a script or vite plugin to do this for me but this is just bad for the community

Importance

i cannot use SvelteKit without it

Additional Information

No response

@bluwy
Copy link
Member

bluwy commented Dec 30, 2021

I think another way is to add it to app.html, but IIRC SvelteKit doesn't process it, and links to styles or scripts are static. Perhaps SvelteKit should have some way of transforming app.html, similar to Vite's index.html.

@bluwy bluwy added p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. feature request New feature or request labels Dec 30, 2021
@Conduitry
Copy link
Member

Related: #1530

@ghostdevv
Copy link
Member Author

@bluwy Yep SK doesn't process app.html - already tried

@Kapsonfire-DE
Copy link
Contributor

cant you just add a style tag inside your __layout.svelte to head with vanilla js on first render?

@ghostdevv
Copy link
Member Author

that wouldn't solve any of the issues I brought up @Kapsonfire-DE

@Kapsonfire-DE
Copy link
Contributor

Kapsonfire-DE commented Jan 3, 2022 via email

@ghostdevv
Copy link
Member Author

If you read my original post then you will see why layout related styling isn't optimal. @Kapsonfire-DE

@Kapsonfire-DE
Copy link
Contributor

What I'm missing? If you reflect the style in the head (outside of svelte) it wont be removed. Nothing will flicker, resets wont remove it and so on. The Idea to put this in in __layout and reflect it outside of svelte is that the css will be processed by SK - and multiple to the style will not be loaded multiple times since its cached

@ebeloded
Copy link

ebeloded commented Jan 5, 2022

This is very much wanted 👍

@ghostdevv
Copy link
Member Author

@Kapsonfire-DE While putting styles in your app.svelte works, these scripts/styles don't get processed by Svelte Kit which leads to limitations or extra scripts / build tools needing to be installed. Having the official way to be putting global scripts/styles in a layout (which isn't even global since it can be reset) is a bad move.

@Kapsonfire-DE
Copy link
Contributor

I didnt talk about app.svelte.
You can just put in in __layout.svelte or any other component. Make it processed by svelte kit. After that, copy the css with vanilla javascript into the documents head outside of the svelte scope, which makes them persistent and global.

@ghostdevv
Copy link
Member Author

Again even if you can make that work (at a initial guess it would be additional script(s)) it's terrible DX. Would love to know what your root issue is with this proposed solution @Kapsonfire-DE

@ratorx
Copy link

ratorx commented Jan 6, 2022

It would be great if app.html could just be processed by Vite in some way after SvelteKit is done with it. Vite already handles all the bundling etc. properly and works the way you would expect if you for example include a Typescript file instead of a Javascript one.

My specific use case is for pages with hydrate = false where I want to inject a small amount of deferred Javascript (or Typescript) for a background canvas animation. Achieving this with SvelteKit requires hydrating the entire page, which seems like a huge waste, especially if the animation is written in plain Javascript.

@ghostdevv
Copy link
Member Author

ghostdevv commented Jan 7, 2022

I have updated my post to add in that ideas @ratorx

Sorry for the mis-tag @borntofrappe - github mobile being slow

@bseib
Copy link

bseib commented Jan 12, 2022

Would like to see a solution to this as well.

I have a naive question between these two proposed options:

  1. A global js/ts file and global css/scss file
  2. A global js/ts file

Would choosing 2. mean doing an import 'path/to/global.scss'; in the ts or js file? Would that still have FOUC issues? Does a "CSS in JS" solution always come with FOUC issues? (Go easy on me, there's a gap in my understanding with what the vite + svelte plugin is actually doing.)

At the moment I'm building a single global.css file in an external dir with all our global scss files, using rollup. The build drops that global.css file into sveltekit's src/static dir, and I'm loading it in my __layout.svelte file with:

<svelte:head>
    <link rel="stylesheet" href="/global.css">
</svelte:head>

It gets rid of my FOUC, but of course the css file is large since there is no tree shaking. The whole approach feels clunky, and __layout seems the wrong place for "global" stuff. I'm building a strictly static site with many pages -- no SSR, not a SPA. It would seem that the same tree-shaking and code splitting for CSS could still apply somehow with a global scss file(s) and generate the appropriate <link > entries. Letting a developer supply a list of arbitrary "entry points", or "build points" to have svelte + vite build would be useful. I guess I could also argue "why build/output anything that won't be used".

@ghostdevv
Copy link
Member Author

@bseib Hopefully the FOUC issue at this point is close to nothing, you will always get it in dev mode but in production hopefully it's limited to slower connection (not much kit can do about this anyway and when building static or ssr the issue shouldn't be present since it should be loading styles in the head of the page).

Yes putting the global ts/js file would mean you put the scripts inside of that with with a import statement, this is how it's done in vanilla svelte + vite projects for example. We could even combine the global style and global js so that you wouldn't need to do any importing and there is a content seperation.

If you are currently already building your css file then you should put it in static and link to it in your app.html as then it will be global, if you don't wish to do this you might as well get rid of your extra build step as svelte kit can handle this for you

Code splitting global styles doesn't make sense as even if you wanted to strip styles that don't apply then it would essentially no longer be global but scoped to your enitre app, which while that is a interesting concept wouldn't be global styling

@Hai-San
Copy link

Hai-San commented Feb 21, 2022

Today I'm having the same problem to apply a .scss file globally to reset the css. It would be very important to have a way to apply styles and scripts globally.

@thdoan
Copy link

thdoan commented Feb 28, 2022

Hi all, I'm a little late to the party, but hopefully there's still a lot of fun to be had ^^. I just created my first SvelteKit skeleton project and currently have my global CSS reset in static/css/common.css with the following code in src/app.html:

<link rel="stylesheet" href="/css/common.css">

I suppose this is fine for simple projects, but what if I want to replace common.css with common.scss? Is there a method that is considered "best practice" in SvelteKit to implement a global SCSS?

@Mark2M
Copy link

Mark2M commented Mar 30, 2022

Had the same problem, no global.css found. Changed the link in app.html to ‘%svelte.assets%/css/global.css” for dev path static/css/global.css and its working.

@Rich-Harris Rich-Harris added this to the 1.0 milestone Apr 28, 2022
@Dan503
Copy link

Dan503 commented May 7, 2022

I worked around this issue by doing the following:

  1. Create an src/App.svelte file with the following in it:
// src/App.svelte

<slot />

<style lang="scss" global>
    @use './global.scss';
</style>
  1. Create a src/global.scss file with your global styles in it.

  2. Import App.svelte like this in src/routes/index.svelte:

// src/routes/index.svelte

<script lang="ts">
    import App from '../App.svelte'
</script>

<App>
    <p>content</p>
</App>

The main downside is that you have to import <App> and wrap it around every route.

It would be nicer if SvelteKit provided a way to just have a global scss file.

@felixsanz
Copy link

People usually (if not always) import global CSS inside __layout.svelte, but that doesn't make sense because global CSS is usually page/app related things, like CSS resets, custom properties / variables, etc. I think of it like this:

<html>
  <body>
    <Layout>
      <main>

Why are we setting CSS for <html> and <body> inside <Layout>?

Also, if you have __error.svelte, you need to import the global CSS file there too! And now with the new Named Layouts feature is also worse because if you have 10 different layouts that share the same CSS resets... you have to import it 10 times. Even if the output CSS is not duplicated and file is only imported once... it doesn't make sense as Layouts shouldn't be responsible for this!

A layout is a visual representation of a page, it shouldn't be responsible for setting variables or resetting css among other things.

I think we need an entrypoint for global CSS that is outside of layouts, for example an option inside svelte.config.js like:

const config = {
  kit: {
    'globalCss or cssEntrypoint': 'src/lib/styles/index.css',
  }
}

I tried using prependData inside postcss with svelte-preprocess but that's worse because the variables get added over and over again.

Having <style> or <link> inside app.html doesn't let you process those files with postcss or any other preprocessor, so not really a solution.

@Dan503
Copy link

Dan503 commented May 9, 2022

Don't forget that the solution must support Sass/Less as well

@felixsanz
Copy link

that file can be read and preprocessed

@XNA-bit
Copy link

XNA-bit commented May 25, 2022

I found this article and it really works.
https://www.closingtags.com/global-css-in-sveltekit/

@raquentin
Copy link

raquentin commented Jun 5, 2022

Someone may have already mentioned this but the best way to get global styles in my experience is to include something like this in your __layout.svelte:

<style>
    :global(:root) {
        --green: #293d3a;
        --pink: #ff7965;
        --white: #ffffff;
        --grey: #222222;
    }
...
...
...
</style>

@CodeWithOz
Copy link

Just want to add, for anyone using the <svelte:head> method to insert the global stylesheet into the head tag, HMR doesn't pick up changes to the stylesheet normally, but you don't need to restart your server to apply the changes. Instead you can comment out the <link> in the <svelte:head> tag to remove the stylesheet from the build, save the file so HMR picks up the removed stylesheet, then uncomment the <link> tag and save again so HMR restores the stylesheet with your changes. Not the smoothest method but for me it's better than stopping and restarting the dev server.

@bhvngt
Copy link

bhvngt commented Jul 21, 2022

I have been facing this issue while trying to import an external dependency under script type="module" tag. I need it this way so that one of my dependency installs first before anything else gets installed.

<html lang="en">
	<head>
		<script type="module">
			import '@master/css';
		</script>

What I have realised is that though vite handles this without any issue. However, since sveltekit takes over html processing, it throws Failed to resolve module specifier <external dependency> error. It suggest for relative urls which in my case did not work due to nested external dependency. Here's the reproduction of the issue.

For the time being I have linked external dependency file from node_modules to my static folder and then linking it with script tag inside app.html. This is definitely hacky.

Would be great if svelte kit allows vite to preprocess app.html after it has done its side of the processing.

@dummdidumm
Copy link
Member

Heads up: Under the new layouts proposal, the root +layout.svelte would always be guaranteed to exist and be loaded, so you can import global styles or scripts there. #6124

@bhvngt
Copy link

bhvngt commented Aug 22, 2022

That will be awesome.

@dummdidumm Currently all the scripts are loaded by sveltekit as module scripts that are deferred automatically by the browsers. This leads to FOUC for global styles imported within layout components on slower connections when html is parsed before style is loaded. Just wondering if this will this get addressed too?

Currently I am using app.html to ensure that non-deferred scripts and styles are loaded before any other processing happens.

@Rich-Harris
Copy link
Member

Rich-Harris commented Aug 24, 2022

FOUC should not be possible for styles imported by the root layout, since those styles are injected into the document (as a <style> during dev, and as a <link> in production). If you have an example of it, please raise an issue with a repro!

Prior to #6174 it was possible to end up in a FOUCy situation because you might land on a page that bypassed the root layout. That's no longer possible — under the new system, every page inherits from the root layout, which means it's a perfect place to add global scripts and styles:

<!-- src/routes/+layout.svelte -->
<script>
  import '$lib/javascript-that-should-run-before-hydration.js';
  import './global.css';
  import { afterNavigate } from '@sveltejs/kit';
  
  afterNavigate(() => {
    // this will run after every single navigation, making it an
    // ideal place to put e.g. analytics tracking code etc
  });
</script>

Note that $lib/javascript-that-should-run-before-hydration.js can even do async work, thanks to top-level await.

I'm pretty sure this issue can therefore be closed — any objections?

@ebeloded
Copy link

ebeloded commented Aug 25, 2022

The problem with FOUC still exists for a SPA, as was outlined in this issue: #1948

If I create a new SvelteKit project and set { fallback: 200.html } in static adapter, and add the import './global.css' in the +layout.svelte file, the desired global CSS is not included in the fallback HTML file, and is loaded with JS, resulting in FOUC.

Reproduction: https://github.com/ebeloded/sveltekit-spa-global-styles

@Rich-Harris
Copy link
Member

That's not FOUC. FOUC means content is present but unstyled, and results in a jarring transition. This is just a blank page showing until the content and styles are rendered simultaneously. If you don't want a blank page, don't use an SPA.

Looks like we can close this issue — thanks all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc.
Projects
None yet
Development

No branches or pull requests