sveltejs / kit

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

Can CSS be inlined in the server-rendered pages ? #962

Closed Litarvan closed 2 years ago

Litarvan commented 3 years ago

Currently, HTML is rendered in the response by the server, but the CSS is linked through attributes, thus delaying proper page rendering until the stylesheet request is done.

Is it possible to inline CSS (or at least the 'start' stylesheet) like Next.JS does? Or is it planned?

Maggi64 commented 3 years ago

CSS loading got worse in comparison to sapper i think. With the migration to sveltekit i saw a loss in 18 lighthouse points. This seems mostly related to more css files that need to be loaded. I can provide more detailed info if needed.

Sveltekit ![image](https://user-images.githubusercontent.com/20517052/114319507-2658c300-9b12-11eb-8e60-ba17cc16fd55.png) ![image](https://user-images.githubusercontent.com/20517052/114319549-3ffa0a80-9b12-11eb-858e-08574448de1b.png)
Sapper ![image](https://user-images.githubusercontent.com/20517052/114319602-6c158b80-9b12-11eb-8946-b2d7761a743b.png)
benmccann commented 3 years ago

This seems mostly related to more css files that need to be loaded.

Sapper used rollup-plugin-css-chunks to chunk the CSS files in the same way that the JS files were chunked. Vite is most compatible with Rollup plugins, so potentially you could include that in your build. If that doesn't work, you'd have to put in a feature request over in that repo if you'd like to see that behavior added to Vite

andreiborisov commented 3 years ago

Inlining critical CSS is a huge win for perceived performance, even when loading assets through HTTP/2. I'd say this is a crucial feature on the road to SvelteKit's production-ready status.

Worth mentioning that it's needed both for SSR and static pages.

Maggi64 commented 3 years ago

Sapper used rollup-plugin-css-chunks to chunk the CSS files in the same way that the JS files were chunked. Vite is most compatible with Rollup plugins, so potentially you could include that in your build.

Sadly didn't work. I tried to disable vite css splitting, but it doesn't seem to respect my config settings.

andreiborisov commented 3 years ago

I've successfully mitigated the issue by using HTTP/2 Server Push, depending on your backend, you might wanna look into that

mvolfik commented 3 years ago

Interesting to note, CSS is inlined when using dev server, but not during build (adapter-static in my case)

mehdiraized commented 3 years ago

i use postcss config in sveltekit starter but not work in build project

livehtml commented 3 years ago

Please give us opportunity to do this. It's too important and needed thing in SvelteKit. Thanks.

Rich-Harris commented 2 years ago

As of the most recent version, you can inline stylesheets smaller than a certain size with the inlineStyleThreshold option:

// svelte.config.js
export default {
  kit: {
    // inline all stylesheets smaller than 1kb
    inlineStyleThreshold: 1024
  }
};
julienchazal commented 2 years ago

thx @Rich-Harris !

CSS is inlined, but is it normal that in the HTML still appears :

<link disabled rel="stylesheet" href="/_app/assets/index-f3af5aa6.css">

Chrome is loading this CSS file, and it contains CSS that has been inlined

gerardo-rodriguez commented 2 years ago

As of the most recent version, you can inline stylesheets smaller than a certain size with the inlineStyleThreshold option:

// svelte.config.js
export default {
  kit: {
    // inline all stylesheets smaller than 1kb
    inlineStyleThreshold: 1024
  }
};

This is great, @Rich-Harris! πŸŽ‰

Does it matter where the source CSS file exists? Does it matter how or where the CSS file is imported? Does the use of an adapter (I'm using @sveltejs/adapter-static) disable the inlineStyleThreshold setting?

I'm setting inlineStyleThreshold to different values (including extremely large values) and I don't see anything getting inlined. I wonder what I'm doing wrong. We are running version 1.0.0-next.254. Is this the best place to ask these types of questions, or should I ask elsewhere? πŸ€”

My endgoal is to inline both the JS and CSS, I found this CSS option first so figured I'd start there.

Thank you! I've enjoyed using SvelteKit and Svelte so much thus far! πŸ˜„

wiesson commented 2 years ago

I'm setting inlineStyleThreshold to different values (including extremely large values) and I don't see anything getting inlined.

Same here!

pavelloz commented 2 years ago

Im afraid i have the same issue.

With prerender: false it just plain doesnt work.

With prerender true i can find:

Im using static adapter so it shouldnt matter, but i did the test to see if it will make a difference. And it did, just not in the file that i was expecting to.

=> npx envinfo --npmPackages "{svelte,@sveltejs/*,vite}"           

  npmPackages:
    @sveltejs/adapter-static: next => 1.0.0-next.29 
    @sveltejs/kit: next => 1.0.0-next.302 
    svelte: ^3.46.4 => 3.46.4 
robots4life commented 2 years ago

@pavelloz I can confirm with prerender: false it does not work, but I think this is the desired behavior when using the static adapter.

Adapter for SvelteKit apps that prerenders your entire site as a collection of static files. If you'd like to prerender only some pages, you will need to use a different adapter together with the prerender option.

For a site that uses adapter static and has a high value for inlineStyleThreshold I get a build with inline CSS in the head.

svelte.config.js https://github.com/robots4life/daiki/blob/3a3df50070e147b9a709d9b2d6dbba02c6da361a/svelte.config.js#L21

/build/index.html https://github.com/robots4life/daiki/blob/c5c24877baa0000a6c98427b243f4ac88db991c6/build/index.html#L9

Don't mind the fact that other adapters are installed, they are not used in the svelte.config.js file. Here are the versions of the pacakges. npm ls

daiki@0.0.1 /shared/httpd/daiki
β”œβ”€β”€ @sveltejs/adapter-auto@1.0.0-next.33
β”œβ”€β”€ @sveltejs/adapter-node@1.0.0-next.73
β”œβ”€β”€ @sveltejs/adapter-static@1.0.0-next.29
β”œβ”€β”€ @sveltejs/kit@1.0.0-next.303
β”œβ”€β”€ autoprefixer@10.4.4
β”œβ”€β”€ env-cmd@10.1.0
β”œβ”€β”€ eslint-config-prettier@8.5.0
β”œβ”€β”€ eslint-plugin-svelte3@3.4.1
β”œβ”€β”€ eslint@7.32.0
β”œβ”€β”€ postcss@8.4.12
β”œβ”€β”€ prettier-plugin-svelte@2.6.0
β”œβ”€β”€ prettier@2.6.1
β”œβ”€β”€ svelte@3.46.4
└── tailwindcss@3.0.23

However.. given https://www.filamentgroup.com/lab/load-css-simpler/ I like to change the output of the disabled CSS file from

<link rel="stylesheet" href="/_app/assets/pages/__layout.svelte-01120fe0.css" disabled media="(max-width: 0)">

to

<link rel="stylesheet" href="/_app/assets/pages/__layout.svelte-01120fe0.css" media="print" onload="this.media='all'">

To start, the link's media attribute is set to print. β€œPrint” is a media type that says, β€œapply this stylesheet’s rules for print-based media,” or in other words, apply them when the user tries to print the page. Admittedly, we want our stylesheet to apply to all media (especially screens) and not just print, but by declaring a media type that doesn’t match the current environment, we can achieve an interesting and useful effect: the browser will load the stylesheet without delaying page rendering, asynchronously! That’s helpful, but it’s not all we want. We also want the CSS to actually apply to the screen environment once it loads. For that, we can use the onload attribute to set the link's media to all when it finishes loading.

Could this be added as a option to the static adapter perhaps ?

johntron commented 2 years ago

inlineStyleThreshold doesn't work for me either (using adapter-static). There may be a regression if it was working 7 months ago when @robots4life made the commit that worked for him.

I should note that I'm using PostCSS: svelte.config.js

newsroomdev commented 2 years ago

Thank you for reopening @benmccann.

I did some exploration last night and noticed that the inlineStyleThreshold is always set to 0 after it's returned from the options function in kit/src/core/config/index.js. I believe there may be a bug with the number validator in kit/src/core/config/options.js, despite its similarity to the adjacent string_array and boolean validators. I hope this helps!

dummdidumm commented 2 years ago

Could someone provide a minimum reproducible so we can look into it? I did set inlineThreshold to an arbitrary high number and ran the build of the SvelteKit site; styles were inlined as expected. Don't get confused by the <link href="..."> tags still present, they are disabled (there should be such an attribute on them) so not actually loaded - it's a hint for Vite to not do funky stuff.

The-Noah commented 2 years ago

The styles inside the link tags are loaded though, if you open DevTools and go to the network tab you can see them being loaded.

dummdidumm commented 2 years ago

That's strange, I don't see this behavior (using Chrome); according to the spec a disabled link tag should be neither downloaded nor executed. A reproducible would really help.

The-Noah commented 2 years ago

7130

dummdidumm commented 2 years ago

Thanks, closing in favor of that issue.

dummdidumm commented 2 years ago

The inline styles being loaded seems to have been a bug in Vite. It fails with 3.1.x, but works with 3.2.x. Updating to the latest version of Vite fixes the bug.