Rails Asset Pipeline Solution for Ie 4096 Selector/Stylesheet Limit

Rails asset pipeline solution for IE 4096 selector/stylesheet limit

We have an automated (albeit somehow awkward) solution working in production for a Rails 3.1 app with asset pipeline in place. Ryan already referenced the solution in his question but I try to come up with a more comprehensive answer.

The asset pipeline pipes an asset through different Sprocket engines.

So you might have e.g. a ie.css.sass.erb that runs through the ERB Sprocket engine and then passed to the Sass Sprocket engine etc. But it is always one file in and one file out.

In this special problem, we would like to have 1 inbound file and n outbound files.
We have not found a way to make this possible with sprockets. But we found a workaround:

Provide an ie.css.sass that includes the complete stylesheet and a ie_portion2.css.sass.split2 that just imports the complete ie.css file:

//= include 'ie.css'

For the split2 file extension, we register a Sprockets Engine:

require 'css_splitter'
Rails.application.assets.register_engine '.split2', CssSplitter::SprocketsEngine

When evaluating assets with the split2 extension, we pass its content to the CssSplitter and instruct it to extract the part 2 (> 4095 selectors):

require 'tilt'
module CssSplitter

class SprocketsEngine < Tilt::Template
def self.engine_initialized?
true
end

def prepare
end

def evaluate(scope, locals, &block)
part = scope.pathname.extname =~ /(\d+)$/ && $1 || 0
CssSplitter.split_string data, part.to_i
end
end
end

This would also work for further parts (split3, ...).

The CSS Splitter recognizes valid places where stylesheets can be split into parts with less than 4096 selectors and returns the requested part.

The result is a ie_portion2.css which you have to link in the head and precompile separately.

I hope my revised CSS Splitter Gist is complete enough to employ the solution.

Update:

The CssSplitter mentionend above has been release as a gem now: https://github.com/zweilove/css_splitter

Sass: Dealing with the IE 4095 selectors per stylesheet restriction

Mixins are usable across multiple files. However, it is logically not possible that @extend may work with multiple files. It is the purpose of this directive to result in a single rule
(which should not be duplicated across multiple files). Therefore, I cannot split up files.

Thus, I implemented a splitter: https://gist.github.com/1131536

After these two commits have found their way into Sass and Compass, you can use the following hook in your Rails config/compass.rb in order to automatically create the additional stylesheets for IE:

on_stylesheet_saved do |filename|
if File.exists?(filename)
CssSplitter.split(filename)
end
end

Update:

The CssSplitter mentionend above has been release as a gem: https://github.com/zweilove/css_splitter

Does IE 8 have a limit on number of stylesheets per page?

Yes, IE8 (and even IE9 apparently) limit the number of style sheets to 31 per page.

Telerik has an article and test page which demonstrate the issue. According to comments in the same article, the 4096 rules per file limitation has been marked as Won't Fix in Microsoft Connect but I've been unable to verify that.

Conditionally avoid using Application.css in HAML layout with Asset Pipeline? (IE9 4096)

The comments in this SO Question led me to the answer. I used :plain to encapsulate the [if IE 9]. That prevented whatever weird processing was injecting application.css again. While it fixes it for now, I don't consider this maintainable in the long run.

:plain
<!--[if IE 9]>
= stylesheet_link_tag "stylesheet1.css", :media => "all"
= stylesheet_link_tag "stylesheet2.css", :media => "all"
= stylesheet_link_tag "stylesheet3.css", :media => "all"
...many more...
<![endif]-->

Internet Explorer's CSS rules limits

Referring the following from Microsoft:

  • Stylesheet Limits in Internet Explorer
  • KB - A webpage that uses CSS styles does not render correctly in Internet Explorer

The rules for IE9 are:

  • A sheet may contain up to 4095 selectors (Demo)
  • A sheet may @import up to 31 sheets
  • @import nesting supports up to 4 levels deep

The rules for IE10 are:

  • A sheet may contain up to 65534 selectors
  • A sheet may @import up to 4095 sheets
  • @import nesting supports up to 4095 levels deep

Testing the 4095 rule by sheet limit

By way of confirmation, I've created a gist with 3 files. One HTML, and two CSS files.

  • The first file contains 4096 selectors and means that its final selector doesn't get read in.
  • The second file (4095.css) has one less selector, and gets read in, and works perfectly in IE (even though its already read another 4095 selectors from the previous file.

Rails 4 x SASS: load specific stylesheet in Internet Explorer

For all IE versions:

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<!-- Calling application stylesheet file only once -->
<!--[if IE]>
<%= stylesheet_link_tag 'ie', media: 'all', 'data-turbolinks-track' => true %>
<![endif]-->

And instead of specifying ie.css, you can change it to:

Rails.application.config.assets.precompile += %w( *.css )

So that you don't have to add each and every file separately.
What I generally use in my projects:

Rails.application.config.assets.precompile += %w( *.js *.css *.png *.jpg *.jpeg )

Rails asset pipleline: compile to multiple stylesheets

To tell rails about additional files you wish to have precompiled, you can add them to the config.assets.precompile setting.

config.assets.precompile += ["other_application.css"]

You only see application.css in your HTML because that's the only file you're including

<%= stylesheet_link_tag "application" %>

If you have some custom.css.scss in your apps/assets/stylesheets directory, it will be compiled just like application.css.

For example, I might have

- _common.css.scss
- application.css.erb.scss
- other_application.css.erb.scss

in app/assets/stylesheets. In the top of the non-partial files I will put

@import "common";

to include _common.css.scss. I can now reference either stylesheet independent of one another in a layout.

<%= stylesheet_link_tag "application" %>
# or
<%= stylesheet_link_tag "other_application" %>


Related Topics



Leave a reply



Submit