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

Support some way of inlining CSS into the HTML #6737

Closed
4 tasks done
dantman opened this issue Feb 3, 2022 · 9 comments · Fixed by #7869
Closed
4 tasks done

Support some way of inlining CSS into the HTML #6737

dantman opened this issue Feb 3, 2022 · 9 comments · Fixed by #7869

Comments

@dantman
Copy link

dantman commented Feb 3, 2022

Clear and concise description of the problem

I would like some way to have a CSS file compiled by Vite end up as an inline <style> tag in the resulting .html file. But without dumping everything into the html like vite-plugin-singlefile.

I have a lightweight splash/loading screen built into my html so the user doesn't see a blank white page while JS is loading. This needs a bit of CSS, however if this CSS is located in a separate CSS file there is an undesirable FOUC in between when the HTML for the loading screen's and when the separate delayed network request for the loading screen's CSS. So it's strongly desirable to include the loading screen's CSS inline with the loading screen HTML.

This however is not possible in Vite. In fact Vite actively does the opposite, any inline <style> styles in html get extracted out into an external stylesheet.

Suggested solution

We already have ?inline which has a similar behaviour in JS, i.e. return it raw instead of bundling it into a file. So one idea might be to make ?inline on an @import in <style> inline those styles into the <style> tag the import is present in.

<style>
@import url('./style.css?inline');
</style>

Alternative

I thought the easiest way to implement this as a plugin and tried implementing it in multiple ways. However this turned out to be absurdly difficult.

  • A pre transform plugin doesn't get CSS compiled by the user's postcss plugins and have to re-implement all of Vite's CSS handling.
  • A post transform plugin doesn't get the CSS at all, instead it gets a junk ES module so it can't extract it.
  • Vite has no way to append a postcss plugin without overriding the user's postcss config, so you can't add a PostCSS that'll extract something like @media inline { ... } either.

Additional context

No response

Validations

@meduzen
Copy link
Contributor

meduzen commented Feb 12, 2022

I face a similar issue (can it be considered the same?) where I’d like a tag <style> to remain in the end bundle, mainly because it’s embed in a SVG spritesheet, but Vite strips it from the build HTML and move it to the bundled CSS file.

Sample HTML:

<body>
    <svg style="display: none;">
        <symbol id="lock-path" viewBox="0 0 13 18" fill="currentColor">
            <!-- This 👇 is removed by Vite -->
            <style>
                .lock-path--unlocked { --locked-path: none; }
                .lock-locked-path { display: var(--locked-path, unset); }
            </style>
            <path d="M10.608 8.357h-9A1.625 1.625 0 0 0 .001 9.964s-.002 2.575 0 4.5C.003 16.42 1.611 17.998 3.537 18H8.68a3.553 3.553 0 0 0 3.535-3.536v-4.5a1.625 1.625 0 0 0-1.607-1.607Zm-3.536 4.702v2.048a.33.33 0 0 1-.321.321H5.465a.33.33 0 0 1-.321-.321v-2.048c-.699-.792-.193-2.099.964-2.136 1.143.037 1.663 1.344.964 2.136Z"/>
            <path d="M6.108 0C3.483.003 1.29 2.197 1.287 4.821v2.893h1.928V4.821c0-1.575 1.317-2.892 2.893-2.892 1.576 0 2.893 1.317 2.893 2.892 0 .17.15.322.321.322h1.286a.33.33 0 0 0 .322-.322C10.927 2.197 8.733.003 6.108 0Z"/>
            <path class="lock-locked-path" d="M9 4.8h1.93v2.91H9z"/>
        </symbol>
    </svg>
</body>

The <style> tag is moved and the end of the CSS, and I don’t want that:

.lock-path--unlocked{--locked-path:none}.lock-locked-path{display:var(--locked-path,unset)}

Maybe a solution could be to have a magic comment and/or a setting to keep inline CSS in the HTML and/or what @dantman is suggesting.

@meduzen
Copy link
Contributor

meduzen commented Apr 1, 2022

The issue is worse in 2.9 (in both 2.9.0 and 2.9.1): in addition to the problem previously mentioned,, now it’s broken during dev time, which means this:

