Files
orion/docs/frontend/tailwind-css.md
Samir Boulahtit 8a367077e1 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>
2025-12-04 22:24:45 +01:00

6.8 KiB

Tailwind CSS Build Guide

Version: 2.0 Last Updated: December 2024 Audience: Frontend Developers


Overview

The platform uses Tailwind CSS v4 with the Standalone CLI - no Node.js required. Each frontend (admin, vendor, shop, platform) has its own dedicated CSS configuration.


Architecture

How It Works

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 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

Each frontend loads its own CSS:

<!-- Admin -->
<link rel="stylesheet" href="{{ url_for('static', path='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') }}" />

CSS-First Configuration (Tailwind v4)

Tailwind v4 uses CSS-first configuration instead of tailwind.config.js. All customization happens directly in CSS files.

Source File Structure

/* static/admin/css/tailwind.css */

/* 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 { ... }
}

Key Directives

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-*

Custom Color Palette

All frontends share the same Windmill Dashboard color palette:

@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 */
}

Building Tailwind CSS

Prerequisites

Install the standalone CLI (one-time setup):

make tailwind-install

Or manually:

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

Build all frontends (includes all classes, larger files):

make tailwind-dev

Production Build

Build with minification (smaller files):

make tailwind-build

Watch Mode

Watch for changes during development:

make tailwind-watch

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 :class="{ 'dark': dark }">
  <body class="bg-white dark:bg-gray-900">

Toggle dark mode with JavaScript:

document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', dark);

Adding Custom Utilities

Add custom utilities in the source CSS file:

@layer utilities {
  .shadow-outline-purple {
    box-shadow: 0 0 0 3px hsla(262, 97%, 81%, 0.45);
  }

  .text-balance {
    text-wrap: balance;
  }
}

Adding Custom Components

Add reusable component classes:

@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;
  }
}

Troubleshooting

Classes not appearing

  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

Tailwind v4 uses JIT compilation and scans for complete class names. Dynamic classes work automatically if the full class name appears in your templates:

<!-- This works - full class names visible -->
<div :class="isActive ? 'bg-green-600' : 'bg-red-600'">

Class name changes (v2 → v4)

v2.x v4.x
overflow-ellipsis text-ellipsis
flex-grow-0 grow-0
flex-shrink-0 shrink-0

Best Practices

  1. Always rebuild after CSS changes

    make tailwind-dev
    
  2. Use production builds for deployment

    make tailwind-build
    
  3. Keep each frontend's CSS isolated - Don't cross-import between frontends

  4. Use @apply sparingly - Prefer utility classes in templates when possible

  5. Test in both light and dark modes