This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 27303 - Allow <link> in body
Summary: Allow <link> in body
Status: RESOLVED MOVED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: Other other
: P2 normal
Target Milestone: Unsorted
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL:
Whiteboard: [good first bug]
Keywords:
: 27304 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-11-11 18:15 UTC by Ilya Grigorik
Modified: 2016-05-25 06:30 UTC (History)
13 users (show)

See Also:


Attachments
<link> in <body> (2014-08-15 HA run) (16.21 KB, text/csv)
2014-12-16 00:57 UTC, Ilya Grigorik
Details

Description Ilya Grigorik 2014-11-11 18:15:03 UTC
Current spec: "If the rel attribute is used, the element is restricted to the head element."

http://www.w3.org/TR/html5/document-metadata.html#the-link-element

In practice, developers do not follow above restriction and embed <link>'s in body, and all browsers support this behavior as well. Removing this constraint has multiple benefits: 

1) It allows UA's to stop treating this as an error condition and instead use position of <link rel=stylesheet> in the document as an optimization hint to improve rendering performance - e.g. content above <link> should not block first paint, which is similar to how blocking scripts behave today. Some browsers already implement this behavior, and some sites rely on this behavior to improve painting performance; Baidu experience shows that this can deliver significant perf improvement. 

2) <link rel=import> is already being used in the wild inside of <body> to import HTML and other content.

3) It makes the spec consistent with what's actually implemented and deployed in the wild (by UAs and site developers).

whatwg thread with background discussion: http://lists.w3.org/Archives/Public/public-web-perf/2014Oct/0115.html
Comment 1 Ian 'Hixie' Hickson 2014-11-12 19:11:41 UTC
The spec requires browsers to support this, but the reason developers should not do it is that it leads to bad user experiences — flashes of unstyled content, higher lag, and so on.
Comment 2 Ian 'Hixie' Hickson 2014-11-12 19:11:53 UTC
*** Bug 27304 has been marked as a duplicate of this bug. ***
Comment 3 Ilya Grigorik 2014-11-12 23:32:57 UTC
(In reply to Ian 'Hixie' Hickson from comment #1)
> The spec requires browsers to support this, but the reason developers should
> not do it is that it leads to bad user experiences — flashes of unstyled
> content, higher lag, and so on.

To the contrary, we have good examples of how relaxing this constraint can help improve performance:

1) http://lists.w3.org/Archives/Public/public-web-perf/2014Oct/0117.html - Baidu data showing significant improvement in time to first paint for a modified Blink engine.

2) At TPAC, IE guys shared examples where large sites (e.g. amazon.com) are explicitly triggering this behavior for IE visitors to achieve faster paints -- this is despite the fact that to trigger this error condition they need to make the page inactive for several hundred milliseconds.

Based on discussion at TPAC, we had rough consensus between Blink, IE, and Safari, that this is both reasonable and implementable. In fact, all browsers already support this case, it's just that we treat it as an error condition. Removing this constraint would allow us to optimize for this case and deliver better performance.

To be clear, we're not modifying how stylesheets are processed:
- all stylesheets apply against the full page, regardless of their position
- best practice is to put stylesheets at the top

However, (officially) allowing <link> in body would allow us to:
- use position of the <link> as a hint for what can or cannot be painted
- above strategy can be triggered via opt-in

Concretely, consider this example:

<html>
 <style> /* critical css */ </style>
 <body> 
 <header>...</header>
 
  <style rel=stylesheet href="other.css"/>
  <section>...</section>
  </body>
</html>

With above semantics we could paint the <header> content and block painting of later <section> until other.css is available. This pattern would enable much faster rendering of critical content.
Comment 4 Ian 'Hixie' Hickson 2014-11-13 23:48:22 UTC
Why is this better than <style scoped>@import url(other.css)</style> ?
Comment 5 Ilya Grigorik 2014-11-14 23:48:39 UTC
(In reply to Ian 'Hixie' Hickson from comment #4)
> Why is this better than <style scoped>@import url(other.css)</style> ?

I'd argue that the answer to this doesn't really matter. 

a) Developers already use <link> in body.
b) Browsers support use of <link> in body.
c) With imports, <link> in body is a first class use case - e.g. https://github.com/bterlson/ecmascript/blob/master/spec/index.html#L75

So, <link> in body is a fact of life. The spec may not recognize this, but that's how it is used in practice. I'm proposing that we align the spec with how <link> is being used in the wild.

And, it just so happens, that (officially) allowing <link> in body has other positive side-effects. For example, for rel=stylesheet it allows the user agent to use its location in the document as a rendering hint (perhaps with an opt-in). Whether and how the UA decides to implement this, is up to them -- we're not changing the semantics of how CSS works.

More: http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Nov/0051.html
Comment 6 Ian 'Hixie' Hickson 2014-11-19 22:12:03 UTC
Lots of things are used in practice that are non-conforming. They're non-conforming because they're not _good_ practice. I think this is true here. That's why we have scoped style sheets.

