Automate IE8 print CSS from media query

(Tim Davison) #1


UPDATE: Actually found out what the root issue was, so the fix below is no longer necessary. See my comment further down.  


Adaptations to other purposes may prove useful, but not for the case I originally wrote it.  



I need to support older browsers, more specifically IE8 (don't go there, I know, you are preaching to the converted, but you don't pay my bills).

  I recently found and started using Respond.js with great success, so it's become one of the standard features of my builds.  For those who don't know, Respond.js parses your CSS3 media queries and generates IE8-equivalent rules and implements them.  Suddenly your site is responsive in IE8 as well, like magic, with no more special IE8 classes or anything.  I am suitably impressed, and recommend looking it up.
I recently came across a related issue.  I was to ensure print stylesheets looked and behaved correctly in IE8 as well (I know, right. Might as well go grab the chisel and clay tablets).  Respond.js doesn't seem to do anything about @media print, just min and max width.  
Previously my implementation would have been copy+paste the @media print rules into an IE8-only CSS file and embedded within conditional comments.  The software engineer in me cries every time I do this - I have duplicated CSS, rather than a single location, and my once beautiful source code is now scarred with abominations (conditional comments).
Inspired and following on from Respond.js, I've developed this fix (hack may be more accurate) to embed print styles directly into the page derived from your CSS3 print media rules but adjusted to work for IE8.
Step 1: Create Design Areas
Add the following to your design area (I've set print="no" - I prefer doing this and then using a print command later, but do as you prefer):

<MySource_AREA id_name="ie8" design_area="nest_content" cache="1" print="no"><MySource_SET name="type_codes" value="page" /></MySource_AREA>
<MySource_AREA id_name="ie8_detect" design_area="show_if" print="no">
  <MySource_SET name="condition" value="user_agent"/>
  <MySource_SET name="condition_user_agent" value="MSIE [5-8]" />
  <MySource_THEN><MySource_PRINT id_name="ie8" /></MySource_THEN>

This Show-If area means the nested content area will only be printed if user agent string detects IE8 or lower.
Step 2: Update your CSS with delimiters
There's 3 requirements:

  • All print media queries must be in a single file.
  • There can only be one @media print declaration.
  • You have to add a CSS comment as a delimiter just before the closing brace of the print styles.  E.g.
@media print {               <-- this line is used to indicate start writing

/* end media print */ <-- this line needs to be added, must be before the closing brace

Step 3: Create IE8 Std Page and point design customisation to it
Create a Std Page asset, raw HTML etc, and set it's contents to this code (it's a keyword replacement - it may wrap in the forum, but should all be on one line)

%globals_asset_file_contents:XXX^replace:(?s).*(@media print {):<style media="print">^replace:(\/\* end media print \*\/)(?s).*:</style>%

XXX should be the asset ID of your main.css file (or equivalent).  Point the design customisation for area 'ie8' above to this page (you may have called the design area something different).
A little explanation, this keyword gets the contents of the CSS file, strips out everything except the print query rules, and wraps it up in a <style> tag.
The result:
Once you have done the above the page should render as normal.  In modern browsers there should be no difference - your raw code remains beautiful, the show-if ensures this rubbish doesn't get printed..  But in an older browser, the following should have been injected:

<style media="print">
  ...                              <-- these lines should be the same as in your print media query

So now you have IE8 print rules being automatically generated from CSS3 media queries.  Working example is
Q: I'm using HTML5, and the print styles are not working.
A: Make sure you have the print shiv (similar to html5shiv).  I use Modernizr - but you need to specifically add print shiv to the custom build, it's not selected by default.
Q: CSS inlining, really?  External stylesheets get better performance.
A: There's a big difference between inline styles and inlining, and who cares?  We're talking about IE8.  There's no mobile devices that I'm aware that use it, and most IE8 users are within a corporate network, so speed/bandwidth is a relative non-issue for the couple of extra lines required (I find my print CSS is usually less than 30 lines)
Q: But User-Agent string can be easily spoofed.
A: Again, who cares?  If someone has gone to the effort to identify themselves with a different user-agent string then give them what they've asked for.
Q: Your example site is using a lot of conditional comments now, are you a hypocrite?
A: Now that I have these 2 parts working (responsiveness from Respond.js and printing from the above approach) I will be refactoring all those conditionals out.  But I plan to savour it, perhaps throw a something like a divorce party.

(Cgrist) #2

How does this work with caching though, presumably this would only work correctly for sites not using Squid (or some other proxy cache), due to the server side user agent condition? To get around that, I think you'd need to display the print styles all the time? 

(Tim Davison) #3

It's a good question, one that I can't answer easily.  Perhaps one of the Squiz experts can advise?


I know for Show-If's, if my browser cache for the page has not expired I need to refresh to pick up a change (e.g. login before and after).  Personally I set browser cache pretty low (a couple of minutes) but leave Matrix cache high (24 hrs).  Presumably for majority cases it's relatively unlikely the same session would change the user agent within a few minutes (if at all).


But it is a good broader question, how does Matrix cache handle show-if's?  Surely it's not re-building the Matrix cache each time.

(Tim Davison) #4

After doing a little more testing I've found that my solution, beautiful and compelling as it may seem, is totally unnecessary.  It seems the Respond.js library actually is creating a print stylesheet after all.  I had fallen victim to the problem of not having the html5 printshiv.  Basically, IE8 refusing to render any styles against any of the new HTML elements.  


So, yeah.  Whole lot of work for nothing (although solving the problem is most of the fun).


In summary, I recommend using Respond.js to give you quick and easy responsive designs and print.