Import CSS & CSS Modules

Steve Kinney

Steve Kinney


Check out a free preview of the full Vite course

The "Import CSS & CSS Modules" Lesson is part of the full, Vite course featured in this preview video. Here's what you'd learn in this lesson:

Steve discusses different options for pulling in CSS in a Vite project and how Vite resolves relative paths and bundles the CSS. They also demonstrate how to import CSS in JavaScript and explain how it can be useful for splitting out stylesheets. CSS modules and how to use them to scope styles to specific files is also covered in this segment.


Transcript from the "Import CSS & CSS Modules" Lesson

>> The site, it's fine. It looks a little ugly. Let's talk about some of the options for pulling in some CSS. We've got a few options and the most kinda basic standard one works the way that you think it does, which is we can pull in a link tag, all right?

We've got that, and again, it is relative. The nice part is, effectively, all this bundling and everything is happening from your index.html file, right? It like that is what the kind of the Vite entry point is. And Vite's looking at this index.html file and it's trying to resolve any of these relative paths to what your development setup is, and then kinda bundling it and building it all in place as well.

And it will actually do this for a bunch of things. Obviously, as you can see anything I put in the HTML, anything that I had in the JavaScript that obviously worked too, right? And then actually, if I use the URL function in CSS or anything along those lines, those will actually also be resolved.

So you don't necessarily have to have everything in your public folder, so on and so forth. You can kind of just import it as needed and not think about the fact that Vite exists at all and get all of this for free and everything kinda working the way that we would hope it works.

So this will work. As you can see, it's a little prettier now. I don't threaten to be a designer, but it's better, right, in this case. And there's a few other options. Let's go ahead and build it just to see, we'll see no surprises in here. We'll see exactly what we think, which is now we also have a CSS file.

Great, and again, you could build this over, and over, and over again. If the CSS file didn't change, that SHA will be the same, right? That means that if somebody has that cache, they won't have to refresh it, even if you change the JavaScript and redeploy that. It's based on the contents of the file.

So only the things that you change will have new hashes and effectively bust the cache in this case. So four, you're getting that also for free out of the box, which is pretty standard fare for a lot of these tools, but it's nice to have. And so we've got that in place.

There are a few other ways to do this. Another one is, and you've probably seen this from various other frameworks and build tools, is we can import it in our JavaScript as well. And the reason why this is powerful is we just saw in the last section that what we can do is split out our JavaScript based on how we import it, right?

And so any tag I put in HTML, well, yeah, it'll get loaded with HTML tag gets loaded. But if you have styles that are only needed for one particular file, then in that case, it will also be split out as well, right? And so, for instance, let's say, in counter, that's where I'm gonna choose to pull in the styles, right, as an initial, just to demonstrate a point.

So do import, and here we'll say, .styles or style.css, right? In effect, it looks the same, right? But if, for instance, I did not end up needing this file in some way, shape, or form, right, you'll see exactly that it won't load it until it's needed in this case.

So if we go to index at this point, let's just not pull in that counter logic. Did I not take the dugout? If you don't save files, they don't change, right? So now, it is only loaded when I have the counter in place. So if we go ahead and pull that back in, then it's loaded as needed, right?

And so when that file is loaded, then in that case, you get it. And so now, you can begin to split out your style sheets. And if you really wanted to.just to kind of add a little bit of depth to our understanding here, you could, I don't know why you would, but you could.

I have a terrible reason why you would that you shouldn't do, and I'm not gonna tell you. You can do a script, we'll say type=module, Where we can do import, style.css, and let me just go take it out of that counter now. That effectively does the same thing.

But at that point, you're like, well, I had to do that if I want it to get like pre-processed by Vite and have it go through post-CSS and all that stuff. No, you don't have to do that. If you just use the link tag, it will go through Vite.

Vite is going to look for basically every relative path in that index.html file. And if it knows what to do with it, you don't have to do the import syntax. That link would also process it through Vite the same way as we saw earlier. If you do have a use case, why you might choose to do that is if there's critical CSS that you wanted to have in a link tag that's immediately loaded, and then additional CSS that you know you're gonna want but you wanna preload it.

If there's a unique need that you need to solve for, this is totally a valid way to do it. And we'll look at some other ones at the very end. But generally speaking, this doesn't really give you that much different margin. All that will do, let's actually take a look at the DOM real quick.

All that statement does is it has Vite loaded, where it will go ahead and, in this case, it's gonna put it right here as well, right? So you've got both different options. Either works, and depending on what problem you're trying to solve for. Cool, so the other thing that Vite supports is CSS modules, right?

Cuz any file that we import in any of the various ways that I showed you, they will all be added globally, right? And this is where PostCSS gets involved a little bit, which is, you saw there's no difference in what's in that style CSS and what got loaded in the file itself.

And so what we could do, for instance, is the convention of Vite. This is, again, where we're now into the second super Vite-specific thing, which is, one, the relative path. But then, two, a different file extension for CSS you would like to treat as the CSS module. What is a CSS module?

A CSS module is basically, hey, I only want these scoped to just this file that I'm pulling in. I'm gonna apply the styles programmatically, make sure that they're not globally available. Let's take a look at that in practice, and I think it'll make it a little bit clearer.

So I could, for instance, I've got this counter CSS. We'll say, .count, and we'll say, I don't know, font-size is 6rem. Seems legit, and if I load it like this with just counter CSS, well, it's gonna be loaded globally. But if I rename the file, and I say .module.CSS, it's gonna behave a little bit differently now, right?

And what's gonna happen is I can go in to, let's say, counter, for instance. And we'll say, We can start with even import styles from counter.module.css. And I'm gonna start just by console logging that object for you. Right, and we'll go ahead and we'll take a look at that.

And you can see that it gave us an object. So styles.count, count being the name of the class, right, is now this funny, unique thing that is probably, technically globally available, but through obscurity not. And this is pretty common in this case. We actually believe we go into source and we look at this counter, right?

It is actually like going ahead, it has made it a JavaScript file. And you can see that it will, for instance, add this class into the DOM, but it will be uniquely scoped. So now, what we can do is we can go in here for the given countElement, for instance.

We could say, countElement.classList.add(styles.count). And if we look at that DOM node now, we'll go ahead and we'll see, scroll down a little bit. I could have collapsed that, go into the body, we'll find our count. You can see it's got that unique class put on it in this case.

So that's scoped directly. And all you had to do was use the dot module. And now, it'll be that scoping instead of a global style in this case and you get it for free. You can also go ahead and just do this as well. That's probably a problem, not too much, variable name but that will also be the same style in this case.

And so this will work in Vanilla JavaScript obviously, it will also work in React. Like a lot of the other frameworks have other ways to have scoped modules like Svelte, you just put a style tag in the module. I think Vue is the same thing, that Vite doesn't count, cuz I was talking about Vue.

And so different frameworks kinda have pieces of this built in, but guess how Vue and Svelte are doing it under the hood. They're using Vite, so technically accounts. But for React, which doesn't have a really great way to load CSS, you can use this technique and it will work flawlessly.

And they are dynamically loaded only when this module is loaded. So if you have it on a given React component or what have you, it will then be code split and only loaded as needed, and not in the full bundle. And everything will be split out, and you will have nice small little chunks to load in this case.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now