Skip to content

Instantly share code, notes, and snippets.

@FMCorz
Last active June 30, 2016 17:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FMCorz/10015057 to your computer and use it in GitHub Desktop.
Save FMCorz/10015057 to your computer and use it in GitHub Desktop.
Chunk CSS into smaller chunks
<?php
function chunk($css, $every = 4095) {
$chunks = array();
$offsets = array();
$offset = 0;
$selectorcount = 0;
$lastvalidoffset = 0;
$lastvalidoffsetselectorcount = 0;
$inrule = 0;
$inmedia = false;
$mediacoming = false;
// Remove the comments. Because it's easier, safer and probably a lot of other good reasons.
$css = preg_replace('#/\*(.*?)\*/#m', '', $css);
$strlen = strlen($css);
// Walk through the CSS content character by character.
for ($i = 1; $i <= $strlen; $i++) {
$char = $css[$i - 1];
$offset = $i;
// Is that a media query that I see coming towards us?
if ($char === '@') {
if (!$inmedia && substr($css, $offset, 5) === 'media') {
$mediacoming = true;
}
}
// So we are entering a rule or a media query...
if ($char === '{') {
if ($mediacoming) {
$inmedia = true;
$mediacoming = false;
} else {
$inrule++;
$selectorcount++;
}
}
// Let's count the number of selectors, but only if we are not in a rule as they
// can contain commas too.
if (!$inrule && $char === ',') {
$selectorcount++;
}
// We reached the end of something.
if ($char === '}') {
// Oh, we are in a media query.
if ($inmedia) {
if (!$inrule) {
// This is the end of the media query, let's note that it is safe to split here.
$inmedia = false;
$lastvalidoffset = $offset;
$lastvalidoffsetselectorcount = $selectorcount;
} else {
// We were in a rule, in the media query.
$inrule--;
}
} else {
$inrule--;
if (!$inrule) {
// We exited the rule, it is safe to split here.
$lastvalidoffset = $offset;
$lastvalidoffsetselectorcount = $selectorcount;
}
}
}
// Alright, this is splitting time...
if ($selectorcount >= $every) {
if (!$lastvalidoffset) {
// We must have reached more selectors into one set than we were allowed. That means that either
// the chunk size value is too small, or that we have a gigantic group of selectors, or that a media
// query contains more selectors than the chunk size. We have to ignore this because we do not
// support split inside a group of selectors or media query.
debugging('Could not find a safe place to split at ' . $offset . ', ignoring...', DEBUG_DEVELOPER);
} else {
// We identify the offset to split at and reset the number of selectors found from there.
$offsets[] = $lastvalidoffset;
$selectorcount = $selectorcount - $lastvalidoffsetselectorcount;
$lastvalidoffset = 0;
}
}
}
// Now that we have got the offets, we can chunk the CSS.
$offsetcount = count($offsets);
foreach ($offsets as $key => $index) {
$start = 0;
if ($key > 0) {
$start = $offsets[$key - 1];
}
// From somewhere up to the offset.
$chunks[] = substr($css, $start, $index - $start);
}
// Add the last chunk, from the last offset to the end of the string.
$chunks[] = substr($css, end($offsets));
return $chunks;
}
@andrewnicols
Copy link

Might be worth adding an:

if (count($chunks)) > $somelimit) {
    debugging('Warning, you have too much CSS for IE to handle');
}

too to warn about >= 32 import limit.

@andrewnicols
Copy link

Other useful snippets:
http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/10164546.aspx
http://support.microsoft.com/kb/262161
A sheet may contain up to 4095 rules A sheet may @import up to 31 sheets @import nesting supports up to 4 levels deep

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