Skip to content

Instantly share code, notes, and snippets.

@ChristianPeters
Created August 8, 2011 10:22
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save ChristianPeters/1131536 to your computer and use it in GitHub Desktop.
Save ChristianPeters/1131536 to your computer and use it in GitHub Desktop.
Split CSS files so that there are no more than a given number of selectors in one style sheet. This is a tool to cope with Internet Explorer's limitation of max. 4095 selectors per stylesheet.
require 'rake'
require 'css_splitter'
desc 'split css files'
namespace :css do
task :split do
infile = ENV['infile'] || raise("missing infile")
outdir = ENV['outdir'] || File.dirname(infile)
max_selectors = ENV['max_selectors'] || 4095
CssSplitter.split(infile, outdir, max_selectors)
end
task :count_selectors do
css_file = ENV['css_file'] || raise("missing css file")
CssSplitter.count_selectors(css_file)
end
end
module CssSplitter
def self.split(infile, outdir = File.dirname(infile), max_selectors = 4095)
raise "infile could not be found" unless File.exists? infile
rules = IO.readlines(infile, "}")
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.nil?
# The infile remains the first file
file_id = 1
selectors_count = 0
output = nil
rules.each do |rule|
rule_selectors_count = count_selectors_of_rule rule
selectors_count += rule_selectors_count
# Nothing happens until the selectors limit is reached for the first time
if selectors_count > max_selectors
# Close current file if there is already one
output.close if output
# Prepare next file
file_id += 1
filename = File.join(outdir, File.basename(infile, File.extname(infile)) + "_#{file_id.to_s}" + File.extname(infile))
output = File.new(filename, "w")
output.write charset_statement
# Reset count with current rule count
selectors_count = rule_selectors_count
end
output.write rule if output
end
end
def self.count_selectors(css_file)
raise "file could not be found" unless File.exists? css_file
rules = IO.readlines(css_file, '}')
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.first.nil?
rules.inject(0) {|count, rule| count + count_selectors_of_rule(rule)}.tap do |result|
puts File.basename(css_file) + " contains #{result} selectors."
end
end
def self.count_selectors_of_rule(rule)
rule.partition(/\{/).first.scan(/,/).count.to_i + 1
end
end
@korny
Copy link

korny commented Mar 13, 2012

Automatic splitting for compass.rb:

on_stylesheet_saved do |path|
  CssSplitter.split(path) unless path[/\d+$/]
end

/via @joschka

@ChristianPeters
Copy link
Author

There is a follow up gist with a SprocketsEngine for integration with the Rails Asset pipeline: https://gist.github.com/2398394

@10thfloor
Copy link

Thank you for this, came in very handy.

@kennyma603
Copy link

where to put css_splitter.rake and css_splitter.rb files? would be good if there is some instruction. thanks.

@peschee
Copy link

peschee commented Nov 20, 2012

I'm kind of lost. Any points on where these files should go to integrate with the compass compile / watch or sass build workflow?

@jhilden
Copy link

jhilden commented Jan 12, 2013

FYI: This gist has been turned into a gem here: https://github.com/zweilove/css_splitter

@t22james
Copy link

t22james commented Oct 5, 2014

This does appear to have a bug, in that it will split in the middle of a media query... Discovered this at the top of my second of split files...

@charset "UTF-8";

  /* line 8, ../scss/global/_mixins.scss */
  tr.hidden-lg {
    display: table-row !important;
  }

  /* line 9, ../scss/global/_mixins.scss */
  th.hidden-lg,
  td.hidden-lg {
    display: table-cell !important;
  }
}

Note the hanging bracket which is closing a media query which was opened in the first of the split css files.... Anyone able to factor this case into the snippet?

@todesstoss
Copy link

Hello.
I'm not familiar with ruby, but i tried to fix this tool to suit my needs:
IE have limits not only on selector count but also *.css file size, sometimes fat stylesheets files can throw errors in IE (something like: Out of memory at line XXX.) So we need to split first 4095 selectors in separate file too and include them instead of full css file.
for example:

<!-- ie 10 and above and all other browsers -->
<!--[if gt IE 9]><!-->
<link type="text/css" rel="stylesheet" media="all" href="css/screen.css" />
<!--<![endif]-->
<!-- only ie 9 and below -->
<!--[if lte IE 9]>
  <link type="text/css" rel="stylesheet" media="all" href="css/screen_1.css" />
  <link type="text/css" rel="stylesheet" media="all" href="css/screen_2.css" />
<![endif]-->

If you have same issues check my gist, it works perfect:
https://gist.github.com/todesstoss/1c3ac8310de7714a65e6

Thanks.

@kweij
Copy link

kweij commented May 18, 2015

Hi,

Has anyone overcome the issue of media queries that t22james has ran into? This issue seems like a mayor bug and reason not to use this script to me, but since it's about 4 years old by now, maybe someone has already fixed it.

Thanks in advance

@benlwilliams
Copy link

I'm having same media query split problem.as @t22james

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