However, if this is something that's also being discussed by e-mail, then we should probably close this bug and discuss the issue there. I'd rather not split conversations across bugs and e-mail.
Comment 7 Ilya Grigorik 2014-12-01 21:33:07 UTC
(In reply to Ian 'Hixie' Hickson from comment #6)
> Lots of things are used in practice that are non-conforming. They're
> non-conforming because they're not _good_ practice. I think this is true
> here. That's why we have scoped style sheets.

Scoped stylesheets are not supported [1] by any browser except FF and require substantial rewrite of the application if you're actually relying on "scoped" semantics. On the other hand, if you're not relying on scoped semantics, then they offer nothing extra over <link> in body... which is supported today across all browsers. Further, we have concrete examples of where <link> in body *is* a good practice - e.g. amazon.com + IE example, Baidu experience with their modified Blink engine (see comment #3). 

It is true that you can cause unnecessary reflows by specifying stylesheets later in the document, but that's nothing new: many applications already delay CSS injection [2] and use other tricks to remove (some) CSS from the critical rendering path, and have to guard for this case. For most developers, <link> in head is and will remain to be the right recommendation, but when used correctly <link> in body can be (and already is, for some) a performance best practice. 

[1] http://caniuse.com/#feat=style-scoped
[2] https://github.com/filamentgroup/loadCSS
Comment 8 Ian 'Hixie' Hickson 2014-12-03 19:53:01 UTC
If the argument is that we should drop scoped style sheets, then that changes the calculus substantially. Is that what you are arguing? I thought people were pretty firmly convinced that we in fact _do_ want scoped style sheets. It's certainly been one of the more frequent requests I get from developers over the years.
Comment 9 Simon Pieters 2014-12-04 13:44:28 UTC
I think the argument is that scoped stylesheets is an orthogonal feature. For some use cases it makes perfect sense to want to use scoped stylesheets, but for this use case it is a non-goal and actually makes things harder for the author.

Consider how authors would typically achieve the desired effect here. First, the author would write all of the CSS for the site. Then, a tool would be used to determine the minimum subset of the full stylesheet required to render the "above the fold", and put the subset in an inline <style> in head. Then, the full stylesheet is external and is inserted in a manner that does not block rendering, e.g. inserted into <head> with a <script> somewhere in <body>, or, as amazon.com does, using <link> somewhere in body.

With this workflow, having the full external stylesheet be a scoped stylesheet doesn't really work unless you wrote the CSS with that in mind from the beginning.
Comment 10 Ilya Grigorik 2014-12-14 00:31:34 UTC
(In reply to Simon Pieters from comment #9)
> I think the argument is that scoped stylesheets is an orthogonal feature.
> For some use cases it makes perfect sense to want to use scoped stylesheets,
> but for this use case it is a non-goal and actually makes things harder for
> the author.

Yes, exactly. 

I just did a quick scan [1] of the top 1K Alexa sites: ~10% (95 of 956 sites in the dataset) use <link> in <body>. That's a significant fraction.

[1] https://gist.github.com/igrigorik/c0a440d9550ba4a4a4f2
Comment 11 Simon Pieters 2014-12-15 09:47:02 UTC
What are those 95 URLs?
Comment 12 Ilya Grigorik 2014-12-16 00:57:02 UTC
Created attachment 1559 [details]
<link> in <body> (2014-08-15 HA run)
Comment 13 Ian 'Hixie' Hickson 2014-12-18 01:32:34 UTC
We should decide if this is to be discussed here or by e-mail. If it's to be discussed here, then please e-mail the list replying to the thread mentioned earlier, pointing to this bug, and saying that you're happier discussing this on the bug. If it's to be discussed on the list, then please close this bug and we'll discuss it on the list.

If we're discussing it in bugs, then what would be helpful for me would be a clear statement of the problem(s) we're trying to solve. So far this bug has mostly been about solutions, not problems.
Comment 14 Ilya Grigorik 2015-01-22 18:25:34 UTC
(In reply to Ian 'Hixie' Hickson from comment #13)
> We should decide if this is to be discussed here or by e-mail. If it's to be
> discussed here, then please e-mail the list replying to the thread mentioned
> earlier, pointing to this bug, and saying that you're happier discussing
> this on the bug. If it's to be discussed on the list, then please close this
> bug and we'll discuss it on the list.

I'll ping the list and point them here.
 
> If we're discussing it in bugs, then what would be helpful for me would be a
> clear statement of the problem(s) we're trying to solve. So far this bug has
> mostly been about solutions, not problems.

Problem: current spec [1] restricts the use of <link rel> to within the <head> element only. I believe this restriction should be removed: 

a) This policy is not enforced by any browser - i.e. all support <link> outside of <head>.
b) This policy is not respected by site authors - e.g. ~10% of top 1K Alexa sites declare stylesheets in body [2].
c) This policy runs counter to the new emerging patterns of using <link> outside of <head> - see [3].
d) This policy restricts browsers from enabling new rendering optimizations. We have real-world experience showing that removing this constraint can help deliver faster page rendering and improve user experience (e.g. amazon.com is using this [4] to deliver faster rendering to IE users), and Baidu experiments [5] with modified Blink engine back this as well.

