- name
- CSS验证器
- description
- 当验证CSS代码时,分析CSS语法,检查样式规范,优化CSS性能。验证响应式设计,检查可访问性,和最佳实践。
- license
- MIT
# CSS验证器技能
## 概述
CSS验证是保证前端样式质量的重要环节。不当的CSS会导致样式冲突、性能问题和维护困难。需要系统性的CSS验证和优化方法。
**核心原则**: 好的CSS应该结构清晰、性能优良、易于维护。坏的CSS会导致样式冲突、加载缓慢、难以调试。
## 何时使用
**始终:**
- 验证CSS语法正确性
- 优化CSS性能
- 检查响应式设计
- 审查CSS优先级
- 可访问性审计
- CSS组织结构审查
**触发短语:**
- "CSS验证器"
- "验证CSS语法"
- "优化CSS性能"
- "检查响应式设计"
- "审查CSS优先级"
- "CSS组织审查"
## CSS验证功能
### 语法验证
- 有效CSS选择器检查
- 正确属性值验证
- 颜色格式验证
- 单位一致性检查
### 性能优化
- 未使用样式检测
- 优先级分析
- 媒体查询优化
- 动画性能检查
### 最佳实践
- 命名规范检查
- 组件组织验证
- 响应式断点检查
- 可访问性考虑
## 常见CSS问题
### 高优先级问题
```
问题:
CSS选择器优先级过高,难以覆盖
错误示例:
.container .section .item.special#main { color: red; } (优先级过高)
后果:
- 难以覆盖样式
- CSS代码膨胀
- 维护困难
解决方案:
1. 使用低优先级选择器
2. 采用BEM命名规范
3. 避免过度嵌套
4. 使用CSS模块化
```
### 响应式设计问题
```
问题:
固定宽度设计,缺少媒体查询
错误示例:
.container { width: 1200px; } /* 固定宽度 */
后果:
- 移动端显示异常
- 用户体验差
- SEO排名降低
解决方案:
1. 使用相对单位
2. 添加媒体查询
3. 采用弹性布局
4. 移动优先设计
```
### 性能问题
```
问题:
CSS性能低效,影响页面加载
错误示例:
- 过度使用@import
- 大量重复样式
- 复杂选择器
- 未优化的动画
解决方案:
1. 减少CSS文件大小
2. 优化选择器性能
3. 使用CSS压缩
4. 优化动画性能
```
### 可访问性问题
```
问题:
CSS可访问性不足,影响用户使用
错误示例:
- 颜色对比度不足
- 缺少焦点样式
- 依赖颜色传达信息
- 字体大小过小
解决方案:
1. 确保颜色对比度
2. 添加焦点指示器
3. 提供多种信息传达方式
4. 使用合适的字体大小
```
## 代码实现示例
### CSS验证器
```python
import re
from typing import List, Dict, Any, Optional, Tuple
from dataclasses import dataclass
from enum import Enum
class IssueSeverity(Enum):
"""问题严重程度"""
ERROR = "error"
WARNING = "warning"
INFO = "info"
class IssueCategory(Enum):
"""问题类别"""
SYNTAX = "syntax"
PERFORMANCE = "performance"
ACCESSIBILITY = "accessibility"
BEST_PRACTICE = "best_practice"
@dataclass
class CSSIssue:
"""CSS问题"""
line_number: int
column: int
severity: IssueSeverity
category: IssueCategory
message: str
suggestion: str
rule_id: str
@dataclass
class ValidationResult:
"""验证结果"""
file_path: str
issues: List[CSSIssue]
metrics: Dict[str, Any]
score: float
class CSSValidator:
def __init__(self):
self.issues: List[CSSIssue] = []
self.css_rules = []
def validate_css(self, css_content: str, file_path: str) -> ValidationResult:
"""验证CSS内容"""
self.issues = []
# 解析CSS
self.css_rules = self._parse_css(css_content)
# 执行各种检查
self._check_syntax(css_content)
self._check_performance(css_content)
self._check_accessibility(css_content)
self._check_best_practices(css_content)
# 计算指标
metrics = self._calculate_metrics(css_content)
# 计算评分
score = self._calculate_score()
return ValidationResult(
file_path=file_path,
issues=self.issues,
metrics=metrics,
score=score
)
def _parse_css(self, css_content: str) -> List[Dict[str, Any]]:
"""解析CSS规则"""
rules = []
# 简化的CSS解析
selector_pattern = r'([^{]+)\s*{([^}]*)}'
matches = re.finditer(selector_pattern, css_content, re.MULTILINE)
for match in matches:
selector = match.group(1).strip()
properties = match.group(2).strip()
rules.append({
'selector': selector,
'properties': properties,
'line': css_content[:match.start()].count('\n') + 1
})
return rules
def _check_syntax(self, css_content: str) -> None:
"""检查CSS语法"""
lines = css_content.split('\n')
for line_num, line in enumerate(lines, 1):
line = line.strip()
if not line or line.startswith('/*'):
continue
# 检查选择器语法
if '{' in line:
selector_part = line.split('{')[0].strip()
if not self._is_valid_selector(selector_part):
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.ERROR,
category=IssueCategory.SYNTAX,
message=f"无效的选择器: {selector_part}",
suggestion="检查选择器语法是否正确",
rule_id="invalid_selector"
))
# 检查属性语法
if ':' in line and not line.startswith('@'):
if not self._is_valid_property(line):
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.ERROR,
category=IssueCategory.SYNTAX,
message="CSS属性语法错误",
suggestion="检查属性和值的格式",
rule_id="invalid_property"
))
def _check_performance(self, css_content: str) -> None:
"""检查CSS性能"""
lines = css_content.split('\n')
for line_num, line in enumerate(lines, 1):
line = line.strip()
# 检查高优先级选择器
if self._has_high_specificity(line):
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.WARNING,
category=IssueCategory.PERFORMANCE,
message="选择器优先级过高",
suggestion="简化选择器,降低优先级",
rule_id="high_specificity"
))
# 检查昂贵的属性
expensive_props = ['box-shadow', 'border-radius', 'transform', 'filter']
for prop in expensive_props:
if prop in line and ':' in line:
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.INFO,
category=IssueCategory.PERFORMANCE,
message=f"使用性能昂贵的属性: {prop}",
suggestion=f"谨慎使用{prop},考虑性能影响",
rule_id="expensive_property"
))
def _check_accessibility(self, css_content: str) -> None:
"""检查可访问性"""
lines = css_content.split('\n')
for line_num, line in enumerate(lines, 1):
line = line.strip()
# 检查固定字体大小
if 'font-size:' in line and any(unit in line for unit in ['px', 'pt']):
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.WARNING,
category=IssueCategory.ACCESSIBILITY,
message="使用固定字体大小",
suggestion="使用相对单位如rem或em",
rule_id="fixed_font_size"
))
# 检查仅依赖颜色
if 'color:' in line and not any(keyword in css_content for keyword in ['border', 'background', 'text-decoration']):
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.WARNING,
category=IssueCategory.ACCESSIBILITY,
message="仅依赖颜色传达信息",
suggestion="添加其他视觉提示如边框或背景",
rule_id="color_only"
))
def _check_best_practices(self, css_content: str) -> None:
"""检查最佳实践"""
lines = css_content.split('\n')
for line_num, line in enumerate(lines, 1):
line = line.strip()
# 检查!important使用
if '!important' in line:
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.WARNING,
category=IssueCategory.BEST_PRACTICE,
message="使用!important",
suggestion="避免使用!important,重构选择器优先级",
rule_id="important_usage"
))
# 检查ID选择器
if '#' in line and '{' in line:
selector = line.split('{')[0].strip()
id_count = selector.count('#')
if id_count > 1:
self.issues.append(CSSIssue(
line_number=line_num,
column=0,
severity=IssueSeverity.WARNING,
category=IssueCategory.BEST_PRACTICE,
message="使用多个ID选择器",
suggestion="使用类选择器替代ID选择器",
rule_id="multiple_ids"
))
def _is_valid_selector(self, selector: str) -> bool:
"""检查选择器是否有效"""
# 简化的选择器验证
if not selector:
return False
# 检查基本语法
invalid_chars = ['<', '>', '(', ')', '+', '~', '=']
return not any(char in selector for char in invalid_chars)
def _is_valid_property(self, line: str) -> bool:
"""检查属性是否有效"""
if ':' not in line:
return False
parts = line.split(':', 1)
if len(parts) != 2:
return False
property_name = parts[0].strip()
property_value = parts[1].strip()
# 检查属性名
if not re.match(r'^[a-zA-Z-]+$', property_name):
return False
# 检查属性值
if not property_value or property_value.endswith(';'):
return False
return True
def _has_high_specificity(self, line: str) -> bool:
"""检查是否有高优先级"""
if '{' not in line:
return False
selector = line.split('{')[0].strip()
# 计算优先级
id_count = selector.count('#')
class_count = selector.count('.')
element_count = len(re.findall(r'\b[a-zA-Z]+\b', selector)) - class_count
# 简化的优先级检查
return id_count > 0 or class_count > 3 or element_count > 5
def _calculate_metrics(self, css_content: str) -> Dict[str, Any]:
"""计算CSS指标"""
lines = css_content.split('\n')
metrics = {
'total_lines': len(lines),
'total_rules': len(self.css_rules),
'total_selectors': sum(1 for rule in self.css_rules if rule['selector']),
'total_properties': sum(len(rule['properties'].split(';')) for rule in self.css_rules),
'issues_by_severity': {
severity.value: len([issue for issue in self.issues if issue.severity == severity])
for severity in IssueSeverity
},
'issues_by_category': {
category.value: len([issue for issue in self.issues if issue.category == category])
for category in IssueCategory
}
}
# 计算复杂度指标
metrics['average_specificity'] = self._calculate_average_specificity()
metrics['unused_percentage'] = self._estimate_unused_percentage()
return metrics
def _calculate_average_specificity(self) -> float:
"""计算平均优先级"""
if not self.css_rules:
return 0.0
total_specificity = 0
for rule in self.css_rules:
selector = rule['selector']
specificity = self._calculate_specificity(selector)
total_specificity += specificity
return total_specificity / len(self.css_rules)
def _calculate_specificity(self, selector: str) -> int:
"""计算选择器优先级"""
id_count = selector.count('#')
class_count = selector.count('.')
element_count = len(re.findall(r'\b[a-zA-Z]+\b', selector)) - class_count
return id_count * 100 + class_count * 10 + element_count
def _estimate_unused_percentage(self) -> float:
"""估算未使用CSS百分比"""
# 简化实现
return 15.0 # 示例值
def _calculate_score(self) -> float:
"""计算CSS评分"""
score = 100.0
# 根据问题严重程度扣分
severity_weights = {
IssueSeverity.ERROR: 10,
IssueSeverity.WARNING: 5,
IssueSeverity.INFO: 1
}
for issue in self.issues:
score -= severity_weights[issue.severity]
return max(0, score)
# CSS优化器
class CSSOptimizer:
def __init__(self):
self.optimizations = []
Voir sur GitHub