· 5 min read Posted by Gustavo Fão Valvassori

Using CSS libraries with Compose for Web

Using third-party styles have a few gotchas. Here are some tips and tricks to help you out.
Brett Jordan - https://unsplash.com/pt-br/fotografias/fotografia-time-lapse-do-homem-fazendo-truque-de-skate-_DCMGTbgXuU
Credit: Brett Jordan - https://unsplash.com/pt-br/fotografias/fotografia-time-lapse-do-homem-fazendo-truque-de-skate-_DCMGTbgXuU

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:

  1. Use a CDN and include it in your HTML file;
  2. Add the CSS file to your resources folder and include it in your HTML file;
  3. 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!