Skip to content

Module Stylesheet <link> tags - #12339

Closed
KurtCattiSchmidt wants to merge 17 commits into
whatwg:mainfrom
KurtCattiSchmidt:sas-fetch
Closed

Module Stylesheet <link> tags#12339
KurtCattiSchmidt wants to merge 17 commits into
whatwg:mainfrom
KurtCattiSchmidt:sas-fetch

Conversation

@KurtCattiSchmidt

@KurtCattiSchmidt KurtCattiSchmidt commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Adds support for <link type=module rel=stylesheet>.

(See WHATWG Working Mode: Changes for more details.)

Addresses #10673


/indices.html ( diff )
/links.html ( diff )
/semantics.html ( diff )
/webappapis.html ( diff )

@KurtCattiSchmidt KurtCattiSchmidt added the agenda+ To be discussed at a triage meeting label Apr 8, 2026
@noamr

noamr commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

From an initial look this seems to be on the right track. I need to spend some time on the details though.
cc @emilio

@keithamus

This comment was marked as outdated.

@noamr

noamr commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Right now all of the shadowroot* properties map to attachShadow({}) properties. Do we want to add adoptedStyleSheets to ShadowRootInit?

Note that the current properties of ShadowRootInit become read only on the ShadowRoot, unlike adoptedStylesheets whch is a mutable array-like thing.

So I think it might be nice to include it in the init dictionary but I could also see why it's not necessary.

@KurtCattiSchmidt

Copy link
Copy Markdown
Contributor Author

Right now all of the shadowroot* properties map to attachShadow({}) properties. Do we want to add adoptedStyleSheets to ShadowRootInit?

Agreed with @noamr's response here - since there's already an adoptedStyleSheets property on the imperative version that references a mutable array, we don't need to add it again to ShadowRootInit.

Think of shadowrootadoptedstylesheets as a purely declarative thing, since it's lacking in the declarative version but present in the imperative version.

@emilio emilio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question, might be me misunderstanding CSS modules, but I'd figure I'd ask...

Comment thread source Outdated
@jstenback jstenback removed the agenda+ To be discussed at a triage meeting label Apr 30, 2026
Comment thread source
Comment thread source Outdated
brave-builds pushed a commit to brave/chromium that referenced this pull request May 12, 2026
This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniec@microsoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1629010}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request May 12, 2026
This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniec@microsoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1629010}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request May 12, 2026
This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniec@microsoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1629010}
lando-worker Bot pushed a commit to mozilla-firefox/firefox that referenced this pull request May 13, 2026
…a=testonly

Automatic update from web-platform-tests
[CSS Modules] Update fetching strategy

This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniec@microsoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmi@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1629010}

--

wpt-commits: e564867fbfe3cc70cc8d43ddac392b7b848bc7e4
wpt-pr: 59805
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this pull request May 14, 2026
…a=testonly

Automatic update from web-platform-tests
[CSS Modules] Update fetching strategy

