Skip to content

Commit be9314e

Browse files
[CSS Modules] Add Blob URL to explainer (#1175)
Updates the explainer to prioritize the Blob approach over DataURI.
1 parent 69defeb commit be9314e

File tree

1 file changed

+39
-4
lines changed

1 file changed

+39
-4
lines changed

ShadowDOM/explainer.md

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,9 @@ Earlier versions of this document used the `<script>` tag for declaring CSS Modu
270270

271271
User agents allow for disabling JavaScript, and declarative modules should still work with JavaScript disabled. However, the module graph as it exists today only functions with script enabled. Browser engines should confirm whether this is feasible with their current implementations. Chromium has been verified as compatible, but other engines such as WebKit and Gecko have not been verified yet.
272272

273-
### Syntactic Sugar For Import Maps with Data URI
273+
### Syntactic Sugar For Import Maps with Blob URL
274274

275-
The simplest approach for Declarative CSS Modules is to treat them as syntactic sugar that generates an Import Map entry containing a specifier and a data URI containing the module contents.
275+
The simplest approach for Declarative CSS Modules is to treat them as syntactic sugar that generates an Import Map entry containing a specifier and a Blob URL referencing a Blob containing the module contents.
276276

277277
For example, a Declarative CSS Module defined as follows:
278278
```html
@@ -284,10 +284,13 @@ For example, a Declarative CSS Module defined as follows:
284284
...would be syntactic sugar for:
285285

286286
```html
287+
<script>
288+
const blob_url = URL.createObjectURL(new Blob(["#content { color: red; }"], {type: "text/css"}));
289+
</script>
287290
<script type="importmap">
288291
{
289292
"imports": {
290-
"foo": "data:text/css,%23content { color: red; }"
293+
"foo": "<value of `blob_url`>"
291294
}
292295
}
293296
</script>
@@ -314,6 +317,38 @@ This approach does have a few limitations:
314317
- The `<style>` definition *must* occur before it is imported, otherwise the import map will not be populated. Based on developer feedback, this is not a major limitation.
315318
- Since Import Maps have no knowledge of an underlying type for their mappings, declarative modules with the same specifier (e.g. "foo"), but differing types (e.g. one JavaScript module with a specifier of "foo" and one CSS module with a specifier of "foo") would create separate entries in the generated import map, and only the first definition would actually be mapped. See [Open Issues](#open-issues) for some potential solutions to this scenario.
316319

320+
Blob URLs are active for the lifetime of the page on which they were created and are revoked via `revokeObjectURL`. A developer could theoretically discover the URL generated from
321+
a Declarative CSS Module and revoke it, but this doesn't expose any new issues as this scenario is already possible to do imperatively.
322+
323+
There are several options for managing the lifetime of the generated Blob object. For instance, it could be revoked when the `<style type="module">` that created it is disconnected. This
324+
would give developers some options for managing Blob lifetimes, but once revoked, Blob URLs cannot be reused, so re-inserting the `<style type="module">` tag cannot undo it being
325+
removed. Generating a new Blob URL and adding it to the Import Map will not work either, since Import Maps will ignore subsequent entries with an existing specifier. By default,
326+
Blob URLs generated with Declarative CSS Modules would be tied to the lifetime of the document, with no options for revoking them. This would result in consistent behaviors for developers,
327+
at the expense of flexibility with resource management. Not exposing the ability to revoke the Blob URL aligns with how Import Maps behave, so it is the preferred option.
328+
329+
Alternatively, a data URI could be used instead of a Blob URL. However, using a Blob URL offers several performance advantages over a data URI, such as avoiding URL-encoding and a much
330+
smaller Import Map value string stored in memory.
331+
332+
Using Data URI's, a Declarative CSS Module defined as follows:
333+
```html
334+
<style type="module" specifier="foo">
335+
#content { color: red; }
336+
</style>
337+
```
338+
339+
...would be syntactic sugar for:
340+
341+
```html
342+
<script type="importmap">
343+
{
344+
"imports": {
345+
"foo": "data:text/css,%23content { color: red; }"
346+
}
347+
}
348+
</script>
349+
```
350+
The data URI must be URL-encoded, because many CSS selectors have special meaning in URLs. One example is the `#` ID selector in CSS, which is a fragment identifier in URLs and can only
351+
exist once in a URL. Importing via `shadowrootadoptedstylesheets` would work exactly the same as the Blob URL example above.
317352

318353
### Detailed Parsing Workflow
319354

@@ -332,7 +367,7 @@ In the following example:
332367
</my-element>
333368
```
334369

335-
Upon parsing the `<style>` tag above, an [import map string](https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string) is generated with JSON containing a [map](https://infra.spec.whatwg.org/#ordered-map) with a key of "imports". The [value](https://infra.spec.whatwg.org/#map-value) associated with this key is another JSON [map](https://infra.spec.whatwg.org/#ordered-map) with a single entry with a [key](https://infra.spec.whatwg.org/#map-key) containing the value of the `specifier` attribute on the `<style>` tag (in this case, "foo"). The [value](https://infra.spec.whatwg.org/#map-value) associated with this key is a [data URI](https://www.rfc-editor.org/rfc/rfc2397) with a [scheme](https://url.spec.whatwg.org/#concept-url-scheme) of "data", a [media type](https://www.rfc-editor.org/rfc/rfc2397) of "text/css", and [data](https://www.rfc-editor.org/rfc/rfc2397) consisting of a [UTF-8 percent encoded](https://url.spec.whatwg.org/#utf-8-percent-encode) value of the [text content](https://html.spec.whatwg.org/#get-the-text-steps) of the `<style>` tag.
370+
Upon parsing the `<style>` tag above, an [import map string](https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string) is generated with JSON containing a [map](https://infra.spec.whatwg.org/#ordered-map) with a key of "imports". The [value](https://infra.spec.whatwg.org/#map-value) associated with this key is another JSON [map](https://infra.spec.whatwg.org/#ordered-map) with a single entry with a [key](https://infra.spec.whatwg.org/#map-key) containing the value of the `specifier` attribute on the `<style>` tag (in this case, "foo"). The [value](https://infra.spec.whatwg.org/#map-value) associated with this key is a [Blob URI](https://w3c.github.io/FileAPI/#dfn-Blob) with a [media type](https://www.rfc-editor.org/rfc/rfc2397) of "text/css" and a value of the [text content](https://html.spec.whatwg.org/#get-the-text-steps) of the `<style>` tag. Alternatively, the [value](https://infra.spec.whatwg.org/#map-value) associated with the key is a [data URI](https://www.rfc-editor.org/rfc/rfc2397) with a [scheme](https://url.spec.whatwg.org/#concept-url-scheme) of "data", a [media type](https://www.rfc-editor.org/rfc/rfc2397) of "text/css", and [data](https://www.rfc-editor.org/rfc/rfc2397) consisting of a [UTF-8 percent encoded](https://url.spec.whatwg.org/#utf-8-percent-encode) value of the [text content](https://html.spec.whatwg.org/#get-the-text-steps) of the `<style>` tag.
336371

337372
Note that unlike a regular `<style>` tag with CSS content, the `sheet` attribute defined in the [LinkStyle interface](https://www.w3.org/TR/cssom-1/#the-linkstyle-interface) would always be empty for Declarative CSS Modules. Similarly, updating the text content of the `<style>` tag would not update the generated [import map string](https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string), which is exactly how [import maps](https://html.spec.whatwg.org/multipage/webappapis.html#import-maps) behave when their text content is modified.
338373

0 commit comments

Comments
 (0)