A plugin for integrating TailwindCSS with Plotly Dash applications using Dash 3.x hooks. Supports both Tailwind CSS v3 and v4.
- Online Mode: Uses Tailwind CSS CDN for quick setup
- Offline Mode: Builds optimized CSS using Tailwind CLI
- Automatic Build: Automatically builds Tailwind CSS on app startup
- Flexible Configuration: Customizable input/output paths and config files
- Automatic Cleanup: Automatically removes generated files to keep directory clean
- Node.js Management: Automatically download and use specific Node.js versions
- Class-based Architecture: Clean, object-oriented design for better maintainability
- Comprehensive Testing: Full test coverage including unit tests, integration tests, and Dash-specific tests
- Custom Theme Configuration: Extend Tailwind's default theme with custom colors, spacing, and more
- Configurable Cleanup: Control whether intermediate files are cleaned up after build
- Tailwind CSS v3 & v4 Support: Supports both Tailwind CSS version 3 and 4
pip install dash-tailwindcss-pluginOr for development:
pip install -e .For development with test dependencies:
pip install -e .[test]For development with both development and test dependencies:
pip install -e .[dev,test]from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Initialize with CDN mode (default is Tailwind CSS v3)
setup_tailwindcss_plugin(mode="online")
# Or specify Tailwind CSS version (v3 or v4)
# setup_tailwindcss_plugin(mode="online", tailwind_version="4")
app = Dash(__name__)
app.layout = html.Div([
html.H1("Hello, TailwindCSS!", className="text-3xl font-bold text-blue-600"),
html.P("This is styled with Tailwind CSS CDN.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Initialize with offline mode (default)
setup_tailwindcss_plugin(
mode="offline",
tailwind_version="3", # Specify Tailwind CSS version (v3 or v4)
content_path=["**/*.py"], # Files to scan for Tailwind classes
plugin_tmp_dir="_tailwind", # Temporary directory for plugin files
output_css_path="_tailwind/tailwind.css", # Output CSS file
config_js_path="_tailwind/tailwind.config.js", # Tailwind config file
download_node=True, # Download Node.js if not found
node_version="18.17.0" # Specify Node.js version to download
)
app = Dash(__name__)
app.layout = html.Div([
html.H1("Hello, TailwindCSS!", className="text-3xl font-bold text-blue-600"),
html.P("This is styled with locally built Tailwind CSS.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)You can specify a custom temporary directory for plugin files:
from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Initialize with custom plugin temporary directory
setup_tailwindcss_plugin(
mode="offline",
plugin_tmp_dir="_my_tailwind", # Custom temporary directory
input_css_path="_my_tailwind/input.css",
output_css_path="_my_tailwind/output.css",
config_js_path="_my_tailwind/config.js"
)
app = Dash(__name__)
app.layout = html.Div([
html.H1("Custom Directory", className="text-3xl font-bold text-green-600"),
html.P("This uses a custom temporary directory.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)You can control whether to skip rebuilding if CSS was recently generated:
from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Initialize with custom skip build parameters
setup_tailwindcss_plugin(
mode="offline",
skip_build_if_recent=True, # Skip build if CSS was recently generated
skip_build_time_threshold=10 # Consider CSS recent if generated within 10 seconds
)
app = Dash(__name__)
app.layout = html.Div([
html.H1("Smart Rebuild", className="text-3xl font-bold text-purple-600"),
html.P("This uses smart rebuild behavior.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)You can extend Tailwind's default theme by providing a custom theme configuration:
from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Define custom theme configuration
theme_config = {
"colors": {
"brand": {
"50": "#eff6ff",
"100": "#dbeafe",
"200": "#bfdbfe",
"300": "#93c5fd",
"400": "#60a5fa",
"500": "#3b82f6",
"600": "#2563eb",
"700": "#1d4ed8",
"800": "#1e40af",
"900": "#1e3a8a"
}
},
"borderRadius": {
"none": "0px",
"sm": "0.125rem",
"DEFAULT": "0.25rem",
"md": "0.375rem",
"lg": "0.5rem",
"xl": "0.75rem",
"2xl": "1rem",
"3xl": "1.5rem",
"full": "9999px"
}
}
# Initialize with custom theme configuration
setup_tailwindcss_plugin(
mode="offline",
tailwind_theme_config=theme_config
)
app = Dash(__name__)
app.layout = html.Div([
html.H1("Custom Theme", className="text-3xl font-bold text-brand-500"),
html.P("This uses a custom brand color.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)By default, the plugin cleans up intermediate files after building. You can disable this behavior:
from dash import Dash, html
from dash_tailwindcss_plugin import setup_tailwindcss_plugin
# Initialize with cleanup disabled
setup_tailwindcss_plugin(
mode="offline",
clean_after=False # Keep intermediate files after build
)
app = Dash(__name__)
app.layout = html.Div([
html.H1("No Cleanup", className="text-3xl font-bold text-blue-600"),
html.P("Intermediate files will be kept after build.", className="text-gray-700 mt-4")
])
if __name__ == "__main__":
app.run(debug=True)dash-tailwindcss-plugin/
├── dash_tailwindcss_plugin/
│ ├── __init__.py # Exports main plugin function
│ ├── plugin.py # Main plugin implementation with _TailwindCSSPlugin class
│ ├── cli.py # Command-line interface with _TailwindCLI class
│ └── utils.py # Utility functions for Node.js management, file operations, etc.
├── tests/
│ ├── test_plugin.py # Unit tests for plugin core functionality
│ ├── test_utils.py # Unit tests for utility functions
│ ├── test_cli.py # Unit tests for CLI interface
│ ├── test_integration.py # Integration tests for build process
│ ├── test_dash_simple.py # Simple Dash integration tests (no browser required)
│ ├── test_dash_integration.py # Dash end-to-end integration tests (requires browser automation)
│ └── test_dash_callbacks.py # Dash callback and layout tests
├── example_app.py # Example Dash
application
├── requirements.txt # Runtime dependencies
├── requirements-test.txt # Test dependencies
├── setup.py # Setup script for installation
├── pyproject.toml # Build configuration
├── pytest.ini # Pytest configuration
├── ruff.toml # Ruff configuration (linting)
└── README.md # This file- Python 3.8+
- Dash 3.0+
- Node.js 12+ (for offline mode, optional if using download_node feature)
- Adds Tailwind CSS CDN script to the app's HTML head using
hooks.index() - No build process required
- Larger CSS file (includes all Tailwind classes)
- Supports both Tailwind CSS v3 (default CDN: https://cdn.tailwindcss.com) and v4 (default CDN: https://registry.npmmirror.com/@tailwindcss/browser/4/files/dist/index.global.js)
- Uses
hooks.setup(priority=3)to build Tailwind CSS on app startup - Uses
hooks.route(name=built_tailwindcss_link, methods=('GET',), priority=2)to serve the generated CSS file - Uses
hooks.index(priority=1)to inject the CSS link into the HTML head - Automatically installs Tailwind CLI if not present
- Scans specified files for Tailwind classes to create optimized CSS
- Automatically downloads Node.js if requested and not found in PATH
- Automatically cleans up temporary files after build (unless disabled)
- Smart Rebuild: Skips rebuilding if CSS file was generated within the last 5 seconds
- Supports both Tailwind CSS v3 and v4 with appropriate CLI packages
The plugin accepts the following parameters:
mode: "online" or "offline" (default: "offline")tailwind_version: "3" or "4" (default: "3")content_path: Glob patterns for files to scan (default: ["**/*.py"])plugin_tmp_dir: Temporary directory for plugin files (default: "_tailwind")input_css_path: Path to input CSS file (default: "_tailwind/tailwind_input.css")output_css_path: Path to output CSS file (default: "_tailwind/tailwind.css")config_js_path: Path to Tailwind config file (default: "_tailwind/tailwind.config.js")cdn_url: CDN URL for online mode (default: "https://cdn.tailwindcss.com")download_node: Whether to download Node.js if not found (default: False)node_version: Node.js version to download if download_node is True (default: "18.17.0")tailwind_theme_config: Dictionary of custom theme configuration for Tailwind CSS (default: None)clean_after: Whether to clean up generated files after build (default: True)skip_build_if_recent: Whether to skip build if CSS file was recently generated (default: True)skip_build_time_threshold: Time threshold in seconds to consider CSS file as recent (default: 5)
- Clone the repository
- Install dependencies:
pip install -r requirements.txt - Install test dependencies:
pip install -r requirements-test.txt - Install in development mode:
pip install -e . - Run example:
python example_app.py
For development with test dependencies:
pip install -e .[test]For development with both development and test dependencies:
pip install -e .[dev,test]This project uses a layered testing approach:
- Basic tests - Run without browser automation (recommended for most cases)
- Advanced tests - Require browser automation for end-to-end testing
# Install test dependencies
pip install -r requirements-test.txt
# Run basic tests (no browser automation required)
python -m pytest tests/test_plugin.py tests/test_utils.py tests/test_cli.py tests/test_integration.py tests/test_dash_simple.py
# Run all tests (including those requiring browser automation)
python -m pytest tests/
# Run specific test files
python -m pytest tests/test_plugin.py
python -m pytest tests/test_utils.py
python -m pytest tests/test_cli.py
python -m pytest tests/test_dash_simple.py
# Run tests with verbose output
python -m pytest tests/ -v
# Run tests with coverage report
python -m pytest tests/ --cov=dash_tailwindcss_plugin --cov-report=htmlSee tests/README.md for more detailed information about running tests.
python -m buildThis will create both source distribution and wheel files in the dist/ directory.
The package includes a command-line interface:
dash-tailwindcss-plugin init # Initialize Tailwind config
dash-tailwindcss-plugin build # Build CSS manually
dash-tailwindcss-plugin watch # Watch for changes
dash-tailwindcss-plugin clean # Clean up generated filesAll commands support the following options:
--tailwind-version VERSION: Version of Tailwind CSS to use (3 or 4) (default: "3")--content-path INPUT: Glob pattern for files to scan for Tailwind classes. Can be specified multiple times. (default: ["**/*.py"])--plugin-tmp-dir PATH: Temporary directory for plugin files (default: "./_tailwind")--input-css-path PATH: Path to input CSS file (default: "./_tailwind/tailwind_input.css")--output-css-path OUTPUT: Path to output CSS file (default: "./_tailwind/tailwind.css")--config-js-path CONFIG: Path to Tailwind config file (default: "./_tailwind/tailwind.config.js")--tailwind-theme-config JSON: JSON string of custom theme configuration for Tailwind CSS--download-node: Download Node.js if not found in PATH--node-version VERSION: Node.js version to download (if --download-node is used)--clean-after: Clean up generated files after build (only for build command)
Example:
dash-tailwindcss-plugin build --download-node --node-version 18.17.0Example with multiple content paths:
dash-tailwindcss-plugin build --content-path "**/*.py" --content-path "**/*.js"Example with custom theme configuration:
dash-tailwindcss-plugin build --tailwind-theme-config "{\"colors\":{\"brand\":{\"500\":\"#3b82f6\"}}}"Example with Tailwind CSS v4:
dash-tailwindcss-plugin build --tailwind-version 4Example with custom plugin temporary directory:
dash-tailwindcss-plugin build --plugin-tmp-dir "./my-tailwind" --input-css-path "./my-tailwind/input.css" --output-css-path "./my-tailwind/output.css" --config-js-path "./my-tailwind/config.js"The plugin follows a clean, object-oriented architecture:
- _TailwindCSSPlugin (plugin.py): Main plugin class that handles all Tailwind CSS integration
- _TailwindCLI (cli.py): CLI tool class that provides command-line interface
- Utility Functions (utils.py): Helper functions for Node.js management, file operations, etc.
setup_tailwindcss_plugin(): Main entry point for the pluginmain(): Entry point for the CLI tool
This design ensures clean separation of concerns and makes the codebase easier to maintain and extend.