font-loading-audit
Audits font loading behavior: FOIT/FOUT detection via timed screenshots, font-display validation per family, font file sizes and format efficiency (WOFF2 vs WOFF vs TTF), preload link validation, unused font declarations, and subsetting opportunities. Produces a font-by-font report with loading timeline and recommendations.
What this skill does
# Font Loading Audit
Perform a comprehensive font loading audit. Inspects every @font-face
declaration, checks font-display strategy, measures font file transfer sizes,
validates preload hints, detects unused font declarations, identifies format
inefficiencies, and captures timed screenshots to detect FOIT (Flash of
Invisible Text) and FOUT (Flash of Unstyled Text).
## When to Use
- Diagnosing invisible or unstyled text flashes during page load.
- Verifying that `font-display: swap` or `optional` is set correctly.
- Checking that fonts are served in WOFF2 format for optimal compression.
- Identifying unused @font-face declarations that waste bandwidth.
- Auditing `<link rel="preload" as="font">` correctness.
- Estimating subsetting savings for fonts with limited character usage.
## Prerequisites
- **Playwright MCP server** connected and responding (all `mcp__playwright__browser_*` tools available).
- **Chromium-based browser** required for CDP CSS domain, Network domain, and `document.fonts` API.
- Target page must be reachable from the browser instance.
## Workflow
### Step 1 -- Set Up Network Monitoring for Font Requests
Enable CDP Network monitoring before navigation to capture font request
timing, transfer sizes, and content types.
```javascript
browser_run_code({
code: `async (page) => {
const client = await page.context().newCDPSession(page);
await client.send('Network.enable');
const fontRequests = {};
client.on('Network.requestWillBeSent', (params) => {
const url = params.request.url;
if (url.match(/\\.(woff2?|ttf|otf|eot)(\\?|$)/i) || params.type === 'Font') {
fontRequests[params.requestId] = {
url,
method: params.request.method,
timestamp: params.timestamp,
initiator: params.initiator ? {
type: params.initiator.type,
url: params.initiator.url || null
} : null
};
}
});
client.on('Network.responseReceived', (params) => {
if (fontRequests[params.requestId]) {
fontRequests[params.requestId].status = params.response.status;
fontRequests[params.requestId].mimeType = params.response.mimeType;
fontRequests[params.requestId].protocol = params.response.protocol;
fontRequests[params.requestId].responseTimestamp = params.timestamp;
fontRequests[params.requestId].headers = {
contentLength: params.response.headers['content-length'] || null,
contentType: params.response.headers['content-type'] || null,
cacheControl: params.response.headers['cache-control'] || null,
accessControlAllowOrigin: params.response.headers['access-control-allow-origin'] || null
};
}
});
client.on('Network.loadingFinished', (params) => {
if (fontRequests[params.requestId]) {
fontRequests[params.requestId].encodedDataLength = params.encodedDataLength;
fontRequests[params.requestId].finishedTimestamp = params.timestamp;
}
});
client.on('Network.loadingFailed', (params) => {
if (fontRequests[params.requestId]) {
fontRequests[params.requestId].failed = true;
fontRequests[params.requestId].errorText = params.errorText;
fontRequests[params.requestId].blockedReason = params.blockedReason || null;
}
});
page.__fontRequests = fontRequests;
page.__cdpClient = client;
return 'Font network monitoring enabled';
}`
})
```
### Step 2 -- Capture Early Screenshot (FOIT/FOUT Detection)
Take a screenshot immediately after navigation starts to capture the initial
text rendering state before custom fonts load.
```
browser_navigate({ url: "<target_url>" })
```
Take the first screenshot as quickly as possible after navigation to catch
FOIT (invisible text) or FOUT (system font fallback):
```
browser_take_screenshot({ type: "png", filename: "font-loading-t0-initial.png" })
```
Wait 500ms and capture another:
```
browser_wait_for({ time: 0.5 })
```
```
browser_take_screenshot({ type: "png", filename: "font-loading-t1-500ms.png" })
```
Wait 1 second more:
```
browser_wait_for({ time: 1 })
```
```
browser_take_screenshot({ type: "png", filename: "font-loading-t2-1500ms.png" })
```
Wait until fonts are fully loaded:
```
browser_wait_for({ time: 2 })
```
```
browser_take_screenshot({ type: "png", filename: "font-loading-t3-final.png" })
```
### Step 3 -- Check document.fonts API Status
Enumerate all fonts tracked by the browser's FontFaceSet API to check
their load status.
```javascript
browser_evaluate({
function: `() => {
const fontSet = document.fonts;
const fonts = [];
fontSet.forEach((fontFace) => {
fonts.push({
family: fontFace.family,
style: fontFace.style,
weight: fontFace.weight,
stretch: fontFace.stretch,
unicodeRange: fontFace.unicodeRange,
display: fontFace.display,
status: fontFace.status // 'unloaded', 'loading', 'loaded', 'error'
});
});
// Group by family
const byFamily = {};
for (const font of fonts) {
const family = font.family.replace(/['"]/g, '');
if (!byFamily[family]) {
byFamily[family] = { variants: [], display: font.display, statuses: new Set() };
}
byFamily[family].variants.push({
weight: font.weight,
style: font.style,
status: font.status,
display: font.display
});
byFamily[family].statuses.add(font.status);
}
// Convert sets for serialization
const familyReport = {};
for (const [family, data] of Object.entries(byFamily)) {
familyReport[family] = {
variantCount: data.variants.length,
display: data.display,
allLoaded: Array.from(data.statuses).every(s => s === 'loaded'),
statuses: Array.from(data.statuses),
variants: data.variants
};
}
return {
readyState: fontSet.status, // 'loading' or 'loaded'
totalFontFaces: fonts.length,
byFamily: familyReport
};
}`
})
```
### Step 4 -- Extract @font-face Rules via CDP
Use the CDP CSS domain to enumerate all @font-face rules from all stylesheets,
including their font-display values and source URLs.
```javascript
browser_run_code({
code: `async (page) => {
const client = page.__cdpClient || await page.context().newCDPSession(page);
await client.send('CSS.enable');
// Get all stylesheets
const fontFaceRules = [];
const styleSheetIds = [];
// Collect stylesheet IDs
client.on('CSS.styleSheetAdded', (params) => {
styleSheetIds.push(params.header);
});
// Wait for stylesheets to be reported
await page.waitForTimeout(1000);
// Get stylesheet text and parse @font-face rules
for (const header of styleSheetIds) {
try {
const { text } = await client.send('CSS.getStyleSheetText', {
styleSheetId: header.styleSheetId
});
// Extract @font-face blocks
const fontFaceRegex = /@font-face\\s*\\{([^}]+)\\}/gi;
let match;
while ((match = fontFaceRegex.exec(text)) !== null) {
const block = match[1];
const getProperty = (prop) => {
const propRegex = new RegExp(prop + '\\\\s*:\\\\s*([^;]+)', 'i');
const m = block.match(propRegex);
return m ? m[1].trim() : null;
};
fontFaceRules.push({
family: (getProperty('font-family') || '').replace(/['"]/g, ''),
style: getProperty('font-style') || 'normal',
weight: getProperty('font-weight') || '400',
display: getProperty('font-display') || null,
src: getProperty('src'),
unicodeRange: getProperty('unicode-range') || null,
sourceSheet: header.sourceURL || header.title || 'inline',
isInline: header.isInline || false
});
}
} catch (e) {
// Some stylesheets may not be accessibleRelated 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.