Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
AI-powered UI styling skill with 67 UI styles and 161 reasoning rules for professional interface design.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/tailwind_config_gen.py
1#!/usr/bin/env python32"""3Tailwind CSS Configuration Generator45Generate tailwind.config.js/ts with custom theme configuration.6Supports colors, fonts, spacing, breakpoints, and plugin recommendations.7"""89import argparse10import json11import sys12from pathlib import Path13from typing import Any, Dict, List, Optional141516class TailwindConfigGenerator:17"""Generate Tailwind CSS configuration files."""1819def __init__(20self,21typescript: bool = True,22framework: str = "react",23output_path: Optional[Path] = None,24):25"""26Initialize generator.2728Args:29typescript: If True, generate .ts config, else .js30framework: Framework name (react, vue, svelte, nextjs)31output_path: Output file path (default: auto-detect)32"""33self.typescript = typescript34self.framework = framework35self.output_path = output_path or self._default_output_path()36self.config: Dict[str, Any] = self._base_config()3738def _default_output_path(self) -> Path:39"""Determine default output path."""40ext = "ts" if self.typescript else "js"41return Path.cwd() / f"tailwind.config.{ext}"4243def _base_config(self) -> Dict[str, Any]:44"""Create base configuration structure."""45return {46"darkMode": ["class"],47"content": self._default_content_paths(),48"theme": {49"extend": {}50},51"plugins": []52}5354def _default_content_paths(self) -> List[str]:55"""Get default content paths for framework."""56paths = {57"react": [58"./src/**/*.{js,jsx,ts,tsx}",59"./index.html",60],61"vue": [62"./src/**/*.{vue,js,ts,jsx,tsx}",63"./index.html",64],65"svelte": [66"./src/**/*.{svelte,js,ts}",67"./src/app.html",68],69"nextjs": [70"./app/**/*.{js,ts,jsx,tsx}",71"./pages/**/*.{js,ts,jsx,tsx}",72"./components/**/*.{js,ts,jsx,tsx}",73],74}75return paths.get(self.framework, paths["react"])7677def add_colors(self, colors: Dict[str, str]) -> None:78"""79Add custom colors to theme.8081Args:82colors: Dict of color_name: color_value83Value can be hex (#3b82f6) or variable (hsl(var(--primary)))84"""85if "colors" not in self.config["theme"]["extend"]:86self.config["theme"]["extend"]["colors"] = {}8788self.config["theme"]["extend"]["colors"].update(colors)8990def add_color_palette(self, name: str, base_color: str) -> None:91"""92Add full color palette (50-950 shades) for a base color.9394Args:95name: Color name (e.g., 'brand', 'primary')96base_color: Base color in oklch format or hex97"""98# For simplicity, use CSS variable approach99if "colors" not in self.config["theme"]["extend"]:100self.config["theme"]["extend"]["colors"] = {}101102self.config["theme"]["extend"]["colors"][name] = {103"50": f"var(--color-{name}-50)",104"100": f"var(--color-{name}-100)",105"200": f"var(--color-{name}-200)",106"300": f"var(--color-{name}-300)",107"400": f"var(--color-{name}-400)",108"500": f"var(--color-{name}-500)",109"600": f"var(--color-{name}-600)",110"700": f"var(--color-{name}-700)",111"800": f"var(--color-{name}-800)",112"900": f"var(--color-{name}-900)",113"950": f"var(--color-{name}-950)",114}115116def add_fonts(self, fonts: Dict[str, List[str]]) -> None:117"""118Add custom font families.119120Args:121fonts: Dict of font_type: [font_names]122e.g., {'sans': ['Inter', 'system-ui', 'sans-serif']}123"""124if "fontFamily" not in self.config["theme"]["extend"]:125self.config["theme"]["extend"]["fontFamily"] = {}126127self.config["theme"]["extend"]["fontFamily"].update(fonts)128129def add_spacing(self, spacing: Dict[str, str]) -> None:130"""131Add custom spacing values.132133Args:134spacing: Dict of name: value135e.g., {'18': '4.5rem', 'navbar': '4rem'}136"""137if "spacing" not in self.config["theme"]["extend"]:138self.config["theme"]["extend"]["spacing"] = {}139140self.config["theme"]["extend"]["spacing"].update(spacing)141142def add_breakpoints(self, breakpoints: Dict[str, str]) -> None:143"""144Add custom breakpoints.145146Args:147breakpoints: Dict of name: width148e.g., {'3xl': '1920px', 'tablet': '768px'}149"""150if "screens" not in self.config["theme"]["extend"]:151self.config["theme"]["extend"]["screens"] = {}152153self.config["theme"]["extend"]["screens"].update(breakpoints)154155def add_plugins(self, plugins: List[str]) -> None:156"""157Add plugin requirements.158159Args:160plugins: List of plugin names161e.g., ['@tailwindcss/typography', '@tailwindcss/forms']162"""163for plugin in plugins:164if plugin not in self.config["plugins"]:165self.config["plugins"].append(plugin)166167def recommend_plugins(self) -> List[str]:168"""169Get plugin recommendations based on configuration.170171Returns:172List of recommended plugin package names173"""174recommendations = []175176# Always recommend animation plugin177recommendations.append("tailwindcss-animate")178179# Framework-specific recommendations180if self.framework == "nextjs":181recommendations.append("@tailwindcss/typography")182183return recommendations184185def generate_config_string(self) -> str:186"""187Generate configuration file content.188189Returns:190Configuration file as string191"""192if self.typescript:193return self._generate_typescript()194return self._generate_javascript()195196def _generate_typescript(self) -> str:197"""Generate TypeScript configuration."""198plugins_str = self._format_plugins()199200config_json = json.dumps(self.config, indent=2)201202# Remove plugin array from JSON (we'll add it with require())203config_obj = self.config.copy()204config_obj.pop("plugins", None)205config_json = json.dumps(config_obj, indent=2)206207return f"""import type {{ Config }} from 'tailwindcss'208209const config: Config = {{210{self._indent_json(config_json, 1)}211plugins: [{plugins_str}],212}}213214export default config215"""216217def _generate_javascript(self) -> str:218"""Generate JavaScript configuration."""219plugins_str = self._format_plugins()220221config_obj = self.config.copy()222config_obj.pop("plugins", None)223config_json = json.dumps(config_obj, indent=2)224225return f"""/** @type {{import('tailwindcss').Config}} */226module.exports = {{227{self._indent_json(config_json, 1)}228plugins: [{plugins_str}],229}}230"""231232def _format_plugins(self) -> str:233"""Format plugins array for config."""234if not self.config["plugins"]:235return ""236237plugin_requires = [238f"require('{plugin}')" for plugin in self.config["plugins"]239]240return ", ".join(plugin_requires)241242def _indent_json(self, json_str: str, level: int) -> str:243"""Add indentation to JSON string."""244indent = " " * level245lines = json_str.split("\n")246# Skip first and last lines (braces)247indented = [indent + line for line in lines[1:-1]]248return "\n".join(indented)249250def write_config(self) -> tuple[bool, str]:251"""252Write configuration to file.253254Returns:255Tuple of (success, message)256"""257try:258config_content = self.generate_config_string()259260self.output_path.write_text(config_content)261262return True, f"Configuration written to {self.output_path}"263264except OSError as e:265return False, f"Failed to write config: {e}"266267def validate_config(self) -> tuple[bool, str]:268"""269Validate configuration.270271Returns:272Tuple of (valid, message)273"""274# Check content paths exist275if not self.config["content"]:276return False, "No content paths specified"277278# Check if extending empty theme279if not self.config["theme"]["extend"]:280return True, "Warning: No theme extensions defined"281282return True, "Configuration valid"283284285def main():286"""CLI entry point."""287parser = argparse.ArgumentParser(288description="Generate Tailwind CSS configuration",289formatter_class=argparse.RawDescriptionHelpFormatter,290epilog="""291Examples:292# Generate TypeScript config for Next.js293python tailwind_config_gen.py --framework nextjs294295# Generate JavaScript config with custom colors296python tailwind_config_gen.py --js --colors brand:#3b82f6 accent:#8b5cf6297298# Add custom fonts299python tailwind_config_gen.py --fonts display:"Playfair Display,serif"300301# Add custom spacing and breakpoints302python tailwind_config_gen.py --spacing navbar:4rem --breakpoints 3xl:1920px303304# Add recommended plugins305python tailwind_config_gen.py --plugins306""",307)308309parser.add_argument(310"--framework",311choices=["react", "vue", "svelte", "nextjs"],312default="react",313help="Target framework (default: react)",314)315316parser.add_argument(317"--js",318action="store_true",319help="Generate JavaScript config instead of TypeScript",320)321322parser.add_argument(323"--output",324type=Path,325help="Output file path",326)327328parser.add_argument(329"--colors",330nargs="*",331metavar="NAME:VALUE",332help="Custom colors (e.g., brand:#3b82f6)",333)334335parser.add_argument(336"--fonts",337nargs="*",338metavar="TYPE:FAMILY",339help="Custom fonts (e.g., sans:'Inter,system-ui')",340)341342parser.add_argument(343"--spacing",344nargs="*",345metavar="NAME:VALUE",346help="Custom spacing (e.g., navbar:4rem)",347)348349parser.add_argument(350"--breakpoints",351nargs="*",352metavar="NAME:WIDTH",353help="Custom breakpoints (e.g., 3xl:1920px)",354)355356parser.add_argument(357"--plugins",358action="store_true",359help="Add recommended plugins",360)361362parser.add_argument(363"--validate-only",364action="store_true",365help="Validate config without writing file",366)367368args = parser.parse_args()369370# Initialize generator371generator = TailwindConfigGenerator(372typescript=not args.js,373framework=args.framework,374output_path=args.output,375)376377# Add custom colors378if args.colors:379colors = {}380for color_spec in args.colors:381try:382name, value = color_spec.split(":", 1)383colors[name] = value384except ValueError:385print(f"Invalid color spec: {color_spec}", file=sys.stderr)386sys.exit(1)387generator.add_colors(colors)388389# Add custom fonts390if args.fonts:391fonts = {}392for font_spec in args.fonts:393try:394font_type, family = font_spec.split(":", 1)395fonts[font_type] = [f.strip().strip("'\"") for f in family.split(",")]396except ValueError:397print(f"Invalid font spec: {font_spec}", file=sys.stderr)398sys.exit(1)399generator.add_fonts(fonts)400401# Add custom spacing402if args.spacing:403spacing = {}404for spacing_spec in args.spacing:405try:406name, value = spacing_spec.split(":", 1)407spacing[name] = value408except ValueError:409print(f"Invalid spacing spec: {spacing_spec}", file=sys.stderr)410sys.exit(1)411generator.add_spacing(spacing)412413# Add custom breakpoints414if args.breakpoints:415breakpoints = {}416for bp_spec in args.breakpoints:417try:418name, width = bp_spec.split(":", 1)419breakpoints[name] = width420except ValueError:421print(f"Invalid breakpoint spec: {bp_spec}", file=sys.stderr)422sys.exit(1)423generator.add_breakpoints(breakpoints)424425# Add recommended plugins426if args.plugins:427recommended = generator.recommend_plugins()428generator.add_plugins(recommended)429print(f"Added recommended plugins: {', '.join(recommended)}")430print("\nInstall with:")431print(f" npm install -D {' '.join(recommended)}")432433# Validate434valid, message = generator.validate_config()435if not valid:436print(f"Validation failed: {message}", file=sys.stderr)437sys.exit(1)438439if message.startswith("Warning"):440print(message)441442# Validate only mode443if args.validate_only:444print("Configuration valid")445print("\nGenerated config:")446print(generator.generate_config_string())447sys.exit(0)448449# Write config450success, message = generator.write_config()451print(message)452sys.exit(0 if success else 1)453454455if __name__ == "__main__":456main()457