Loading
Inline CSS Info and Size

Inline CSS Info and Size

Analyzes all inline <style> tags on the page, measuring their size and identifying optimization opportunities. This helps understand how much CSS is inlined and whether it's being used effectively.

When inline CSS is good:

ScenarioBenefit
Critical CSS (< 14 KB)Eliminates render-blocking request, faster FCP
Above-the-fold stylesImmediate rendering without waiting for external CSS
Single-page appsReduces HTTP requests for small style chunks

When inline CSS is problematic:

IssueImpactThreshold
Too largeBloats HTML, slower TTFB> 14 KB (TCP slow start)
DuplicatedWasted bytes, harder maintenanceSame styles in multiple <style> tags
Not criticalDelays HTML parsingStyles for below-the-fold content
UncachedRe-downloaded on every pageLarge styles that could be external

14 KB Rule: The first TCP round-trip can deliver ~14 KB. Critical CSS should fit within this budget for optimal First Contentful Paint.

Snippet

// Inline CSS Analysis - Find and analyze all inline style tags
// https://webperf-snippets.nucliweb.net
 
(() => {
  const formatBytes = (bytes) => {
    if (bytes === 0) return "0 B";
    const k = 1024;
    const sizes = ["B", "KB", "MB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
  };
 
  const formatKB = (bytes) => (bytes / 1024).toFixed(2);
 
  // Get all inline style tags
  const styleTags = Array.from(document.querySelectorAll("style"));
 
  if (styleTags.length === 0) {
    console.group("%cπŸ“ Inline CSS Analysis", "font-weight: bold; font-size: 14px;");
    console.log("%cβœ… No inline <style> tags found.", "color: #22c55e; font-weight: bold;");
    console.log("The page uses external stylesheets only.");
    console.groupEnd();
    return;
  }
 
  // Analyze each style tag
  const styles = styleTags.map((style, index) => {
    const content = style.innerHTML;
    const size = new Blob([content]).size;
    const parent = style.parentElement?.tagName.toLowerCase() || "unknown";
    const inHead = style.closest("head") !== null;
 
    // Count approximate number of rules
    const ruleCount = (content.match(/\{[^}]*\}/g) || []).length;
 
    // Get preview (first 100 chars, cleaned up)
    const preview = content
      .replace(/\s+/g, " ")
      .trim()
      .slice(0, 80);
 
    // Check for media queries
    const hasMediaQueries = /@media/i.test(content);
 
    // Check for keyframes (animation)
    const hasKeyframes = /@keyframes/i.test(content);
 
    return {
      index: index + 1,
      element: style,
      size,
      sizeKB: formatKB(size),
      parent,
      inHead,
      ruleCount,
      preview: preview + (content.length > 80 ? "..." : ""),
      hasMediaQueries,
      hasKeyframes,
      content,
    };
  });
 
  // Calculate totals and find issues
  const totalSize = styles.reduce((sum, s) => sum + s.size, 0);
  const totalRules = styles.reduce((sum, s) => sum + s.ruleCount, 0);
  const criticalBudget = 14 * 1024; // 14 KB
 
  // Find duplicated content
  const contentMap = new Map();
  styles.forEach((s) => {
    const normalized = s.content.replace(/\s+/g, " ").trim();
    if (!contentMap.has(normalized)) {
      contentMap.set(normalized, []);
    }
    contentMap.get(normalized).push(s.index);
  });
  const duplicates = Array.from(contentMap.entries())
    .filter(([_, indices]) => indices.length > 1)
    .map(([_, indices]) => indices);
 
  // Rating based on total size
  let rating, ratingColor;
  if (totalSize <= criticalBudget * 0.5) {
    rating = "Good";
    ratingColor = "#22c55e";
  } else if (totalSize <= criticalBudget) {
    rating = "Acceptable";
    ratingColor = "#f59e0b";
  } else {
    rating = "Too Large";
    ratingColor = "#ef4444";
  }
 
  // Display results
  console.group("%cπŸ“ Inline CSS Analysis", "font-weight: bold; font-size: 14px;");
 
  // Summary
  console.log("");
  console.log("%cSummary:", "font-weight: bold;");
  console.log(`   Total inline <style> tags: ${styles.length}`);
  console.log(`   Total size: ${formatBytes(totalSize)} (${formatKB(totalSize)} KB)`);
  console.log(`   Total CSS rules: ~${totalRules}`);
  console.log(
    `   Budget used: ${((totalSize / criticalBudget) * 100).toFixed(0)}% of 14 KB`
  );
  console.log(`   Rating: %c${rating}`, `color: ${ratingColor}; font-weight: bold;`);
 
  // Visual budget bar
  const budgetPct = Math.min((totalSize / criticalBudget) * 100, 100);
  const barWidth = Math.round(budgetPct / 5);
  const bar = "β–ˆ".repeat(barWidth) + "β–‘".repeat(20 - barWidth);
  console.log(`   [${bar}] ${budgetPct.toFixed(0)}%`);
 
  // Details table
  console.log("");
  console.group(
    `%cπŸ“Š Style Tags (${styles.length})`,
    "color: #3b82f6; font-weight: bold;"
  );
 
  const tableData = styles.map((s) => ({
    "#": s.index,
    Location: s.inHead ? "head" : "body",
    Size: formatBytes(s.size),
    Rules: s.ruleCount,
    "Media Queries": s.hasMediaQueries ? "Yes" : "-",
    Animations: s.hasKeyframes ? "Yes" : "-",
    Preview: s.preview,
  }));
  console.table(tableData);
 
  // Elements for inspection
  console.log("");
  console.log("%cπŸ”Ž Elements for inspection:", "font-weight: bold;");
  styles.forEach((s) => {
    const warning = s.size > 5 * 1024 ? " ⚠️ Large" : "";
    console.log(`${s.index}. (${formatBytes(s.size)})${warning}`, s.element);
  });
 
  console.groupEnd();
 
  // Issues
  const issues = [];
 
  if (totalSize > criticalBudget) {
    issues.push({
      type: "size",
      message: `Total inline CSS (${formatBytes(totalSize)}) exceeds 14 KB critical budget`,
      suggestion: "Move non-critical styles to external stylesheet",
    });
  }
 
  if (duplicates.length > 0) {
    issues.push({
      type: "duplicate",
      message: `Found ${duplicates.length} duplicated style block(s)`,
      details: duplicates.map((d) => `Tags #${d.join(", #")} have identical content`),
      suggestion: "Consolidate duplicate styles into a single block",
    });
  }
 
  const largeBlocks = styles.filter((s) => s.size > 5 * 1024);
  if (largeBlocks.length > 0) {
    issues.push({
      type: "large",
      message: `${largeBlocks.length} style block(s) over 5 KB`,
      details: largeBlocks.map((s) => `Tag #${s.index}: ${formatBytes(s.size)}`),
      suggestion: "Consider moving large blocks to external cached stylesheets",
    });
  }
 
  const bodyStyles = styles.filter((s) => !s.inHead);
  if (bodyStyles.length > 0) {
    issues.push({
      type: "location",
      message: `${bodyStyles.length} style block(s) in <body>`,
      details: bodyStyles.map((s) => `Tag #${s.index}`),
      suggestion: "Move styles to <head> or use external stylesheets for better parsing",
    });
  }
 
  if (issues.length > 0) {
    console.log("");
    console.group("%c⚠️ Issues Found", "color: #ef4444; font-weight: bold;");
 
    issues.forEach((issue) => {
      console.log("");
      console.log(`%c${issue.message}`, "font-weight: bold;");
      if (issue.details) {
        issue.details.forEach((d) => console.log(`   β€’ ${d}`));
      }
      console.log(`   β†’ ${issue.suggestion}`);
    });
 
    console.groupEnd();
  } else {
    console.log("");
    console.log(
      "%cβœ… No issues found. Inline CSS is within recommended limits.",
      "color: #22c55e; font-weight: bold;"
    );
  }
 
  // Recommendations
  console.log("");
  console.group("%cπŸ“ Best Practices", "color: #3b82f6; font-weight: bold;");
  console.log("");
  console.log("β€’ Keep critical CSS under 14 KB (fits in first TCP round-trip)");
  console.log("β€’ Inline only above-the-fold critical styles");
  console.log("β€’ Move large stylesheets to external files for caching");
  console.log("β€’ Avoid duplicate style blocks");
  console.log("β€’ Place <style> tags in <head> for optimal parsing");
  console.log("");
  console.log("%cExample critical CSS pattern:", "font-weight: bold;");
  console.log('%c<head>\n  <style>/* Critical above-fold CSS here */</style>\n  <link rel="preload" href="main.css" as="style">\n  <link rel="stylesheet" href="main.css" media="print" onload="this.media=\'all\'">\n</head>', "font-family: monospace; color: #22c55e;");
  console.groupEnd();
 
  console.groupEnd();
})();

Understanding the Results

Summary Section:

  • Total inline style tags and combined size
  • Approximate CSS rule count
  • Percentage of 14 KB critical budget used
  • Visual budget bar

Details Table:

ColumnDescription
#Style tag index
LocationWhether in <head> or <body>
SizeByte size of the CSS content
RulesApproximate number of CSS rules
Media QueriesContains @media rules
AnimationsContains @keyframes
PreviewFirst 80 characters of CSS

Issues Detected:

IssueWhy It Matters
Total size > 14 KBExceeds TCP slow start budget, delays FCP
Duplicate blocksWasted bytes, maintenance burden
Large blocks (> 5 KB)Could benefit from external caching
Styles in <body>May cause reflows during parsing

The 14 KB Rule

The first TCP round-trip delivers approximately 14 KB of data. For optimal performance:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  First 14 KB (one round-trip)               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ HTML + Critical CSS + Critical JS     β”‚  β”‚
β”‚  β”‚ β†’ Enough to render above-the-fold     β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Further Reading