How the extract-static-html Skill Handles CSS Inlining: A Complete Technical Guide
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 and 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 (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-cssflags
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, 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, lines 55-71) to transform external references into embedded resources:
- Asset Fetching: For each extracted URL, the skill downloads the referenced binary asset (images, fonts, SVGs, etc.)
- Base64 Encoding: Binary data is converted to data-URI format (
data:<mime>;base64,…) - 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
- DOM Injection: The transformed CSS is injected into the page inside a
<style>element, or a<style type="text/tailwindcss">element when Tailwind@applydirectives 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 script provides command-line flags to control CSS source aggregation:
# 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:
// 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 imagesto 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
extractCssUrlshandles malformed CSS and records precise text offsets for reliable replacement - The end-to-start replacement strategy in
replaceCssUrlsmaintains 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →