Files
orion/docs/frontend/tailwind-css.md
Samir Boulahtit 4e28d91a78 refactor: migrate templates and static files to self-contained modules
Templates Migration:
- Migrate admin templates to modules (tenancy, billing, monitoring, marketplace, etc.)
- Migrate vendor templates to modules (tenancy, billing, orders, messaging, etc.)
- Migrate storefront templates to modules (catalog, customers, orders, cart, checkout, cms)
- Migrate public templates to modules (billing, marketplace, cms)
- Keep shared templates in app/templates/ (base.html, errors/, partials/, macros/)
- Migrate letzshop partials to marketplace module

Static Files Migration:
- Migrate admin JS to modules: tenancy (23 files), core (5 files), monitoring (1 file)
- Migrate vendor JS to modules: tenancy (4 files), core (2 files)
- Migrate shared JS: vendor-selector.js to core, media-picker.js to cms
- Migrate storefront JS: storefront-layout.js to core
- Keep framework JS in static/ (api-client, utils, money, icons, log-config, lib/)
- Update all template references to use module_static paths

Naming Consistency:
- Rename static/platform/ to static/public/
- Rename app/templates/platform/ to app/templates/public/
- Update all extends and static references

Documentation:
- Update module-system.md with shared templates documentation
- Update frontend-structure.md with new module JS organization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 14:34:16 +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/public/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