blob: c7e85b231e8d182d3a6d39a7cd5f114c2c05ba62 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
#!/bin/bash
# TailwindCSS Pre-commit Hook
# Validates TailwindCSS usage and optimizations before commits
set -e
echo "🎨 Running TailwindCSS pre-commit checks..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
# Check if TailwindCSS config exists
check_tailwind_config() {
print_status $BLUE "Checking TailwindCSS configuration..."
if [[ ! -f "tailwind.config.js" && ! -f "tailwind.config.ts" ]]; then
print_status $RED "❌ No TailwindCSS configuration file found"
exit 1
fi
print_status $GREEN "✅ TailwindCSS configuration found"
}
# Validate CSS utility usage patterns
validate_utility_patterns() {
print_status $BLUE "Validating TailwindCSS utility patterns..."
# Check for overly long class strings (potential refactoring candidates)
local long_classes=$(grep -r "class[Name]*=['\"][^'\"]*['\"]" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null | \
sed -E 's/.*class[Name]*=["'\''`]([^"'\''`]*)["'\''`].*/\1/' | \
awk 'length($0) > 150 { print FILENAME ":" FNR ":" $0 }' || true)
if [[ -n "$long_classes" ]]; then
print_status $YELLOW "⚠️ Found potentially complex utility combinations (>150 characters):"
echo "$long_classes"
print_status $YELLOW "Consider extracting these into components or using @apply directive"
fi
# Check for hardcoded colors (should use design tokens)
local hardcoded_colors=$(grep -r "bg-\(red\|blue\|green\|yellow\|purple\|pink\|indigo\)-[0-9]" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null || true)
if [[ -n "$hardcoded_colors" ]]; then
print_status $YELLOW "⚠️ Found hardcoded color utilities. Consider using semantic color tokens:"
echo "$hardcoded_colors" | head -5
fi
print_status $GREEN "✅ Utility patterns validated"
}
# Check for responsive design patterns
validate_responsive_patterns() {
print_status $BLUE "Checking responsive design patterns..."
# Look for mobile-first violations (desktop-first patterns)
local desktop_first=$(grep -r "class[Name]*=['\"][^'\"]*['\"]" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null | \
sed -E 's/.*class[Name]*=["'\''`]([^"'\''`]*)["'\''`].*/\1/' | \
grep -E "(^| )(block|flex|grid|hidden)" | \
grep -E "(lg|xl|2xl):(block|flex|grid|hidden)" | \
grep -vE "(sm|md):" | head -5 || true)
if [[ -n "$desktop_first" ]]; then
print_status $YELLOW "⚠️ Potential desktop-first patterns detected. Consider mobile-first approach:"
echo "$desktop_first"
fi
print_status $GREEN "✅ Responsive patterns checked"
}
# Build and analyze CSS bundle size
analyze_bundle_size() {
print_status $BLUE "Analyzing CSS bundle size..."
# Check if build script exists
if npm run --silent 2>/dev/null | grep -q "build\|build:css"; then
# Build CSS
npm run build:css >/dev/null 2>&1 || npm run build >/dev/null 2>&1 || {
print_status $YELLOW "⚠️ Could not run CSS build command"
return 0
}
# Find the generated CSS file
local css_file=$(find . -name "*.css" -path "*/dist/*" -o -path "*/build/*" -o -path "*/.next/static/css/*" 2>/dev/null | head -1)
if [[ -n "$css_file" && -f "$css_file" ]]; then
local size=$(wc -c < "$css_file")
local size_kb=$((size / 1024))
print_status $GREEN "📊 CSS bundle size: ${size_kb}KB"
# Warn if bundle is large
if [[ $size_kb -gt 100 ]]; then
print_status $YELLOW "⚠️ CSS bundle is large (${size_kb}KB). Consider optimization:"
print_status $YELLOW " - Review unused utilities"
print_status $YELLOW " - Optimize content paths in tailwind.config.js"
print_status $YELLOW " - Use CSS purging effectively"
fi
else
print_status $YELLOW "⚠️ Could not find generated CSS file"
fi
else
print_status $YELLOW "⚠️ No build script found in package.json"
fi
}
# Check for accessibility considerations
validate_accessibility() {
print_status $BLUE "Checking accessibility patterns..."
# Check for focus states on interactive elements
local missing_focus=$(grep -r "class[Name]*=['\"][^'\"]*['\"]" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null | \
grep -E "(button|input|select|textarea)" | \
grep -v "focus:" | head -3 || true)
if [[ -n "$missing_focus" ]]; then
print_status $YELLOW "⚠️ Interactive elements without focus states detected:"
echo "$missing_focus"
print_status $YELLOW "Consider adding focus: states for accessibility"
fi
# Check for proper contrast utilities
local low_contrast=$(grep -r "text-gray-[123]00" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null || true)
if [[ -n "$low_contrast" ]]; then
print_status $YELLOW "⚠️ Potentially low contrast text colors found:"
echo "$low_contrast" | head -3
print_status $YELLOW "Verify accessibility contrast ratios"
fi
print_status $GREEN "✅ Accessibility patterns checked"
}
# Check for performance anti-patterns
validate_performance() {
print_status $BLUE "Checking performance patterns..."
# Check for layout-shifting animations
local layout_animations=$(grep -r "transition-\(width\|height\|top\|left\)" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null || true)
if [[ -n "$layout_animations" ]]; then
print_status $YELLOW "⚠️ Layout-affecting transitions found (may cause performance issues):"
echo "$layout_animations" | head -3
print_status $YELLOW "Consider using transform-based animations instead"
fi
# Check for excessive arbitrary values
local arbitrary_values=$(grep -r "\[\w*\]" src/ --include="*.jsx" --include="*.tsx" --include="*.vue" --include="*.html" 2>/dev/null | wc -l)
if [[ $arbitrary_values -gt 10 ]]; then
print_status $YELLOW "⚠️ High usage of arbitrary values ($arbitrary_values instances)"
print_status $YELLOW "Consider adding values to your TailwindCSS configuration"
fi
print_status $GREEN "✅ Performance patterns checked"
}
# Validate content configuration
validate_content_config() {
print_status $BLUE "Validating content configuration..."
local config_file="tailwind.config.js"
if [[ -f "tailwind.config.ts" ]]; then
config_file="tailwind.config.ts"
fi
# Check if content paths are specific enough
if ! grep -q "components" "$config_file" 2>/dev/null; then
print_status $YELLOW "⚠️ Consider adding specific content paths for better purging"
fi
# Check for safelist configuration for dynamic classes
if grep -r "class[Name]*=.*\${" src/ --include="*.jsx" --include="*.tsx" >/dev/null 2>&1; then
if ! grep -q "safelist" "$config_file" 2>/dev/null; then
print_status $YELLOW "⚠️ Dynamic class generation detected but no safelist configured"
print_status $YELLOW "Consider adding a safelist to prevent CSS purging of dynamic classes"
fi
fi
print_status $GREEN "✅ Content configuration validated"
}
# Main execution
main() {
local start_time=$(date +%s)
# Run all checks
check_tailwind_config
validate_utility_patterns
validate_responsive_patterns
validate_accessibility
validate_performance
validate_content_config
analyze_bundle_size
local end_time=$(date +%s)
local duration=$((end_time - start_time))
print_status $GREEN "✅ All TailwindCSS checks completed in ${duration}s"
print_status $BLUE "Ready to commit! 🚀"
}
# Run the main function
main
|