refactor: migrate vendor APIs to token-based context and consolidate architecture
## Vendor-in-Token Architecture (Complete Migration) - Migrate all vendor API endpoints from require_vendor_context() to token_vendor_id - Update permission dependencies to extract vendor from JWT token - Add vendor exceptions: VendorAccessDeniedException, VendorOwnerOnlyException, InsufficientVendorPermissionsException - Shop endpoints retain require_vendor_context() for URL-based detection - Add AUTH-004 architecture rule enforcing vendor context patterns - Fix marketplace router missing /marketplace prefix ## Exception Pattern Fixes (API-003/API-004) - Services raise domain exceptions, endpoints let them bubble up - Add code_quality and content_page exception modules - Move business logic from endpoints to services (admin, auth, content_page) - Fix exception handling in admin, shop, and vendor endpoints ## Tailwind CSS Consolidation - Consolidate CSS to per-area files (admin, vendor, shop, platform) - Remove shared/cdn-fallback.html and shared/css/tailwind.min.css - Update all templates to use area-specific Tailwind output files - Remove Node.js config (package.json, postcss.config.js, tailwind.config.js) ## Documentation & Cleanup - Update vendor-in-token-architecture.md with completed migration status - Update architecture-rules.md with new rules - Move migration docs to docs/development/migration/ - Remove duplicate/obsolete documentation files - Merge pytest.ini settings into pyproject.toml 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Tailwind CSS Build Guide
|
||||
|
||||
**Version:** 1.0
|
||||
**Version:** 2.0
|
||||
**Last Updated:** December 2024
|
||||
**Audience:** Frontend Developers
|
||||
|
||||
@@ -8,17 +8,7 @@
|
||||
|
||||
## 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).
|
||||
The platform uses [Tailwind CSS v4](https://tailwindcss.com/) with the **Standalone CLI** - no Node.js required. Each frontend (admin, vendor, shop, platform) has its own dedicated CSS configuration.
|
||||
|
||||
---
|
||||
|
||||
@@ -27,180 +17,230 @@ This layered approach allows:
|
||||
### 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
|
||||
Tailwind Standalone CLI (single binary, no npm)
|
||||
│
|
||||
├── static/admin/css/tailwind.css → tailwind.output.css (Admin)
|
||||
├── static/vendor/css/tailwind.css → tailwind.output.css (Vendor)
|
||||
├── static/shop/css/tailwind.css → tailwind.output.css (Shop)
|
||||
└── static/platform/css/tailwind.css → tailwind.output.css (Platform)
|
||||
```
|
||||
|
||||
### 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 |
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `~/.local/bin/tailwindcss` | Standalone CLI binary (v4.x) |
|
||||
| `static/*/css/tailwind.css` | CSS-first source configuration |
|
||||
| `static/*/css/tailwind.output.css` | Compiled output (do not edit) |
|
||||
|
||||
### Template Loading Order
|
||||
### Template Loading
|
||||
|
||||
Each frontend loads its own CSS:
|
||||
|
||||
```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';">
|
||||
<!-- Admin -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='admin/css/tailwind.output.css') }}" />
|
||||
|
||||
<!-- 2. Custom overrides (built via npm) -->
|
||||
<link rel="stylesheet" href="/static/admin/css/tailwind.output.css" />
|
||||
<!-- Vendor -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='vendor/css/tailwind.output.css') }}" />
|
||||
|
||||
<!-- Shop -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='shop/css/tailwind.output.css') }}" />
|
||||
|
||||
<!-- Platform -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='platform/css/tailwind.output.css') }}" />
|
||||
```
|
||||
|
||||
See [CDN Fallback Strategy](cdn-fallback-strategy.md) for details on offline support.
|
||||
|
||||
---
|
||||
|
||||
## How Tailwind Purging Works
|
||||
## CSS-First Configuration (Tailwind v4)
|
||||
|
||||
Tailwind generates thousands of utility classes. To keep the CSS file small, it "purges" (removes) unused classes by scanning your source files.
|
||||
Tailwind v4 uses **CSS-first configuration** instead of `tailwind.config.js`. All customization happens directly in CSS files.
|
||||
|
||||
### Content Paths
|
||||
### Source File Structure
|
||||
|
||||
The `purge.content` array in `tailwind.config.js` tells Tailwind where to look for class usage:
|
||||
```css
|
||||
/* static/admin/css/tailwind.css */
|
||||
|
||||
```javascript
|
||||
purge: {
|
||||
content: [
|
||||
'public/**/*.html',
|
||||
'app/templates/**/*.html', // Jinja2 templates
|
||||
'static/**/*.js', // Alpine.js components
|
||||
],
|
||||
// ...
|
||||
/* Import Tailwind */
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Content sources for tree-shaking */
|
||||
@source "../../../app/templates/admin/**/*.html";
|
||||
@source "../../js/**/*.js";
|
||||
|
||||
/* Custom theme (colors, fonts, spacing) */
|
||||
@theme {
|
||||
--color-gray-50: #f9fafb;
|
||||
--color-gray-900: #121317;
|
||||
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Dark mode variant */
|
||||
@variant dark (&:where(.dark, .dark *));
|
||||
|
||||
/* Custom utilities */
|
||||
@layer utilities {
|
||||
.shadow-outline-purple {
|
||||
box-shadow: 0 0 0 3px hsla(262, 97%, 81%, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom components */
|
||||
@layer components {
|
||||
.form-input { ... }
|
||||
.btn-primary { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Safelist
|
||||
### Key Directives
|
||||
|
||||
Some classes are used dynamically in Alpine.js expressions and can't be detected by scanning. These must be added to the `safelist`:
|
||||
| Directive | Purpose | Example |
|
||||
|-----------|---------|---------|
|
||||
| `@import "tailwindcss"` | Import Tailwind base | Required at top |
|
||||
| `@source` | Content paths for purging | `@source "../../../app/templates/**/*.html"` |
|
||||
| `@theme` | Custom design tokens | `--color-purple-600: #7e3af2;` |
|
||||
| `@variant` | Custom variants | `@variant dark (&:where(.dark, .dark *))` |
|
||||
| `@layer utilities` | Custom utility classes | `.shadow-outline-*` |
|
||||
| `@layer components` | Custom components | `.form-input`, `.btn-*` |
|
||||
|
||||
```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',
|
||||
],
|
||||
---
|
||||
|
||||
## Custom Color Palette
|
||||
|
||||
All frontends share the same Windmill Dashboard color palette:
|
||||
|
||||
```css
|
||||
@theme {
|
||||
/* Gray (custom darker palette) */
|
||||
--color-gray-50: #f9fafb;
|
||||
--color-gray-100: #f4f5f7;
|
||||
--color-gray-200: #e5e7eb;
|
||||
--color-gray-300: #d5d6d7;
|
||||
--color-gray-400: #9e9e9e;
|
||||
--color-gray-500: #707275;
|
||||
--color-gray-600: #4c4f52;
|
||||
--color-gray-700: #24262d;
|
||||
--color-gray-800: #1a1c23;
|
||||
--color-gray-900: #121317;
|
||||
|
||||
/* Purple (primary) */
|
||||
--color-purple-600: #7e3af2;
|
||||
|
||||
/* Plus: red, orange, yellow, green, teal, blue, indigo, pink, cool-gray */
|
||||
}
|
||||
```
|
||||
|
||||
**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):
|
||||
Install the standalone CLI (one-time setup):
|
||||
|
||||
```bash
|
||||
npm install
|
||||
make tailwind-install
|
||||
```
|
||||
|
||||
Or using Make:
|
||||
Or manually:
|
||||
|
||||
```bash
|
||||
make npm-install
|
||||
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64
|
||||
chmod +x tailwindcss-linux-x64
|
||||
mv tailwindcss-linux-x64 ~/.local/bin/tailwindcss
|
||||
```
|
||||
|
||||
### Development Build
|
||||
|
||||
For development, build without purging (includes all classes, larger file):
|
||||
Build all frontends (includes all classes, larger files):
|
||||
|
||||
```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):
|
||||
Build with minification (smaller files):
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
|
||||
# Or using Make
|
||||
make tailwind-build
|
||||
```
|
||||
|
||||
---
|
||||
### Watch Mode
|
||||
|
||||
## Available npm Scripts
|
||||
Watch for changes during development:
|
||||
|
||||
| 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',
|
||||
// ...
|
||||
]
|
||||
```bash
|
||||
make tailwind-watch
|
||||
```
|
||||
|
||||
### Option 2: Extend the theme
|
||||
---
|
||||
|
||||
Add custom values to `tailwind.config.js`:
|
||||
## Makefile Targets
|
||||
|
||||
| Target | Description |
|
||||
|--------|-------------|
|
||||
| `make tailwind-install` | Install Tailwind standalone CLI |
|
||||
| `make tailwind-dev` | Build all CSS (development) |
|
||||
| `make tailwind-build` | Build all CSS (production, minified) |
|
||||
| `make tailwind-watch` | Watch for changes |
|
||||
|
||||
---
|
||||
|
||||
## Dark Mode
|
||||
|
||||
The platform uses class-based dark mode with the `.dark` class on the `<html>` element:
|
||||
|
||||
```html
|
||||
<html :class="{ 'dark': dark }">
|
||||
<body class="bg-white dark:bg-gray-900">
|
||||
```
|
||||
|
||||
Toggle dark mode with JavaScript:
|
||||
|
||||
```javascript
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'brand': '#123456',
|
||||
},
|
||||
spacing: {
|
||||
'128': '32rem',
|
||||
},
|
||||
},
|
||||
document.documentElement.classList.toggle('dark');
|
||||
localStorage.setItem('darkMode', dark);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding Custom Utilities
|
||||
|
||||
Add custom utilities in the source CSS file:
|
||||
|
||||
```css
|
||||
@layer utilities {
|
||||
.shadow-outline-purple {
|
||||
box-shadow: 0 0 0 3px hsla(262, 97%, 81%, 0.45);
|
||||
}
|
||||
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option 3: Rebuild CSS
|
||||
---
|
||||
|
||||
After any config change, rebuild the CSS:
|
||||
## Adding Custom Components
|
||||
|
||||
```bash
|
||||
make tailwind-dev
|
||||
Add reusable component classes:
|
||||
|
||||
```css
|
||||
@layer components {
|
||||
.form-input {
|
||||
@apply block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm;
|
||||
@apply focus:border-purple-500 focus:ring-1 focus:ring-purple-500;
|
||||
@apply dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg;
|
||||
@apply hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -209,105 +249,51 @@ make tailwind-dev
|
||||
|
||||
### Classes not appearing
|
||||
|
||||
1. **Check purge paths** - Ensure your file is in a scanned directory
|
||||
2. **Check safelist** - Dynamic classes need to be safelisted
|
||||
1. **Check @source paths** - Ensure your templates are in a scanned directory
|
||||
2. **Check class exists** - Tailwind v4 may have renamed some classes
|
||||
3. **Rebuild CSS** - Run `make tailwind-dev` after config changes
|
||||
|
||||
### Dynamic classes in Alpine.js
|
||||
|
||||
**Problem:** Classes in `:class` bindings may be purged.
|
||||
Tailwind v4 uses JIT compilation and scans for complete class names. Dynamic classes work automatically if the full class name appears in your templates:
|
||||
|
||||
```html
|
||||
<!-- This class might be purged -->
|
||||
<!-- This works - full class names visible -->
|
||||
<div :class="isActive ? 'bg-green-600' : 'bg-red-600'">
|
||||
```
|
||||
|
||||
**Solution:** Add to safelist:
|
||||
### Class name changes (v2 → v4)
|
||||
|
||||
```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 { ... }
|
||||
```
|
||||
| v2.x | v4.x |
|
||||
|------|------|
|
||||
| `overflow-ellipsis` | `text-ellipsis` |
|
||||
| `flex-grow-0` | `grow-0` |
|
||||
| `flex-shrink-0` | `shrink-0` |
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always rebuild after config changes**
|
||||
1. **Always rebuild after CSS changes**
|
||||
```bash
|
||||
make tailwind-dev
|
||||
```
|
||||
|
||||
2. **Add dynamic classes to safelist** to prevent purging
|
||||
2. **Use production builds for deployment**
|
||||
```bash
|
||||
make tailwind-build
|
||||
```
|
||||
|
||||
3. **Use production builds for deployment** to minimize file size
|
||||
3. **Keep each frontend's CSS isolated** - Don't cross-import between frontends
|
||||
|
||||
4. **Check existing classes first** before adding custom ones - Tailwind likely has what you need
|
||||
4. **Use @apply sparingly** - Prefer utility classes in templates when possible
|
||||
|
||||
5. **Use consistent color scales** (e.g., `purple-600`, `purple-700`) for hover states
|
||||
5. **Test in both light and dark modes**
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Frontend Overview](overview.md)
|
||||
- [UI Components](shared/ui-components.md)
|
||||
- [Tailwind CSS Official Docs](https://tailwindcss.com/docs)
|
||||
- [Tailwind CSS v4 Blog Post](https://tailwindcss.com/blog/tailwindcss-v4)
|
||||
|
||||
Reference in New Issue
Block a user