DEV Community

Cover image for Replacing Twind with Tailwind in Fresh
Ondřej Tuček
Ondřej Tuček

Posted on • Edited on

Replacing Twind with Tailwind in Fresh

Update FRESH 1.6

Since the latest release of fresh (1.6), you can actually use the official Tailwind CSS package in NPM and so unless you want a solution that does not include node_modules/ folder, you can consider this article obsolete.

Intro

Greetings, fellow human beings 👋
Today, I would like to share my experience with fresh and why you should replace the styling from twind -> taiwlind-cli.

What is fresh?

Fresh is "The next-gen web framework", which could be translated to "Hey! I am cool deno-based SSR framework". Fresh, instead of using react uses preact, which is tiny(3kb) alternative to regular-sized react. Preact, just like react, uses .jsx / .tsx files and syntax and is compatible with react-based libraries.

Cool! But what is twind, and why should we care? Well, twind is a tiny implementation of tailwind css in javascript. Awesome, so what is the problem? Why not stick with it? The answer to this is simple. Currently, they don't have extension for vs-codium and their extension for regular vs-code is not working. There are hacky fixes, but why should I care if I can use regular old tailwind? (I know it's against deno's whole purpose, but I guess one exception is fine?)

Before we continue...

Before we continue any further, it's useful to mention, that I have prepared a template repository for all of you, who are just starting out with fresh and are not looking to replace the twind, but rather make a brand-new project.

Without any further a do, here is the link.

Replacing twind in existing code-base

So you already have a fresh project and looking for a way to replace the twind? Well I have a good news, look no further as I will show the way.

Step 1. Removing it

First, we need to remove it from your fresh.json file as our dependency.

{
  "lock": false,
  "tasks": {
    "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
    "start": "deno run -A --watch=static/,routes/ dev.ts",
    "build": "deno run -A dev.ts build",
    "preview": "deno run -A main.ts",
    "update": "deno run -A -r https://fresh.deno.dev/update ."
  },
  "lint": {
    "rules": {
      "tags": [
        "fresh",
        "recommended"
      ]
    },
    "exclude": [
      "_fresh"
    ]
  },
  "fmt": {
    "exclude": [
      "_fresh"
    ]
  },
  "imports": {
    "$fresh/": "https://deno.land/x/fresh@1.4.2/",
    "preact": "https://esm.sh/preact@10.15.1",
    "preact/": "https://esm.sh/preact@10.15.1/",
    "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.1",
    "@preact/signals": "https://esm.sh/*@preact/signals@1.1.3",
    "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",

    -------- REMOVE THESE LINES ---------------
    "twind": "https://esm.sh/twind@0.16.19",
    "twind/": "https://esm.sh/twind@0.16.19/",
    -------------------------------------------
    "$std/": "https://deno.land/std@0.193.0/"
  },
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}
Enter fullscreen mode Exit fullscreen mode

Great! Now we got rid of it as dependency. As a next step, you should also remove the twind.config.js file, as we won't be using it anymore.

By removing the config file, we need to update our main.ts file to following:

/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />

import "$std/dotenv/load.ts";

import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";

// Remove this import statemet
import config from "./fresh.config.ts";

// Replace following line with just: await start(manifest);
await start(manifest, config);
Enter fullscreen mode Exit fullscreen mode

Awesome! Next, we need to update the fresh config file of the twind removal.

// original
import { defineConfig } from "$fresh/server.ts";
import twindPlugin from "$fresh/plugins/twind.ts"
import twindConfig from "./twind.config.ts";
export default defineConfig({
  plugins: [twindPlugin(twindConfig)]
});

// ---------------------------------------

// updated
import { defineConfig } from "$fresh/server.ts";

export default defineConfig({
  plugins: [],
});
Enter fullscreen mode Exit fullscreen mode

Voilà! Now are got ourselves fresh project without any styling.

Step 2. Installing tailwind

So now we have a fresh project running, but it looks like garbage, or at least as HTML without any CSS. To fix this, we need to install tailwind CSS.

Fetching the binary file

To continue, we need to fetch ourselves a copy of tailwind binary. Here is a link to the release page. Download the copy for your platform and rename it to just tailwindcss. We will use this binary to transpile our classes into a regular old CSS file.

Now, we need to update our start task in deno.json > tasks.

{
  "lock": false,
  "tasks": {
    "start": "tailwindcss -i ./static/styles/input.css -o ./static/styles/tailwind.css --minify --watch & deno run -A --watch=static/,routes/ dev.ts",
   },
...rest of the deno.json code
Enter fullscreen mode Exit fullscreen mode

Alright! We are half way done. Now we have to create tailwind.config.cjs file with following code:

/** @type {import('https://esm.sh/tailwindcss@3.1.8').Config} */
module.exports = {
  content: [
    "./routes/**/*.{tsx,ts}",
    "./islands/**/*.{tsx,ts}",
    "./components/**/*.{tsx,ts}",
  ],
  theme: {
    extends: {},
  },
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode

Once that is done, we need to create a new file in /static/styles/input.css and give it the following code:

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

@layer components {
}
Enter fullscreen mode Exit fullscreen mode

Linking the styling

Update the _app.tsx located at /routes of the following component:

import { Head } from "$fresh/runtime.ts";
import { AppProps } from "$fresh/src/server/types.ts";

export default function App(props: AppProps) {
  const { Component } = props;

  return (
    <html>
      <Head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
        <title>Fresh project | starter code</title>
        <link rel="icon" type="image/png" href="../favicon.ico"></link>
        <link rel="stylesheet" href="../styles/tailwind.css" />
      </Head>
      <body className="antialiased">
        <Component />
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Ta Da! Your application should now be styled by the original Tailwind CSS. If you have any questions, don't be afraid to ask. If you want to see a deployment ready app, check out the template repository in the second paragraph.

Top comments (5)

Collapse
 
alanvncs profile image
Alan Vinicius Silva • Edited

Nice tutorial! Thanks!

I found a way to do it without using the tailwindcss binary:
As you may know, Deno supports npm modules (at least Deno 1.23.1), so it is possible to run tailwindcss directly from command line.

deno run -A npm:tailwindcss -i ./static/styles/input.css -o ./static/styles/tailwind.css --minify --watch & deno run -A --watch=static/,routes/ dev.ts
Enter fullscreen mode Exit fullscreen mode
  • deno run works like npx: Run a module with no need to install it
  • -A gives it permission to access local files (cache)
Collapse
 
asqit profile image
Ondřej Tuček

Thank you for your addition. With the recent release of Fresh, you can actually use NPM's version of tailwind CSS, just like you did with your implementation.

Collapse
 
rawestmoreland profile image
Richard W

If i want to deploy this production, I assume I would need the tailwind-cli installed somewhere? Have you deployed this to production?

Collapse
 
asqit profile image
Ondřej Tuček

I have deployed my portfolio website with this template. You actually don't need the CLI for production (put it to .gitignore for all I care), as what it only does is it compiles your classes to a regular .css file in /static/styles/tailwind.css.

In the last code example, there is a _app.tsx which basically states how our HTML should be rendered. In it, you find a regular HTML link to the compiled tailwind.css.

Collapse
 
rawestmoreland profile image
Richard W

Makes sense. Thanks!