A lightweight, framework-agnostic Tailwind CSS build pipeline with fully offline Font Awesome support, no CDNs, no runtime dependencies, no surprises in production.
Built to drop into the root of any backend project (Django, Go, PHP, or anything else that serves HTML), and get out of your way.
- ✅ Tailwind CSS v3 with full tree-shaking (only the classes you use are included)
- ✅ Offline Font Awesome v7, icons load without any internet connection
- ✅ Works with any backend: Django, Go, PHP, plain HTML, you name it
- ✅ One-command setup script (Linux/macOS and Windows)
- ✅ Development watch mode + minified production builds
- ✅ Isolated local test preview (no backend needed)
- ✅ Clean, minimal pipeline, no webpack, no vite, no magic
tailwind-build-pipeline/ ← lives in your project root (see note below)
┣ node_modules/
┣ src/
┃ ┗ styles.css ← your CSS entry point
┣ .gitignore
┣ index.html ← local preview + test page
┣ LICENSE
┣ package.json
┣ package-lock.json
┣ postcss.config.js
┣ README.md
┣ setup.sh ← setup script for Linux/macOS
┣ setup.bat ← setup script for Windows
┗ tailwind.config.js
And after setup, your static folder (one level up) will look like:
../static/
┣ css/
┃ ┗ tailwind.min.css ← generated CSS, link this in your HTML
┗ webfonts/ ← Font Awesome icon fonts (copied by setup script)
┣ fa-brands-400.woff2
┣ fa-solid-900.woff2
┗ ... (all other font files)
This pipeline is designed to live in the root of your project, as a sibling to your static folder. The build scripts output CSS to ../static/css/ and fonts go to ../static/webfonts/.
A typical Django project layout with this pipeline would look like:
my_django_project/
┣ tailwind-build-pipeline/ ← this repo goes here
┣ static/
┃ ┣ css/
┃ ┃ ┗ tailwind.min.css
┃ ┗ webfonts/
┣ templates/
┣ my_app/
┣ manage.py
┗ ...
A Go or PHP project would follow the same pattern, the pipeline sits next to wherever your static/ or public/ folder is.
Tip: You can rename the folder from
tailwind-build-pipelineto anything you like, such astw/orassets/. Just keep it at the same level as your static folder.
git clone https://github.com/brandnova/tailwind-build-pipeline.git
cd tailwind-build-pipelineOr if you're adding it to an existing project, just drop the folder in next to your static/ directory.
npm installThis installs Tailwind CSS, PostCSS, Autoprefixer, Font Awesome, and the typography plugin.
Linux / macOS:
chmod +x setup.sh
bash setup.shor via npm:
npm run setup:linWindows:
setup.bator via npm:
npm run setup:winThe setup script handles everything in one pass:
- Checks that
node_modulesis present (runsnpm installif not) - Creates
../static/css/if it doesn't exist - Creates
../static/webfonts/if it doesn't exist - Copies Font Awesome webfonts from
node_modules/@fortawesome/fontawesome-free/webfonts/into../static/webfonts/ - Runs an initial Tailwind build against
index.htmlso your CSS file exists before you start your server
After running it, your ../static/ folder will be ready to serve.
<link rel="stylesheet" href="/static/css/tailwind.min.css">Or however your project handles static linking.
That's it. You're ready to build.
| Command | What it does |
|---|---|
npm run setup:lin |
One-time setup: creates folders, copies webfonts, runs initial build |
npm run setup:win |
Same as above, for Windows |
npm run dev |
Watch mode: rebuilds CSS on every file change |
npm run build |
Production build: minified, tree-shaken CSS |
npm run test |
Local preview build: scans index.html only, outputs to ./dist/ |
During active development, run:
npm run devThis watches your project files for changes and rebuilds ../static/css/tailwind.min.css automatically whenever you add, remove, or change Tailwind classes in your templates.
Important: Tailwind only knows about files you tell it about. Make sure your template paths are configured correctly in tailwind.config.js (see the Configuration section below). If a class isn't in any scanned file, it won't appear in the output CSS.
Before deploying, run:
npm run buildThis generates a minified, fully tree-shaken CSS file, only the Tailwind classes you actually used are included. The output is a single file at ../static/css/tailwind.min.css.
Commit this file to your repository (or generate it as part of your CI/CD pipeline) so your server doesn't need Node.js to serve the styles.
To test the pipeline in isolation, without running a backend at all:
npm run testThis:
- Scans
index.htmlfor Tailwind classes and Font Awesome icons - Generates a minimal CSS file at
./dist/tailwind.min.css - Copies
index.htmlinto./dist/index.html
Open ./dist/index.html directly in your browser. If the button is blue and the icons render, everything is working.
(Note: Icons might not render because the
webfontsfolder does not exist in your./distdirectory.webfontsneed to be in the same level as the parent folder containing your minified CSS file for fonts to work)
Open tailwind.config.js and edit the content array to include your template files:
content: [
"./index.html",
"../templates/**/*.html", // Django HTML templates
"../**/*.php", // PHP files
"../templates/**/*.html", // Go templates
"../src/**/*.{js,jsx,ts,tsx}", // React components
],Tailwind scans these files at build time and removes any class that doesn't appear in them. If a class is missing from your output CSS, it's almost always because the file containing it isn't listed here.
Extend the theme in tailwind.config.js:
theme: {
extend: {
colors: {
brand: {
DEFAULT: '#4F46E5',
light: '#818CF8',
dark: '#3730A3',
},
},
fontFamily: {
display: ['"Playfair Display"', 'serif'],
},
spacing: {
'18': '4.5rem',
'128': '32rem',
},
},
},Dark mode is pre-configured to use the class strategy. Add the dark class to your root <html> element to activate it:
<html class="dark">You can toggle it with a bit of JavaScript:
document.documentElement.classList.toggle('dark');In your templates, use dark: variants:
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-white">
...
</div>Font Awesome is bundled from node_modules and served entirely offline. Use icons the same way you always have:
<!-- Solid icons -->
<i class="fas fa-lock"></i>
<i class="fas fa-check"></i>
<i class="fas fa-arrow-right"></i>
<!-- Regular icons -->
<i class="far fa-envelope"></i>
<!-- Brand icons -->
<i class="fab fa-github"></i>
<i class="fab fa-twitter"></i>
<!-- Sizing -->
<i class="fas fa-star text-xl"></i>
<i class="fas fa-star text-2xl text-yellow-400"></i>Why offline? CDN-loaded Font Awesome can fail in restricted environments, slow connections, or offline-first PWAs. This pipeline bakes the fonts directly into your static folder so they always load, no internet required.
Font Awesome's CSS references icon font files (.woff2, .ttf, etc.) using a relative path that resolves to ../webfonts/ relative to where the CSS file is served from.
Because your CSS is served from /static/css/tailwind.min.css, the fonts must be at /static/webfonts/, exactly where the setup script puts them.
If icons are rendering as blank squares or tofu characters, it almost always means the webfonts folder is missing or not being served. Double-check that:
../static/webfonts/exists and contains.woff2files- Your backend is configured to serve the
static/directory - You've run the setup script (or manually copied the fonts)
Make sure STATICFILES_DIRS or STATIC_ROOT is configured to pick up your static/ folder. In settings.py:
import os
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]During development, Django's runserver serves static files automatically. For production, run collectstatic and serve the files via your web server (Nginx, etc.) or a storage backend.
Serve your static/ folder as a file server:
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))Point your web server's document root at the parent directory and ensure static/ is accessible. Reference the CSS normally:
<link rel="stylesheet" href="/static/css/tailwind.min.css">The setup script is safe to run multiple times. It will overwrite the webfonts with a fresh copy and regenerate the CSS. Run it again whenever you:
- Clone the project onto a new machine
- Delete and reinstall
node_modules - Upgrade Font Awesome to a new version
- Do not use the Font Awesome CDN alongside this setup. The CDN version will conflict with the offline version. Remove any
<link>tocdnjs.cloudflare.comorkit.fontawesome.com. - Commit
../static/css/tailwind.min.cssto your repository so it's available on your server without needing Node.js installed. - Do not commit
node_modules/, it's gitignored. Anyone cloning the project runsnpm installthenbash setup.sh. - The
dist/folder generated bynpm run testis also gitignored. It's for local previewing only. - If you rename the
tailwind-build-pipelinefolder, the relative paths (../static/) still resolve correctly as long as it remains a direct sibling of yourstatic/folder.
my_project/
┣ tailwind-build-pipeline/
┃ ┣ node_modules/
┃ ┣ src/
┃ ┃ ┗ styles.css
┃ ┣ dist/ ← test builds (gitignored)
┃ ┣ .gitignore
┃ ┣ index.html
┃ ┣ package.json
┃ ┣ postcss.config.js
┃ ┣ setup.sh
┃ ┣ setup.bat
┃ ┗ tailwind.config.js
┣ static/
┃ ┣ css/
┃ ┃ ┗ tailwind.min.css ← link this in your HTML
┃ ┗ webfonts/ ← Font Awesome fonts (auto-copied by setup)
┗ ... (your backend files)
MIT, free for personal and commercial use. See LICENSE.