Styling Remix using Tailwind and PostCSS
Tags: javascript, react, remix, webdev
Published: 2021-12-04 04:46:25

## Table of Contents
  * <a href="#tldr" target="_self">TL;DR: Source and Demo</a>
  * <a href="#introduction" target="_self">Introduction</a>
  * <a href="#dependencies" target="_self">Dependencies</a>
  * <a href="#update-scripts" target="_self">Add scripts</a>
  * <a href="#styles-presets" target="_self">Styles Presets</a>
  * <a href="#styles-config" target="_self">PostCSS and Tailwind config</a>
  * <a href="#remix-integration" target="_self">Remix Integration</a>
  * <a href="#sample-usage" target="_self">Tailwind sample usage</a>
  * <a href="#vscode-plugins" target="_self">VSCode Plugins</a>
  * <a href="#conclusion" target="_self">Conclusion</a>

> ### TL;DR: Source and Demo<a name="tldr"></a>
> Here's a live [demo](https://playground-jb6w7fn95-codegino.vercel.app/words/add)
> Link to the [source code](https://github.com/codegino/playground/tree/part3/styling-tailwind)
> Link to [step by step commits](https://github.com/codegino/playground/commits/part3/styling-tailwind)

<br />

### Introduction <a name="introduction"></a>

In my last [blog post](https://dev.to/codegino/remix-styling-using-vanilla-css-4g6b), I discussed how to style a Remix app using Vanilla CSS. This blog will show how to integrate Tailwind and PostCSS into our Remix app.

<br />

### Dependencies <a name="dependencies"></a>

- [`autoprefixer`](https://github.com/postcss/autoprefixer)
- [`postcss`](https://github.com/postcss/postcss)
- [`postcss-cli`](https://github.com/postcss/postcss-cli)
- [`postcss-import`](https://github.com/postcss/postcss-import)
- [`tailwindcss`](https://github.com/tailwindlabs/tailwindcss)
- [`cssnano`](https://github.com/cssnano/cssnano)


#### Installation

```sh
npm install -D autoprefixer postcss postcss-cli postcss-import tailwindcss cssnano
```

OR if you prefer `yarn`

```sh
yarn add -D autoprefixer postcss postcss-cli postcss-import tailwindcss cssnano
```

### Add scripts to `package.json` <a name="update-scripts"></a>

<br />

#### Add Script for CSS generation

```json
// package.json
"scripts": {
  // ...
  "css:watch": "npm run css:build -- --watch",
  "css:build": "postcss styles/**/*.css --dir app/styles",
  "css:prod": "npm run css:build -- --env production",
  // ...
},
```

> Replace `npm run` with `yarn` if you prefer to use yarn

I don't want to commit those generated CSS files to the repo, so I'll be adding them to `.gitignore`

```.gitignore
app/styles/*.css
```


#### Add Script for cleaning up build files


```json
// package.json
"scripts": {
  // ...
  "build": "npm run css:prod && remix build",
  "prebuild": "rimraf ./public/build \"./app/styles/**/*.css\""
  // ...
},
```

<br />

#### Running the scripts

- Development

Run `npm run css:watch` in one terminal and `remix dev` in another

```sh
npm run css:watch
```

```sh
npm run dev
```

> DISCLAIMER: Don't expect it will work immediately. We still need to configure a few things with Tailwind and PostCSS.

#### OPTIONAL: Run multiple scripts in a single command

- Production

```sh
npm run build
```

If you are not a fan of multiple terminals, use `concurrently` to run `css:watch` and `remix dev` in parallel

```json
// package.json
"scripts": {
  // ...
  "dev": "concurrently npm run css:watch && remix dev",
  // ...
}
```

<br />

### Tailwind and App styles presets <a name="styles-presets"></a>

<br />

#### Tailwind styles

We need to explicitly declare the features we want to use in our CSS.
Here's a [reference](https://tailwindcss.com/docs/functions-and-directives#tailwind) of what you can use.

```css
/* styles/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind screens;
```

#### App CSS presets
Some CSS defaults I prefer

```css
/* styles/app.css */
:root {
  --color-primary-light: hsl(210, 100%, 98%);
  --color-primary-100: hsl(210, 100%, 95%);
  --color-primary-200: hsl(210, 100%, 85%);
  --color-primary-300: hsl(210, 100%, 80%);
  --color-primary-400: hsl(210, 100%, 75%);
  --color-primary-500: hsl(210, 100%, 60%);
  --color-primary-600: hsl(210, 100%, 50%);
  --color-primary-700: hsl(210, 100%, 40%);
  --color-primary-800: hsl(210, 100%, 30%);
  --color-primary-900: hsl(210, 100%, 20%);
  --color-primary-dark: hsl(210, 100%, 2%);
}

input,
select,
textarea {
  @apply text-black;
}

@media (prefers-color-scheme: dark) {
  html {
    @apply bg-black text-white;
  }
}
```

<br/>

### PostCSS and Tailwind configuration <a name="styles-config"></a>

<br/>

#### PostCSS Config File

```js
// postcss.config.js
module.exports = {
  plugins: [
    require("tailwindcss"),
    require("autoprefixer"),
    require("postcss-import"),
    process.env.NODE_ENV === "production" &&
      require("cssnano")({
        preset: "default",
      }),
  ],
};
```

<br />

#### Tailwind Config File

```js
// tailwind.config.js
module.exports = {
  mode: process.env.NODE_ENV ? "jit" : undefined,
  // To purge CSS in .ts .tsx files
  purge: ["./app/**/*.{ts,tsx}"], 
  darkMode: "media", // Use media queries for dark mode
  theme: {
    extend: {
      colors: {
        // color scheme is defined in /app.css
        // To enable text-primary-xxx, bg-primary-xxx, or border-primary-xxx
        primary: {
          light: "var(--color-primary-light)",
          100: "var(--color-primary-100)",
          200: "var(--color-primary-200)",
          300: "var(--color-primary-300)",
          400: "var(--color-primary-400)",
          500: "var(--color-primary-500)",
          600: "var(--color-primary-600)",
          700: "var(--color-primary-700)",
          800: "var(--color-primary-800)",
          900: "var(--color-primary-900)",
          dark: "var(--color-primary-dark)",
        },
      },
    },
  },
  variants: {}, // activate any variant you want here
  plugins: [], // add any plugin you need here
};
```

<br />

### Integrating styles in Remix Code <a name="remix-integration"></a>

Add a reference of the generated CSS files using `links` in `app/root.tsx`

```tsx
// app/root.js
// ...
import type { LinksFunction } from "remix";
import tailwindStyles from "~/styles/tailwind.css";
import appStyles from "~/styles/app.css";

export let links: LinksFunction = () => {
  return [
    { rel: "stylesheet", href: tailwindStyles },
    {
      rel: "stylesheet",
      href: appStyles,
    },
  ];
};
// ...
```

<br />

### Styling a component <a name="sample-usage"></a>

Use Tailwind, as usual; add Tailwind's class names added inside the `className` prop.

```tsx
//app/components/word-form/index.tsx
import { Form, useTransition } from "remix";
import { Word, WordType } from "~/models/word";
import { Button } from "../basic/button";
import { Input } from "../basic/input";
import { Select } from "../basic/select";
import { TextArea } from "../basic/textarea";

export function WordForm({ word }: { word?: Word }) {
  let transition = useTransition();

  return (
    <Form
      method="post"
      className={`
        px-3 py-4 rounded flex flex-col gap-2 border-2
      `}
    >
      <div>Form State: {transition.state}</div>
      <div>
        <label className="block text-xs" htmlFor="name">
          Word
        </label>
        <Input
          id="name"
          name="name"
          type="text"
          placeholder="Word"
          required
          defaultValue={word?.name ?? ""}
          disabled={Boolean(word?.name)}
        />
      </div>
      <div>
        <label className="block text-xs" htmlFor="type">
          Type
        </label>
        <Select
          id="type"
          name="type"
          defaultValue={word?.type ?? WordType.NOUN}
        >
          <option value={WordType.NOUN}>Noun</option>
          <option value={WordType.VERB}>Verb</option>
          <option value={WordType.ADJECTIVE}>Adjective</option>
        </Select>
      </div>
      {/*TextAreas*/}
      <Button type="submit" color="primary">
        Submit
      </Button>
    </Form>
  );
}
// ...
```

> If you're wondering where the above file came from, that is from my last [blog post](https://dev.to/codegino/remix-styling-using-vanilla-css-4g6b#reusable-component-style).


<br />

### VSCode Plugins <a name="vscode-plugins"></a>
Here are some plugins that you can use to get a better experience using Tailwind and PostCSS in VSCode.

- [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss)
- [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)

<br />

## Conclusion <a name="conclusion"></a>
Integrating Tailwind and PostCSS in Remix is straightforward as we don't need to hack into the framework to make them work. We quickly achieved an extendable and customizable CSS generation boilerplate by adding a few configurations.