next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Compiler does not place css @imports in own style tags the way babel used to.

Open scott-coates opened this issue 2 years ago • 19 comments

What version of Next.js are you using?

12.0.7

What version of Node.js are you using?

14.18.1

What browser are you using?

chrome

What operating system are you using?

mac0s

How are you deploying your application?

vercel

Describe the Bug

I just got bit in the upgrade.

In nextjs v10, this is considered valid:

* {
    font-family: "Abril Fatface";
}

@import url('https://fonts.googleapis.com/css2?family=Abril+Fatface&display=block');

This works because each @import statement is wrapped in its own style tag.

In nextjs v12, @import statements are not wrapped in their own style tag.

Expected Behavior

I expect each @import statement is wrapped in its own style tag.

To Reproduce

In nextjs v12, add this to global.css

* {
    font-family: "Abril Fatface";
}

@import url('https://fonts.googleapis.com/css2?family=Abril+Fatface&display=block');

scott-coates avatar Dec 19 '21 01:12 scott-coates

The move to SWC does not affect .css files, can you provide a full reproduction?

timneutkens avatar Dec 20 '21 13:12 timneutkens

I've made a repo at https://github.com/scottccoates/nextjs-12-css-test.

To reproduce:

  1. cd next-10
  2. npm run dev
  3. Observe the sparkly font
  4. Stop the process
  5. cd ../next-12
  6. npm run dev
  7. The sparkly font is now gone

Observe the global.css in both folders. They are identical. But it does not work on Next v12. This is because in prior versions, @import statements (found in global.css) are "hoisted" into their own script tags.

scott-coates avatar Dec 21 '21 08:12 scott-coates

I can confirm this as an issue, but as Tim said, it's not related to SWC. (I tried adding a .babelrc file, and this still reproduces)

I could narrow it down to this change: https://github.com/vercel/next.js/compare/v11.1.3-canary.99...v11.1.3-canary.100

Most likely this PR #30165

Prior this, the generated CSS looked like this:

@import url(https://fonts.googleapis.com/css2?family=Emilys+Candy&display=block);

body,html {
    padding: 0;
    margin: 0;
    font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif
}

a {
    color: inherit;
    text-decoration: none
}

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    font-family: Emilys Candy
}

But after, it was left untouched:

body,html {
    padding: 0;
    margin: 0;
    font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif
}

a {
    color: inherit;
    text-decoration: none
}

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    font-family: Emilys Candy
}

@import url("https://fonts.googleapis.com/css2?family=Emilys+Candy&display=block");

balazsorban44 avatar Dec 21 '21 11:12 balazsorban44

#30567 and #30569 might be related

balazsorban44 avatar Dec 21 '21 11:12 balazsorban44

Interesting find, @balazsorban44, about the changed behavior with @import being hoisted to the top of the compiled file. Using the repro repo I was able to confirm that the font importing still works if:

  1. The import is placed in the CSS before the font is referenced.
  2. The SASS compiler is used to compile global as an scss file instead of a css one (sass apparently does the import-to-top hoisting itself).

zackdotcomputer avatar Dec 22 '21 11:12 zackdotcomputer

Per #30567:

Loading the font with next dev works but not with next build (next start).

I can confirm that importing fonts works in dev but not with next build. Generated styles in production are not valid.

scott-coates avatar Dec 28 '21 00:12 scott-coates

Interesting @scottccoates - it appeared to work for me with a production build in the cases I listed above. Do you mean it doesn't work in only the case of having @import below the font's usage in a plain .css file? Or does it fail for you in scss files and/or even if the import is above the font's usage?

zackdotcomputer avatar Dec 28 '21 00:12 zackdotcomputer

@zackdotcomputer We somehow hit the daily limit for deploys on vercel. I will have to wait but I'll get back to you asap.

scott-coates avatar Dec 28 '21 03:12 scott-coates

