feat: add Tailwind CSS configuration and build setup
- Add package.json with Tailwind dependencies - Add tailwind.config.js and postcss.config.js - Add source tailwind.css file - Generate tailwind.output.css for admin and vendor - Add Tailwind CSS documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
313
docs/frontend/tailwind-css.md
Normal file
313
docs/frontend/tailwind-css.md
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
# Tailwind CSS Build Guide
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Last Updated:** December 2024
|
||||||
|
**Audience:** Frontend Developers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The platform uses [Tailwind CSS](https://tailwindcss.com/) for styling with a **dual-layer architecture**:
|
||||||
|
|
||||||
|
1. **Base Layer (CDN):** Tailwind CSS v2.2.19 loaded via CDN with local fallback
|
||||||
|
2. **Override Layer (npm build):** Custom Windmill Dashboard theme built from Tailwind v1.4.6
|
||||||
|
|
||||||
|
This layered approach allows:
|
||||||
|
- Fast loading via CDN for base utilities
|
||||||
|
- Custom theme extensions (colors, dark mode, forms) via npm build
|
||||||
|
- Offline support via local fallback
|
||||||
|
|
||||||
|
> **Note:** A migration to Tailwind v3.4 is planned. See [Migration Plan](tailwind-migration-plan.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
```
|
||||||
|
Browser loads:
|
||||||
|
1. CDN Tailwind 2.2.19 (base utilities)
|
||||||
|
└── Fallback: static/shared/css/tailwind.min.css
|
||||||
|
|
||||||
|
2. Custom tailwind.output.css (overrides/extensions)
|
||||||
|
└── Built from: static/admin/css/tailwind.css
|
||||||
|
└── Contains: Windmill Dashboard theme, custom colors, dark mode
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Files
|
||||||
|
|
||||||
|
| File | Version | Purpose |
|
||||||
|
|------|---------|---------|
|
||||||
|
| CDN `tailwindcss@2.2.19` | 2.2.19 | Base Tailwind utilities |
|
||||||
|
| `static/shared/css/tailwind.min.css` | 2.2.19 | Local fallback for CDN |
|
||||||
|
| `tailwind.config.js` | 1.4.6 | Custom build configuration |
|
||||||
|
| `static/admin/css/tailwind.css` | - | Source file with directives |
|
||||||
|
| `static/admin/css/tailwind.output.css` | 1.4.6 | Compiled custom styles |
|
||||||
|
| `static/vendor/css/tailwind.output.css` | 1.4.6 | Compiled custom styles |
|
||||||
|
|
||||||
|
### Template Loading Order
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- 1. Base Tailwind from CDN (with fallback) -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
|
||||||
|
onerror="this.onerror=null; this.href='/static/shared/css/tailwind.min.css';">
|
||||||
|
|
||||||
|
<!-- 2. Custom overrides (built via npm) -->
|
||||||
|
<link rel="stylesheet" href="/static/admin/css/tailwind.output.css" />
|
||||||
|
```
|
||||||
|
|
||||||
|
See [CDN Fallback Strategy](cdn-fallback-strategy.md) for details on offline support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How Tailwind Purging Works
|
||||||
|
|
||||||
|
Tailwind generates thousands of utility classes. To keep the CSS file small, it "purges" (removes) unused classes by scanning your source files.
|
||||||
|
|
||||||
|
### Content Paths
|
||||||
|
|
||||||
|
The `purge.content` array in `tailwind.config.js` tells Tailwind where to look for class usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
purge: {
|
||||||
|
content: [
|
||||||
|
'public/**/*.html',
|
||||||
|
'app/templates/**/*.html', // Jinja2 templates
|
||||||
|
'static/**/*.js', // Alpine.js components
|
||||||
|
],
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Safelist
|
||||||
|
|
||||||
|
Some classes are used dynamically in Alpine.js expressions and can't be detected by scanning. These must be added to the `safelist`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
purge: {
|
||||||
|
content: [...],
|
||||||
|
safelist: [
|
||||||
|
'bg-orange-600',
|
||||||
|
'bg-green-600',
|
||||||
|
'bg-red-600',
|
||||||
|
'hover:bg-orange-700',
|
||||||
|
'hover:bg-green-700',
|
||||||
|
'hover:bg-red-700',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to add to safelist:**
|
||||||
|
|
||||||
|
- Classes used in Alpine.js `:class` bindings with dynamic conditions
|
||||||
|
- Classes constructed from variables (e.g., `bg-${color}-500`)
|
||||||
|
- Classes used only in JavaScript, not in HTML templates
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building Tailwind CSS
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Install Node.js dependencies (one-time setup):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Or using Make:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make npm-install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Build
|
||||||
|
|
||||||
|
For development, build without purging (includes all classes, larger file):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build admin CSS
|
||||||
|
npm run tailwind:admin
|
||||||
|
|
||||||
|
# Build vendor CSS
|
||||||
|
npm run tailwind:vendor
|
||||||
|
|
||||||
|
# Or using Make
|
||||||
|
make tailwind-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Build
|
||||||
|
|
||||||
|
For production, build with purging and minification (smaller file):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Or using Make
|
||||||
|
make tailwind-build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Available npm Scripts
|
||||||
|
|
||||||
|
| Script | Command | Description |
|
||||||
|
|--------|---------|-------------|
|
||||||
|
| `npm run tailwind:admin` | Build admin CSS (dev) | Fast, includes all classes |
|
||||||
|
| `npm run tailwind:vendor` | Build vendor CSS (dev) | Fast, includes all classes |
|
||||||
|
| `npm run build:admin` | Build admin CSS (prod) | Purged and minified |
|
||||||
|
| `npm run build:vendor` | Build vendor CSS (prod) | Purged and minified |
|
||||||
|
| `npm run build` | Build all (prod) | Runs both production builds |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding New Utility Classes
|
||||||
|
|
||||||
|
If you need a class that doesn't exist in the compiled CSS:
|
||||||
|
|
||||||
|
### Option 1: Check if it's being purged
|
||||||
|
|
||||||
|
The class might exist but is being removed. Add it to the `safelist` in `tailwind.config.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
safelist: [
|
||||||
|
'your-new-class',
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Extend the theme
|
||||||
|
|
||||||
|
Add custom values to `tailwind.config.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
'brand': '#123456',
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
'128': '32rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Rebuild CSS
|
||||||
|
|
||||||
|
After any config change, rebuild the CSS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make tailwind-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Classes not appearing
|
||||||
|
|
||||||
|
1. **Check purge paths** - Ensure your file is in a scanned directory
|
||||||
|
2. **Check safelist** - Dynamic classes need to be safelisted
|
||||||
|
3. **Rebuild CSS** - Run `make tailwind-dev` after config changes
|
||||||
|
|
||||||
|
### Dynamic classes in Alpine.js
|
||||||
|
|
||||||
|
**Problem:** Classes in `:class` bindings may be purged.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- This class might be purged -->
|
||||||
|
<div :class="isActive ? 'bg-green-600' : 'bg-red-600'">
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:** Add to safelist:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
safelist: ['bg-green-600', 'bg-red-600']
|
||||||
|
```
|
||||||
|
|
||||||
|
### Large CSS file in development
|
||||||
|
|
||||||
|
Development builds include all Tailwind classes (~1.2MB). This is normal.
|
||||||
|
|
||||||
|
Production builds purge unused classes, resulting in much smaller files (~50-100KB typically).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
### tailwind.config.js Structure
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
module.exports = {
|
||||||
|
// Where to scan for class usage
|
||||||
|
purge: {
|
||||||
|
content: [...],
|
||||||
|
safelist: [...],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Theme customization
|
||||||
|
theme: {
|
||||||
|
// Override defaults
|
||||||
|
colors: {...},
|
||||||
|
|
||||||
|
// Extend defaults
|
||||||
|
extend: {
|
||||||
|
fontFamily: {...},
|
||||||
|
maxHeight: {...},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Variant configuration (hover, focus, dark mode, etc.)
|
||||||
|
variants: {
|
||||||
|
backgroundColor: ['hover', 'focus', 'dark', 'dark:hover'],
|
||||||
|
textColor: ['hover', 'dark'],
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
|
||||||
|
// Plugins
|
||||||
|
plugins: [
|
||||||
|
require('tailwindcss-multi-theme'),
|
||||||
|
require('@tailwindcss/custom-forms'),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dark Mode
|
||||||
|
|
||||||
|
The platform uses `tailwindcss-multi-theme` for dark mode. Dark mode classes use the `.theme-dark` parent selector:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Light mode */
|
||||||
|
.bg-white { ... }
|
||||||
|
|
||||||
|
/* Dark mode */
|
||||||
|
.theme-dark .dark\:bg-gray-800 { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always rebuild after config changes**
|
||||||
|
```bash
|
||||||
|
make tailwind-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Add dynamic classes to safelist** to prevent purging
|
||||||
|
|
||||||
|
3. **Use production builds for deployment** to minimize file size
|
||||||
|
|
||||||
|
4. **Check existing classes first** before adding custom ones - Tailwind likely has what you need
|
||||||
|
|
||||||
|
5. **Use consistent color scales** (e.g., `purple-600`, `purple-700`) for hover states
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- [Frontend Overview](overview.md)
|
||||||
|
- [UI Components](shared/ui-components.md)
|
||||||
|
- [Tailwind CSS Official Docs](https://tailwindcss.com/docs)
|
||||||
361
docs/frontend/tailwind-migration-plan.md
Normal file
361
docs/frontend/tailwind-migration-plan.md
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
# Tailwind CSS Migration Plan: v1.4/v2.2 → v3.4
|
||||||
|
|
||||||
|
**Created:** December 2024
|
||||||
|
**Status:** Planned
|
||||||
|
**Estimated Time:** 2-3 hours
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
### Two Tailwind Setups
|
||||||
|
|
||||||
|
| Component | Version | Source | Purpose |
|
||||||
|
|-----------|---------|--------|---------|
|
||||||
|
| Base styles | 2.2.19 | CDN + local fallback | Core Tailwind utilities for all frontends |
|
||||||
|
| Custom overrides | 1.4.6 | npm build | Windmill Dashboard theme (admin/vendor) |
|
||||||
|
|
||||||
|
### Current Files
|
||||||
|
|
||||||
|
```
|
||||||
|
package.json # tailwindcss: 1.4.6
|
||||||
|
tailwind.config.js # v1.4 format config
|
||||||
|
static/shared/css/tailwind.min.css # CDN fallback (v2.2.19)
|
||||||
|
static/admin/css/tailwind.output.css # Built overrides
|
||||||
|
static/vendor/css/tailwind.output.css # Built overrides
|
||||||
|
```
|
||||||
|
|
||||||
|
### Current Plugins
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@tailwindcss/custom-forms": "0.2.1", // DEPRECATED in v3
|
||||||
|
"tailwindcss-multi-theme": "1.0.3" // May not work with v3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Goals
|
||||||
|
|
||||||
|
1. Upgrade npm Tailwind to v3.4.x (latest stable)
|
||||||
|
2. Upgrade CDN Tailwind to v3.4.x
|
||||||
|
3. Update local fallback file
|
||||||
|
4. Replace deprecated plugins
|
||||||
|
5. Update config to v3 format
|
||||||
|
6. Verify all frontends work correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Migration
|
||||||
|
|
||||||
|
### Phase 1: Backup Current State
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create backup branch
|
||||||
|
git checkout -b backup/tailwind-v1.4
|
||||||
|
git add -A
|
||||||
|
git commit -m "Backup before Tailwind v3 migration"
|
||||||
|
git checkout master
|
||||||
|
|
||||||
|
# Create migration branch
|
||||||
|
git checkout -b feat/tailwind-v3-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Update npm Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove old packages
|
||||||
|
npm uninstall tailwindcss tailwindcss-multi-theme @tailwindcss/custom-forms
|
||||||
|
|
||||||
|
# Install new packages
|
||||||
|
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
|
||||||
|
npm install -D @tailwindcss/forms @tailwindcss/typography
|
||||||
|
|
||||||
|
# Verify versions
|
||||||
|
npm list tailwindcss
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected versions:**
|
||||||
|
- tailwindcss: ^3.4.x
|
||||||
|
- postcss: ^8.4.x
|
||||||
|
- autoprefixer: ^10.4.x
|
||||||
|
|
||||||
|
### Phase 3: Update tailwind.config.js
|
||||||
|
|
||||||
|
Replace the entire config file:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// tailwind.config.js - Tailwind CSS v3.4
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
// v3: 'content' replaces 'purge'
|
||||||
|
content: [
|
||||||
|
'app/templates/**/*.html',
|
||||||
|
'static/**/*.js',
|
||||||
|
],
|
||||||
|
|
||||||
|
// v3: safelist for dynamic classes
|
||||||
|
safelist: [
|
||||||
|
'bg-orange-600',
|
||||||
|
'bg-green-600',
|
||||||
|
'bg-red-600',
|
||||||
|
'hover:bg-orange-700',
|
||||||
|
'hover:bg-green-700',
|
||||||
|
'hover:bg-red-700',
|
||||||
|
// Add any other dynamic classes used in Alpine.js
|
||||||
|
],
|
||||||
|
|
||||||
|
// v3: darkMode replaces tailwindcss-multi-theme
|
||||||
|
darkMode: 'class', // or 'media' for OS preference
|
||||||
|
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
// Custom colors from Windmill Dashboard
|
||||||
|
colors: {
|
||||||
|
gray: {
|
||||||
|
50: '#f9fafb',
|
||||||
|
100: '#f4f5f7',
|
||||||
|
200: '#e5e7eb',
|
||||||
|
300: '#d5d6d7',
|
||||||
|
400: '#9e9e9e',
|
||||||
|
500: '#707275',
|
||||||
|
600: '#4c4f52',
|
||||||
|
700: '#24262d',
|
||||||
|
800: '#1a1c23',
|
||||||
|
900: '#121317',
|
||||||
|
},
|
||||||
|
purple: {
|
||||||
|
50: '#f6f5ff',
|
||||||
|
100: '#edebfe',
|
||||||
|
200: '#dcd7fe',
|
||||||
|
300: '#cabffd',
|
||||||
|
400: '#ac94fa',
|
||||||
|
500: '#9061f9',
|
||||||
|
600: '#7e3af2',
|
||||||
|
700: '#6c2bd9',
|
||||||
|
800: '#5521b5',
|
||||||
|
900: '#4a1d96',
|
||||||
|
},
|
||||||
|
// Add other custom colors as needed
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
|
||||||
|
},
|
||||||
|
maxHeight: {
|
||||||
|
'xl': '36rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms'), // Replaces @tailwindcss/custom-forms
|
||||||
|
require('@tailwindcss/typography'), // Optional: for prose content
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Update CSS Source File
|
||||||
|
|
||||||
|
Update `static/admin/css/tailwind.css`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Tailwind CSS v3 directives */
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* Custom component classes if needed */
|
||||||
|
@layer components {
|
||||||
|
.form-input {
|
||||||
|
@apply block w-full rounded-md border-gray-300 shadow-sm
|
||||||
|
focus:border-purple-500 focus:ring-purple-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select {
|
||||||
|
@apply block w-full rounded-md border-gray-300 shadow-sm
|
||||||
|
focus:border-purple-500 focus:ring-purple-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-checkbox {
|
||||||
|
@apply rounded border-gray-300 text-purple-600
|
||||||
|
focus:ring-purple-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Update postcss.config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// postcss.config.js
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 6: Update package.json Scripts
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"tailwind:admin": "npx tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css",
|
||||||
|
"tailwind:vendor": "npx tailwindcss -i static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css",
|
||||||
|
"tailwind:watch": "npx tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css --watch",
|
||||||
|
"build:admin": "npx tailwindcss -i static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css --minify",
|
||||||
|
"build:vendor": "npx tailwindcss -i static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css --minify",
|
||||||
|
"build": "npm run build:admin && npm run build:vendor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 7: Update Dark Mode Implementation
|
||||||
|
|
||||||
|
**Old approach (tailwindcss-multi-theme):**
|
||||||
|
```html
|
||||||
|
<body class="theme-dark">
|
||||||
|
<div class="bg-white dark:bg-gray-800">
|
||||||
|
```
|
||||||
|
|
||||||
|
**New approach (Tailwind v3 native):**
|
||||||
|
```html
|
||||||
|
<body class="dark">
|
||||||
|
<div class="bg-white dark:bg-gray-800">
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files to update:**
|
||||||
|
1. `app/templates/admin/base.html` - Change `theme-dark` to `dark`
|
||||||
|
2. `app/templates/vendor/base.html` - Change `theme-dark` to `dark`
|
||||||
|
3. `static/admin/js/init-alpine.js` - Update dark mode toggle logic
|
||||||
|
4. `static/vendor/js/init-alpine.js` - Update dark mode toggle logic
|
||||||
|
|
||||||
|
**JavaScript update example:**
|
||||||
|
```javascript
|
||||||
|
// Old
|
||||||
|
document.body.classList.toggle('theme-dark');
|
||||||
|
|
||||||
|
// New
|
||||||
|
document.documentElement.classList.toggle('dark');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 8: Update CDN and Local Fallback
|
||||||
|
|
||||||
|
**Option A: Use Tailwind Play CDN (development only)**
|
||||||
|
```html
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: Build and serve locally (recommended for production)**
|
||||||
|
|
||||||
|
Since Tailwind v3 doesn't have a pre-built CDN CSS file (it's JIT-only), the recommended approach is:
|
||||||
|
|
||||||
|
1. Remove CDN loading from templates
|
||||||
|
2. Load only the built `tailwind.output.css`
|
||||||
|
3. Include all needed utilities in your build
|
||||||
|
|
||||||
|
**Update base templates:**
|
||||||
|
```html
|
||||||
|
<!-- OLD: CDN with fallback -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
|
||||||
|
onerror="this.onerror=null; this.href='{{ url_for('static', path='shared/css/tailwind.min.css') }}';">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
|
||||||
|
|
||||||
|
<!-- NEW: Local only (v3) -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 9: Build and Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Build CSS
|
||||||
|
npm run tailwind:admin
|
||||||
|
npm run tailwind:vendor
|
||||||
|
|
||||||
|
# Start development server
|
||||||
|
make dev
|
||||||
|
|
||||||
|
# Test all frontends:
|
||||||
|
# - http://localhost:8000/admin/dashboard
|
||||||
|
# - http://localhost:8000/vendor/{code}/dashboard
|
||||||
|
# - http://localhost:8000/ (shop)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 10: Verify Checklist
|
||||||
|
|
||||||
|
- [ ] Admin dashboard loads correctly
|
||||||
|
- [ ] Vendor dashboard loads correctly
|
||||||
|
- [ ] Shop frontend loads correctly
|
||||||
|
- [ ] Dark mode toggle works
|
||||||
|
- [ ] All buttons have correct colors
|
||||||
|
- [ ] Forms display correctly (inputs, selects, checkboxes)
|
||||||
|
- [ ] Responsive design works (mobile/tablet/desktop)
|
||||||
|
- [ ] No console errors
|
||||||
|
- [ ] Production build works (`npm run build`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Breaking Changes Reference
|
||||||
|
|
||||||
|
### Class Name Changes (v2 → v3)
|
||||||
|
|
||||||
|
| v2.x | v3.x | Notes |
|
||||||
|
|------|------|-------|
|
||||||
|
| `overflow-ellipsis` | `text-ellipsis` | Renamed |
|
||||||
|
| `flex-grow-0` | `grow-0` | Shortened |
|
||||||
|
| `flex-shrink-0` | `shrink-0` | Shortened |
|
||||||
|
| `decoration-clone` | `box-decoration-clone` | Renamed |
|
||||||
|
|
||||||
|
### Plugin Changes
|
||||||
|
|
||||||
|
| Old Plugin | New Plugin | Notes |
|
||||||
|
|------------|------------|-------|
|
||||||
|
| `@tailwindcss/custom-forms` | `@tailwindcss/forms` | Complete rewrite |
|
||||||
|
| `tailwindcss-multi-theme` | Built-in `darkMode: 'class'` | Native support |
|
||||||
|
|
||||||
|
### Config Changes
|
||||||
|
|
||||||
|
| v1.x/v2.x | v3.x |
|
||||||
|
|-----------|------|
|
||||||
|
| `purge: [...]` | `content: [...]` |
|
||||||
|
| `variants: {...}` | Removed (JIT generates all) |
|
||||||
|
| `mode: 'jit'` | Default (not needed) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If issues arise:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Switch back to backup branch
|
||||||
|
git checkout backup/tailwind-v1.4
|
||||||
|
|
||||||
|
# Or reset changes
|
||||||
|
git checkout master
|
||||||
|
git reset --hard HEAD~1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post-Migration Tasks
|
||||||
|
|
||||||
|
1. Update `docs/frontend/tailwind-css.md` with v3 information
|
||||||
|
2. Update `docs/frontend/cdn-fallback-strategy.md` (or remove CDN references)
|
||||||
|
3. Remove `static/shared/css/tailwind.min.css` if no longer needed
|
||||||
|
4. Update any documentation referencing old class names
|
||||||
|
5. Consider adding Tailwind IntelliSense VS Code extension config
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Tailwind CSS v3 Upgrade Guide](https://tailwindcss.com/docs/upgrade-guide)
|
||||||
|
- [Tailwind CSS v3 Documentation](https://tailwindcss.com/docs)
|
||||||
|
- [@tailwindcss/forms Plugin](https://github.com/tailwindlabs/tailwindcss-forms)
|
||||||
|
- [Dark Mode in Tailwind v3](https://tailwindcss.com/docs/dark-mode)
|
||||||
55
package.json
Normal file
55
package.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"name": "windmill-dashboard",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "A multi theme, completely accessible, with components and pages examples, ready for production dashboard.",
|
||||||
|
"scripts": {
|
||||||
|
"tailwind:admin": "tailwindcss build static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css",
|
||||||
|
"tailwind:vendor": "tailwindcss build static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css",
|
||||||
|
"build:admin": "env NODE_ENV=production postcss static/admin/css/tailwind.css -o static/admin/css/tailwind.output.css",
|
||||||
|
"build:vendor": "env NODE_ENV=production postcss static/admin/css/tailwind.css -o static/vendor/css/tailwind.output.css",
|
||||||
|
"build": "npm run build:admin && npm run build:vendor",
|
||||||
|
"cz": "git-cz",
|
||||||
|
"release": "release-it"
|
||||||
|
},
|
||||||
|
"author": "Estevan Maito <ejmaito@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@release-it/conventional-changelog": "1.1.4",
|
||||||
|
"@tailwindcss/custom-forms": "0.2.1",
|
||||||
|
"autoprefixer": "9.8.0",
|
||||||
|
"color": "3.1.2",
|
||||||
|
"commitizen": "4.1.2",
|
||||||
|
"cssnano": "4.1.10",
|
||||||
|
"cz-conventional-changelog": "3.2.0",
|
||||||
|
"postcss-cli": "7.1.1",
|
||||||
|
"release-it": "13.6.4",
|
||||||
|
"tailwindcss": "1.4.6",
|
||||||
|
"tailwindcss-multi-theme": "1.0.3"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"tailwind",
|
||||||
|
"windmill",
|
||||||
|
"dashboard",
|
||||||
|
"template",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
"release-it": {
|
||||||
|
"github": {
|
||||||
|
"release": true
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"publish": false
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"@release-it/conventional-changelog": {
|
||||||
|
"preset": "angular",
|
||||||
|
"infile": "CHANGELOG.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "./node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
postcss.config.js
Normal file
9
postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('tailwindcss'),
|
||||||
|
require('autoprefixer'),
|
||||||
|
require('cssnano')({
|
||||||
|
preset: 'default',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
4
static/admin/css/tailwind.css
Normal file
4
static/admin/css/tailwind.css
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/* Tailwind CSS source file for admin panel */
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
File diff suppressed because one or more lines are too long
65241
static/vendor/css/tailwind.output.css
vendored
65241
static/vendor/css/tailwind.output.css
vendored
File diff suppressed because one or more lines are too long
233
tailwind.config.js
Normal file
233
tailwind.config.js
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||||
|
const plugin = require('tailwindcss/plugin')
|
||||||
|
const Color = require('color')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
purge: {
|
||||||
|
content: [
|
||||||
|
'public/**/*.html',
|
||||||
|
'app/templates/**/*.html',
|
||||||
|
'static/**/*.js',
|
||||||
|
],
|
||||||
|
// Safelist classes used dynamically in Alpine.js expressions
|
||||||
|
safelist: [
|
||||||
|
'bg-orange-600',
|
||||||
|
'bg-green-600',
|
||||||
|
'bg-red-600',
|
||||||
|
'hover:bg-orange-700',
|
||||||
|
'hover:bg-green-700',
|
||||||
|
'hover:bg-red-700',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
themeVariants: ['dark'],
|
||||||
|
customForms: (theme) => ({
|
||||||
|
default: {
|
||||||
|
'input, textarea': {
|
||||||
|
'&::placeholder': {
|
||||||
|
color: theme('colors.gray.400'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
colors: {
|
||||||
|
transparent: 'transparent',
|
||||||
|
white: '#ffffff',
|
||||||
|
black: '#000000',
|
||||||
|
gray: {
|
||||||
|
'50': '#f9fafb',
|
||||||
|
'100': '#f4f5f7',
|
||||||
|
'200': '#e5e7eb',
|
||||||
|
'300': '#d5d6d7',
|
||||||
|
'400': '#9e9e9e',
|
||||||
|
'500': '#707275',
|
||||||
|
'600': '#4c4f52',
|
||||||
|
'700': '#24262d',
|
||||||
|
'800': '#1a1c23',
|
||||||
|
'900': '#121317',
|
||||||
|
// default values from Tailwind UI palette
|
||||||
|
// '300': '#d2d6dc',
|
||||||
|
// '400': '#9fa6b2',
|
||||||
|
// '500': '#6b7280',
|
||||||
|
// '600': '#4b5563',
|
||||||
|
// '700': '#374151',
|
||||||
|
// '800': '#252f3f',
|
||||||
|
// '900': '#161e2e',
|
||||||
|
},
|
||||||
|
'cool-gray': {
|
||||||
|
'50': '#fbfdfe',
|
||||||
|
'100': '#f1f5f9',
|
||||||
|
'200': '#e2e8f0',
|
||||||
|
'300': '#cfd8e3',
|
||||||
|
'400': '#97a6ba',
|
||||||
|
'500': '#64748b',
|
||||||
|
'600': '#475569',
|
||||||
|
'700': '#364152',
|
||||||
|
'800': '#27303f',
|
||||||
|
'900': '#1a202e',
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
'50': '#fdf2f2',
|
||||||
|
'100': '#fde8e8',
|
||||||
|
'200': '#fbd5d5',
|
||||||
|
'300': '#f8b4b4',
|
||||||
|
'400': '#f98080',
|
||||||
|
'500': '#f05252',
|
||||||
|
'600': '#e02424',
|
||||||
|
'700': '#c81e1e',
|
||||||
|
'800': '#9b1c1c',
|
||||||
|
'900': '#771d1d',
|
||||||
|
},
|
||||||
|
orange: {
|
||||||
|
'50': '#fff8f1',
|
||||||
|
'100': '#feecdc',
|
||||||
|
'200': '#fcd9bd',
|
||||||
|
'300': '#fdba8c',
|
||||||
|
'400': '#ff8a4c',
|
||||||
|
'500': '#ff5a1f',
|
||||||
|
'600': '#d03801',
|
||||||
|
'700': '#b43403',
|
||||||
|
'800': '#8a2c0d',
|
||||||
|
'900': '#771d1d',
|
||||||
|
},
|
||||||
|
yellow: {
|
||||||
|
'50': '#fdfdea',
|
||||||
|
'100': '#fdf6b2',
|
||||||
|
'200': '#fce96a',
|
||||||
|
'300': '#faca15',
|
||||||
|
'400': '#e3a008',
|
||||||
|
'500': '#c27803',
|
||||||
|
'600': '#9f580a',
|
||||||
|
'700': '#8e4b10',
|
||||||
|
'800': '#723b13',
|
||||||
|
'900': '#633112',
|
||||||
|
},
|
||||||
|
green: {
|
||||||
|
'50': '#f3faf7',
|
||||||
|
'100': '#def7ec',
|
||||||
|
'200': '#bcf0da',
|
||||||
|
'300': '#84e1bc',
|
||||||
|
'400': '#31c48d',
|
||||||
|
'500': '#0e9f6e',
|
||||||
|
'600': '#057a55',
|
||||||
|
'700': '#046c4e',
|
||||||
|
'800': '#03543f',
|
||||||
|
'900': '#014737',
|
||||||
|
},
|
||||||
|
teal: {
|
||||||
|
'50': '#edfafa',
|
||||||
|
'100': '#d5f5f6',
|
||||||
|
'200': '#afecef',
|
||||||
|
'300': '#7edce2',
|
||||||
|
'400': '#16bdca',
|
||||||
|
'500': '#0694a2',
|
||||||
|
'600': '#047481',
|
||||||
|
'700': '#036672',
|
||||||
|
'800': '#05505c',
|
||||||
|
'900': '#014451',
|
||||||
|
},
|
||||||
|
blue: {
|
||||||
|
'50': '#ebf5ff',
|
||||||
|
'100': '#e1effe',
|
||||||
|
'200': '#c3ddfd',
|
||||||
|
'300': '#a4cafe',
|
||||||
|
'400': '#76a9fa',
|
||||||
|
'500': '#3f83f8',
|
||||||
|
'600': '#1c64f2',
|
||||||
|
'700': '#1a56db',
|
||||||
|
'800': '#1e429f',
|
||||||
|
'900': '#233876',
|
||||||
|
},
|
||||||
|
indigo: {
|
||||||
|
'50': '#f0f5ff',
|
||||||
|
'100': '#e5edff',
|
||||||
|
'200': '#cddbfe',
|
||||||
|
'300': '#b4c6fc',
|
||||||
|
'400': '#8da2fb',
|
||||||
|
'500': '#6875f5',
|
||||||
|
'600': '#5850ec',
|
||||||
|
'700': '#5145cd',
|
||||||
|
'800': '#42389d',
|
||||||
|
'900': '#362f78',
|
||||||
|
},
|
||||||
|
purple: {
|
||||||
|
'50': '#f6f5ff',
|
||||||
|
'100': '#edebfe',
|
||||||
|
'200': '#dcd7fe',
|
||||||
|
'300': '#cabffd',
|
||||||
|
'400': '#ac94fa',
|
||||||
|
'500': '#9061f9',
|
||||||
|
'600': '#7e3af2',
|
||||||
|
'700': '#6c2bd9',
|
||||||
|
'800': '#5521b5',
|
||||||
|
'900': '#4a1d96',
|
||||||
|
},
|
||||||
|
pink: {
|
||||||
|
'50': '#fdf2f8',
|
||||||
|
'100': '#fce8f3',
|
||||||
|
'200': '#fad1e8',
|
||||||
|
'300': '#f8b4d9',
|
||||||
|
'400': '#f17eb8',
|
||||||
|
'500': '#e74694',
|
||||||
|
'600': '#d61f69',
|
||||||
|
'700': '#bf125d',
|
||||||
|
'800': '#99154b',
|
||||||
|
'900': '#751a3d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
maxHeight: {
|
||||||
|
'0': '0',
|
||||||
|
xl: '36rem',
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', ...defaultTheme.fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
backgroundColor: [
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
'active',
|
||||||
|
'odd',
|
||||||
|
'dark',
|
||||||
|
'dark:hover',
|
||||||
|
'dark:focus',
|
||||||
|
'dark:active',
|
||||||
|
'dark:odd',
|
||||||
|
],
|
||||||
|
display: ['responsive', 'dark'],
|
||||||
|
textColor: [
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'active',
|
||||||
|
'dark',
|
||||||
|
'dark:focus-within',
|
||||||
|
'dark:hover',
|
||||||
|
'dark:active',
|
||||||
|
],
|
||||||
|
placeholderColor: ['focus', 'dark', 'dark:focus'],
|
||||||
|
borderColor: ['focus', 'hover', 'dark', 'dark:focus', 'dark:hover'],
|
||||||
|
divideColor: ['dark'],
|
||||||
|
boxShadow: ['focus', 'dark:focus'],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('tailwindcss-multi-theme'),
|
||||||
|
require('@tailwindcss/custom-forms'),
|
||||||
|
plugin(({ addUtilities, e, theme, variants }) => {
|
||||||
|
const newUtilities = {}
|
||||||
|
Object.entries(theme('colors')).map(([name, value]) => {
|
||||||
|
if (name === 'transparent' || name === 'current') return
|
||||||
|
const color = value[300] ? value[300] : value
|
||||||
|
const hsla = Color(color).alpha(0.45).hsl().string()
|
||||||
|
|
||||||
|
newUtilities[`.shadow-outline-${name}`] = {
|
||||||
|
'box-shadow': `0 0 0 3px ${hsla}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addUtilities(newUtilities, variants('boxShadow'))
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user