core-web-vitals-audit
Measure Core Web Vitals (LCP, INP, CLS) with full attribution across mobile, tablet, and desktop viewports. Produces a multi-viewport report card with pass/fail grades, element-level attribution, and annotated screenshots.
What this skill does
# Core Web Vitals Audit
Perform a comprehensive Core Web Vitals assessment across three viewport
breakpoints. Installs PerformanceObserver-based instrumentation, drives
realistic user interactions, and harvests LCP, INP, and CLS with full
attribution data.
## When to Use
- Evaluating page performance before a release.
- Diagnosing which element is the Largest Contentful Paint.
- Identifying layout shift sources and their visual impact.
- Measuring interaction responsiveness (INP) on a real page.
- Comparing performance across mobile, tablet, and desktop viewports.
## Prerequisites
- **Playwright MCP server** connected and responding (all `mcp__playwright__browser_*` tools available).
- **Chromium-based browser** required for `PerformanceObserver` with full attribution (`layoutShift.sources`, `largest-contentful-paint` element attribution, `event` timing breakdowns).
- Target page must be reachable from the browser instance.
## Workflow
Repeat the following steps for each viewport:
| Viewport | Width | Height |
|----------|-------|--------|
| Mobile | 375 | 667 |
| Tablet | 768 | 1024 |
| Desktop | 1440 | 900 |
### Step 1 -- Resize the Viewport
Call `browser_resize` with the viewport dimensions.
```
browser_resize({ width: 375, height: 667 })
```
### Step 2 -- Navigate to the Target Page
Call `browser_navigate` to load the page. This triggers a fresh page load so
LCP and CLS observers capture the full loading lifecycle.
```
browser_navigate({ url: "<target_url>" })
```
### Step 3 -- Install Performance Observers
Call `browser_evaluate` with the following function to set up observers for all
three Core Web Vitals before any interactions occur.
```javascript
browser_evaluate({
function: `() => {
window.__cwv = { lcp: null, cls: { value: 0, sources: [] }, interactions: [] };
// --- LCP Observer ---
new PerformanceObserver((list) => {
const entries = list.getEntries();
const last = entries[entries.length - 1];
window.__cwv.lcp = {
value: last.startTime,
renderTime: last.renderTime,
loadTime: last.loadTime,
size: last.size,
element: last.element ? last.element.tagName + (last.element.id ? '#' + last.element.id : '') : null,
url: last.url || null
};
// Store element reference for screenshot annotation
window.__cwv._lcpElement = last.element || null;
}).observe({ type: 'largest-contentful-paint', buffered: true });
// --- CLS Observer ---
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
window.__cwv.cls.value += entry.value;
if (entry.sources) {
for (const src of entry.sources) {
window.__cwv.cls.sources.push({
node: src.node ? src.node.tagName + (src.node.id ? '#' + src.node.id : '') + (src.node.className ? '.' + String(src.node.className).split(' ')[0] : '') : null,
previousRect: src.previousRect ? { x: src.previousRect.x, y: src.previousRect.y, width: src.previousRect.width, height: src.previousRect.height } : null,
currentRect: src.currentRect ? { x: src.currentRect.x, y: src.currentRect.y, width: src.currentRect.width, height: src.currentRect.height } : null
});
}
}
}
}
}).observe({ type: 'layout-shift', buffered: true });
// --- INP / Event Timing Observer ---
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
window.__cwv.interactions.push({
name: entry.name,
duration: entry.duration,
startTime: entry.startTime,
processingStart: entry.processingStart,
processingEnd: entry.processingEnd,
inputDelay: entry.processingStart - entry.startTime,
processingTime: entry.processingEnd - entry.processingStart,
presentationDelay: entry.startTime + entry.duration - entry.processingEnd,
interactionId: entry.interactionId,
target: entry.target ? entry.target.tagName + (entry.target.id ? '#' + entry.target.id : '') : null
});
}
}).observe({ type: 'event', durationThreshold: 0, buffered: true });
return 'CWV observers installed';
}`
})
```
### Step 4 -- Perform Realistic Interactions
After observers are installed, simulate realistic user behavior to trigger INP
measurements and potential layout shifts.
1. **Scroll the page** -- call `browser_evaluate`:
```javascript
browser_evaluate({
function: `() => {
window.scrollBy(0, window.innerHeight * 2);
return 'scrolled';
}`
})
```
2. **Wait for content to settle** -- call `browser_wait_for`:
```
browser_wait_for({ time: 2 })
```
3. **Click the primary CTA** -- take a `browser_snapshot` to identify the
primary call-to-action, then call `browser_click` on it using the ref from
the snapshot.
4. **Type in a search field** (if present) -- use `browser_snapshot` to locate
a search input, then call `browser_type` with a short query string.
5. **Wait again** for any async responses:
```
browser_wait_for({ time: 2 })
```
### Step 5 -- Harvest Metrics
Call `browser_evaluate` to collect all recorded data.
```javascript
browser_evaluate({
function: `() => {
const data = window.__cwv;
// Compute INP (p98 of interaction durations)
const durations = data.interactions
.filter(i => i.interactionId > 0)
.map(i => i.duration)
.sort((a, b) => a - b);
let inp = 0;
if (durations.length > 0) {
const p98Index = Math.min(Math.ceil(durations.length * 0.98) - 1, durations.length - 1);
inp = durations[p98Index];
}
return {
lcp: data.lcp,
cls: { value: Math.round(data.cls.value * 10000) / 10000, sources: data.cls.sources.slice(0, 10) },
inp: {
value: inp,
totalInteractions: durations.length,
slowest5: data.interactions
.filter(i => i.interactionId > 0)
.sort((a, b) => b.duration - a.duration)
.slice(0, 5)
}
};
}`
})
```
### Step 6 -- Annotate and Screenshot the LCP Element
Call `browser_evaluate` to highlight the LCP element, then take a screenshot.
```javascript
browser_evaluate({
function: `() => {
const el = window.__cwv._lcpElement;
if (el) {
el.style.outline = '4px solid red';
el.style.outlineOffset = '2px';
el.scrollIntoView({ block: 'center' });
return 'LCP element highlighted: ' + el.tagName + (el.id ? '#' + el.id : '');
}
return 'No LCP element reference available';
}`
})
```
```
browser_take_screenshot({ type: "png", filename: "cwv-<viewport>-lcp.png" })
```
### Step 7 -- Repeat for Next Viewport
Go back to Step 1 with the next viewport dimensions.
## Interpreting Results
### Thresholds (per Google Web Vitals)
| Metric | Good | Needs Improvement | Poor |
|--------|--------------|-------------------|----------|
| LCP | < 2500 ms | 2500 -- 4000 ms | > 4000 ms |
| CLS | < 0.1 | 0.1 -- 0.25 | > 0.25 |
| INP | < 200 ms | 200 -- 500 ms | > 500 ms |
### Report Card Format
For each viewport, produce a summary:
```
## Core Web Vitals -- Mobile (375x667)
| Metric | Value | Rating |
|--------|---------|--------------------|
| LCP | 1850 ms | GOOD |
| CLS | 0.032 | GOOD |
| INP | 245 ms | NEEDS IMPROVEMENT |
### LCP Attribution
- Element: IMG#hero-banner
- Size: 285,600 px
- Render Time: 1850 ms
### CLS Sources (top shifts)
1. DIV.ad-slot -- shifted 120px downward at load
2. IMG.lazy -- shifted 45px when decoded
### Slowest Interactions
1. click on BUTTON#submit -- 245ms (inputDelay: 12ms, processing: 180ms, presentation: 53ms)
2. keydown on INPUT#search -- 89ms (inputDelay: 4ms, processing: 62ms, presentation: 23ms)
```
### What to Look For
- Related in Security
mac-ops
IncludedComprehensive macOS workstation operations — diagnose kernel panics, identify failing drives, audit launchd startup items, decode wake reasons, triage TCC permission denials, manage APFS snapshots, recover from no-boot. Use for: Mac is slow, slow bootup, won't boot, kernel panic, kernel_task hot, mds_stores CPU, photoanalysisd, cloudd, login loop, gray screen, sleep wake failure, drive failing, IO errors, APFS snapshots eating space, Time Machine local snapshots, Spotlight indexing, launchd, LaunchAgent, LaunchDaemon, login items, TCC permissions, Full Disk Access, Screen Recording denied, Gatekeeper, quarantine, com.apple.quarantine, app is damaged, helper tool, /Library/PrivilegedHelperTools, pmset, wake reasons, dark wake, sysdiagnose, panic.ips, DiagnosticReports, configuration profile, MDM profile, remote diagnostics over SSH.
a11y-audit
IncludedRun accessibility audits on web projects combining automated scanning (axe-core, Lighthouse) with WCAG 2.1 AA compliance mapping, manual check guidance, and structured reporting. Output is configurable: markdown report only, markdown plus machine-readable JSON, or markdown plus issue tracker integration. Use this skill whenever the user mentions "accessibility audit", "a11y audit", "WCAG audit", "accessibility check", "compliance scan", or asks to check a web project for accessibility issues. Also trigger when the user wants to verify WCAG conformance or map findings to a specific standard (CAN-ASC-6.2, EN 301 549, ADA/AODA).
erpclaw
IncludedAI-native ERP system with self-extending OS. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting. 413 actions across 14 domains, 43 expansion modules. Constitutional guardrails, adversarial audit, schema migration. Double-entry GL, immutable audit trail, US GAAP.
assess
IncludedAssesses and rates quality 0-10 across multiple dimensions (correctness, maintainability, security, performance, testability, simplicity) with pros/cons analysis. Compares against project conventions and prior decisions from memory. Produces structured evaluation reports with actionable improvement suggestions. Use when evaluating code, designs, architectures, or comparing alternative approaches.
spring-boot-security-jwt
IncludedProvides JWT authentication and authorization patterns for Spring Boot 3.5.x covering token generation with JJWT, Bearer/cookie authentication, database/OAuth2 integration, and RBAC/permission-based access control using Spring Security 6.x. Use when implementing authentication or authorization in Spring Boot applications.
code-hardcode-audit
IncludedDetect hardcoded values, magic numbers, and leaked secrets. TRIGGERS - hardcode audit, magic numbers, PLR2004, secret scanning.