@zackdotcomputer I just realized I can run next build to view what the bundled css files will look like in production (it will be named something like .next/static/css/5a1c6eae982d3adb11d2.css).

After installing sass and changing my global.css to global.scss, it is still not working the way I expect.

Do you mean it doesn't work in only the case of having @import below the font's usage in a plain .css file? Or does it fail for you in scss files and/or even if the import is above the font's usage?

It fails regardless of where the @import statement occurs. I have this at the top of my global.scss file:

@import url('https://fonts.googleapis.com/css2?family=Work+Sans&display=block');
@import url('https://fonts.googleapis.com/css2?family=Yeseva%20One&display=block');

Yet after running next build, my bundled css file does not hoist these imports to the top.

If I go back to next 10 and run next build, I can see these fonts are hoisted to the top of my bundled css file.

See the screenshots for more detail.

Next 10

image

Next 12

image

scott-coates avatar Dec 28 '21 11:12 scott-coates

Update on previous comment:

I was importing bootstrap first in my _app.js file, that was causing my issue:

import 'bootstrap/dist/css/bootstrap.min.css';
import '../styles/global.scss';

If i switch the import order, making the global css file first, it works, even without scss.

scott-coates avatar Dec 28 '21 18:12 scott-coates

Confirming that @scottccoates mentioned above.

I had Tailwind definitions at the top of the file and then a font Google Font import afterwards like this

/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap");

Moving the imports as the top first thing on the file, solved the problem

/* ./styles/globals.css */
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap");

@tailwind base;
@tailwind components;
@tailwind utilities;

I also confirm this working correctly on Next 11

Hope this is useful.

garyvh2 avatar Jan 04 '22 20:01 garyvh2

Nah, moving @import ... around doesn't fix anything for me.

My CSS files used to have @imports at the top as seen here: https://guildplanner.pro/_next/static/css/4b3115bd6e9f5f53cd0c.css

But with NextJs 12, those leading @imports are gone now.

shehi avatar Jan 04 '22 21:01 shehi

Same problem with @import on the top

lucasoz avatar Feb 07 '22 18:02 lucasoz

I can confirm that this issue is still occurring with nextjs 12.1.0 production mode. Does anyone know any alternative way to load google fonts while using swcMinify?

Edit: The issue does not seem to be related to swc, and occurs even with swcMinify = false in nextjs 12+ versions; I worked around it in the following way:

  1. remove @import google fonts from the scss files
  2. new imports.css file that contains all the @import statements, which is imported first in _app.tsx This works with swc enabled too, production & dev versions.

kavadithya avatar Feb 20 '22 09:02 kavadithya

For font loading, we recommend leveraging our built-in font optimization: https://nextjs.org/docs/basic-features/font-optimization

balazsorban44 avatar Feb 20 '22 14:02 balazsorban44

Thanks @balazsorban44 - while it would still be great to get this working with CSS/SCSS inline imports, it's good to know that link tags in head not only should work as an alternative but also give some optimization.

zackdotcomputer avatar Feb 20 '22 21:02 zackdotcomputer

Inside of your purely JS/TS Next app, the guidance is to use @import in a separate stylesheet and then place a CSS import into the /pages/_app.js file: https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet

I'm sure there is a technical reason for all of this but doing a CSS import into a JS app feels kind of bizarre to me. But it does fix this issue.

jserrao avatar Jun 14 '22 11:06 jserrao

Really looks like the order of imports makes sense. Font @import started working after I have rearranged my scss imports.

andreynovikov avatar Oct 25 '22 11:10 andreynovikov

My font imports weren't working (using Emotion's Global component) and it was because I had them with comments before each one, removing comments and leaving only the imports at the start of the css solved this issue.

This works:

const globalCSS = `
  @import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700&display=swap');
  @import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap');

  (other global styles...)
`;

and then:

<Global styles={css`${globalCSS}`} />

franciscojs12 avatar Oct 26 '22 14:10 franciscojs12