v1.26.3
gildas-lormeau/SingleFilev1.26.3Sep 22, 2026by gildas-lormeau
Upgrading?
AI Summary
Updates single-file-core to version 1.6.10 and optimizes self-extracting archives by deduplicating stylesheets and generated images. Fixes numerous CSS parsing bugs related to cascade layers, @scope, revert-layer, and vendor prefixes.
Key Highlights
- Optimized self-extracting archives by deduplicating stylesheets and generated images (e.g., 736 files reduced to 263, saving 3.2 MB of CSS).
- Fixed critical CSS parsing issues, including incorrect ordering of Cascade Layers and handling of @starting-style blocks.
- Corrected handling of vendor-prefixed CSS values (like -webkit-linear-gradient) which were previously dropped.
- Improved protection of revert-layer and fixed @scope root resolution issues.
- Fixed crossorigin attribute handling in standalone archives to prevent stylesheet loss.
Full Release Notes
### Changes
- single-file-core is updated from 1.6.9 to 1.6.10, see https://github.com/gildas-lormeau/single-file-core/releases/tag/v1.6.10
- In a self-extracting archive, a stylesheet is stored once per content: several `<link>` elements that resolve to the same CSS share one file whatever URL each came from, and an `@import` chain is merged from the leaves up, so two stylesheets differing only in the file they import merge once their imports have. Measured over 24 real pages that is 736 stylesheet files down to 263, and 3.2 MB less CSS for the browser to parse when the saved page is opened. The key is the content and not the URL, so copies the unused-styles pass left different from each other stay separate files
- In a self-extracting archive, the images SingleFile generates itself are stored as files instead of being inlined as data URIs: the snapshot it takes of a video frame for a poster, the icon it puts beside a blocked video, and the bitmap of a `<canvas>`. Elements that produce the same bytes share one file. A self-contained page is unchanged, since it has nowhere to put a file
- The "remove unused styles" pass is faster: the css-tree balance buffer is cleared only where it was used, the revert-layer scan is cached per rule and per declaration, a selector is copied by cloning it rather than re-parsing it, and a nested selector is parsed once instead of twice
### Fixes
- A standalone `@starting-style` block was minified as an unconditional group, so its declarations joined the same context as the normal rules, won the order tie-break and pruned the normal declaration. The saved element stayed in its before-transition state
- Cascade layers were ordered wrongly in three ways: a sublayer outranked its parent's own declarations, where CSS Cascade 5 puts those in an implicit last sublayer, so `@layer a { p { color: red } @layer b { p { color: blue } } }` rendered red live and blue saved; every anonymous `@layer {}` block shared one identity, so two of them were treated as a single layer; and the important twin of each case inverted the same way
- Several shapes of `revert-layer` escaped the protection that keeps what it rolls back to: rolling back to a shorthand declared in the layer below, `all: revert-layer`, `var(--missing, revert-layer)`, a `revert-layer` written on a state-dependent rule such as `p:hover`, and one whose stripped selector matches nothing in the page
- `@scope` roots were resolved wrongly in several cases: a prelude-less `@scope` nested inside another took the enclosing scope's roots instead of the parent element of the stylesheet's owner node; a scoped selector's match and its scope eligibility could be counted from two different roots; and proximity ignored the limits of the root it was counted to
- A nested selector substituted one parent alternative at a time into every `&`, so `.a, .b { & + & { color: red } }` was only ever tried as `.a + .a` and `.b + .b`, never as the `.a + .b` the browser matches, and the rule was removed from the saved page
- CSS identifiers were compared by their spelling, so an escape or a capital defeated the protections: `\61` was not recognised as the layer name `a`, and the same held for pseudo-class and at-rule names
- A vendor-prefixed value was dropped as invalid from every real capture, because only the function's name was tested for support rather than the whole value: `background-image: -webkit-linear-gradient(red, blue)` disappeared
- The assumption that a sanitized selector matches a superset of the real rule did not hold inside `:not()`, `:has()` or the `of` argument of `:nth-child()`, which the sanitizer does not descend into
- An `@import url(x.css) layer;` naming an anonymous layer with the bare keyword was inlined unlayered, so the imported rules beat every layer they were meant to sit under. An inlined import also lost its `supports()` condition, and now keeps the layer and the conditions it was written under
- An iframe whose `src` is replaced by `srcdoc` could be hidden by the page's own CSS: web.dev ships `iframe:not([src]) { display: none }`, so the saved page computed `display: none` for a frame the live page showed. An empty `src` is kept on the element
- An archive unzipped and opened from disk lost every stylesheet whose `<link>` carries `crossorigin`, because a browser refuses a CORS-mode request for a sibling `file://` resource. On a capture of a GitHub page that was all 29 of its 29 links. The attribute is dropped when the URL is rewritten into the archive
- Saving a page that SingleFile had already saved changed which elements the structural pseudo-classes match, because the invalid-nesting repair produced a shape a parser does not reproduce
### Dependencies
- css-tree updated to 3.2.1, which fixes an unquoted `url()` whose last character is an escaped space
Related Repositories
affaan-m/ECC★ 285,964
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
react/react★ 251,629
The library for web and native user interfaces.
nodejs/node★ 122,530
Node.js JavaScript runtime ✨🐢🚀✨
Leonxlnx/taste-skill★ 100,769
Agent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
louislam/uptime-kuma★ 92,737
A fancy self-hosted monitoring tool