Styling Remix using Tailwind and PostCSS How to style a Remix app using Tailwind and PostCSS Saturday, December 4, 2021
TL;DR: Source and Demo 
Here's a live demo 
Link to the source code 
Link to step by step commits 
 
Introduction  
In my last blog post , 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.
Dependencies  
Installation 
sh Copy npm install -D autoprefixer postcss postcss-cli postcss-import tailwindcss cssnano 
OR if you prefer yarn
sh Copy yarn add -D autoprefixer postcss postcss-cli postcss-import tailwindcss cssnano 
Add scripts to package.json  
Add Script for CSS generation 
package.json Copy 1 2 3 4 5 6 7 "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 Copy app/styles/*.css 
Add Script for cleaning up build files 
package.json Copy 1 2 3 4 5 6 "scripts": {   // ...   "build": "npm run css:prod && remix build",   "prebuild": "rimraf ./public/build \"./app/styles/**/*.css\""   // ... }, 
Running the scripts 
Run npm run css:watch in one terminal and remix dev in another
sh Copy 1 npm run css:watch 
sh Copy 1 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 
sh Copy 1 npm run build 
If you are not a fan of multiple terminals, use concurrently to run css:watch and remix dev in parallel
package.json Copy 1 2 3 4 5 "scripts": {   // ...   "dev": "concurrently npm run css:watch && remix dev",   // ... } 
Tailwind and App styles presets  
Tailwind styles 
We need to explicitly declare the features we want to use in our CSS.
Here's a reference  of what you can use.
styles/tailwind.css Copy 1 2 3 4 @tailwind  base ; @tailwind  components ; @tailwind  utilities ; @tailwind  screens ; 
App CSS presets 
Some CSS defaults I prefer
styles/app.css Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 :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 ;    } } 
PostCSS and Tailwind configuration  
PostCSS Config File 
postcss.config.js Copy 1 2 3 4 5 6 7 8 9 10 11 module . exports   =   {    plugins :   [      require ( "tailwindcss" ) ,      require ( "autoprefixer" ) ,      require ( "postcss-import" ) ,     process . env . NODE_ENV   ===   "production"   &&        require ( "cssnano" ) ( {          preset :   "default" ,        } ) ,    ] , } ; 
Tailwind Config File 
tailwind.config.js Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 } ; 
Integrating styles in Remix Code  
Add a reference of the generated CSS files using links in app/root.tsx
app/root.tsx Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // ... 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 ,      } ,    ] ; } ; // ... 
Styling a component  
Use Tailwind, as usual; add Tailwind's class names added inside the className prop.
app/components/word-form/index.tsx Copy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 .
 
VSCode Plugins  
Here are some plugins that you can use to get a better experience using Tailwind and PostCSS in VSCode.
Conclusion  
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.
If you find this useful and you want to support me
Get latest updates directly into your mailbox.
Connect with me