TailwindCSS with Yew

We’ve looked at using Yew with Rust in my post WASM with Rust (and Yew), let’s take things a step further by adding TailwindCSS with Yew.

  • Install the tailwind-cli
  • In your root folder run
    npm init 
    

    just select defaults for to get things started

  • Now let’s install tailwindcss using
    npm install --save-dev tailwindcss
    
  • Ensure tailwindcss cli is installed to your package using
    npm install tailwindcss @tailwindcss/cli
    
  • You can actually remove pretty much everything from the package.json file, mine looks like this
    {
      "dependencies": {
        "@tailwindcss/cli": "^4.1.12"
      }
    }
    
  • Create a styles folder and place the tailwind.css file into it
  • Change the tailwind.config.js to look like this
    /** @type {import('tailwindcss').Config} */
    module.exports = {
        content: [
            "./index.html",
            "./src/**/*.rs"
        ],
        theme: {
            extend: {},
        },
        plugins: [],
    }
    

Using Tailwindcss

We’re going to want to use the tailwindcss CLI to generate our application’s .css file into the ./dist folder otherwise our application will not be able to use it, but we’ll leave that step until a little later. For now let’s start by manually running the CLI to generate our .css file, which we’ll store in our root application folder.

Run the following command from the terminal

npx @tailwindcss/cli -i ./styles/tailwind.css -o ./app.css

This generates an app.css file, but actually it’s not a lot of use at this point, however you can take a look at what’s generated by tailwindcss CLI in this app.css file.

What we really want to do is, as part of the build, is generate the file and then reference it from our application.

As we’re using trunk from our previous posts, we can use the trunk hooks to generate the file. I’m running on Windows so will use powershell to run the command. Open the Trunk.toml file (I’ll include my whole file below) and add the [[hooks]] section where we will create a pre-build step that generates the app.css in the application root as we did manually

[serve]
address = "127.0.0.1"
port = 8080

[[hooks]]
stage = "pre_build"
command = "powershell"
command_arguments = ["-Command", "npx" , "@tailwindcss/cli -i ./styles/tailwind.css -o ./app.css"]

However, as mentioned already we need this file in the ./dist folder. Copying the file is no use as we need to link to the file in our index.html file.

We do this by creating the link to our app.css but marking it so that trunk will generate the file in the ./dist folder for us. Here’s the index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>My WASM App</title>
    <link data-trunk rel="sass" href="index.scss" />
    <link data-trunk rel="css" href="app.css" />
  </head>
  <body></body>
</html>

Note the use of data-trunk for both our app.css but also the default created index.scss.

Using the TailwindCSS in our code

We’ve now got the .css file generated as part of the trunk build and the file copied to ./dist so now we can use the CSS classes in our code, so here’s an example of a simply layout

use yew::prelude::*;

#[function_component(Layout)]
pub fn counter() -> Html {
  html! {
    <div class="min-h-screen w-screen flex flex-col">
      <nav class="bg-red-800 text-white px-6 py-4 flex justify-between items-center">
        <div class="text-base font-semibold">{ "My Application"  }</div>
        <ul class="flex space-x-6">
          <li><a href="#" class="hover:text-gray-300 text-base">{"Home"}</a></li>
          <li><a href="#" class="hover:text-gray-300 text-base">{"About"}</a></li>
          <li><a href="#" class="hover:text-gray-300 text-base">{"Services"}</a></li>
          <li><a href="/counter" class="hover:text-gray-300 text-base">{"Counter"}</a></li>
        </ul>
     </nav>
     <main class="flex-grow bg-gray-100 p-6">
       <p class="text-gray-700">{"Content foes here"}</p>
     </main>
   </div>
  }
}

Here’s an example of the counter code

use yew::prelude::*;

#[function_component(Counter)]
pub fn counter() -> Html {
    let counter = use_state(|| 0);
    let on_add_click = {
        let c = counter.clone();
        move |_| { c.set(*c + 1); }
    };

    let on_subtract_click = {
        let c = counter.clone();
        move |_| { c.set(*c - 1); }
    };

    html! {
        <div>
            <button onclick={on_add_click}
                style="width: 100px;"
                class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    { "+1" }</button>
            <p style="text-align: center">{ *counter }</p>
            <button onclick={on_subtract_click}
                style="width: 100px;"
                class="bg-emerald-500 hover:bg-emerald-700 text-white font-bold py-2 px-4 rounded">
                    { "-1" }</button>
        </div>
    }
}