phy-css-dead-code
Unused CSS detector and dead selector auditor. Scans your stylesheet files against all HTML, JSX, TSX, Vue, and template files to find selectors that are defined but never referenced anywhere in your codebase. Detects CSS framework (Tailwind, Bootstrap, CSS Modules, vanilla CSS, Sass/SCSS) and applies the right analysis strategy for each. Reports estimated byte savings per dead rule, finds !important overuse, duplicate selectors, and orphaned media-query blocks. Generates a safe-deletion plan with exact grep verification commands. Works with PostCSS, Sass, Less, and raw CSS. Zero external API — uses npx and grep only. Triggers on "unused CSS", "dead CSS", "CSS bloat", "remove unused styles", "CSS cleanup", "what CSS can I delete", "/css-dead-code".
What this skill does
# CSS Dead Code Auditor
CSS only grows. Every sprint adds new rules; almost none get removed. A feature gets deleted — the styles stay. A component gets renamed — the old class selectors stay. After a year, 30-40% of your stylesheet is dead weight: downloaded by every user, parsed by every browser, doing nothing.
Point this skill at your project and get a complete inventory of dead selectors, their exact file locations, estimated byte savings, and safe-deletion commands.
**Works with any CSS methodology. No config. No build step. Zero external APIs.**
---
## Trigger Phrases
- "unused CSS", "dead CSS selectors", "CSS dead code"
- "CSS bloat", "remove unused styles", "CSS cleanup"
- "what CSS can I delete", "CSS coverage", "unused classes"
- "Tailwind purge", "Bootstrap unused", "CSS audit"
- "stylesheet cleanup", "orphaned selectors"
- "/css-dead-code"
---
## How to Provide Input
```bash
# Option 1: Audit the current directory
/css-dead-code
# Option 2: Specific CSS file or directory
/css-dead-code src/styles/
/css-dead-code assets/main.css
# Option 3: Specify content directory (where HTML/JSX lives)
/css-dead-code --content src/
# Option 4: Specific framework
/css-dead-code --framework tailwind
/css-dead-code --framework vanilla
/css-dead-code --framework bootstrap
# Option 5: Show only selectors that can be safely deleted (high confidence)
/css-dead-code --safe-only
# Option 6: Estimate size savings only (quick mode)
/css-dead-code --size-estimate
# Option 7: Check a single selector
/css-dead-code --check ".btn-legacy-primary"
```
---
## Step 1: Detect CSS Framework and Project Structure
```bash
# Detect CSS framework from project files
FRAMEWORK="unknown"
if [ -f "tailwind.config.js" ] || [ -f "tailwind.config.ts" ] || [ -f "tailwind.config.cjs" ]; then
FRAMEWORK="tailwind"
elif grep -r "bootstrap" package.json 2>/dev/null | grep -q "\"bootstrap\""; then
FRAMEWORK="bootstrap"
elif find . -name "*.module.css" -not -path "*/node_modules/*" 2>/dev/null | head -1 | grep -q "."; then
FRAMEWORK="css-modules"
elif find . -name "*.scss" -o -name "*.sass" -not -path "*/node_modules/*" 2>/dev/null | head -3 | grep -q "."; then
FRAMEWORK="sass"
else
FRAMEWORK="vanilla"
fi
echo "Detected framework: $FRAMEWORK"
# Find all CSS/SCSS/SASS/Less files
CSS_FILES=$(find . \( -name "*.css" -o -name "*.scss" -o -name "*.sass" -o -name "*.less" \) \
-not -path "*/node_modules/*" \
-not -path "*/dist/*" \
-not -path "*/.next/*" \
-not -path "*/build/*" \
2>/dev/null)
CSS_COUNT=$(echo "$CSS_FILES" | wc -l | tr -d ' ')
echo "Found $CSS_COUNT CSS/SCSS files"
# Find all template/component files
CONTENT_FILES=$(find . \( -name "*.html" -o -name "*.jsx" -o -name "*.tsx" \
-o -name "*.vue" -o -name "*.svelte" -o -name "*.hbs" \
-o -name "*.erb" -o -name "*.blade.php" -o -name "*.pug" \) \
-not -path "*/node_modules/*" \
-not -path "*/dist/*" \
2>/dev/null)
CONTENT_COUNT=$(echo "$CONTENT_FILES" | wc -l | tr -d ' ')
echo "Found $CONTENT_COUNT template/component files to cross-reference"
```
---
## Step 2: Framework-Specific Analysis
### Tailwind CSS
```bash
# Check tailwind.config.js content paths
echo "=== Tailwind Config Check ==="
python3 -c "
import re, json
try:
with open('tailwind.config.js') as f:
content = f.read()
# Extract content array
content_match = re.search(r'content\s*:\s*\[(.*?)\]', content, re.DOTALL)
if content_match:
print('Content paths configured:')
for line in content_match.group(1).split(','):
line = line.strip().strip('\"\'')
if line:
print(f' {line}')
else:
print('⚠️ No content array found in tailwind.config.js — all classes may be purged!')
except FileNotFoundError:
print('tailwind.config.js not found')
"
# Find classes actually used in content files
echo ""
echo "=== Tailwind Classes In Use ==="
# Extract all class="" values from content files
grep -rh "class[Name]*=\"[^\"]*\"\|class[Name]*={[^}]*}" . \
--include="*.jsx" --include="*.tsx" --include="*.html" --include="*.vue" \
--exclude-dir="node_modules" --exclude-dir="dist" 2>/dev/null | \
grep -oE '[a-z][a-z0-9:-]+' | sort -u | head -50
# Run Tailwind CLI to see what would be purged
if command -v npx &>/dev/null; then
echo ""
echo "Running Tailwind dry-run to check purged classes..."
npx --yes tailwindcss -i ./input.css -o /tmp/tw-output.css --minify 2>/dev/null
echo "Output size: $(du -h /tmp/tw-output.css 2>/dev/null | cut -f1)"
fi
```
### Vanilla CSS / SCSS
```python
# Extract all selectors from CSS files and cross-reference with content
import re, os, sys
from pathlib import Path
from collections import defaultdict
def extract_selectors(css_content):
"""Extract all CSS selectors from a stylesheet."""
# Remove comments
css = re.sub(r'/\*.*?\*/', '', css_content, flags=re.DOTALL)
# Remove @-rules (media queries, keyframes) content for now
css = re.sub(r'@[^{]+\{[^}]+\}', '', css)
# Extract selectors (before the { block)
selectors = re.findall(r'([^{}@][^{}]*)\s*\{', css)
result = []
for sel_group in selectors:
# Split on commas for multi-selector rules
for sel in sel_group.split(','):
sel = sel.strip()
if sel and not sel.startswith('@'):
result.append(sel)
return result
def extract_class_refs(content):
"""Extract all class and ID references from template/component files."""
classes = set()
ids = set()
# class="foo bar baz"
for match in re.finditer(r'class(?:Name)?\s*=\s*["\']([^"\']+)["\']', content):
classes.update(match.group(1).split())
# className={`...`} dynamic classes
for match in re.finditer(r'["\']([a-z][a-z0-9-_]+)["\']', content):
classes.add(match.group(1))
# id="foo"
for match in re.finditer(r'\bid\s*=\s*["\']([^"\']+)["\']', content):
ids.add(match.group(1))
return classes, ids
# Collect all selectors from CSS files
all_selectors = defaultdict(list) # selector -> [(file, line_num)]
css_dirs = ['.']
for css_dir in css_dirs:
for fpath in Path(css_dir).rglob('*.css'):
if 'node_modules' in str(fpath) or 'dist' in str(fpath):
continue
content = fpath.read_text(errors='ignore')
for sel in extract_selectors(content):
all_selectors[sel].append(str(fpath))
print(f"Total unique selectors found: {len(all_selectors)}")
# Collect all class/id references from content files
all_classes = set()
all_ids = set()
content_exts = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.erb', '.hbs']
for root, dirs, files in os.walk('.'):
dirs[:] = [d for d in dirs if d not in {'node_modules', 'dist', '.next', 'build', '.git'}]
for fname in files:
if any(fname.endswith(ext) for ext in content_exts):
fpath = Path(root) / fname
content = fpath.read_text(errors='ignore')
c, i = extract_class_refs(content)
all_classes.update(c)
all_ids.update(i)
print(f"Unique class references found in templates: {len(all_classes)}")
# Find unused selectors
unused = []
for selector, locations in all_selectors.items():
# Extract the class name from the selector
class_matches = re.findall(r'\.([a-zA-Z][a-zA-Z0-9_-]*)', selector)
id_matches = re.findall(r'#([a-zA-Z][a-zA-Z0-9_-]*)', selector)
is_used = False
for cls in class_matches:
if cls in all_classes:
is_used = True
break
for id_name in id_matches:
if id_name in all_ids:
is_used = True
break
# Skip element-only selectors (body, h1, p, etc.) — too risky to flag
if not class_matches and not id_matches:
continue # element selector, skip
if not is_used:
unused.append((selector, locations))
print(f"\nUnused selectors: {len(unused)}")
for sel, locs in sorted(unused, key=lambda x: len(x[1]), reverse=True)[:Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.