Documentation

The TailPySCSS Bible

The complete manual for Python developers building production-grade UIs without Node.js. Master the framework, learn the patterns, and ship beautiful applications.

Why TailPySCSS?

Modern web development is trapped in the JavaScript ecosystem. To style a Python web app, you're forced to install Node.js, manage npm packages, configure Webpack, and deal with PostCSS. This is absurd for backend developers.

TailPySCSS breaks this dependency chain. It's a pure-Python CSS framework that gives you the power of utility classes (like Tailwind) and SCSS composition (like Bootstrap), but with zero JavaScript tooling.

  • 🚫 No Node.js: No npm, no package.json, no node_modules folder weighing 300MB.
  • ⚑ Zero Runtime: It's a build-time tool. Scans your Python/HTML files and generates static CSS.
  • 🐍 Framework Agnostic: Works with Flask, Django, FastAPI, Streamlit, or even static HTML.
  • 🎨 SCSS Power: Built on LibSass. Use variables, nesting, mixins, and the full power of SCSS.
  • 🌲 Tree Shaking: Only generates CSS for classes you actually use.

Installation

TailPySCSS is distributed via PyPI. Install it in your virtual environment:

$ pip install tailpyscss

Verify the installation:

$ tailpyscss --version
tailpyscss, version 0.5.3

βœ… Best Practice: Virtual Environments

Always use a virtual environment (venv or conda) to isolate project dependencies. This prevents version conflicts across projects.

CLI User Guide

The TailPySCSS CLI has three essential commands. Understanding each is critical to a smooth workflow.

Command: tailpyscss init

Run this once in your project root. It scaffolds the directory structure and creates configuration files.

$ tailpyscss init
[*] Created static/css/
[*] Created styles/main.scss
[*] Created tailpy_config.py
βœ“ Initialization complete!

What Gets Created?

  • static/css/styles.css - The generated CSS output (served to your app).
  • styles/main.scss - Your custom SCSS entry point.
  • tailpy_config.py - Configuration file (colors, spacing, screens).

⚠️ Warning: Running Init Multiple Times

Running init again will overwrite styles/main.scss and tailpy_config.py. Always back up custom code first!

Command: tailpyscss watch

Use this during development. It watches your project for file changes and rebuilds CSS automatically (Hot Reloading).

$ tailpyscss watch
[*] Watching for changes in .py, .html, .scss files...
[+] Rebuilt CSS in 0.03s
[*] Detected change in app.py
[+] Rebuilt CSS in 0.02s

How It Works

  1. Monitors all .py, .html, and .scss files.
  2. On change, it rescans for utility classes and reruns the SCSS compiler.
  3. Outputs updated styles.css.

βœ… Best Practice: Keep Watch Running

Run watch in a dedicated terminal window. This ensures you always see your CSS changes reflected instantly.

Command: tailpyscss build

Use this for production deployments. It performs a one-time optimized build without watching.

$ tailpyscss build
Tree Shaking Active: Found 42 contexts
Build complete. Output: static/css/styles.css

When to Use This

  • In your Dockerfile (during the build step).
  • In CI/CD pipelines (GitHub Actions, GitLab CI).
  • Before committing to version control (to ensure CSS is up-to-date).

Methodology: The Pro Way (BEM + Utilities)

For scalable, maintainable projects, we recommend the BEM (Block Element Modifier) methodology combined with utility classes for modifiers.

What is BEM?

BEM is a naming convention for CSS classes that enforces semantic structure:

  • Block: A standalone component (.card).
  • Element: A part of the block (.card__header, .card__body).
  • Modifier: A variation of the block (.card--featured).

βœ… Best Practice: BEM for Structure, Utilities for State

Use BEM classes to define the structure of your component (layout, hierarchy). Use utility classes for state (colors, spacing, typography).

Example: Clean Card Component

HTML (Readable)

<div class="card card--featured">
    <div class="card__header">
        <h2 class="text-xl font-bold text-gray-900">
            Article Title
        </h2>
    </div>
    <div class="card__body">
        <p class="text-gray-600">
            This is a clean, readable structure.
        </p>
    </div>
    <div class="card__footer">
        <button class="bg-indigo-600 text-white px-4 py-2 rounded">
            Read More
        </button>
    </div>