In short, the current constraint does not match what's implemented by browsers, how <link> is used by site authors, and restricts both groups from developing and deploying new patterns that can improve both the developer and user experience (e.g. faster page rendering).

[1] http://www.w3.org/TR/html5/document-metadata.html#the-link-element
[2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=27303#c10
[3] https://lists.w3.org/Archives/Public/public-whatwg-archive/2014Nov/0017.html
[4] https://www.w3.org/Bugs/Public/show_bug.cgi?id=27303#c3
[5] https://lists.w3.org/Archives/Public/public-web-perf/2014Oct/0117.html
Comment 15 Simon Pieters 2015-01-23 10:00:58 UTC
(In reply to Ilya Grigorik from comment #14)
> I believe this restriction should be removed: 
> 
> a) This policy is not enforced by any browser - i.e. all support <link>
> outside of <head>.

I think this is not a valid argument. It is a consideration to take into account if we want to allow this for other reasons, i.e. "is it backwards compatible?". Authoring conformance requirements are primarily about pointing out authoring mistakes, whether it "works" in browsers or not. There are plenty of things that work in browsers but are nonetheless invalid (e.g. misnested tags).

> b) This policy is not respected by site authors - e.g. ~10% of top 1K Alexa
> sites declare stylesheets in body [2].

I think this is not a valid argument without analyzing how they use it. Are they mistakes? Are they using it deliberatly for better loading performance? If almost all of them use it by mistake, that is an argument for keeping the status quo.

> c) This policy runs counter to the new emerging patterns of using <link>
> outside of <head> - see [3].

I think <link rel=import> is a pretty different beast and shouldn't influence requirements on <link rel=stylesheet>.

> d) This policy restricts browsers from enabling new rendering optimizations.

No it doesn't. We're discussing authoring conformance, it doesn't affect implementations at all. Browsers can optimize this exactly the same regardless. (We could specify how the optimization should work in the spec, but this bug doesn't ask for that.)

> We have real-world experience showing that removing this constraint can help
> deliver faster page rendering and improve user experience (e.g. amazon.com
> is using this [4] to deliver faster rendering to IE users), and Baidu
> experiments [5] with modified Blink engine back this as well.

*This* is a valid argument. Please focus on this part. :-)

