From workflow-skills
Audit Astro applications for performance issues including bundle size, hydration patterns, image optimization, build-time performance, accessibility, and Core Web Vitals. Use when optimizing performance, debugging slow pages, or preparing for production deployment.
npx claudepluginhub arosenkranz/claude-code-config --plugin workflow-skillsThis skill is limited to using the following tools:
Comprehensive performance analysis for Astro applications covering JavaScript bundles, hydration strategies, images, CSS, build-time performance, accessibility, and Core Web Vitals.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Designs, implements, and audits WCAG 2.2 AA accessible UIs for Web (ARIA/HTML5), iOS (SwiftUI traits), and Android (Compose semantics). Audits code for compliance gaps.
Comprehensive performance analysis for Astro applications covering JavaScript bundles, hydration strategies, images, CSS, build-time performance, accessibility, and Core Web Vitals.
Detect over-hydration:
# Find all client directives in .astro files
grep -r "client:" src/ --include="*.astro"
# Count by directive type
grep -r "client:load" src/ --include="*.astro" | wc -l
grep -r "client:idle" src/ --include="*.astro" | wc -l
grep -r "client:visible" src/ --include="*.astro" | wc -l
Check for:
client:load on non-critical components (header, footer, static content)client:visible used for below-the-fold contentclient:idle for non-critical interactive elementsBundle size targets:
Find unoptimized images:
# Find <img> tags (not using astro:assets)
grep -r "<img" src/ --include="*.astro" | grep -v "astro:assets"
# Find images without width/height (CLS risk)
grep -r "<img" src/ --include="*.astro" | grep -v "width=" | grep -v "height="
# Find images without alt text (accessibility)
grep -r "<img" src/ --include="*.astro" | grep -v "alt="
Check for:
<img> instead of <Image> from astro:assetswidth and height attributes (causes CLS)alt text (accessibility violation)loading="lazy" for below-the-fold images<Image> component with format="webp"Remote image handling:
---
import { Image } from 'astro:assets';
// ✅ Use inferSize for remote images
<Image
src="https://example.com/image.jpg"
alt="Description"
inferSize
loading="lazy"
/>
---
Image performance targets:
Detect CSS issues:
# Find scoped styles (check for duplication)
grep -r "<style>" src/ --include="*.astro" -A 5
# Find @apply usage (can bloat CSS)
grep -r "@apply" src/ --include="*.astro" --include="*.css"
# Check for unused Tailwind classes (run in project root)
npx tailwindcss --minify --output dist/check.css
Check for:
@apply (defeats utility-first benefits)content paths in tailwind.config.mjsCSS optimization:
// tailwind.config.mjs
export default {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
],
// ✅ JIT mode enabled by default in Tailwind 3+
};
CSS performance targets:
Audit client directives:
# Get directive usage stats
echo "client:load: $(grep -r 'client:load' src/ --include='*.astro' | wc -l)"
echo "client:idle: $(grep -r 'client:idle' src/ --include='*.astro' | wc -l)"
echo "client:visible: $(grep -r 'client:visible' src/ --include='*.astro' | wc -l)"
echo "client:media: $(grep -r 'client:media' src/ --include='*.astro' | wc -l)"
echo "client:only: $(grep -r 'client:only' src/ --include='*.astro' | wc -l)"
Review patterns:
client:load components per pageclient:load on components > 50KBclient:load on components below the foldclient:visible or client:idleclient:loadCheck for build-time issues:
# Monitor build time
time npm run build
# Check static path generation
grep -r "getStaticPaths" src/pages --include="*.astro"
# Count generated pages
ls -1 dist/**/*.html | wc -l
Build performance checks:
getStaticPaths returning > 1000 routesgetStaticPaths without cachingPagination strategy:
---
// ✅ Paginate large collections
export async function getStaticPaths({ paginate }) {
const posts = await getCollection('blog');
// Warn for large collections
if (posts.length > 1000) {
console.warn(`⚠️ Large collection: ${posts.length} posts. Pagination recommended.`);
}
return paginate(posts, {
pageSize: 20, // 20-50 items per page
});
}
---
Build-time targets:
Performance targets:
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | ≤ 2.5s | 2.5s - 4.0s | > 4.0s |
| FID (First Input Delay) | ≤ 100ms | 100ms - 300ms | > 300ms |
| CLS (Cumulative Layout Shift) | ≤ 0.1 | 0.1 - 0.25 | > 0.25 |
| TTFB (Time to First Byte) | ≤ 800ms | 800ms - 1800ms | > 1800ms |
| FCP (First Contentful Paint) | ≤ 1.8s | 1.8s - 3.0s | > 3.0s |
LCP optimization:
---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<!-- ✅ Optimize LCP image -->
<Image
src={heroImage}
alt="Hero"
width={1200}
height={600}
format="webp"
loading="eager" // Don't lazy load LCP image
fetchpriority="high"
class="w-full"
/>
FID/INP optimization:
client:idle, client:visible)is:inline sparingly (increases bundle size)CLS optimization:
width and height on imagesmin-heightfont-display: swap for web fontsResource loading:
---
// ✅ Preload critical assets
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin />
// ✅ Preconnect to external domains
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://api.example.com" />
// ✅ Prefetch next page
<link rel="prefetch" href="/blog/next-article" />
---
Check for:
Enable View Transitions:
---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---
<head>
<ViewTransitions />
</head>
Prefetching configuration:
// astro.config.mjs
export default defineConfig({
prefetch: {
defaultStrategy: 'viewport', // Prefetch links in viewport
prefetchAll: false, // Only prefetch visible links
},
});
Benefits:
Automated checks:
# Find images without alt text
grep -r "<img" src/ --include="*.astro" | grep -v 'alt='
# Find inputs without labels
grep -r '<input' src/ --include="*.astro" | grep -v 'aria-label' | grep -v 'aria-labelledby'
# Check heading hierarchy
grep -r '<h[1-6]' src/ --include="*.astro"
# Find buttons without accessible names
grep -r '<button' src/ --include="*.astro" | grep -v 'aria-label' | grep -v '>'
Accessibility checks:
:focus-visible)Accessibility targets:
Lighthouse accessibility score:
# Install Lighthouse CLI
npm install -g @lhci/cli
# Run audit (including accessibility)
lhci autorun --collect.url=http://localhost:4321 --collect.numberOfRuns=3
# Or use Chrome DevTools:
# 1. Open DevTools (F12)
# 2. Go to Lighthouse tab
# 3. Select Performance + Accessibility + Best Practices
# 4. Click "Analyze page load"
Lighthouse targets:
# Install visualizer
npm install --save-dev rollup-plugin-visualizer
# Add to astro.config.mjs
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
vite: {
plugins: [
visualizer({
open: true,
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
}),
],
},
});
# Build and view bundle
npm run build
# Opens stats.html in browser
Analyze:
# Run WebPageTest via CLI
npx webpagetest test https://example.com \
--key YOUR_API_KEY \
--location Dulles:Chrome \
--runs 3 \
--first-view-only
Metrics to track:
# Profile Astro build
DEBUG=vite:* npm run build 2>&1 | grep "transform"
# Measure build time
time npm run build
# Check output size
du -sh dist/
Build time analysis:
Problem: Too much JavaScript shipped to client
Detection:
# Count client directives
grep -r "client:" src/ --include="*.astro" | wc -l
# Find client:load usage
grep -r "client:load" src/ --include="*.astro"
Solutions:
client:load with client:idle or client:visibleImpact: 40-80KB JS reduction per component
Problem: Large images causing slow LCP and high CLS
Detection:
# Find <img> tags not using Image component
grep -r "<img" src/ --include="*.astro" | grep -v "Image"
# Find images without dimensions
grep -r "<img" src/ --include="*.astro" | grep -v "width="
Solutions:
<Image> from astro:assetswidth and heightformat="webp" or format="avif"loading="lazy" for below-the-fold imagesfetchpriority="high" for LCP imageImpact: 50-80% file size reduction, 1-2s LCP improvement
Problem: Large CSS bundle from unused Tailwind utilities
Detection:
# Check CSS bundle size
ls -lh dist/_astro/*.css
# Find @apply usage
grep -r "@apply" src/ --include="*.astro" --include="*.css"
Solutions:
content paths in tailwind.config.mjs@apply with direct utilitiesImpact: 20-40KB CSS reduction
Problem: Fonts and CSS blocking initial render
Detection:
Solutions:
<!-- ✅ Preload critical fonts -->
<link
rel="preload"
href="/fonts/inter.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<!-- ✅ Use font-display: swap -->
<style>
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* Show fallback while loading */
}
</style>
Impact: 0.5-1.5s FCP improvement
Problem: Too many pages generated, long build times
Detection:
# Count HTML files
ls -1 dist/**/*.html | wc -l
# Measure build time
time npm run build
Solutions:
getStaticPaths output (< 1000 pages)Impact: 50-80% build time reduction
Problem: Missing alt text, poor semantics, low contrast
Detection:
# Find accessibility issues
grep -r "<img" src/ | grep -v 'alt='
grep -r '<input' src/ | grep -v 'label'
Solutions:
Impact: Improved usability for 15%+ of users
client:load with client:idle on non-critical componentsloading="lazy" to below-the-fold imagesfetchpriority="high"<img> to <Image> for optimization@apply to direct utilitiesclient:visibleExample configuration:
// performance-budget.json
{
"budgets": [
{
"resourceType": "script",
"budget": 100 // KB
},
{
"resourceType": "stylesheet",
"budget": 50 // KB
},
{
"resourceType": "image",
"budget": 300 // KB per page
},
{
"resourceType": "total",
"budget": 500 // KB total
}
],
"build": {
"maxBuildTime": 300, // 5 minutes
"maxPages": 1000
}
}
Monitor in CI:
# .github/workflows/performance.yml
- name: Performance Budget
run: |
npx bundlesize
npm run build
BUILD_TIME=$(expr $(date +%s) - $START_TIME)
if [ $BUILD_TIME -gt 300 ]; then
echo "Build time exceeded 5 minutes"
exit 1
fi
Web Vitals reporting:
---
// src/layouts/BaseLayout.astro
---
<script>
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
// Send to analytics service
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(metric),
headers: { 'Content-Type': 'application/json' },
});
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
</script>
Build time tracking:
// Track build metrics over time
const buildMetrics = {
timestamp: new Date().toISOString(),
buildTime: process.env.BUILD_TIME,
pageCount: /* count HTML files */,
bundleSize: /* measure dist/ size */,
};
Before deploying to production:
Ask Claude to audit your Astro app:
Claude will analyze your project and provide actionable recommendations.