</div>

SCSS (styles/_components.scss)

.card {
    background: white;
    border-radius: 0.75rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    overflow: hidden;

    &--featured {
        border: 2px solid #4f46e5;
    }

    &__header {
        padding: 1.5rem;
        border-bottom: 1px solid #e5e7eb;
    }

    &__body {
        padding: 1.5rem;
    }

    &__footer {
        padding: 1rem 1.5rem;
        background: #f9fafb;
    }
}

Why This Works

  • Readable HTML: At a glance, you understand the component hierarchy.
  • Scoped Styles: All .card styles are isolated in one SCSS block.
  • Reusability: The .card can be reused across the app.
  • Utilities for Flexibili ty: You still get to use text-xl, font-bold, etc. for quick tweaks.

Methodology: The Rapid Way (100% Utility Classes)

For prototypes, dashboards, or simple one-off pages, you can skip SCSS entirely and use only utility classes.

<!-- Utility-Only Approach -->
<div class="bg-white rounded-lg shadow p-6">
    <h2 class="text-2xl font-bold text-gray-900 mb-4">Quick Prototype</h2>
    <p class="text-gray-600 mb-6">Built in 5 minutes.</p>
    <button class="bg-indigo-600 text-white px-6 py-2 rounded hover:bg-indigo-700 transition">
        Click Me
    </button>
</div>

⚠️ Warning: Readability at Scale

This approach works great for small pages. But for complex UIs with 20+ components, it becomes unmaintainable. You'll end up with 50-class divs that are impossible to debug.

When to Use Utility-Only

  • Admin dashboards (internal tools).
  • Landing pages (one-off marketing pages).
  • Prototypes (testing ideas quickly).

Mixing Approaches: BEM + UI Components + Utilities

The ultimate workflow combines all three: BEM for custom components, ui="..." for framework components, and utilities for modifiers.

<!-- Hybrid Approach -->
<div class="profile-card">
    <!-- Framework Component -->
    <button ui="button" class="bg-indigo-600 text-white">Follow</button>
    
    <!-- BEM Component -->
    <div class="profile-card__avatar">
        <img src="user.jpg" class="w-16 h-16 rounded-full">
    </div>
    
    <!-- Utility Classes -->
    <h3 class="text-lg font-bold text-gray-900">John Doe</h3>
    <p class="text-sm text-gray-600">Software Engineer</p>
</div>

βœ… Best Practice: Use What Fits

Don't be dogmatic. Use BEM for complex components, framework components where they fit, and utilities everywhere else.

🚫 Anti-Patterns to Avoid

Learn from these common mistakes to keep your codebase clean.

❌ Anti-Pattern #1: The "Div Soup"

Using utility classes to build deeply nested layouts without any semantic naming. This is debugging hell.

<!-- BAD: What does this even do? -->
<div class="flex flex-col items-center justify-center p-6 bg-white rounded-lg shadow">
    <div class="flex items-center gap-4">
        <div class="w-12 h-12 bg-indigo-600 rounded-full"></div>
        <div class="flex flex-col">
            <div class="text-lg font-bold">?????</div>
            <div class="text-sm text-gray-600">?????</div>
        </div>
    </div>
</div>

❌ Anti-Pattern #2: Ignoring SCSS

TailPySCSS gives you the full power of SCSS. Use it! Don't be afraid to write actual CSS for animations, transitions, or complex selectors.

❌ Anti-Pattern #3: Not Using tailpy_config.py

Hardcoding colors like #4f46e5 in your HTML instead of defining them in tailpy_config.py as primary. This makes theming impossible.

🌢️ Integration: Flask

Flask works out-of-the-box with TailPySCSS. No configuration neededβ€”just point your templates at the generated CSS.

Step 1: Directory Structure

my_flask_app/
β”œβ”€β”€ app.py
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ base.html
β”‚   └── index.html
β”œβ”€β”€ static/
β”‚   └── css/
β”‚       └── styles.css  <-- TailPySCSS outputs here
β”œβ”€β”€ styles/
β”‚   └── main.scss       <-- Your custom SCSS
└── tailpy_config.py