> In short, the current constraint does not match what's implemented by
> browsers, how <link> is used by site authors, and restricts both groups from
> developing and deploying new patterns that can improve both the developer
> and user experience (e.g. faster page rendering).
> 
> [1] http://www.w3.org/TR/html5/document-metadata.html#the-link-element
> [2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=27303#c10
> [3]
> https://lists.w3.org/Archives/Public/public-whatwg-archive/2014Nov/0017.html
> [4] https://www.w3.org/Bugs/Public/show_bug.cgi?id=27303#c3
> [5] https://lists.w3.org/Archives/Public/public-web-perf/2014Oct/0117.html
Comment 16 Simon Pieters 2015-01-23 10:30:38 UTC
(In reply to Ilya Grigorik from comment #12)
> Created attachment 1559 [details]
> <link> in <body> (2014-08-15 HA run)

Looking at the first 20, none of them use it for better page loading perf AFAICT. They're not mistakes, either, though, but appear to be how they assembly their pages from several templates or components and don't want to buffer everything just to put a <link> in <head> instead of in <body>.

http://www.yandex.com.tr/
http://www.marketwatch.com/
http://www.olx.in/
http://www.semrush.com/

Doesn't appear to use link in body now.

http://www.ebay.co/
http://www.petdoof.com/
http://www.hubspot.com/
http://www.kaskus.co.id/
http://www.telegraph.co.uk/
http://www.ebay.fr/
http://www.intuit.com/
http://www.alarabiya.net/
http://www.clickbank.com/
http://www.ok.ru/
http://www.one.lv/
http://www.ero-advertising.com/
http://www.4shared.com/
http://www.dreamstime.com/
http://www.bycontext.com/

Doesn't appear to use it for better page perf, more like a stylesheet for a particular component. There is external CSS in <head> also.

http://www.quikr.com/

Doesn't appear to use link in body now. However it does insert a stylesheet <link> with script for an ad.
Comment 17 Ilya Grigorik 2015-01-23 19:13:21 UTC
(In reply to Simon Pieters from comment #15)
> (In reply to Ilya Grigorik from comment #14)
> > I believe this restriction should be removed: 

Re, a & b: fair enough.

> > c) This policy runs counter to the new emerging patterns of using <link>
> > outside of <head> - see [3].
> 
> I think <link rel=import> is a pretty different beast and shouldn't
> influence requirements on <link rel=stylesheet>.

Perhaps. FWIW, current spec makes no such distinction and disallows any <link> with rel attribute outside of <head>. That said, this is probably an entirely different thread/bug.

> > d) This policy restricts browsers from enabling new rendering optimizations.
> 
> No it doesn't. We're discussing authoring conformance, it doesn't affect
> implementations at all. Browsers can optimize this exactly the same
> regardless. (We could specify how the optimization should work in the spec,
> but this bug doesn't ask for that.)

This seems at odds? First we say any <link rel=stylesheet> outside of head is invalid, but then we would document an optimization strategy that allows exactly this? I'm OK if we can manage this, but it feels odd.

> > We have real-world experience showing that removing this constraint can help
> > deliver faster page rendering and improve user experience (e.g. amazon.com
> > is using this [4] to deliver faster rendering to IE users), and Baidu
> > experiments [5] with modified Blink engine back this as well.
> 
> *This* is a valid argument. Please focus on this part. :-)

Ok. The specific behavior and optimization we're interested in is to allow partial page rendering that does not block on the full CSSOM. Specifically, we would like to allow the position of the <link> element in the DOM to inform the UA about which parts of the page may be painted without blocking on remaining CSS that has not yet been downloaded and/or processed. In effect, this behavior would mimic <script> element which does not block rendering of the content above it. This can be an opt-in behavior for sites that choose to leverage this optimization.

---

CSS bytesize for top Alexa sites (desktop and mobile) [1]:
type	median	seventy_fifth	ninetieth	 
desktop	29070	65255	138816	 
mobile	21458	51318	115983

Median CSS size on both desktop and mobile exceeds the initial congestion window, which means that today the browser has to hold painting page content for at least 2 roundtrips. For sites in 75th and 90th percentiles this incurs 4~5+ RTTs, which can easily translate into seconds on a high-RTT connection.

By allowing the above optimization the site can unblock the browser from painting partial page content: we would (a) only need partial HTML and CSS to get useful pixels on the screen, and (b) remove the mandatory multi-RTT penalty of todays CSSOM model. 

[1] https://gist.github.com/igrigorik/3ec49bc83ba9e1876d34
Comment 18 Ian 'Hixie' Hickson 2015-01-25 18:27:37 UTC
> Problem: current spec [1] restricts the use of <link rel> to within the
> <head> element only.

That's not a problem.

A problem or use case is a technology-agnostic statement, like, "pages should be fast to load", followed by a description of why the current technology doesn't allow it, or makes it unnecessarily hard, like "there is no way to provide styles for content in the second half of the page just before it's needed rather than providing it earlier when it isn't needed and would slow down rendering because browsers don't render the content until they have all the style sheets".
Comment 19 Simon Pieters 2015-01-26 07:34:20 UTC
(In reply to Ilya Grigorik from comment #17)
> This seems at odds? First we say any <link rel=stylesheet> outside of head
> is invalid, but then we would document an optimization strategy that allows
> exactly this? I'm OK if we can manage this, but it feels odd.

What I meant was that if we were to specify the optimization, it would affect UAs, but still the UA requirements and authoring requirements are separate. The HTML parser has optimizations specified for things that are invalid, FWIW (e.g. Noah's Ark clause).
Comment 20 Ilya Grigorik 2015-01-27 03:20:57 UTC
(In reply to Ian 'Hixie' Hickson from comment #18)
> 
> A problem or use case is a technology-agnostic statement, like, "pages
> should be fast to load", followed by a description of why the current
> technology doesn't allow it, or makes it unnecessarily hard, like "there is
> no way to provide styles for content in the second half of the page just
> before it's needed rather than providing it earlier when it isn't needed and
> would slow down rendering because browsers don't render the content until
> they have all the style sheets".

I'm good with that formulation (thanks! :)) but with one modification: this shouldn't be limited to just top vs. bottom half of the page. So, something like..

---

Pages should deliver a fast first-render. However, there is no way to provide styles for partial page content just before it is needed. Today the site has to provide all styles ahead of time, which slows down rendering because the browser must first download all styles before rendering any content to the screen. Due to the size of CSS on a typical site (20~110KB) this translates to multiple (2~5+) network roundtrips, which can easily delay rendering for 1s+.

---
Comment 21 Ian 'Hixie' Hickson 2015-01-29 23:02:36 UTC
As far as I can tell, there's already a simple solution for this: <style scoped>. Each <section> can have its own <style scoped> blob that describes the styles for that section, provided literally as that section is reached by the parser. The styles can be inline, minimising latency, or externally using @import.

Using <link rel=stylesheet> for this specific problem would be bad design, since it can affect earlier elements, leading to unstable styling.

<style scoped> works just like <link rel=stylesheet> works in down-level clients, so it has the same backwards-compatibility story. This seems therefore strictly superior.
Comment 22 Simon Pieters 2015-01-30 08:44:32 UTC
(In reply to Ian 'Hixie' Hickson from comment #21)
> As far as I can tell, there's already a simple solution for this: <style
> scoped>. 

See comment 9.
Comment 23 Ilya Grigorik 2015-01-31 04:23:41 UTC
+1 to Simon's comment. 

Also, note that backwards-compat story for scoped styles is pretty bad: browsers that don't support "scoped" keyword will treat the @import as global and will leak all styles into global context, which means that you can't practically rely on the most important feature of "scoped" and are forced to write all stylesheets in such a way that they don't overlap or override each other, at which point the whole "scoped" thing becomes a no-op.
Comment 24 Ilya Grigorik 2015-02-13 00:57:56 UTC
Interesting research from NNG [1] on importance of optimizing "above the fold" content: "What appears at the top of the page vs. what’s hidden will always influence the user experience—regardless of screen size. The average difference in how users treat info above vs. below the fold is 84%".

Enabling developers to make the ATF content visible sooner, which is what we're trying to enable here, would be a nice UX+performance win.


[1] http://www.nngroup.com/articles/page-fold-manifesto/
Comment 25 Ian 'Hixie' Hickson 2015-03-04 19:46:08 UTC
(In reply to Ilya Grigorik from comment #20)
> 
> Pages should deliver a fast first-render. However, there is no way to
> provide styles for partial page content just before it is needed.

Yes there is, <style scoped>.


> Today the
> site has to provide all styles ahead of time, which slows down rendering
> because the browser must first download all styles before rendering any
> content to the screen.

This is false, because <style scoped> lets you do exactly this.


> Due to the size of CSS on a typical site (20~110KB)
> this translates to multiple (2~5+) network roundtrips, which can easily
> delay rendering for 1s+.

All the styles could be inline, or they could be in external cached files, all of which can be fetched by the preparser.


(In reply to Simon Pieters from comment #9)
> I think the argument is that scoped stylesheets is an orthogonal feature.

I don't see why it'd be orthogonal. The feature request is "provide styles for partial page content just before it is needed", which exactly describes what <style scoped> does.

<style scoped> also has many advantages, such as making it not possible for the styles to impact earlier content, which is a key requirement for performance since otherwise you end up restyling earlier content when you get to the later style.


> For some use cases it makes perfect sense to want to use scoped stylesheets,
> but for this use case it is a non-goal and actually makes things harder for
> the author.

Why?


> Consider how authors would typically achieve the desired effect here. First,
> the author would write all of the CSS for the site. Then, a tool would be
> used to determine the minimum subset of the full stylesheet required to
> render the "above the fold", and put the subset in an inline <style> in
> head. Then, the full stylesheet is external and is inserted in a manner that
> does not block rendering, e.g. inserted into <head> with a <script>
> somewhere in <body>, or, as amazon.com does, using <link> somewhere in body.

I would not recommend this design approach. It's not scalable or maintainable.

I would recommend creating the styles for each component of the site separately. If you use this approach, then it is trivial to extract just the styles needed for the below-the-fold part of the site.


> With this workflow, having the full external stylesheet be a scoped
> stylesheet doesn't really work unless you wrote the CSS with that in mind
> from the beginning.

With this workflow, you only have one external style sheet so you need it at the top regardless.


(In reply to Ilya Grigorik from comment #23)
> 
> Also, note that backwards-compat story for scoped styles is pretty bad:
> browsers that don't support "scoped" keyword will treat the @import as
> global and will leak all styles into global context, which means that you
> can't practically rely on the most important feature of "scoped" and are
> forced to write all stylesheets in such a way that they don't overlap or
> override each other, at which point the whole "scoped" thing becomes a no-op.

This is exactly what you'd have to do with a <link rel=stylesheet> as well.


(In reply to Ilya Grigorik from comment #24)
> Interesting research from NNG [1] on importance of optimizing "above the
> fold" content: "What appears at the top of the page vs. what’s hidden will
> always influence the user experience—regardless of screen size. The average
> difference in how users treat info above vs. below the fold is 84%".
> 
> Enabling developers to make the ATF content visible sooner, which is what
> we're trying to enable here, would be a nice UX+performance win.

It's already possible with <style scoped>.
Comment 26 Simon Pieters 2015-03-05 11:50:29 UTC
(In reply to Ian 'Hixie' Hickson from comment #25)
> I don't see why it'd be orthogonal. The feature request is "provide styles
> for partial page content just before it is needed", which exactly describes
> what <style scoped> does.
> 
> <style scoped> also has many advantages, such as making it not possible for
> the styles to impact earlier content, which is a key requirement for
> performance since otherwise you end up restyling earlier content when you
> get to the later style.

Restyling earlier content is a tiny performance cost compared to blocking rendering until external stylesheets have loaded.


> Why?

Because it requires them to change their workflow from whatever they're doing now to fit the constraints of <style scoped>.


> I would not recommend this design approach. It's not scalable or
> maintainable.

It is as scalable and as maintainable as using only external stylesheets, since it's just an automated publication step that doesn't affect how the site is being developed or maintained.

This approach seems to be what at least some perf-savvy Web developers actually use and recommend. See e.g.

http://www.filamentgroup.com/lab/performance-rwd.html "DETERMINING THE INLINE CSS"


> I would recommend creating the styles for each component of the site
> separately. If you use this approach, then it is trivial to extract just the
> styles needed for the below-the-fold part of the site.

I would guess that people do this already, but have an automated publication step that concatenates stylesheets into one file to reduce requests. (This might be unnecessary or counter-productive with HTTP/2.)

I don't follow how creating styles for each component makes any difference to extracting the necessary styles. Extracting the necessary styles is trivial anyway since it's just a script that does it.


> With this workflow, you only have one external style sheet so you need it at
> the top regardless.

The point is that initial rendering should not be blocked on loading it. But maybe other developers use multiple external stylesheets, I don't think it makes much of a difference here.
Comment 27 Ilya Grigorik 2015-03-10 05:30:25 UTC
FWIW, it turns out that both IE and Firefox already treat CSS in body as non-blocking:

http://www.webpagetest.org/video/compare.php?tests=150305_7M_92E-l%3Achrome-r%3A1-c%3A0%2C150305_Y4_91W-l:firefox-r%3A1-c%3A0,150310_JQ_9JW-r:1-c:0-l:ie11&thumbSize=100&ival=500&end=visual

The above behavior goes even further then what we're proposing here. Seems like Blink is trailing the rest here; we should probably just change Blink's default behavior.
Comment 28 Simon Pieters 2015-03-10 16:14:47 UTC
(In reply to Simon Pieters from comment #26)
> > With this workflow, you only have one external style sheet so you need it at
> > the top regardless.
> 
> The point is that initial rendering should not be blocked on loading it. But
> maybe other developers use multiple external stylesheets, I don't think it
> makes much of a difference here.

I suppose when there is a single external stylesheet, the use case could be addressed by adding an attribute to <link> that makes it not block rendering (and not block <script>s from running, either), even if in <head>.
Comment 29 Ilya Grigorik 2015-03-10 17:23:17 UTC
(In reply to Simon Pieters from comment #28)
> I suppose when there is a single external stylesheet, the use case could be
> addressed by adding an attribute to <link> that makes it not block rendering
> (and not block <script>s from running, either), even if in <head>.

Yes, albeit this is hard to reason about as developer.. While the CSS may not block DCL you also have no idea when it will be applied (presumably, whenever the fetch is done). As a result, if you mark your "first" CSS as blocking and only put in the "above the fold" styles, then you run the risk of flashing unstyled content for "below the fold". Whereas with proposal we're iterating on here, the position of the link element would mark as a simple demarcation for which content can be rendered and which should be held back.

P.S. rel=preload [1] will provide the equivalent of above behavior where the CSS fetch does not block rendering; and application can define own execution logic. 

[1] http://w3c.github.io/preload/#early-fetch-and-application-defined-execution
Comment 30 Ian 'Hixie' Hickson 2015-03-30 22:45:43 UTC
(In reply to Simon Pieters from comment #26)
> 
> Restyling earlier content is a tiny performance cost compared to blocking
> rendering until external stylesheets have loaded.

Assuming you are referring to the practice of using <link> in the head, then I agree, but I'm not advocating that so it seems orthogonal to this discussion.


> Because it requires them to change their workflow from whatever they're
> doing now to fit the constraints of <style scoped>.

The constraints are simpler than with <link rel=stylesheet>-in-body. For scoped styles, the constraints are just "put a div around what you're styling". For the link-in-body thing, the constraints are that you have to be really careful to design your style sheet to not affect earlier contents.


> It is as scalable and as maintainable as using only external stylesheets,
> since it's just an automated publication step that doesn't affect how the
> site is being developed or maintained.

It's precisely the automated publication step that I was saying isn't scalable and maintainable. There's no good way to make it work with scripts, for example.


> http://www.filamentgroup.com/lab/performance-rwd.html "DETERMINING THE
> INLINE CSS"

That pretty much makes my case for me, I think. The approach described in that post wouldn't work with anything but the most static of pages and styles.


> I would guess that people do this already, but have an automated publication
> step that concatenates stylesheets into one file to reduce requests.

Then using <style scoped> would be trivial.


> I don't follow how creating styles for each component makes any difference
> to extracting the necessary styles. Extracting the necessary styles is
> trivial anyway since it's just a script that does it.

I don't see how a script can possibly accurately do this. Does it hover over every element? Run every possible script branch?
Comment 31 Simon Pieters 2015-03-31 14:16:44 UTC
(In reply to Ian 'Hixie' Hickson from comment #30)
> Assuming you are referring to the practice of using <link> in the head, then
> I agree, but I'm not advocating that so it seems orthogonal to this
> discussion.

<link> in body.

> The constraints are simpler than with <link rel=stylesheet>-in-body. For
> scoped styles, the constraints are just "put a div around what you're
> styling".

I don't think everyone will find it simpler. If it was simpler then people would be doing that.

> For the link-in-body thing, the constraints are that you have to
> be really careful to design your style sheet to not affect earlier contents.

Yes, but since a script deals with it, you don't have to care at all.

> It's precisely the automated publication step that I was saying isn't
> scalable and maintainable. There's no good way to make it work with scripts,
> for example.

Can you be more specific?
 
> That pretty much makes my case for me, I think. The approach described in
> that post wouldn't work with anything but the most static of pages and
> styles.

Why?

> Then using <style scoped> would be trivial.

Not without modifying the tree to insert <div>s to make <style scroped> allowed. Inserting <div>s might change the result of the other styles and can break scripts; it seems like a bad idea to do that at publication. And people are generally not happy with "unnecessary" <div>s.

> I don't see how a script can possibly accurately do this. Does it hover over
> every element? Run every possible script branch?

Probably not. But the first paint does not need to be perfect for things like hover, it just needs to be good enough to let the user see the page while the rest is loading. But it would be simple to just include all :hover rules in the subset. Scripts is harder but I don't know if anyone has identified that as being an actual problem in practice.
Comment 32 Ian 'Hixie' Hickson 2015-04-07 22:33:01 UTC
(In reply to Simon Pieters from comment #31)
> <link> in body.

Then I'm confused. I don't understand what you're arguing here. Do you mean that <link> in body doesn't block?


> > The constraints are simpler than with <link rel=stylesheet>-in-body. For
> > scoped styles, the constraints are just "put a div around what you're
> > styling".
> 
> I don't think everyone will find it simpler. If it was simpler then people
> would be doing that.

Maybe they don't know about it. If there's something that's not simpler about this then we should find out what it is exactly. Usually in my experience pages already have plenty of <div>s that correspond exactly to blocks of content that you'd be able to style separately.


> > For the link-in-body thing, the constraints are that you have to
> > be really careful to design your style sheet to not affect earlier contents.
> 
> Yes, but since a script deals with it, you don't have to care at all.

Requiring that authors use scripts to use this feature sanely is a non-starter IMHO.


> > It's precisely the automated publication step that I was saying isn't
> > scalable and maintainable. There's no good way to make it work with scripts,
> > for example.
> 
> Can you be more specific?

If you have a <script> that builds a DOM that uses certain rules, there's no way to automatically determine what rules you need to include before the script.


> > That pretty much makes my case for me, I think. The approach described in
> > that post wouldn't work with anything but the most static of pages and
> > styles.
> 
> Why?

Because you can't reliably analyse scripts to figure out what they need.


> > Then using <style scoped> would be trivial.
> 
> Not without modifying the tree to insert <div>s to make <style scroped>
> allowed.

Inserting <div>s is trivial.


> Inserting <div>s might change the result of the other styles and
> can break scripts; it seems like a bad idea to do that at publication.

This is trivially testable. In general, a <div> or other such element already exists in the places where it would make sense to have scoped styles, anyway.


> And people are generally not happy with "unnecessary" <div>s.

I wish that were true, but the Web has pretty resoundly demonstrated that authors have no problem adding <div>s to pages.


> > I don't see how a script can possibly accurately do this. Does it hover over
> > every element? Run every possible script branch?
> 
> Probably not.

Then it doesn't work.
Comment 33 Simon Pieters 2015-04-08 10:06:31 UTC
(In reply to Ian 'Hixie' Hickson from comment #32)
> (In reply to Simon Pieters from comment #31)
> > <link> in body.
> 
> Then I'm confused. I don't understand what you're arguing here. Do you mean
> that <link> in body doesn't block?

...sorry, I meant this: The perf cost of accidentally restyling earlier content using <link> in body is trivial compared to blocking rendering while a <link> in head is loading. This was in response to your statement that <style scoped> making it impossible to restyle earlier content is important for performance. I'm arguing it's not important.

> If you have a <script> that builds a DOM that uses certain rules, there's no
> way to automatically determine what rules you need to include before the
> script.

> Because you can't reliably analyse scripts to figure out what they need.

Yes, OK. I don't know if this is an issue in practice for people using this approach. Maybe things that need to be ready on first paint are built on the server-side, and scripts run after first paint for other things.

> I wish that were true, but the Web has pretty resoundly demonstrated that
> authors have no problem adding <div>s to pages.

It's not a best practice. People also have no problem producing inaccessible pages, but...

> Then it doesn't work.

Well it also doesn't "work" if the user lands on an anchor below the fold, but optimizations are not supposed to be perfect for all edge cases, they're supposed to have a big impact for the common case.
Comment 34 Ian 'Hixie' Hickson 2015-04-09 20:18:49 UTC
> The perf cost of accidentally restyling earlier
> content using <link> in body is trivial compared to blocking rendering while
> a <link> in head is loading.

I agree. But the cases we're comparing aren't <link>-in-head and <link>-in-body. They're <link>-in-body and <style scoped>. The latter has all the benefits of not blocking in head, with all the benefits of being scoped, and the only disadvantage that's been mentioned (which I don't really even view as a disadvantage) is that you have to put it in an element to indicate the scope to which the styles apply.


> This was in response to your statement that
> <style scoped> making it impossible to restyle earlier content is important
> for performance. I'm arguing it's not important.

It's not important compared to blocking in the head, sure. But that doesn't mean it's not important in general. It can easily cost a lost frame.


> > Then it doesn't work.
> 
> Well it also doesn't "work" if the user lands on an anchor below the fold,
> but optimizations are not supposed to be perfect for all edge cases, they're
> supposed to have a big impact for the common case.

As far as I can tell, the big impact is already possible with <style scoped>.
Comment 35 Jake Archibald 2015-05-12 07:16:58 UTC
https://code.google.com/p/chromium/issues/detail?id=481122 - Chromium issue for this
Comment 36 Jake Archibald 2015-05-12 07:40:56 UTC
In terms of CSS loading, there are a couple of use-cases people use https://github.com/filamentgroup/loadCSS for.

1. I wish to load CSS. It shouldn't block, and it should apply as soon as it loads. This is useful for parts of an interface that are an interaction away, or where you consider the partial FOUC to be acceptable (I personally disagree that FOUC is ever acceptable, though).

2. I wish to load CSS. It should block rendering of the elements concerned. This is a little more complicated, but the inlined CSS can make the elements opacity:0, then the loaded css can make it opacity:1, maybe even with a transition. The tricky part is preventing the interface jumping around if the elements load in a particular order. <style scoped> would be a good replacement for this.

3. I wish to load CSS. It should block all subsequent rendering to ensure sections are rendered in order. Neither loadCSS or <style scoped> make this easy, but IE & Firefox already allow <link> to be used for this behaviour, and it's super simple.

Take this:

<style>/* inline styles */</style>
Content
<section class="secondary">
  /* styles for this section, somehow */
  Content
</section>
<section class="tertiary">
  /* styles for this section, somehow */
  Content
</section>

Using loadCSS (or <link async>, if that were a thing), you'd get the sections appearing without all their styles, which may not be what you want. You could use some pretty hacky CSS to work around it, though.

With scoped sheets, the rendering would be blocked for each section, but tertiary may load before secondary, meaning the page would jump around while loading (assuming they're vertically stacked). You could do this:

<style>/* inline styles */</style>
Content
<div>
  <style scoped>/* import */</style>
  <section class="secondary">
    /* styles for this section, somehow */
    Content
  </section>
  <section class="tertiary">
    <style scoped>/* import */</style>
    /* styles for this section, somehow */
    Content
  </section>
</div>

…which would ensure "tertiary" cannot display before "secondary", but this is a hack. You've changed the scoping to accommodate load-order.

Whereas…

<style>/* inline styles */</style>
Content
<section class="secondary">
  <link rel="stylesheet" href="style2">
  Content
</section>
<section class="tertiary">
  <link rel="stylesheet" href="style3">
  Content
</section>

…this works in Firefox & IE today, and it has an acceptable fallback in Safari & Chrome.
Comment 37 Eric Schurman 2015-05-13 18:28:29 UTC
I've been working on performance sensitive complex websites for a long time, and I have to agree with the "allow link in the body" crowd. It also reflects the conscious decisions I've made when building websites for over a decade. 

For fastest customer experience, it's important to flush the chunks of HTML containing the start of the page as soon as possible. It's often trivial to know all the CSS, javascript, and HTML needed for the very start of a page, for example, a site-wide nav and some site wide CSS. 

The content lower in the page may take orders of magnitude longer to calculate on the server, and that content may require different CSS in different scenarios. For example, imagine a search results page that usually just has simple web results, but occasionally has a complex region (like a map or something) shown that has need for a lot of CSS. Or a product detail page which displays very different interfaces for a video game than for an office software product, although the site navigation is the same. Having the ability to link to a large CSS in the body only when needed allows for fast flushing of the page in all scenarios, not blocking rendering of the top of the page, only requesting the large CSS file when needed, the ability to use that file from cache when available, and does this all in a completely cross browser approach - which is hugely important for reducing developer costs. It's typically also trivial to scope these additional CSS files to just the widgets that need them. 

This scenario has come up over and over again in the twenty years I've been building dynamic sites. Although the HTML spec has said it's invalid, we've used it over and over again because every browser has supported it and it's made our customer experience and developer process better.
Comment 38 Domenic Denicola 2016-01-24 05:29:38 UTC
Given how style scoped seems to be on its way out, I think we should do this. I've added the [good first bug] whiteboard label and we'd welcome a pull request at https://github.com/whatwg/html.