# How the extract-static-html Skill Handles CSS Inlining: A Complete Technical Guide

> Learn how the extract-static-html skill inlines CSS with a custom parser and end-to-start replacement strategy for self-contained HTML documents. Discover its technical approach.

- Repository: [Google Labs Code/stitch-skills](https://github.com/google-labs-code/stitch-skills)
- Tags: tech-deep-dive
- Published: 2026-07-12

---

**The extract-static-html skill creates fully self-contained HTML documents by inlining all CSS and converting external resources referenced in stylesheets into base64 data-URIs using a custom character-by-character parser and end-to-start replacement strategy.**

The extract-static-html skill in the `google-labs-code/stitch-skills` repository transforms modern web applications into portable, single-file HTML documents. By eliminating external dependencies, this skill ensures that stylesheets, images, fonts, and other assets referenced via CSS `url()` functions are embedded directly into the markup. Understanding how extract-static-html handles CSS inlining reveals a sophisticated three-stage pipeline designed for complete resource encapsulation.

## The Three-Stage CSS Inlining Pipeline

The inlining process implemented in [`plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/plugins/stitch-design/skills/extract-static-html/scripts/snapshot.ts) and [`extract_inline_html.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/extract_inline_html.ts) transforms external CSS resources into a single, injectable style block through three precise stages.

### Stage 1: Loading and Concatenating CSS Sources

The skill first aggregates all available CSS from multiple sources. According to the options handling in [`extract_inline_html.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/extract_inline_html.ts) (lines 19-33), the skill reads:

- The **index CSS** file specified via `--index-css`
- Additional CSS files provided through `--css-files`
- CSS embedded within the target page via `<link rel="stylesheet">` tags
- Extra CSS content from `--extra-css` flags

All fetched stylesheets are concatenated into a single string variable (`allCss`), creating a unified CSS block that serves as the foundation for the inlining process.

### Stage 2: Parsing url() References with Character-by-Character Precision

Once the CSS is concatenated, the `extractCssUrls` function (implemented in [`snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/snapshot.ts), lines 53-71) performs a sophisticated parsing operation:

- A **character-by-character parser** walks the entire CSS text
- Locates every `url(` occurrence regardless of quoting style (single quotes, double quotes, or unquoted)
- Handles escaped characters, whitespace variations, and malformed tokens
- Records the exact **start and end offsets** of each URL value
- Ignores existing data-URIs to prevent double-processing

This parser tolerance ensures robustness against real-world CSS variations while maintaining precise positional data for the replacement phase.

### Stage 3: Fetching Assets and Replacing URLs with Data-URIs

The final stage uses the `replaceCssUrls` function ([`snapshot.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/snapshot.ts), lines 55-71) to transform external references into embedded resources:

1. **Asset Fetching**: For each extracted URL, the skill downloads the referenced binary asset (images, fonts, SVGs, etc.)
2. **Base64 Encoding**: Binary data is converted to data-URI format (`data:<mime>;base64,…`)
3. **End-to-Start Replacement**: The CSS string is rewritten from **end-to-start** (reverse order) using the recorded offsets. This strategy ensures that earlier indices remain valid as later replacements shorten or lengthen the string
4. **DOM Injection**: The transformed CSS is injected into the page inside a `<style>` element, or a `<style type="text/tailwindcss">` element when Tailwind `@apply` directives are detected

## Handling Iframes and Relative URL Resolution

During the Puppeteer execution phase, the skill performs additional preprocessing to ensure comprehensive CSS coverage. The system **resolves relative URLs** inside inline `<style>` tags and `<link>` elements within same-origin iframes. This guarantees that CSS loaded inside nested frames is converted to absolute URLs before the parsing stage, ensuring complete inlining even for complex, multi-frame applications.

## CLI Configuration and Usage

The [`extract_inline_html.ts`](https://github.com/google-labs-code/stitch-skills/blob/main/extract_inline_html.ts) script provides command-line flags to control CSS source aggregation:

```bash

# Generate static HTML with all CSS inlined

npx tsx extract_inline_html.ts \
  --page src/MockPage.jsx:home.html:"Home Page" \
  --index-css src/index.css \
  --css-files src/theme.css \
  --extra-css src/index.html \
  --outdir .stitch

```

The `--index-css`, `--css-files`, and `--extra-css` parameters allow developers to specify multiple CSS sources that are merged and processed through the inlining pipeline.

### Implementation Example

The replacement operation uses the offset data to surgically update the CSS text:

```typescript
// From snapshot.ts - replaceCssUrls implementation
cssText = replaceCssUrls(cssText, [
  { start: 123, end: 140, dataUri: 'data:image/png;base64,iVBORw0KG...' },
  // Additional replacements processed end-to-start
]);

```

## Key Technical Characteristics

- **Data-URI Encoding**: All binary assets are base64-encoded to eliminate external HTTP dependencies
- **Tailwind Awareness**: The skill detects Tailwind CSS usage and preserves the `type="text/tailwindcss"` attribute when injecting the inlined stylesheet
- **Comprehensive Logging**: The process generates detailed logs such as `✅ Inlined 12 CSS url() references`, `✅ Inlined 3 stylesheets`, and `✅ Inlined 45 images` to verify the transformation completeness

## Summary

- The extract-static-html skill processes CSS through a three-stage pipeline: loading sources, parsing `url()` references with offset tracking, and replacing external URLs with data-URIs
- A **character-by-character parser** in `extractCssUrls` handles malformed CSS and records precise text offsets for reliable replacement
- The **end-to-start replacement strategy** in `replaceCssUrls` maintains string index integrity during URL substitution
- **Same-origin iframe support** ensures CSS from nested frames is resolved and inlined
- The output is a single HTML file containing all markup and one inline style block with every external resource embedded as a data-URI

## Frequently Asked Questions

### How does extract-static-html handle malformed or unusual CSS url() syntax?

The skill uses a custom character-by-character parser in `extractCssUrls` (snapshot.ts) that tolerates quoted and unquoted URLs, escaped characters, arbitrary whitespace, and malformed tokens. This parser records the exact start and end positions of each URL value, ensuring robust extraction even from imperfect CSS code.

### What happens to large binary assets like fonts and images during CSS inlining?

All assets referenced via `url()` are downloaded as binary data and converted to base64-encoded data-URIs. While this increases the HTML file size, it guarantees complete portability without external HTTP dependencies, making the resulting document truly self-contained.

### Can extract-static-html process CSS from cross-origin iframes?

The skill resolves relative URLs and processes CSS from **same-origin** iframes only. Cross-origin iframe restrictions prevent access to stylesheet content, so CSS from external domains cannot be inlined unless explicitly fetched through other means before processing.

### Why does the replaceCssUrls function process replacements from end-to-start?

Processing replacements from end-to-start (reverse order) ensures that modifying the CSS string length at higher indices does not invalidate the offset positions recorded for earlier replacements. This strategy maintains the precision of the character positions captured during the parsing stage, preventing index drift and replacement errors.