Step 2: Base Template (base.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My App{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
</head>
<body class="bg-gray-50">
    <nav class="bg-white shadow">
        <div class="max-w-7xl mx-auto px-4 py-4">
            <a href="/" class="text-xl font-bold text-gray-900">MyApp</a>
        </div>
    </nav>
    
    <main class="max-w-7xl mx-auto px-4 py-8">
        {% block content %}{% endblock %}
    </main>
</body>
</html>

Step 3: Run Flask + Watch

In one terminal, run your Flask app. In another, run tailpyscss watch:

# Terminal 1
$ flask run

# Terminal 2
$ tailpyscss watch

βœ… Production Tip: Flask + Docker

In your Dockerfile, run tailpyscss build before starting the Flask server. This ensures the CSS is always up-to-date.

Production Dockerfile Example

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Build CSS
RUN tailpyscss build

# Run Flask
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]

🎸 Integration: Django

Django requires explicit static file configuration, but once set up, it works seamlessly.

Step 1: Configure Static Files (settings.py)

# settings.py
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static",  # Where TailPySCSS outputs
]

# For production (collectstatic)
STATIC_ROOT = BASE_DIR / "staticfiles"

Step 2: Base Template (base.html)

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Django App{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body class="bg-gray-50">
    {% block content %}{% endblock %}
</body>
</html>

Step 3: Production Deployment

Before deploying, run both tailpyscss build and collectstatic:

# Build CSS
$ tailpyscss build

# Collect static files (for Nginx/Apache)
$ python manage.py collectstatic --noinput

⚠️ Common Pitfall: Forgetting `collectstatic`

In production, Django serves static files from STATIC_ROOT, not STATICFILES_DIRS. Always run collectstatic after building CSS!

⚑ Integration: FastAPI

FastAPI doesn't serve static files by default. You need to mount a StaticFiles directory and use Jinja2 templates.

Step 1: Install Dependencies

$ pip install fastapi uvicorn jinja2 python-multipart

Step 2: App Structure

fastapi_app/
β”œβ”€β”€ main.py
β”œβ”€β”€ templates/
β”‚   └── index.html
β”œβ”€β”€ static/
β”‚   └── css/
β”‚       └── styles.css
└── tailpy_config.py

Step 3: FastAPI App (main.py)

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app = FastAPI()

# Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static")

# Setup Jinja2
templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Step 4: Template (templates/index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FastAPI + TailPySCSS</title>
    <link rel="stylesheet" href="/static/css/styles.css">
</head>
<body class="bg-gray-50 p-8">
    <h1 class="text-3xl font-bold text-gray-900">Hello FastAPI!</h1>
</body>
</html>

Step 5: Run

# Terminal 1
$ uvicorn main:app --reload

# Terminal 2
$ tailpyscss watch

πŸ“Š Integration: Streamlit

Streamlit apps don't support <link> tags natively. Instead, we inject CSS directly using st.markdown.

Step 1: Load CSS Helper

import streamlit as st

def load_tailpyscss(css_file="static/css/styles.css"):
    """Inject TailPySCSS styles into Streamlit"""
    try:
        with open(css_file, "r") as f:
            st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
    except FileNotFoundError:
        st.warning(f"CSS file not found: {css_file}. Run 'tailpyscss build' first!")

# Call at the top of your app
load_tailpyscss()

Step 2: Use Utility Classes

import streamlit as st

load_tailpyscss()

# Custom styled containers
st.markdown('''
<div class="bg-white rounded-lg shadow-lg p-6 mb-4">
    <h2 class="text-2xl font-bold text-gray-900 mb-2">Sales Dashboard</h2>
    <p class="text-gray-600">Real-time analytics</p>
</div>
''', unsafe_allow_html=True)

# Styled metrics grid
st.markdown('''
<div class="grid grid-cols-3 gap-4">
    <div class="bg-indigo-50 p-4 rounded-lg">
        <h3 class="text-indigo-600 font-bold">Revenue</h3>
        <p class="text-2xl font-bold text-gray-900">$45,231</p>
    </div>
</div>
''', unsafe_allow_html=True)

⚠️ Limitation: No Interactive Components

Streamlit's st.button and st.selectbox are pre-styled. You can only style custom HTML that you inject via st.markdown.

🌐 Integration: Plain HTML

For static sites or vanilla JavaScript apps, just link the generated CSS directly.

Step 1: Build CSS

$ tailpyscss build
Build complete. Output: static/css/styles.css

Step 2: Link in HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Static Site</title>
    <link rel="stylesheet" href="static/css/styles.css">
</head>
<body class="bg-gray-100 p-8">
    <div class="max-w-4xl mx-auto bg-white rounded-lg shadow p-8">
        <h1 class="text-3xl font-bold text-gray-900 mb-4">Welcome</h1>
        <p class="text-gray-600">A simple, beautiful page.</p>
    </div>
</body>
</html>

βœ… Pro Tip: Version Your CSS

For static sites, append a version query string to bust browser cache:

<link rel="stylesheet" href="static/css/styles.css?v=1.0.2">

How the Scanning Engine Works

TailPySCSS uses a file-based scanner to detect which utility classes you're using. Here's the process:

  1. Scan: Recursively walks your project directories for .py, .html, and .js files.
  2. Extract: Uses regex to find strings that look like CSS classes (p-4, text-center, grid-cols-3).
  3. Match: Checks if these match any available generators (Layout, Spacing, Colors, Typography, etc.).
  4. Generate SCSS: Writes SCSS code for only the classes you actually use (Tree Shaking).
  5. Compile: LibSass compiles the generated SCSS + your custom SCSS into styles.css.

Example: What Gets Scanned

# app.py
@app.route("/")
def index():
    return render_template("index.html", classes="bg-white p-6 rounded-lg")

# Templates will be scanned for: bg-white, p-6, rounded-lg

Configuration Deep Dive

The tailpy_config.py file controls every aspect of CSS generation. It's pure Python, so you can script it!

Full Configuration Example

# tailpy_config.py

config = {
    # === COLORS ===
    "colors": {
        "primary": "#4f46e5",     # .text-primary, .bg-primary
        "secondary": "#ec4899",
        "accent": "#f59e0b",
        "dark": "#111827",
        "light": "#f9fafb",
    },

    # === SCREENS ===
    "screens": {
        "sm": "640px",
        "md": "768px",
        "lg": "1024px",
        "xl": "1280px",
        "2xl": "1536px",
    },

    # === SPACING ===
    "spacing": {
        "0": "0px",
        "1": "0.25rem",
        "2": "0.5rem",
        "4": "1rem",
        "6": "1.5rem",
        "8": "2rem",
        "12": "3rem",
        "16": "4rem",
    },
}

Dynamic Configuration (Scripted)

Since it's Python, you can generate values dynamically:

# Generate spacing scale automatically
spacing = {}
for i in range(0, 101, 4):
    spacing[str(i)] = f"{i * 0.25}rem"

config = {
    "spacing": spacing  # 0, 4, 8, 12, ... 96, 100
}

Custom SCSS Workflows

TailPySCSS gives you the full power of SCSS. Use it to define reusable components, mixins, and variables.

Example: Using the theme() Function

Access your config values in SCSS using theme():

// styles/main.scss

.my-button {
    background-color: theme('colors.primary');
    padding: theme('spacing.4');
    border-radius: 0.5rem;
    
    &:hover {
        background-color: darken(theme('colors.primary'), 10%);
    }
}

Importing Custom Files

Organize your SCSS into multiple files and import them:

// styles/main.scss
@import "variables";
@import "components/header";
@import "components/footer";

βœ… Best Practice: Partial Files

Prefix partial files with _ (e.g., _variables.scss) so they don't compile independently.

Production Deployment

For production, always run tailpyscss build in your deployment pipeline.

Docker Example (Multi-Stage Build)

FROM python:3.11-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Build CSS with TailPySCSS
RUN tailpyscss build

# === PRODUCTION IMAGE ===
FROM python:3.11-slim

WORKDIR /app
COPY --from=builder /app /app

CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]

GitHub Actions CI/CD

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - run: pip install -r requirements.txt
      - run: tailpyscss build
      
      - name: Deploy to Production
        run: |
          # Your deployment script here