Initial commit
This commit is contained in:
340
skills/style-master/scripts/analyze_styles.py
Executable file
340
skills/style-master/scripts/analyze_styles.py
Executable file
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Style Master - Codebase Style Analyzer
|
||||
|
||||
Analyzes a frontend codebase to understand styling approach, extract design tokens,
|
||||
identify patterns, and detect issues.
|
||||
|
||||
Usage:
|
||||
python analyze_styles.py [--path /path/to/project]
|
||||
python analyze_styles.py --detailed
|
||||
python analyze_styles.py --export report.json
|
||||
|
||||
Examples:
|
||||
python analyze_styles.py
|
||||
python analyze_styles.py --path ../my-app --detailed
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
|
||||
def find_style_files(root_path):
|
||||
"""Find all styling-related files in the project."""
|
||||
style_extensions = {
|
||||
'.css', '.scss', '.sass', '.less',
|
||||
'.module.css', '.module.scss',
|
||||
'.styled.js', '.styled.ts', '.styled.jsx', '.styled.tsx'
|
||||
}
|
||||
|
||||
style_files = {
|
||||
'css': [],
|
||||
'scss': [],
|
||||
'sass': [],
|
||||
'less': [],
|
||||
'css_modules': [],
|
||||
'css_in_js': [],
|
||||
'tailwind_config': [],
|
||||
'other': []
|
||||
}
|
||||
|
||||
for root, dirs, files in os.walk(root_path):
|
||||
# Skip common ignore directories
|
||||
dirs[:] = [d for d in dirs if d not in {'node_modules', '.git', 'dist', 'build', '.next', 'out'}]
|
||||
|
||||
for file in files:
|
||||
file_path = Path(root) / file
|
||||
|
||||
if file == 'tailwind.config.js' or file == 'tailwind.config.ts':
|
||||
style_files['tailwind_config'].append(file_path)
|
||||
elif file.endswith('.module.css') or file.endswith('.module.scss'):
|
||||
style_files['css_modules'].append(file_path)
|
||||
elif '.styled.' in file:
|
||||
style_files['css_in_js'].append(file_path)
|
||||
elif file.endswith('.css'):
|
||||
style_files['css'].append(file_path)
|
||||
elif file.endswith('.scss'):
|
||||
style_files['scss'].append(file_path)
|
||||
elif file.endswith('.sass'):
|
||||
style_files['sass'].append(file_path)
|
||||
elif file.endswith('.less'):
|
||||
style_files['less'].append(file_path)
|
||||
|
||||
return style_files
|
||||
|
||||
|
||||
def detect_styling_approach(style_files, root_path):
|
||||
"""Detect the primary styling approach used."""
|
||||
approaches = []
|
||||
|
||||
if style_files['tailwind_config']:
|
||||
approaches.append('Tailwind CSS')
|
||||
|
||||
if style_files['css_in_js']:
|
||||
approaches.append('CSS-in-JS (Styled Components/Emotion)')
|
||||
|
||||
if style_files['css_modules']:
|
||||
approaches.append('CSS Modules')
|
||||
|
||||
if style_files['scss'] or style_files['sass']:
|
||||
approaches.append('Sass/SCSS')
|
||||
|
||||
if style_files['less']:
|
||||
approaches.append('Less')
|
||||
|
||||
if style_files['css'] and not style_files['css_modules']:
|
||||
approaches.append('Vanilla CSS')
|
||||
|
||||
# Check for UI frameworks
|
||||
package_json = Path(root_path) / 'package.json'
|
||||
if package_json.exists():
|
||||
try:
|
||||
with open(package_json, 'r') as f:
|
||||
data = json.load(f)
|
||||
deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
|
||||
|
||||
if '@mui/material' in deps or '@material-ui/core' in deps:
|
||||
approaches.append('Material UI')
|
||||
if '@chakra-ui/react' in deps:
|
||||
approaches.append('Chakra UI')
|
||||
if 'styled-components' in deps:
|
||||
approaches.append('Styled Components')
|
||||
if '@emotion/react' in deps or '@emotion/styled' in deps:
|
||||
approaches.append('Emotion')
|
||||
except:
|
||||
pass
|
||||
|
||||
return approaches if approaches else ['Unknown']
|
||||
|
||||
|
||||
def extract_colors(content):
|
||||
"""Extract color values from CSS content."""
|
||||
colors = []
|
||||
|
||||
# Hex colors
|
||||
hex_pattern = r'#(?:[0-9a-fA-F]{3}){1,2}\b'
|
||||
colors.extend(re.findall(hex_pattern, content))
|
||||
|
||||
# RGB/RGBA
|
||||
rgb_pattern = r'rgba?\([^)]+\)'
|
||||
colors.extend(re.findall(rgb_pattern, content))
|
||||
|
||||
# HSL/HSLA
|
||||
hsl_pattern = r'hsla?\([^)]+\)'
|
||||
colors.extend(re.findall(hsl_pattern, content))
|
||||
|
||||
# CSS custom properties with color-like names
|
||||
custom_prop_pattern = r'--(?:color|bg|background|border|text)[^:;\s]+:\s*([^;]+)'
|
||||
colors.extend(re.findall(custom_prop_pattern, content))
|
||||
|
||||
return colors
|
||||
|
||||
|
||||
def extract_spacing_values(content):
|
||||
"""Extract spacing values (margin, padding, gap)."""
|
||||
spacing = []
|
||||
|
||||
# Match spacing properties
|
||||
spacing_pattern = r'(?:margin|padding|gap)[^:]*:\s*([^;]+)'
|
||||
matches = re.findall(spacing_pattern, content)
|
||||
|
||||
for match in matches:
|
||||
# Extract numeric values
|
||||
values = re.findall(r'\d+(?:\.\d+)?(?:px|rem|em|%|vh|vw)', match)
|
||||
spacing.extend(values)
|
||||
|
||||
return spacing
|
||||
|
||||
|
||||
def extract_css_custom_properties(content):
|
||||
"""Extract CSS custom properties (variables)."""
|
||||
pattern = r'--([\w-]+):\s*([^;]+)'
|
||||
return dict(re.findall(pattern, content))
|
||||
|
||||
|
||||
def analyze_file_content(file_path):
|
||||
"""Analyze content of a single style file."""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
return {
|
||||
'colors': extract_colors(content),
|
||||
'spacing': extract_spacing_values(content),
|
||||
'custom_properties': extract_css_custom_properties(content),
|
||||
'size': len(content),
|
||||
'lines': content.count('\n') + 1
|
||||
}
|
||||
except Exception as e:
|
||||
return {'error': str(e)}
|
||||
|
||||
|
||||
def generate_report(style_files, root_path, detailed=False):
|
||||
"""Generate analysis report."""
|
||||
print("="*70)
|
||||
print(" Style Master - Codebase Analysis")
|
||||
print("="*70)
|
||||
print(f"\nAnalyzing: {root_path}\n")
|
||||
|
||||
# Styling approach
|
||||
approaches = detect_styling_approach(style_files, root_path)
|
||||
print("## Styling Approach\n")
|
||||
for approach in approaches:
|
||||
print(f" [CHECK] {approach}")
|
||||
print()
|
||||
|
||||
# File counts
|
||||
total_files = sum(len(files) for files in style_files.values())
|
||||
print(f"## Files Found: {total_files}\n")
|
||||
for file_type, files in style_files.items():
|
||||
if files:
|
||||
print(f" {file_type}: {len(files)}")
|
||||
print()
|
||||
|
||||
# Detailed analysis
|
||||
if detailed:
|
||||
print("## Design Token Analysis\n")
|
||||
|
||||
all_colors = []
|
||||
all_spacing = []
|
||||
all_custom_props = {}
|
||||
total_size = 0
|
||||
total_lines = 0
|
||||
|
||||
# Analyze all CSS files
|
||||
for file_type, files in style_files.items():
|
||||
if file_type in {'css', 'scss', 'sass', 'less', 'css_modules'}:
|
||||
for file_path in files:
|
||||
analysis = analyze_file_content(file_path)
|
||||
if 'error' not in analysis:
|
||||
all_colors.extend(analysis['colors'])
|
||||
all_spacing.extend(analysis['spacing'])
|
||||
all_custom_props.update(analysis['custom_properties'])
|
||||
total_size += analysis['size']
|
||||
total_lines += analysis['lines']
|
||||
|
||||
# Color analysis
|
||||
if all_colors:
|
||||
color_counts = Counter(all_colors)
|
||||
print(f"**Colors Found**: {len(color_counts)} unique colors")
|
||||
print(f"\nMost used colors:")
|
||||
for color, count in color_counts.most_common(10):
|
||||
print(f" {color}: used {count} times")
|
||||
print()
|
||||
|
||||
# Spacing analysis
|
||||
if all_spacing:
|
||||
spacing_counts = Counter(all_spacing)
|
||||
print(f"**Spacing Values**: {len(spacing_counts)} unique values")
|
||||
print(f"\nMost used spacing:")
|
||||
for value, count in spacing_counts.most_common(10):
|
||||
print(f" {value}: used {count} times")
|
||||
print()
|
||||
|
||||
# Custom properties
|
||||
if all_custom_props:
|
||||
print(f"**CSS Custom Properties**: {len(all_custom_props)} defined")
|
||||
print("\nExamples:")
|
||||
for prop, value in list(all_custom_props.items())[:10]:
|
||||
print(f" --{prop}: {value}")
|
||||
print()
|
||||
|
||||
# Size stats
|
||||
print(f"**Total CSS Size**: {total_size:,} bytes ({total_size / 1024:.1f} KB)")
|
||||
print(f"**Total Lines**: {total_lines:,}")
|
||||
print()
|
||||
|
||||
# Suggestions
|
||||
print("## Suggestions\n")
|
||||
|
||||
suggestions = []
|
||||
|
||||
if not any(style_files.values()):
|
||||
suggestions.append("[WARNING] No style files found. Consider adding styling to your project.")
|
||||
|
||||
if 'Tailwind CSS' not in approaches and 'CSS-in-JS' not in approaches:
|
||||
suggestions.append("[TIP] Consider modern approaches like Tailwind CSS or CSS-in-JS")
|
||||
|
||||
if style_files['css'] and not style_files['css_modules']:
|
||||
suggestions.append("[TIP] Consider CSS Modules to avoid global namespace pollution")
|
||||
|
||||
if not style_files['tailwind_config'] and len(all_colors) > 20:
|
||||
suggestions.append("[WARNING] Many color values detected - consider establishing a color system")
|
||||
|
||||
if len(all_spacing) > 30:
|
||||
suggestions.append("[WARNING] Many spacing values - consider a consistent spacing scale")
|
||||
|
||||
if detailed and not all_custom_props:
|
||||
suggestions.append("[TIP] No CSS custom properties found - consider using them for theming")
|
||||
|
||||
for suggestion in suggestions:
|
||||
print(f" {suggestion}")
|
||||
|
||||
if not suggestions:
|
||||
print(" [OK] Styling approach looks good!")
|
||||
|
||||
print("\n" + "="*70)
|
||||
|
||||
return {
|
||||
'approaches': approaches,
|
||||
'file_counts': {k: len(v) for k, v in style_files.items()},
|
||||
'colors': len(all_colors) if detailed else None,
|
||||
'spacing_values': len(all_spacing) if detailed else None,
|
||||
'custom_properties': len(all_custom_props) if detailed else None,
|
||||
'suggestions': suggestions
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Analyze codebase styling',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--path',
|
||||
type=str,
|
||||
default='.',
|
||||
help='Path to project root (default: current directory)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--detailed',
|
||||
action='store_true',
|
||||
help='Perform detailed analysis (slower but more comprehensive)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--export',
|
||||
type=str,
|
||||
help='Export report to JSON file'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
root_path = Path(args.path).resolve()
|
||||
|
||||
if not root_path.exists():
|
||||
print(f"[ERROR] Error: Path does not exist: {root_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Find style files
|
||||
style_files = find_style_files(root_path)
|
||||
|
||||
# Generate report
|
||||
report_data = generate_report(style_files, root_path, args.detailed)
|
||||
|
||||
# Export if requested
|
||||
if args.export:
|
||||
with open(args.export, 'w') as f:
|
||||
json.dump(report_data, f, indent=2)
|
||||
print(f"\n Report exported to: {args.export}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
397
skills/style-master/scripts/generate_styleguide.py
Executable file
397
skills/style-master/scripts/generate_styleguide.py
Executable file
@@ -0,0 +1,397 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Style Master - Style Guide Generator
|
||||
|
||||
Generates a living style guide based on codebase analysis.
|
||||
|
||||
Usage:
|
||||
python generate_styleguide.py [--path /path/to/project]
|
||||
python generate_styleguide.py --output STYLEGUIDE.md
|
||||
python generate_styleguide.py --format markdown|json
|
||||
|
||||
Examples:
|
||||
python generate_styleguide.py
|
||||
python generate_styleguide.py --output docs/STYLEGUIDE.md
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def load_template():
|
||||
"""Load style guide template."""
|
||||
template_path = Path(__file__).parent.parent / 'References' / 'style-guide-template.md'
|
||||
|
||||
if template_path.exists():
|
||||
with open(template_path, 'r') as f:
|
||||
return f.read()
|
||||
|
||||
# Fallback basic template
|
||||
return """# {project_name} Style Guide
|
||||
|
||||
*Generated: {date}*
|
||||
|
||||
## Design Tokens
|
||||
|
||||
### Colors
|
||||
|
||||
{colors}
|
||||
|
||||
### Typography
|
||||
|
||||
{typography}
|
||||
|
||||
### Spacing
|
||||
|
||||
{spacing}
|
||||
|
||||
## Components
|
||||
|
||||
{components}
|
||||
|
||||
## Guidelines
|
||||
|
||||
{guidelines}
|
||||
|
||||
---
|
||||
|
||||
*This is a living document. Update as the design system evolves.*
|
||||
"""
|
||||
|
||||
|
||||
def detect_project_name(root_path):
|
||||
"""Detect project name from package.json."""
|
||||
package_json = root_path / 'package.json'
|
||||
|
||||
if package_json.exists():
|
||||
try:
|
||||
with open(package_json, 'r') as f:
|
||||
data = json.load(f)
|
||||
return data.get('name', root_path.name)
|
||||
except:
|
||||
pass
|
||||
|
||||
return root_path.name
|
||||
|
||||
|
||||
def extract_design_tokens_from_tailwind(root_path):
|
||||
"""Extract design tokens from Tailwind config."""
|
||||
config_files = ['tailwind.config.js', 'tailwind.config.ts']
|
||||
|
||||
for config_file in config_files:
|
||||
config_path = root_path / config_file
|
||||
if config_path.exists():
|
||||
try:
|
||||
with open(config_path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
return {
|
||||
'source': 'Tailwind Config',
|
||||
'colors': 'See tailwind.config.js theme.extend.colors',
|
||||
'spacing': 'Using Tailwind default spacing scale',
|
||||
'typography': 'See tailwind.config.js theme.extend.fontFamily'
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_css_variables(root_path):
|
||||
"""Extract CSS custom properties from CSS files."""
|
||||
css_vars = {}
|
||||
|
||||
for css_file in root_path.rglob('*.css'):
|
||||
if 'node_modules' in str(css_file):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(css_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Find CSS custom properties
|
||||
pattern = r'--([\w-]+):\s*([^;]+)'
|
||||
matches = re.findall(pattern, content)
|
||||
|
||||
for var_name, var_value in matches:
|
||||
css_vars[var_name] = var_value.strip()
|
||||
except:
|
||||
continue
|
||||
|
||||
return css_vars
|
||||
|
||||
|
||||
def categorize_css_variables(css_vars):
|
||||
"""Categorize CSS variables by type."""
|
||||
categorized = {
|
||||
'colors': {},
|
||||
'spacing': {},
|
||||
'typography': {},
|
||||
'other': {}
|
||||
}
|
||||
|
||||
color_keywords = ['color', 'bg', 'background', 'border', 'text', 'primary', 'secondary', 'accent']
|
||||
spacing_keywords = ['spacing', 'margin', 'padding', 'gap', 'size']
|
||||
typo_keywords = ['font', 'text', 'heading', 'body', 'line-height', 'letter-spacing']
|
||||
|
||||
for var_name, var_value in css_vars.items():
|
||||
var_lower = var_name.lower()
|
||||
|
||||
if any(keyword in var_lower for keyword in color_keywords):
|
||||
categorized['colors'][var_name] = var_value
|
||||
elif any(keyword in var_lower for keyword in spacing_keywords):
|
||||
categorized['spacing'][var_name] = var_value
|
||||
elif any(keyword in var_lower for keyword in typo_keywords):
|
||||
categorized['typography'][var_name] = var_value
|
||||
else:
|
||||
categorized['other'][var_name] = var_value
|
||||
|
||||
return categorized
|
||||
|
||||
|
||||
def format_tokens_section(tokens_dict, title):
|
||||
"""Format design tokens into markdown."""
|
||||
if not tokens_dict:
|
||||
return f"*No {title.lower()} tokens defined yet.*\n"
|
||||
|
||||
output = ""
|
||||
for var_name, var_value in sorted(tokens_dict.items())[:20]: # Limit to 20
|
||||
output += f"- `--{var_name}`: {var_value}\n"
|
||||
|
||||
if len(tokens_dict) > 20:
|
||||
output += f"\n*...and {len(tokens_dict) - 20} more*\n"
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def generate_guidelines(root_path):
|
||||
"""Generate guidelines based on detected patterns."""
|
||||
guidelines = []
|
||||
|
||||
# Check for Tailwind
|
||||
if (root_path / 'tailwind.config.js').exists() or (root_path / 'tailwind.config.ts').exists():
|
||||
guidelines.append("""
|
||||
### Tailwind CSS Usage
|
||||
|
||||
- **Prefer Tailwind utilities** over custom CSS where possible
|
||||
- **Use @apply sparingly** - only for frequently repeated patterns
|
||||
- **Extend the theme** in tailwind.config.js for custom values
|
||||
- **Use arbitrary values** (e.g., `w-[123px]`) only when necessary
|
||||
""")
|
||||
|
||||
# Check for CSS Modules
|
||||
css_modules = list(root_path.rglob('*.module.css')) + list(root_path.rglob('*.module.scss'))
|
||||
if css_modules:
|
||||
guidelines.append("""
|
||||
### CSS Modules
|
||||
|
||||
- **One module per component** - keep styles colocated
|
||||
- **Use camelCase** for class names in modules
|
||||
- **Avoid global styles** unless absolutely necessary
|
||||
- **Compose classes** to avoid duplication
|
||||
""")
|
||||
|
||||
# Default guidelines
|
||||
guidelines.append("""
|
||||
### General Principles
|
||||
|
||||
- **Mobile-first**: Design for mobile, enhance for desktop
|
||||
- **Accessibility**: Ensure WCAG AA compliance minimum
|
||||
- **Performance**: Optimize for fast loading and rendering
|
||||
- **Consistency**: Use design tokens and established patterns
|
||||
- **Dark mode**: Support both light and dark themes where applicable
|
||||
""")
|
||||
|
||||
return "\n".join(guidelines)
|
||||
|
||||
|
||||
def generate_markdown_styleguide(root_path):
|
||||
"""Generate markdown style guide."""
|
||||
project_name = detect_project_name(root_path)
|
||||
date = datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
# Extract design tokens
|
||||
tailwind_tokens = extract_design_tokens_from_tailwind(root_path)
|
||||
css_vars = extract_css_variables(root_path)
|
||||
categorized = categorize_css_variables(css_vars)
|
||||
|
||||
# Build sections
|
||||
colors_section = format_tokens_section(categorized['colors'], 'Colors')
|
||||
spacing_section = format_tokens_section(categorized['spacing'], 'Spacing')
|
||||
typography_section = format_tokens_section(categorized['typography'], 'Typography')
|
||||
|
||||
if tailwind_tokens:
|
||||
colors_section = f"**Using Tailwind CSS**\n\nSee `tailwind.config.js` for the complete color palette.\n\n{colors_section}"
|
||||
|
||||
components_section = """
|
||||
### Button
|
||||
|
||||
*Component documentation to be added*
|
||||
|
||||
### Card
|
||||
|
||||
*Component documentation to be added*
|
||||
|
||||
### Form Elements
|
||||
|
||||
*Component documentation to be added*
|
||||
|
||||
*Add component documentation as your design system grows.*
|
||||
"""
|
||||
|
||||
guidelines_section = generate_guidelines(root_path)
|
||||
|
||||
# Build full guide
|
||||
styleguide = f"""# {project_name} Style Guide
|
||||
|
||||
**Last Updated**: {date}
|
||||
|
||||
*This is a living document that evolves with the project.*
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This style guide documents the design system, patterns, and conventions used in {project_name}.
|
||||
|
||||
## Design Tokens
|
||||
|
||||
Design tokens are the visual design atoms of the design system. They define colors, typography, spacing, and other fundamental values.
|
||||
|
||||
### Colors
|
||||
|
||||
{colors_section}
|
||||
|
||||
### Typography
|
||||
|
||||
{typography_section}
|
||||
|
||||
### Spacing
|
||||
|
||||
{spacing_section}
|
||||
|
||||
## Components
|
||||
|
||||
Document common component patterns here.
|
||||
|
||||
{components_section}
|
||||
|
||||
## Guidelines
|
||||
|
||||
{guidelines_section}
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Color Contrast
|
||||
|
||||
- Ensure all text has minimum 4.5:1 contrast ratio (WCAG AA)
|
||||
- Large text (18pt+) requires minimum 3:1 contrast ratio
|
||||
|
||||
### Focus States
|
||||
|
||||
- All interactive elements must have visible focus indicators
|
||||
- Focus indicators must have 3:1 contrast ratio with background
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
- All functionality available via keyboard
|
||||
- Logical tab order
|
||||
- Skip links for main content
|
||||
|
||||
## Performance
|
||||
|
||||
### CSS Best Practices
|
||||
|
||||
- Remove unused styles in production
|
||||
- Use CSS containment for complex layouts
|
||||
- Optimize font loading with `font-display: swap`
|
||||
- Minimize use of expensive properties in animations
|
||||
|
||||
## Resources
|
||||
|
||||
- [Design System Documentation](./docs/design-system.md)
|
||||
- [Component Library](./src/components/)
|
||||
- [Accessibility Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
||||
|
||||
---
|
||||
|
||||
*Generated by Style Master on {date}*
|
||||
"""
|
||||
|
||||
return styleguide
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate project style guide',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--path',
|
||||
type=str,
|
||||
default='.',
|
||||
help='Path to project root (default: current directory)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
type=str,
|
||||
default='STYLEGUIDE.md',
|
||||
help='Output file path (default: STYLEGUIDE.md)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--format',
|
||||
choices=['markdown', 'json'],
|
||||
default='markdown',
|
||||
help='Output format (default: markdown)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
root_path = Path(args.path).resolve()
|
||||
|
||||
if not root_path.exists():
|
||||
print(f"[ERROR] Error: Path does not exist: {root_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[NOTE] Generating style guide for: {root_path}")
|
||||
|
||||
# Generate style guide
|
||||
if args.format == 'markdown':
|
||||
styleguide = generate_markdown_styleguide(root_path)
|
||||
|
||||
output_path = Path(args.output)
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(styleguide)
|
||||
|
||||
print(f"[OK] Style guide created: {output_path}")
|
||||
print(f"\n Review and customize the generated style guide")
|
||||
print(f" Add component examples, update guidelines, refine tokens\n")
|
||||
else:
|
||||
# JSON format (for programmatic use)
|
||||
css_vars = extract_css_variables(root_path)
|
||||
categorized = categorize_css_variables(css_vars)
|
||||
|
||||
data = {
|
||||
'project': detect_project_name(root_path),
|
||||
'generated': datetime.now().isoformat(),
|
||||
'tokens': categorized
|
||||
}
|
||||
|
||||
output_path = Path(args.output).with_suffix('.json')
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print(f"[OK] Style guide data exported: {output_path}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
38
skills/style-master/scripts/suggest_improvements.py
Normal file
38
skills/style-master/scripts/suggest_improvements.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Style improvement suggester."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def analyze_and_suggest(root_path):
|
||||
"""Analyze styles and suggest improvements."""
|
||||
suggestions = []
|
||||
|
||||
# Check for modern CSS features
|
||||
css_files = list(root_path.rglob('*.css'))
|
||||
if css_files:
|
||||
content = ''.join([f.read_text() for f in css_files[:10] if 'node_modules' not in str(f)])
|
||||
|
||||
if 'float:' in content:
|
||||
suggestions.append("[TIP] Consider replacing float layouts with Flexbox or Grid")
|
||||
if 'px' in content and 'rem' not in content:
|
||||
suggestions.append("[TIP] Consider using rem units for better accessibility")
|
||||
if '@media' in content and '@container' not in content:
|
||||
suggestions.append("[TIP] Consider container queries for component-level responsiveness")
|
||||
if not re.search(r'--[\w-]+:', content):
|
||||
suggestions.append("[TIP] Consider using CSS custom properties for theming")
|
||||
|
||||
return suggestions
|
||||
|
||||
def main():
|
||||
root = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
|
||||
print(" Analyzing for improvement opportunities...\n")
|
||||
|
||||
suggestions = analyze_and_suggest(root)
|
||||
for s in suggestions:
|
||||
print(f" {s}")
|
||||
|
||||
if not suggestions:
|
||||
print(" [OK] No immediate improvements suggested!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
31
skills/style-master/scripts/validate_consistency.py
Normal file
31
skills/style-master/scripts/validate_consistency.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Style consistency validator - checks for design token adherence."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
def validate_colors(root_path):
|
||||
"""Check for color consistency."""
|
||||
colors = []
|
||||
for css_file in root_path.rglob('*.css'):
|
||||
if 'node_modules' not in str(css_file):
|
||||
try:
|
||||
content = css_file.read_text()
|
||||
colors.extend(re.findall(r'#(?:[0-9a-fA-F]{3}){1,2}\b', content))
|
||||
except: pass
|
||||
|
||||
counts = Counter(colors)
|
||||
print(f" Colors: {len(counts)} unique colors found")
|
||||
if len(counts) > 20:
|
||||
print(f" [WARNING] Consider consolidating to a color system")
|
||||
return counts
|
||||
|
||||
def main():
|
||||
root = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
|
||||
print("[SEARCH] Validating style consistency...\n")
|
||||
validate_colors(root)
|
||||
print("\n[OK] Validation complete")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user