This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniecmicrosoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmimicrosoft.com>
Cr-Commit-Position: refs/heads/main{#1629010}

--

wpt-commits: e564867fbfe3cc70cc8d43ddac392b7b848bc7e4
wpt-pr: 59805

UltraBlame original commit: 04a038fe6e54a754c927f29afe85d3c66476f8a3
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this pull request May 14, 2026
…a=testonly

Automatic update from web-platform-tests
[CSS Modules] Update fetching strategy

This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniecmicrosoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmimicrosoft.com>
Cr-Commit-Position: refs/heads/main{#1629010}

--

wpt-commits: e564867fbfe3cc70cc8d43ddac392b7b848bc7e4
wpt-pr: 59805

UltraBlame original commit: 04a038fe6e54a754c927f29afe85d3c66476f8a3
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this pull request May 14, 2026
…a=testonly

Automatic update from web-platform-tests
[CSS Modules] Update fetching strategy

This change updates the fetch behavior to match
whatwg/html#12339. Note that this modifies the
code for existing import, import(), and modulepreload for consistency,
but the existing behavior for these API's does *not* change in any
circumstance. The only observable change is in the
shadowrootadoptedstylesheets fetching behavior.

By modifying the CSS module fetching to first create the CSSStyleSheet
before fetching and calling replaceSync upon completion, we are able to
remove the placeholder stylesheets that were being inserted for
shadowrootadoptedstylesheets. This simplifies the logic considerably and
will be more performant.

Unlike the other API's for fetching a CSS Module Script,
shadowrootadoptedstylesheets will leave behind an empty CSSStyleSheet in
adoptedStyleSheets if the fetch fails. This simplifies the logic and
seems like a reasonable trade off for a rare condition. Earlier versions
of this CL removed it upon failure, but the implementation was
considerably more complex and had other side effects.

Change-Id: If00445458983f5062a1ca2d83ed0253cc171d1ff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7709812
Reviewed-by: Dan Clark <daniecmicrosoft.com>
Commit-Queue: Kurt Catti-Schmidt <kschmimicrosoft.com>
Cr-Commit-Position: refs/heads/main{#1629010}

--

wpt-commits: e564867fbfe3cc70cc8d43ddac392b7b848bc7e4
wpt-pr: 59805

UltraBlame original commit: 04a038fe6e54a754c927f29afe85d3c66476f8a3
@yoavweiss

Copy link
Copy Markdown
Contributor

@KurtCattiSchmidt - While implementing #10327 in Chromium, I've noticed some failing tentative tests related to this PR that I believe would need to change, given the new behavior of failed module loads.

shadowrootadoptedstylesheets-async-fetch-failure-shared.html and shadowrootadoptedstylesheets-modulepreload-failure.html both assume that a failed fetch is permanent, when that's no longer the case. I believe we'd need to change these tests, and any relevant spec language.

Comment thread source Outdated
Comment thread source Outdated
Comment thread source Outdated
@KurtCattiSchmidt KurtCattiSchmidt changed the title shadowrootadoptedstylesheets on <template> Module Stylesheet <link> tags Aug 24, 2026

@noamr noamr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice work, see comments!

Comment thread source

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make it so that specifiers used in href like this also have to be valid URLs as far as conformance goes.
It's mostly a single word or something which is a valid URL to begin with. Then this is just a URL with using module-specifier for relative resolution rather than ordinary URL resolution

@noamr noamr Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this is an opinion btw, and the whole "href can be a specifier" notion should be discussed with a wider community)

Comment thread source Outdated
Comment thread source Outdated
<code>link</code> element, then <dfn id="the-link-is-an-alternative-stylesheet">the link is an
alternative style sheet</dfn>; in this case, the <code data-x="attr-title">title</code> attribute
must be specified on the <code>link</code> element, with a non-empty value.</p>
<code>link</code> element and the element is not a <span>module stylesheet link</span>, then <dfn

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another cardinality issue - on regular <link rel=stylesheet>, the title attribute on link goes straight to the StyleSheet.title value: https://drafts.csswg.org/cssom/#dom-stylesheet-title. alternate works for selecting which alternate stylesheet and has similar cardinality issues.

Unlike media and disabled, we can't handle it at the element level instead, since it needs to interact with the stylesheet and a browser UI setting.

I assumed these were unofficially deprecated, as Chrome doesn't support them. But it looks like Firefox still does support them, so I'm curious what their thoughts are on this. I think for now, removing this exception for link modules makes sense, as it won't impact interop.

Comment thread source Outdated
Comment thread source Outdated
Comment thread source
Comment thread source
@KurtCattiSchmidt KurtCattiSchmidt added the agenda+ To be discussed at a triage meeting label Aug 26, 2026
@keithamus

Copy link
Copy Markdown
Member

This used to be the PR for adoptedStyleSheets, it seems like it's now for modules in <link>. Is it worth raising a new PR? All of the prior comments are now outdated as they talk about a significantly different concept.

@emilio emilio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, so the current state of the PR defines <link import=""> rather than using type="module". Not objecting or anything, but why?

Would it be possible to share more of the processing model (specially the fetching etc, and the load event firing) with the current href version? I assume that's roughly how it would work, implementation-wise.

Comment thread source
<li><p>Set <var>el</var>'s <span data-x="link module CSS style sheet">module CSS style
sheet</span> to <var>sheet</var>, and <span
data-x="concept-event-fire">fire an event</span> named <code
data-x="event-load">load</code> at <var>el</var>.</p></li>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link load events generally fire async, probably this should use a task.

@noamr

noamr commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Curious, so the current state of the PR defines <link import=""> rather than using type="module". Not objecting or anything, but why?

This came up at WHATNOT, as wanting to keep type for mime-types in links, and also some issues with having a module specifier instead of a URL in href.

By using <link import="" href="url.css"> or <link import="specifier" href="url.css">, the link would act as a regular stylesheet in unsupported browsers, and as a module when supported.

@annevk

annevk commented Aug 28, 2026

Copy link
Copy Markdown
Member

To be clear, I don't think we ended up really reaching a conclusion about the design of this feature at WHATNOT. Perhaps we should first flesh out some of the alternatives in a design document. And I second @keithamus's request for closing this PR as that just makes reviewing unwieldy.

@KurtCattiSchmidt

KurtCattiSchmidt commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@annevk - for context, there is a design document that we discussed at a two hour joint WHATWG-CSSWG meeting a few weeks ago. Minutes are here.

Members of Mozilla, Webkit, and Blink were present and we jointly agreed that <link> was the best approach for this scenario.

The suggestion to use <style> yesterday wasn't explored at that discussion, but I just added it to the document with pros and cons. Feel free to edit the document as you see fit. As-is, I don't think the arguments in favor of <style> are compelling over <link>, but I may have missed some. I'm happy to discuss this and get consensus though. I'm very curious what @jakearchibald and @keithamus think of <style> over <link>.

Agreed that the history makes this confusing. I'll open a new PR.

@KurtCattiSchmidt

Copy link
Copy Markdown
Contributor Author

This used to be the PR for adoptedStyleSheets, it seems like it's now for modules in <link>. Is it worth raising a new PR? All of the prior comments are now outdated as they talk about a significantly different concept.

New PR is at #12860

@KurtCattiSchmidt KurtCattiSchmidt removed the agenda+ To be discussed at a triage meeting label Aug 28, 2026
@annevk

annevk commented Aug 29, 2026

Copy link
Copy Markdown
Member

I might be missing something but I don't see anyone from WebKit in the list of attendees there? But also, WHATWG only makes tentative decisions during meetings so it doesn't really matter.

@noamr

noamr commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

I might be missing something but I don't see anyone from WebKit in the list of attendees there? But also, WHATWG only makes tentative decisions during meetings so it doesn't really matter.

@rniwa was there (not that that makes the conclusions binding)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

8 participants