· 5 min read Posted by Gustavo Fão Valvassori
Using CSS libraries with Compose for Web
We have already learned how to create our own components and how to style them. But we didn’t cover some cases you may face when styling your components. In this post, we will cover how to import third-party styles, use them, and modify them using only compose.
Including CSS files
CSS libraries are very common in web development. With a quick search, you can find many different libraries that you can use to style your applications. But how can you import and use them on Compose?
Basically, there are three ways of doing this, but the first two are very similar. You can:
- Use a CDN and include it in your HTML file;
- Add the CSS file to your resources folder and include it in your HTML file;
- Use an NPM package;
CDN
CDN stands for Content Delivery Network. It is a server network that hosts files and delivers them to the user. It allows you to use hosted files without downloading and serving them yourself. Usually, CDN provides caching and other features that can improve your application performance.
Most public CSS libraries offer a way to download the styles from a CDN. With the stylesheet link, you can just
add the <link>
tag to your HTML file, and you are good to go.
For example, if you check for 98.css
library, you will notice the following code block on their documentation:
<link
rel="stylesheet"
href="https://unpkg.com/98.css"
>
This is the css file for the library. You can just copy this link and add it to your HTML file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Compose Web</title>
<link rel="stylesheet" href="https://unpkg.com/98.css">
</head>
<body>
<div id="root"></div>
<script src="mine-sweeper.js"></script>
</body>
</html>
And voilà, the style is imported and ready to be used.
Local CSS Files
The second way of importing is similar to the first but with one extra step. This strategy consists of downloading and adding the CSS file to your project resource dir. This is useful when you want to ensure you are using a specific library version. For that, just download the CSS file, add it to your resources folder, and include it on your HTML file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Compose Web</title>
<link rel="stylesheet" href="98.css">
</head>
<body>
<div id="root"></div>
<script src="mine-sweeper.js"></script>
</body>
</html>
Like the previous example, now your styles are ready to be used.
NPM
The last solution is more complicated but can be helpful in some cases. This strategy consists of using NPM to manage your styles. This is useful when you want to avoid manually managing your dependencies and updating CSS files. NPM will handle the versioning and dependencies for you.
The first step to use style dependencies from NPM is to enable both CSS and SCSS on Webpack. This will allow the compiler to include the CSS styles on your bundle. For that, Gradle has a helper function that can be called when you are configuring the JS target. To enable it, change your build.gradle.kts file adding the following lines:
kotlin {
js(IR) {
useEsModules()
browser {
commonWebpackConfig {
cssSupport { enabled.set(true) } // Add this
scssSupport { enabled.set(true) } // Add this
}
}
binaries.executable()
}
}
After that, you can include the libraries in your dependencies in the JsMain source set using the npm()
function:
kotlin {
sourceSets {
val jsMain by getting {
dependencies {
implementation(compose.html.core)
implementation(compose.runtime)
implementation(npm("98.css", "0.1.20"))
}
}
}
}
Last, you must import it on your main function. Unfortunately, kotlin does not provide a require()
function, so you
need to declare it yourself (You can copy from the example below).
// Declare require function
external fun require(module: String): dynamic
fun main() {
require("98.css/dist/98.css")
renderComposable(rootElementId = "root") {
Text("Hello World")
}
}
And that’s it. Now your styles are available for you.
Using imported styles
Now that your CSS style is imported, you can start using it on your components. But how can you do that? Let’s start by creating a wrapper for the Window object.
@Composable
fun Window(
attrs: AttrBuilderContext<HTMLDivElement>? = null,
content: ContentBuilder<HTMLDivElement>,
) {
Div(
attrs = {
attrs?.invoke(this)
classes("window")
},
content = content
)
}
As you can see, we are adding the window
class to the div. That looks fine for a single component, but what if we need
to use the same class on multiple components? Or what should we do when we need to modify this style?
A simple trick would be to create a StyleSheet
object, declare your classes as constant values, and use the init function
to change them. For example:
object NineEightCSS : StyleSheet() {
const val window = "window" // Declare the class name
init {
className(window) style {
property("inline-size", "min-content")
position(Position.Absolute)
top(50.percent)
left(50.percent)
transform { translate(-50.percent, -50.percent) }
}
}
}
fun main() {
renderComposable(rootElementId = "root") {
Style(NineEightCSS)
App()
}
}
@Composable
fun Window(
attrs: AttrBuilderContext<HTMLDivElement>? = null,
content: ContentBuilder<HTMLDivElement>,
) {
Div(
attrs = {
attrs?.invoke(this)
classes(NineEightCSS.window) // Using declared constant
},
content = content
)
}
This simple trick helps you solve two problems and makes your code more readable.
Final thoughts
In this post, we learned a few gotchas you may face when using third-party styles. We learned how to import them using CDN, local files, and NPM. We also learned how to use them on our components and modify them using Compose.
In the next post, we will talk about effects and how to listen to events on your components. Stay tuned!