<body>
    <svg style="display: none;">
        <symbol id="coin-smb-path" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 366 512" fill="none">
            <!-- This 👇 is extracted by Vite on build time -->
            <style>
                .coin { color: hsl(0, 0%, 0%); }
                .coin-color { fill: hsl(0, 56%, 52%) }
                .coin-bg { fill: hsl(28, 100%, 61%) }
            </style>
            <path d="M109 109H73v294h36v73h37v36h146v-36h37v-73l37-1V109h-37V37h-37V0H146v36h-37v73z" fill="currentColor"></path>
            <path d="M37 109H0v294h37v73h36v36h147v-36h37v-73l36-1V109h-36V37h-37V0H73v36H37v73z" class="coin-bg"></path>
            <rect x="110" y="403" width="73.3" height="36" fill="currentColor"></rect>
            <rect x="183" y="109" width="36" height="293.3" fill="currentColor"></rect>
            <rect x="74" y="109" width="36" height="293.3" class="coin-color"></rect>
            <rect x="110" y="73" width="73.3" height="36" class="coin-color"></rect>
        </symbol>
    </svg>
</body>

becomes this:

<body>
    <svg style="display: none;">
        <symbol id="coin-smb-path" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 366 512" fill="none">
            <!-- This 👇 appear during dev time -->
            <script type="module" src="/index.html?html-proxy&amp;index=5.css"></script>
            <path d="M109 109H73v294h36v73h37v36h146v-36h37v-73l37-1V109h-37V37h-37V0H146v36h-37v73z" fill="currentColor"></path>
            <path d="M37 109H0v294h37v73h36v36h147v-36h37v-73l36-1V109h-36V37h-37V0H73v36H37v73z" class="coin-bg"></path>
            <rect x="110" y="403" width="73.3" height="36" fill="currentColor"></rect>
            <rect x="183" y="109" width="36" height="293.3" fill="currentColor"></rect>
            <rect x="74" y="109" width="36" height="293.3" class="coin-color"></rect>
            <rect x="110" y="73" width="73.3" height="36" class="coin-color"></rect>
        </symbol>
    </svg>
</body>

And as this line doesn’t load nor inject anything, this is what I see in Vite 2.9 during dev. time:

image

And this is what I see in Vite 2.8:

image

This comes from this project (using Vite 2.8) when I update it to 2.9 on my machine.

IanVS added a commit to storybookjs/builder-vite that referenced this issue Apr 15, 2022
Vite 2.9+ exhibit a flash of unstyled content in the head while the page loads in.

This approach adds some styles from https://github.com/yannbf/mealdrop/blob/feat/lazy-compilation/.storybook/preview-head.html into the head, after vite has finished processing.  This is necessary (instead of just putting the styles in the iframe html) due to vitejs/vite#6737.

With the changes here, we no longer see content like this:

![image](https://user-images.githubusercontent.com/4616705/163624147-70c78cd9-4784-43b3-b2e6-b848fc3f6582.png)
@poyoho
Copy link
Member

poyoho commented Apr 18, 2022

Maybe #7786 can fix this issues. If someone else has time to test, please report in #7786. I will fix it in time.

@meduzen
Copy link
Contributor

meduzen commented Apr 26, 2022

I tested the project mentioned in my previous comment with Vite 2.9.6, and the bug remains.

@IanVS
Copy link
Contributor

IanVS commented Apr 26, 2022

@meduzen do you happen to have a reproduction you can share? Is the style tag still being extracted to a script? I wonder, does it change anything if you move the <style> tag to the head rather than the body?

Edit: I see you linked to the project in your comment above. 👍

@meduzen
Copy link
Contributor

meduzen commented Apr 27, 2022

@IanVS I suppose you could see it, it’s the <style> tags, all in <svg>s (all in <body> in my case), but I guess the expectation is to have the ability to maintain <style> anywhere.

@IanVS
Copy link
Contributor

IanVS commented Apr 27, 2022

@meduzen sorry, but <style> tags must go in the <head>. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style.

The <style> element must be included inside the <head> of the document. In general, it is better to put your styles in external stylesheets and apply them using <link> elements.

When I move your styles there, and run the app, it seems to work correctly.

@IanVS
Copy link
Contributor

IanVS commented Apr 27, 2022

Edit, my mistake, it's okay to have them in SVGs, as you said, but I missed that (even though you said it a few times. 🤦). This seems like it is still a bug with vite, since it should not extract these, I think.

@meduzen
Copy link
Contributor

meduzen commented May 2, 2022

For me, Vite 2.9.7 solves the issue. Many thanks to the contributors. 🙇

@github-actions github-actions bot locked and limited conversation to collaborators May 17, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants